R/mplus.R

Defines functions zmatch logistic lin estimate_mode mplus.get.file.dataset estimate_mode mplus.get.group.dataset mplus.check.dataset.attribute mplus.get.dataset.attribute mplus.check.group.attribute mplus.get.group.attribute mplus.compute.timeseries.acf mplus.list.timeseries.idnums mplus.list.timeseries.variables mplus.get.timeseries.data mplus.plot.timeseries.observed mplus.get.eigenvalues mplus.plot.eigenvalues mplus.plot.bootstrap.distribution mplus.get.bootstrap.point.estimate mplus.get.bootstrap.distribution mplus.list.bootstrap.parameters mplus.plot.discrete.survival.kaplanmeier.vs.baseline mplus.plot.discrete.survival.baseline mplus.plot.discrete.survival.kaplanmeier mplus.get.discrete.survival.basehazard.values mplus.get.discrete.survival.baseline.values mplus.get.discrete.survival.kaplanmeier.values mplus.list.discrete.survival.variables mplus.plot.survival.sample.vs.estimated.logcumulative mplus.plot.survival.kaplanmeier.vs.baseline mplus.plot.survival.estimated.logcumulative mplus.plot.survival.sample.logcumulative mplus.plot.survival.basehazard mplus.plot.survival.baseline mplus.plot.survival.kaplanmeier mplus.get.survival.basehazard.values mplus.compute.survival.estimated.logcumulative.values mplus.get.survival.baseline.values mplus.compute.survival.sample.logcumulative.values mplus.get.survival.kaplanmeier.values mplus.list.survival.variables mplus.plot.irt.tic mplus.plot.irt.iic mplus.plot.irt.icc mplus.compute.irt.iic mplus.compute.irt.icc mplus.list.irt.xvariables mplus.list.irt.variables mplus.list.sensitivity.zvalues mplus.plot.sensitivity mplus.get.sensitivity.xvalues mplus.get.sensitivity.upperci mplus.get.sensitivity.lowerci mplus.get.sensitivity.estimates mplus.list.sensitivity.labels mplus.plot.moderation mplus.get.moderation.xvalues mplus.get.moderation.upperci mplus.get.moderation.lowerci mplus.get.moderation.estimates mplus.list.moderation.labels mplus.plot.loop mplus.get.loop.xvalues mplus.get.loop.upperci mplus.get.loop.lowerci mplus.get.loop.estimates mplus.list.loop.labels mplus.plot.bayesian.plausible.distribution mplus.get.bayesian.plausible.data mplus.list.bayesian.plausible.labels mplus.plot.bayesian.predictive.distribution mplus.plot.bayesian.predictive.scatterplot mplus.get.bayesian.predictive.pvalue_type mplus.get.bayesian.predictive.pvalue mplus.get.bayesian.predictive.upperci mplus.get.bayesian.predictive.lowerci mplus.get.bayesian.predictive.replicated mplus.get.bayesian.predictive.observed mplus.list.bayesian.predictive.labels mplus.plot.bayesian.autocorrelation mplus.plot.bayesian.prior.distribution mplus.plot.bayesian.distribution mplus.plot.bayesian.traceplot mplus.get.bayesian.autocorrelation mplus.get.bayesian.prior.parameter.data mplus.get.bayesian.parameter.data mplus.list.bayesian.parameters mplus.plot.qqnorm mplus.plot.densityplot mplus.plot.histogram mplus.plot.scatterplot mplus.list.cluster.idnums mplus.get.data mplus.list.variables mplus.get.sample_proportions mplus.get.estimated_probabilities mplus.get.time_scores mplus.get.estimated_medians mplus.get.estimated_modes mplus.get.sample_means mplus.get.estimated_means mplus.plot.sample_proportions_and_estimated_probabilities mplus.plot.sample_proportions mplus.plot.estimated_probabilities mplus.plot.sample_and_estimated_means mplus.plot.sample_means mplus.plot.estimated_medians mplus.plot.estimated_modes mplus.plot.estimated_means mplus.list.processes mplus.load mplus.view.plots

# R scripts for extracting and plotting data stored in Mplus graphic
# information in GH5 files.  Uses the rhdf5 package for loading the
# the GH5 file.
#
# Version history:
# 2013-09-13 File Version 3 for Mplus Version 7.3
# 2014-04-30 Fix for sample and estimated means.
# 2014-10-07 Fix IRT ICC and IIC functions, turning properties into integers
# 2014-10-08 Add functions for Discrete survival curves
# 2014-11-20 Fix estimated probabilities function, turning categories into integers.
# 2014-11-21 Add legend to plot of estimated probabilities.
# 2015-03-30 Fix plot for factors
# 2015-06-01 Fix for case-sensitivity on loop label for mplus.get.loop.estimates.
#            Fix estimated probablities and sample propotions functions, turning categories into integers
#                and using model_group_labels instead of generic "Class" in legend.
# 2015-06-09 Add option for mplus.plot.loop to plot multiple labels.
# 2015-09-09 Fix mplus.get.bayesian.autocorrelation and mplus.plot.bayesian.autocorrelation - dimension
#            of parameter was incorrect.  Fix mplus.plot.bayesian.distribution for alignment when ndist
#            is the same as the number of iterations.
# 2015-10-30 Fix mplus.get.sample_proportions and mplus.get.estimated_probabilities when nominal variable
#            present which throws off count of categories for the categorical variables.
# 2015-11-16 Add functions for the new plots that are added in Version 7.4:
#            moderation, sensitivity, bootstrap distribution
# 2016-10-28 Add mplus.plot.qqnorm for normal QQ plots
# 2016-11-03 mplus.get.data should set all 999 to NA for missing values
# 2017-05-22 Add mplus.plot.eigenvalues and mplus.get.eigenvalues
# 2017-06-15 Add option for plotting sample proportions and estimated probabilities for all
#            categories of a single variable to mplus.plot.sample_proportions and mplus.plot.estimated_probabilities.
#            Also add mplus.plot.sample_proportions_and_estimated_probabilities for plotting both.
# 2017-08-09 Fix loop plots for multiple labels in mplus.plot.loop.  Also add more arguments for customizations.
# 2017-10-17 Fix mplus.plot.irt.icc and mplus.plot.irt.iic getting mean of factor in other groups.
# 2021-01-13 Fix functions for bayesian/predictive since attributes changed to datasets.
#
# Written by: Thuy Nguyen
#             Muthen & Muthen
#
# Reference:
#
# Bernd Fischer and Gregoire Pau (). rhdf5: HDF5 interface to R. R
# package version 2.4.0.
#

if (require(rhdf5,quietly=TRUE)) {
	print("Loaded rhdf5 package")
} else {
	print("trying to install rhdf5 from bioconductor.org")
	source("https://bioconductor.org/biocLite.R")
	biocLite("rhdf5")
	if (require(rhdf5)) {
		print("Loaded missing rhdf5 package ")
	} else {
		stop("could not install rhdf5")
	}
}

##########################################################################
#
# mplus.view.plots - loads the file and lists all available plots
#
# arguments:
#    file - the quoted name of an existing GH5 file
#
# eg. mplus.view.plots('ex.gh5')
#
mplus.view.plots <- function(file) {
	mplus.load(file)
}


##########################################################################
#
# mplus.load - loads the file and lists all available plots
#
# arguments:
#    file - the quoted name of an existing GH5 file
#
# eg. mplus.load('ex.gh5')
#
mplus.load <- function(file) {

	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		cat(cstr)
	}

	#H5close()
	gh5 <- h5dump(file, load=TRUE)

	cat(c("\nPlot functions:\n"))

	if ("efa" %in% names(gh5)) {
		if (exists("mplus.plot.eigenvalues",mode="function")) {
			cat(c(" - mplus.plot.eigenvalues('"),file,"')\n",sep="")
		}
	}

	if ("individual_data" %in% names(gh5)) {
		if (exists("mplus.plot.histogram",mode="function")) {
			cat(c(" - mplus.plot.histogram('"),file,"',variable,bins)\n",sep="")
		}
		if (exists("mplus.plot.densityplot",mode="function")) {
			cat(c(" - mplus.plot.densityplot('"),file,"',variable,bins)\n",sep="")
		}
		if (exists("mplus.plot.qqnorm",mode="function")) {
			cat(c(" - mplus.plot.qqnorm('"),file,"',variable)\n",sep="")
		}
		if (exists("mplus.plot.scatterplot",mode="function")) {
			cat(c(" - mplus.plot.scatterplot('"),file,"',xvar,yvar)\n",sep="")
		}

		if (mplus.check.group.attribute(file,"individual_data","timeseries")) {
			if (exists("mplus.plot.timeseries.observed",mode="function")) {
				if (mplus.check.group.attribute(file,'individual_data','cluster')) {
					cat(c(" - mplus.plot.timeseries.observed('"),file,"',variable,idnum)\n",sep="")
				} else {
					cat(c(" - mplus.plot.timeseries.observed('"),file,"',variable)\n",sep="")
				}
			}
		}
	}

	if ("process_data" %in% names(gh5) && "means_and_variances_data" %in% names(gh5)) {
		np <- length(attr(gh5$process_data,"names"))
		for (i in c(1:np)) {
			cstr <- paste(c("process"), as.character(i), sep="")
			proc <- gh5$process_data[[cstr]]

			# Replace the line below with series of low-level function calls
			cstr2 <- paste(c("process_data"),"/",cstr,"", sep="")
			prop <- mplus.get.group.attribute(file, cstr2, 'properties')

			values <- attr(gh5$means_and_variances_data,"names")
			series_type <- as.integer(prop[1])

			if (series_type == 1) {
				sm_ind <- pmatch("y_observed_means",values,nomatch=0)
				if (sm_ind > 0 && exists("mplus.plot.sample_means",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.sample_means('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("y_estimated_means",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.plot.estimated_means",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.estimated_means('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				if (sm_ind>0 && em_ind>0 && exists("mplus.plot.sample_and_estimated_means",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.sample_and_estimated_means('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("y_estimated_modes",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.plot.estimated_modes",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.estimated_modes('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("y_estimated_medians",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.plot.estimated_medians",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.estimated_medians('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}
			} else if (series_type == 2) {
				em_ind <- pmatch("latent_estimated_means",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.plot.estimated_means",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.estimated_means('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("latent_estimated_modes",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.plot.estimated_modes",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.estimated_modes('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("latent_estimated_medians",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.plot.estimated_medians",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.estimated_medians('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}
			} else if (series_type == 3) {
				em_ind <- pmatch("observed_probs",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.plot.sample_proportions",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.sample_proportions('"),file,"','",cstr,"',cat1,cat2)\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("estimated_probs",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.plot.estimated_probabilities",mode="function")) {
					cstr2 <- paste(c(" - mplus.plot.estimated_probabilities('"),file,"','",cstr,"',cat1,cat2)\n",sep="")
					cat(cstr2)
				}
			} else {
				cstr2 <- paste(c("'"),cstr,"' has unknown series type.\n")
				cat(cstr2)
			}
		}
	}

	if ("loop_data" %in% names(gh5)) {
		if (exists("mplus.list.loop.labels",mode="function")) {
			cat(c(" - mplus.list.loop.labels('"),file,"')\n",sep="")
		}
		if (exists("mplus.plot.loop",mode="function")) {
			cat(c(" - mplus.plot.loop('"),file,"',label,showgrid)\n",sep="")
		}
	}

	if ("moderation_data" %in% names(gh5)) {
		if (exists("mplus.list.moderation.labels",mode="function")) {
			cat(c(" - mplus.list.moderation.labels('"),file,"')\n",sep="")
		}
		if (exists("mplus.plot.moderation",mode="function")) {
			cat(c(" - mplus.plot.moderation('"),file,"',label,group,allgroups,showgrid,lloc)\n",sep="")
		}
	}

	if ("sensitivity_data" %in% names(gh5)) {
		if (exists("mplus.list.sensitivity.labels",mode="function")) {
			cat(c(" - mplus.list.sensitivity.labels('"),file,"')\n",sep="")
		}
		if (exists("mplus.plot.sensitivity",mode="function")) {
			cat(c(" - mplus.plot.sensitivity('"),file,"',label,zvalue,group,allgroups,showgrid,lloc,zdecimals)\n",sep="")
		}
	}
	
	if ("irt_data" %in% names(gh5)) {
		if (exists("mplus.list.irt.variables",mode="function")) {
			cat(c(" - mplus.list.irt.variables('"),file,"')\n",sep="")
		}
		if (exists("mplus.list.irt.xvariables",mode="function")) {
			cat(c(" - mplus.list.irt.xvariables('"),file,"')\n",sep="")
		}
		if (exists("mplus.plot.irt.icc",mode="function")) {
			cat(c(" - mplus.plot.irt.icc('"),file,"',group,xvar,uvar,cat,cat2,covariates,xrange,xstep,lloc)\n",sep="")
		}
		if (exists("mplus.plot.irt.iic",mode="function")) {
			cat(c(" - mplus.plot.irt.iic('"),file,"',group,xvar,uvar,covariates,xrange,xstep,lloc)\n",sep="")
		}
		if (exists("mplus.plot.irt.tic",mode="function")) {
			cat(c(" - mplus.plot.irt.tic('"),file,"',group,xvar,uvar,covariates,xrange,xstep)\n",sep="")
		}
	}

	if ("survival_data" %in% names(gh5)) {
		if (exists("mplus.plot.survival.kaplanmeier",mode="function")) {
			cat(c(" - mplus.plot.survival.kaplanmeier('"),file,"',survvar,classnum)\n",sep="")
		}
		if (exists("mplus.plot.survival.baseline",mode="function")) {
			cat(c(" - mplus.plot.survival.baseline('"),file,"',survvar,classnum)\n",sep="")
		}
		if (exists("mplus.plot.survival.basehazard",mode="function")) {
			cat(c(" - mplus.plot.survival.basehazard('"),file,"',survvar,classnum)\n",sep="")
		}
		if (exists("mplus.plot.survival.sample.logcumulative",mode="function")) {
			cat(c(" - mplus.plot.survival.sample.logcumulative('"),file,"',survvar,classnum)\n",sep="")
		}
		if (exists("mplus.plot.survival.estimated.logcumulative",mode="function")) {
			cat(c(" - mplus.plot.survival.estimated.logcumulative('"),file,"',survar,classnum)\n",sep="")
		}
		if (exists("mplus.plot.survival.kaplanmeier.vs.baseline",mode="function")) {
			cat(c(" - mplus.plot.survival.kaplanmeier.vs.baseline('"),file,"',survvar,classnum)\n",sep="")
		}
		if (exists("mplus.plot.survival.sample.vs.estimated.logcumulative",mode="function")) {
			cat(c(" - mplus.plot.survival.sample.vs.estimated.logcumulative('"),file,"',survvar,classnum)\n",sep="")
		}
	}

	if ("discrete_survival_data" %in% names(gh5)) {
		if (exists("mplus.plot.discrete.survival.kaplanmeier",mode="function")) {
			cat(c(" - mplus.plot.discrete.survival.kaplanmeier('"),file,"',survvar,classnum)\n",sep="")
		}
		if (exists("mplus.plot.discrete.survival.baseline",mode="function")) {
			cat(c(" - mplus.plot.discrete.survival.baseline('"),file,"',survvar,classnum)\n",sep="")
		}
		if (exists("mplus.plot.discrete.survival.kaplanmeier.vs.baseline",mode="function")) {
			cat(c(" - mplus.plot.discrete.survival.kaplanmeier.vs.baseline('"),file,"',survvar,classnum)\n",sep="")
		}
	}

	if ("bootstrap_data" %in% names(gh5)) {
		if ("parameters" %in% names(gh5$bootstrap_data)) {
			if (exists("mplus.list.bootstrap.parameters",mode="function")) {
				cat(c(" - mplus.list.bootstrap.parameters('"),file,"')\n",sep="")
			}
			if (exists("mplus.plot.bootstrap.distribution",mode="function")) {
				cat(c(" - mplus.plot.bootstrap.distribution('"),file,"',parameter,bins)\n",sep="")
			}
		}
	}

	if ("bayesian_data" %in% names(gh5)) {
		if ("parameters_autocorr" %in% names(gh5$bayesian_data)) {
			if ("parameters" %in% names(gh5$bayesian_data$parameters_autocorr)) {
				if (exists("mplus.list.bayesian.parameters",mode="function")) {
					cat(c(" - mplus.list.bayesian.parameters('"),file,"',parameter)\n",sep="")
				}
				if (exists("mplus.plot.bayesian.traceplot",mode="function")) {
					cat(c(" - mplus.plot.bayesian.traceplot('"),file,"',parameter)\n",sep="")
				}
				if (exists("mplus.plot.bayesian.distribution",mode="function")) {
					cat(c(" - mplus.plot.bayesian.distribution('"),file,"',parameter,bins)\n",sep="")
				}
			}
			if ("priors" %in% names(gh5$bayesian_data$parameters_autocorr)) {
				if (exists("mplus.plot.bayesian.prior.distribution",mode="function")) {
					cat(c(" - mplus.plot.bayesian.prior.distribution('"),file,"',parameter,bins)\n",sep="")
				}
			}
			if ("autocorrelation" %in% names(gh5$bayesian_data$parameters_autocorr)) {
				if (exists("mplus.plot.bayesian.autocorrelation",mode="function")) {
					cat(c(" - mplus.plot.bayesian.autocorrelation('"),file,"',parameter,chain)\n",sep="")
				}
			}
		}
		if ("predictive" %in% names(gh5$bayesian_data)) {
			if (exists("mplus.list.bayesian.predictive.labels",mode="function")) {
				cat(c(" - mplus.list.bayesian.predictive.labels('"),file,"')\n",sep="")
			}
			if ("observed" %in% names(gh5$bayesian_data$predictive) && "replicated" %in% names(gh5$bayesian_data$predictive)) {
				if (exists("mplus.plot.bayesian.predictive.scatterplot",mode="function")) {
					cat(c(" - mplus.plot.bayesian.predictive.scatterplot('"),file,"',plabel)\n",sep="")
				}
				if (exists("mplus.plot.bayesian.predictive.distribution",mode="function")) {
					cat(c(" - mplus.plot.bayesian.predictive.distribution('"),file,"',plabel,bins)\n",sep="")
				}
			}
		}
		if ("plausible" %in% names(gh5$bayesian_data)) {
			if (exists("mplus.list.bayesian.plausible.labels",mode="function")) {
				cat(c(" - mplus.list.bayesian.plausible.labels('"),file,"')\n",sep="")
			}
			if (exists("mplus.plot.bayesian.plausible.distribution",mode="function")) {
				cat(c(" - mplus.plot.bayesian.plausible.distribution('"),file,"',plauslabel,obs,bins)\n",sep="")
			}
		}
	}

	cat(c("\nPlot data extraction functions:\n"))

	if ("efa" %in% names(gh5)) {
		if (exists("mplus.get.eigenvalues",mode="function")) {
			cat(c(" - mplus.get.eigenvalues('"),file,"')\n",sep="")
		}
	}

	if ("individual_data" %in% names(gh5)) {
		if (exists("mplus.list.variables",mode="function")) {
			cat(c(" - mplus.list.variables('"),file,"')\n",sep="")
		}
		if (exists("mplus.get.data",mode="function")) {
			cat(c(" - mplus.get.data('"),file,"',variable)\n",sep="")
		}
		if (mplus.check.group.attribute(file,"individual_data","timeseries")) {
			if (exists("mplus.list.timeseries.variables",mode="function")) {
				cat(c(" - mplus.list.timeseries.variables('"),file,"')\n",sep="")
			}
			if (exists("mplus.get.timeseries.data",mode="function")) {
				if (mplus.check.group.attribute(file,'individual_data','cluster')) {
					cat(c(" - mplus.get.timeseries.data('"),file,"',variable,idnum)\n",sep="")
				} else {
					cat(c(" - mplus.get.timeseries.data('"),file,"',variable)\n",sep="")
				}
			}
		}
	}

	if ("process_data" %in% names(gh5)) {
		if (exists("mplus.list.processes",mode="function")) {
			cat(c(" - mplus.list.processes('"),file,"')\n",sep="")
		}
	}

	if ("loop_data" %in% names(gh5)) {
		if (exists("mplus.get.loop.estimates",mode="function")) {
			cat(c(" - mplus.get.loop.estimates('"),file,"',label)\n",sep="")
		}
		if (exists("mplus.get.loop.lowerci",mode="function")) {
			cat(c(" - mplus.get.loop.lowerci('"),file,"',label)\n",sep="")
		}
		if (exists("mplus.get.loop.upperci",mode="function")) {
			cat(c(" - mplus.get.loop.upperci('"),file,"',label)\n",sep="")
		}
		if (exists("mplus.get.loop.xvalues",mode="function")) {
			cat(c(" - mplus.get.loop.xvalues('"),file,"')\n",sep="")
		}
	}

	if ("moderation_data" %in% names(gh5)) {
		if (exists("mplus.get.moderation.estimates",mode="function")) {
			cat(c(" - mplus.get.moderation.estimates('"),file,"',label)\n",sep="")
		}
		if (exists("mplus.get.moderation.lowerci",mode="function")) {
			cat(c(" - mplus.get.moderation.lowerci('"),file,"',label)\n",sep="")
		}
		if (exists("mplus.get.moderation.upperci",mode="function")) {
			cat(c(" - mplus.get.moderation.upperci('"),file,"',label)\n",sep="")
		}
		if (exists("mplus.get.moderation.xvalues",mode="function")) {
			cat(c(" - mplus.get.moderation.xvalues('"),file,"')\n",sep="")
		}
	}

	if ("sensitivity_data" %in% names(gh5)) {
		if (exists("mplus.get.sensitivity.estimates",mode="function")) {
			cat(c(" - mplus.get.sensitivity.estimates('"),file,"',label,zvalue,group,zdecimals)\n",sep="")
		}
		if (exists("mplus.get.sensitivity.lowerci",mode="function")) {
			cat(c(" - mplus.get.sensitivity.lowerci('"),file,"',label,zvalue,group,zdecimals)\n",sep="")
		}
		if (exists("mplus.get.sensitivity.upperci",mode="function")) {
			cat(c(" - mplus.get.sensitivity.upperci('"),file,"',label,zvalue,group,zdecimals)\n",sep="")
		}
		if (exists("mplus.get.sensitivity.xvalues",mode="function")) {
			cat(c(" - mplus.get.sensitivity.xvalues('"),file,"')\n",sep="")
		}
		if (exists("mplus.list.sensitivity.zvalues",mode="function")) {
			cat(c(" - mplus.list.sensitivity.zvalues('"),file,"')\n",sep="")
		}
	}
	
	if ("irt_data" %in% names(gh5)) {
		if (exists("mplus.compute.irt.icc",mode="function")) {
			cat(c(" - mplus.compute.irt.icc('"),file,"',group,xvar,uvar,cat,xvector,covariates)\n",sep="")
		}
		if (exists("mplus.compute.irt.iic",mode="function")) {
			cat(c(" - mplus.compute.irt.iic('"),file,"',group,xvar,uvar,xvector,covariates)\n",sep="")
		}
	}

	if ("process_data" %in% names(gh5) && "means_and_variances_data" %in% names(gh5)) {
		np <- length(attr(gh5$process_data,"names"))
		for (i in c(1:np)) {
			cstr <- paste(c("process"), as.character(i), sep="")
			proc <- gh5$process_data[[cstr]]

			# Replace the line below with series of low-level function calls
			cstr2 <- paste(c("process_data"),"/",cstr,"", sep="")
			prop <- mplus.get.group.attribute(file, cstr2, 'properties')

			values <- attr(gh5$means_and_variances_data,"names")

			if (prop[1] == 1) {
				sm_ind <- pmatch("y_observed_means",values,nomatch=0)
				if (sm_ind > 0 && exists("mplus.get.sample_means",mode="function")) {
					cstr2 <- paste(c(" - mplus.get.sample_means('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("y_estimated_means",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.get.estimated_means",mode="function")) {
					cstr2 <- paste(c(" - mplus.get.estimated_means('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("y_estimated_modes",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.get.estimated_modes",mode="function")) {
					cstr2 <- paste(c(" - mplus.get.estimated_modes('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("y_estimated_medians",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.get.estimated_medians",mode="function")) {
					cstr2 <- paste(c(" - mplus.get.estimated_medians('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}
			} else if (prop[1] == 2) {
				em_ind <- pmatch("e_estimated_means",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.get.estimated_means",mode="function")) {
					cstr2 <- paste(c(" - mplus.get.estimated_means('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("e_estimated_modes",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.get.estimated_modes",mode="function")) {
					cstr2 <- paste(c(" - mplus.get.estimated_modes('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("e_estimated_medians",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.get.estimated_medians",mode="function")) {
					cstr2 <- paste(c(" - mplus.get.estimated_medians('"),file,"','",cstr,"')\n",sep="")
					cat(cstr2)
				}
			} else if (prop[1] == 3) {
				em_ind <- pmatch("observed_probs",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.get.sample_proportions",mode="function")) {
					cstr2 <- paste(c(" - mplus.get.sample_proportions('"),file,"','",cstr,"',cat1,cat2)\n",sep="")
					cat(cstr2)
				}

				em_ind <- pmatch("estimated_probs",values,nomatch=0)
				if (em_ind > 0 && exists("mplus.get.estimated_probabilities",mode="function")) {
					cstr2 <- paste(c(" - mplus.get.estimated_probabilities('"),file,"','",cstr,"',cat1,cat2)\n",sep="")
					cat(cstr2)
				}
			} else {
				cstr2 <- paste(c("'"),cstr,"' has unknown series type.\n")
				cat(cstr2)
			}
		}
	}

	if ("survival_data" %in% names(gh5)) {
		if (exists("mplus.list.survival.variables",mode="function")) {
			cat(c(" - mplus.list.survival.variables('"),file,"')\n",sep="")
		}
		if (exists("mplus.get.survival.kaplanmeier.values",mode="function")) {
			cat(c(" - mplus.get.survival.kaplanmeier.values('"),file,"',survvar,classnum,time)\n",sep="")
		}
		if (exists("mplus.compute.survival.sample.logcumulative.values",mode="function")) {
			cat(c(" - mplus.compute.survival.sample.logcumulative.values('"),file,"',survvar,classnum,time)\n",sep="")
		}
		if (exists("mplus.get.survival.baseline.values",mode="function")) {
			cat(c(" - mplus.get.survival.baseline.values('"),file,"',survvar,survvar2,clasnum,time)\n",sep="")
		}
		if (exists("mplus.compute.survival.estimated.logcumulative.values",mode="function")) {
			cat(c(" - mplus.compute.survival.estimated.logcumulative.values('"),file,"',survvar,classnum,time)\n",sep="")
		}
		if (exists("mplus.get.survival.basehazard.values",mode="function")) {
			cat(c(" - mplus.get.survival.basehazard.values('"),file,"',file,survvar,classnum,time)\n",sep="")
		}
	}

	if ("discrete_survival_data" %in% names(gh5)) {
		if (exists("mplus.list.discrete.survival.variables",mode="function")) {
			cat(c(" - mplus.list.discrete.survival.variables('"),file,"')\n",sep="")
		}
		if (exists("mplus.get.discrete.survival.kaplanmeier.values",mode="function")) {
			cat(c(" - mplus.get.discrete.survival.kaplanmeier.values('"),file,"',survvar,classnum,time)\n",sep="")
		}
		if (exists("mplus.get.discrete.survival.baseline.values",mode="function")) {
			cat(c(" - mplus.get.discrete.survival.baseline.values('"),file,"',survvar,survvar2,clasnum,time)\n",sep="")
		}
	}

	if ("bootstrap_data" %in% names(gh5)) {
		if ("parameters" %in% names(gh5$bootstrap_data)) {
			if (exists("mplus.get.bootstrap.distribution",mode="function")) {
				cat(c(" - mplus.get.bootstrap.distribution('"),file,"',parameter)\n",sep="")
			}
			if (exists("mplus.get.bootstrap.point.estimate",mode="function")) {
				cat(c(" - mplus.get.bootstrap.point.estimate('"),file,"',parameter)\n",sep="")
			}
		}
	}

	if ("bayesian_data" %in% names(gh5)) {
		if ("parameters_autocorr" %in% names(gh5$bayesian_data)) {
			if ("parameters" %in% names(gh5$bayesian_data$parameters_autocorr)) {
				if (exists("mplus.get.bayesian.parameter.data",mode="function")) {
					cat(c(" - mplus.get.bayesian.parameter.data('"),file,"',parameter,chain)\n",sep="")
				}
			}
			if ("priors" %in% names(gh5$bayesian_data$parameters_autocorr)) {
				if (exists("mplus.get.bayesian.prior.parameter.data",mode="function")) {
					cat(c(" - mplus.get.bayesian.prior.parameter.data('"),file,"',parameter)\n",sep="")
				}
			}
			if ("autocorrelation" %in% names(gh5$bayesian_data$parameters_autocorr)) {
				if (exists("mplus.get.bayesian.autocorrelation",mode="function")) {
					cat(c(" - mplus.get.bayesian.autocorrelation('"),file,"',parameter,chain)\n",sep="")
				}
			}
		}
		if ("predictive" %in% names(gh5$bayesian_data)) {
			if ("observed" %in% names(gh5$bayesian_data$predictive)) {
				if (exists("mplus.get.bayesian.predictive.observed",mode="function")) {
					cat(c(" - mplus.get.bayesian.predictive.observed('"),file,"',plabel)\n",sep="")
				}
			}
			if ("replicated" %in% names(gh5$bayesian_data$predictive)) {
				if (exists("mplus.get.bayesian.predictive.replicated",mode="function")) {
					cat(c(" - mplus.get.bayesian.predictive.replicated('"),file,"',plabel)\n",sep="")
				}
			}
			if ("pvalues" %in% names(gh5$bayesian_data$predictive)) {
				if (exists("mplus.get.bayesian.predictive.lowerci",mode="function")) {
					cat(c(" - mplus.get.bayesian.predictive.lowerci('"),file,"',plabel)\n",sep="")
				}
				if (exists("mplus.get.bayesian.predictive.upperci",mode="function")) {
					cat(c(" - mplus.get.bayesian.predictive.upperci('"),file,"',plabel)\n",sep="")
				}
				if (exists("mplus.get.bayesian.predictive.pvalue",mode="function")) {
					cat(c(" - mplus.get.bayesian.predictive.pvalue('"),file,"',plabel)\n",sep="")
				}
				if (exists("mplus.get.bayesian.predictive.pvalue_type",mode="function")) {
					cat(c(" - mplus.get.bayesian.predictive.pvalue('"),file,"',plabel)\n",sep="")
				}
			}
		}
		if ("plausible" %in% names(gh5$bayesian_data)) {
			if (exists("mplus.get.bayesian.plausible.data",mode="function")) {
				cat(c(" - mplus.get.bayesian.plausible.data('"),file,"',plauslabel,obs)\n",sep="")
			}
		}
	}

	invisible(file)
}


##########################################################################
#
# mplus.clear - clears all mplus-related data from a previous mplus_load
#
# arguments: none
#
# eg. mplus.clear()
#
#mplus.clear <- function() {
#	cat(c("\nRemoved the following:\n"))
#
#	if (exists("matrix_data",)) {
#	    rm(matrix_data, inherits=TRUE)
#		cat(c(" - matrix_data\n"))
#	}
#	if (exists("process_data",)) {
#	    rm(process_data, inherits=TRUE)
#		cat(c(" - process_data\n"))
#	}
#	if (exists("class_data")) {
#	    rm(class_data, inherits=TRUE)
#		cat(c(" - class_data\n"))
#	}
#	if (exists("categorical_data")) {
#	    rm(categorical_data, inherits=TRUE)
#		cat(c(" - categorical_data\n"))
#	}
#	if (exists("individual_data")) {
#	    rm(individual_data, inherits=TRUE)
#		cat(c(" - individual_data\n"))
#	}
#	if (exists("means_and_variances_data")) {
#	    rm(means_and_variances_data, inherits=TRUE)
#		cat(c(" - means_and_variances_data\n"))
#	}
#}


##########################################################################
#
# mplus.list.processes - list all available processes
#
# arguments:
#    file - the quoted name of an existing GH5 file
#
# eg. mplus.list.processes('ex8.1.gh5')
#
mplus.list.processes <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	#H5close()
	gh5 <- h5dump(file, load=TRUE)

	if (!("process_data" %in% names(gh5))) {
		stop("mplus.list.proceses requires series information.\n\nUse the SERIES option in Mplus to specify series information.\n")
	}

	cat(c("\nList of process names to use in the following functions:\n"))
	cat(c(" - mplus.plot.sample_means\n"))	
	cat(c(" - mplus.plot.estimated_means\n"))	
	cat(c(" - mplus.plot.sample_and_estimated_means\n"))	
	cat(c(" - mplus.plot.sample_proportions\n"))	
	cat(c(" - mplus.plot.estimated_probabilities\n"))	

	cat(c(" - mplus.get.sample_means\n"))	
	cat(c(" - mplus.get.estimated_means\n"))	
	cat(c(" - mplus.get.sample_proportions\n"))	
	cat(c(" - mplus.get.estimated_probabilities\n"))	

	cat(c("\nProcesses:\n"))

	allpnames <- attr(gh5$process_data,"names")
	allpnames
}


##########################################################################
#
# mplus.plot.estimated_means - plot estimated means for the quoted process
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	procstr - the quoted name of a series
#
# eg. mplus.plot.estimated_means('ex8.1.gh5','process1')
#
mplus.plot.estimated_means <-function(file,procstr='process1',ptype='o') {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	#H5close()
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith estimated means.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file, cstr2, 'properties')

	series_type <- prop[1]
	if ( ! (series_type == 1 || series_type == 2) ) {
		cstr <- paste("- process does not have estimated means:",procstr,"\n\n")
		stop(cstr)
	}

	# get the time scores
	xx <- proc$time_scores

	# set up the array for the estimated means
	dims <- attr(proc$time_scores,"dim")
	yy <- mplus.get.estimated_means(file,procstr)

	# plot the means
	cstr <- paste("Estimated means for",procstr)
	plot(xx,yy,xlab="",ylab="",main=cstr,type='n')
	symb <- array(c(21,22,23,24,25),c(dims[1]))
	colors <- rainbow(dims[2])
	for (i in c(1:dims[2])) {
		lines(xx[,i],yy[,i],type=ptype,pch=symb[i],col=colors[i])
	}

	ldesc <- array(0,c(dims[2]))
	lty <- array(0,c(dims[2]))
	lwd <- array(0,c(dims[2]))
	lcol <- array(0,c(dims[2]))
	for (i in c(1:dims[2])) {
		ldesc[i] <- sprintf("Class %d", i)
		lty[i] = 1
		lwd[i] = 2.5
		lcol[i] <- colors[i]
	}
	legend('bottomright',col=lcol,pch=symb,ldesc,lty=lty,lwd=lwd)
}


##########################################################################
#
# mplus.plot.estimated_modes - plot estimated modes for the quoted process
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	procstr - the quoted name of a series
#
# eg. mplus.plot.estimated_modes('ex8.1.gh5','process1')
#
mplus.plot.estimated_modes <-function(file,procstr='process1',ptype='o') {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	#H5close()
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith estimated modes.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file, cstr2, 'properties')

	series_type <- prop[1]
	if ( ! (series_type == 1 || series_type == 2) ) {
		cstr <- paste("- process does not have estimated modes:",procstr,"\n\n")
		stop(cstr)
	}

	# get the time scores
	xx <- proc$time_scores

	# set up the array for the estimated means
	dims <- attr(proc$time_scores,"dim")
	yy <- mplus.get.estimated_modes(file,procstr)

	# plot the means
	cstr <- paste("Estimated modes for",procstr)
	plot(xx,yy,xlab="",ylab="",main=cstr,type='n')
	symb <- array(c(21,22,23,24,25),c(dims[1]))
	colors <- rainbow(dims[2])
	for (i in c(1:dims[2])) {
		lines(xx[,i],yy[,i],type=ptype,pch=symb[i],col=colors[i])
	}

	ldesc <- array(0,c(dims[2]))
	lty <- array(0,c(dims[2]))
	lwd <- array(0,c(dims[2]))
	lcol <- array(0,c(dims[2]))
	for (i in c(1:dims[2])) {
		ldesc[i] <- sprintf("Class %d", i)
		lty[i] = 1
		lwd[i] = 2.5
		lcol[i] <- colors[i]
	}
	legend('bottomright',col=lcol,pch=symb,ldesc,lty=lty,lwd=lwd)
}


##########################################################################
#
# mplus.plot.estimated_medians - plot estimated medians for the quoted process
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	procstr - the quoted name of a series
#
# eg. mplus.plot.estimated_medians('ex8.1.gh5','process1')
#
mplus.plot.estimated_medians <-function(file,procstr='process1',ptype='o') {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	#H5close()
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith estimated medians.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file, cstr2, 'properties')

	series_type <- prop[1]
	if ( ! (series_type == 1 || series_type == 2) ) {
		cstr <- paste("- process does not have estimated medians:",procstr,"\n\n")
		stop(cstr)
	}

	# get the time scores
	xx <- proc$time_scores

	# set up the array for the estimated means
	dims <- attr(proc$time_scores,"dim")
	yy <- mplus.get.estimated_medians(file,procstr)

	# plot the means
	cstr <- paste("Estimated medians for",procstr)
	plot(xx,yy,xlab="",ylab="",main=cstr,type='n')
	symb <- array(c(21,22,23,24,25),c(dims[1]))
	colors <- rainbow(dims[2])
	for (i in c(1:dims[2])) {
		lines(xx[,i],yy[,i],type=ptype,pch=symb[i],col=colors[i])
	}

	ldesc <- array(0,c(dims[2]))
	lty <- array(0,c(dims[2]))
	lwd <- array(0,c(dims[2]))
	lcol <- array(0,c(dims[2]))
	for (i in c(1:dims[2])) {
		ldesc[i] <- sprintf("Class %d", i)
		lty[i] = 1
		lwd[i] = 2.5
		lcol[i] <- colors[i]
	}
	legend('bottomright',col=lcol,pch=symb,ldesc,lty=lty,lwd=lwd)
}


##########################################################################
#
# mplus.plot.sample_means - plot sample means for the quoted process
#
# arguments:
#	procstr - the quoted name of a series
#
# eg. mplus.plot.sample_means('ex6.1.gh5','process1')
#
mplus.plot.sample_means <-function(file,procstr='process1',ptype='o') {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	#H5close()
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith sample means.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file, cstr2, 'properties')

	series_type <- prop[1]

	if ( ! (series_type == 1) ) {
		cstr <- paste("- process does not have sample means:",procstr,"\n\n")
		stop(cstr)
	}

	# get the time scores
	xx <- proc$time_scores

	# set up the array for the estimated means
	dims <- attr(proc$time_scores,"dim")
	yy <- mplus.get.sample_means(file,procstr)

	# plot the means
	cstr <- paste("Sample means for",procstr)
	plot(xx,yy,xlab="",ylab="",main=cstr,type='n')
	symb <- array(c(21,22,23,24,25),c(dims[1]))
	colors <- rainbow(dims[2])
	for (i in c(1:dims[2])) {
		lines(xx[,i],yy[,i],type=ptype,pch=symb[i],col=colors[i])
	}

	ldesc <- array(0,c(dims[2]))
	lty <- array(0,c(dims[2]))
	lwd <- array(0,c(dims[2]))
	lcol <- array(0,c(dims[2]))
	for (i in c(1:dims[2])) {
		ldesc[i] <- sprintf("Class %d", i)
		lty[i] = 1
		lwd[i] = 2.5
		lcol[i] <- colors[i]
	}
	legend('bottomright',col=lcol,pch=symb,ldesc,lty=lty,lwd=lwd)
}


##########################################################################
#
# mplus.plot.sample_and_estimated_means - plot sample and estimated means for the
# quoted process
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	procstr - the quoted name of a series
#
# eg. mplus.plot.sample_and_estimated_means('process1')
#
mplus.plot.sample_and_estimated_means <-function(file,procstr='process1',ptype='o') {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	#H5close()
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith sample and estimated means.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file,cstr2,'properties')

	series_type <- prop[1]
	if ( ! (series_type == 1) ) {
		cstr <- paste("- process does not have sample means:",procstr,"\n\n")
		stop(cstr)
	}

	# get the dimensions of the time_scores array and create an array with twice the size of the
	# first dimension
	dims <- attr(proc$time_scores,"dim")
	xx <- array(0, c(dims[1],2*dims[2]))
	yy <- array(0, c(dims[1],2*dims[2]))

	samp <- mplus.get.sample_means(file,procstr)
	emean <- mplus.get.estimated_means(file,procstr)

	for (i in c(1:dims[1])) {
		for (j in c(1:dims[2])) {
			# set the time scores and pick up sample means
			xx[i,2*j-1] <- proc$time_scores[i,j]
			yy[i,2*j-1] <- samp[i,j]

			# set the time scores and pick up estimated means
			xx[i,2*j] <- proc$time_scores[i,j]
			yy[i,2*j] <- emean[i,j]
		}
	}

	# plot the means
	cstr <- paste("Sample and estimated means for",procstr)
	plot(xx,yy,xlab="",ylab="",main=cstr,type='n')
	symb <- array(c(21,22,23,24,25),c(dims[2]))
	colors <- rainbow(dims[2])
	for (i in c(1:dims[2])) {
		lines(xx[,2*i-1],yy[,2*i-1],type=ptype,pch=symb[i],col=colors[i])
		lines(xx[,2*i],yy[,2*i],type=ptype,pch=symb[i],col=colors[i])
	}

	ldesc <- array(0,c(2*dims[2]))
	lty <- array(0,c(2*dims[2]))
	lwd <- array(0,c(2*dims[2]))
	lcol <- array(0,c(2*dims[2]))
	lsymb <- array(0,c(2*dims[2]))
	for (i in c(1:dims[2])) {
		ldesc[2*i-1] <- sprintf("Sample means, Class %d", i)
		lty[2*i-1] = 1
		lwd[2*i-1] = 2.5
		lsymb[2*i-1] <- symb[i]

		lcol[2*i] <- colors[i]
		ldesc[2*i] <- sprintf("Estimated means, Class %d", i)
		lty[2*i] = 1
		lwd[2*i] = 2.5
		lcol[2*i] <- colors[i]
		lsymb[2*i] <- symb[i]
	}
	legend('bottomright',col=lcol,pch=lsymb,ldesc,lty=lty,lwd=lwd)
}


##########################################################################
#
# mplus.plot.estimated_probabilities - plot estimated probabilities for the
# quoted process, summing up probabilities of the first to the last category
# chosen
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	procstr - the quoted name of a series
#	cat1 - the first category to include
#	cat2 - the last category to include
#
# eg. mplus.plot.estimated_probabilities('ex8.4.gh5','process1',1,1)
#
mplus.plot.estimated_probabilities <- function(file,var,series=FALSE,cat1=1,cat2=1,lposition='top',ptype='o') {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	if (missing(var)) {
		stop("- variable or process name must be given")
	}
	
	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	if (series) {
		# check that the series exists
		if (!("process_data" %in% names(gh5))) {
			stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith estimated probabilities.\n")
		}

		procstr <- var	
		
		# if cat2 is missing and cat1 is given, then we should assign cat2 to cat1.
		if (missing(cat2)) {
			if (!(missing(cat1))) {
				cat2 <- cat1
			}
		}
		
		allpnames <- attr(gh5$process_data,"names")
		pind <- pmatch(procstr, allpnames, nomatch=0)
		if (pind == 0) {
			cstr <- paste("- process does not exist:",procstr,"\n\n")
			stop(cstr)
		}
		
		# get the process
		proc <- gh5$process_data[[procstr]]
		
		# get the series type in properties
		# Replace the line below with series of low-level function calls
		cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
		prop <- mplus.get.group.attribute(file,cstr2,'properties')
		
		series_type <- prop[1]
		if ( !(series_type == 3) ) {
			cstr <- paste("- process does not have estimated probabilities:",procstr,"\n\n")
			stop(cstr)
		}
		
		# get the time scores
		xx <- proc$time_scores
		
		# set up the array for the estimated probabilities
		dims <- attr(proc$time_scores,"dim")
		yy <- mplus.get.estimated_probabilities(file,procstr,series=TRUE,cat1,cat2)
		cstr <- paste("Estimated probabilities for",procstr)
	} else {
		catvars <- mplus.get.group.attribute(file,'categorical_data','var_names')
		vartypes <- as.integer(mplus.get.group.attribute(file,'categorical_data','vtype'))
		categories <- as.integer(mplus.get.group.attribute(file,'categorical_data','categories'))

		var <- toupper(var)
		cat_index <- as.integer(pmatch(var, catvars, nomatch=0))
		if (cat_index == 0) {
			cstr <- paste("- variable not found:",var,"\n\n")
			stop(cstr)
		}
		if (vartypes[cat_index] < 0) {
			cstr <- paste("- variable does not have estimated probabilities:",var,"\n\n")
			stop(cstr)
		}
		if (categories[cat_index] == 0) {
			cstr <- paste("- variable does not have estimated probabilities:",var,"\n\n")
			stop(cstr)
		}
		dims <- attr(yy,'dim')
		yy <- mplus.get.estimated_probabilities(file,var)
		xx <- array(c(1:categories[cat_index]), c(dims[1],dims[2]))
		
		cstr <- paste("Estimated probabilities for",var)
	}

	# plot the probabilities
	plot(xx,yy,xlab="",ylab="",main=cstr,type='n',ylim=c(0:1))
	symb <- array(c(21,22,23,24,25),c(dims[1]))
	colors <- rainbow(dims[2])
	for (i in c(1:dims[2])) {
		lines(xx[,i],yy[,i],type=ptype,pch=symb[i],col=colors[i])
	}

	glabels <- mplus.get.file.dataset(file,'model_group_labels')
	glabels <- gsub("(^\\s+|\\s+$)", "", glabels, perl=TRUE)

	ldesc <- array(0,c(dims[2]))
	lty <- array(0,c(dims[2]))
	lwd <- array(0,c(dims[2]))
	lcol <- array(0,c(dims[2]))
	for (i in c(1:dims[2])) {
		ldesc[i] <- glabels[i] #sprintf("Class %d", i)
		lty[i] = 1
		lwd[i] = 2.5
		lcol[i] <- colors[i]
	}
	legend(lposition,col=lcol,pch=symb,ldesc,lty=lty,lwd=lwd)
}


##########################################################################
#
# mplus.plot.sample_proportions - plot sample proportions for the
# quoted process, summing up proportions of the first to the last category
# chosen
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	procstr - the quoted name of a series
#	cat1 - the first category to include
#	cat2 - the last category to include
#
# eg. mplus.plot.sample_proportions('ex8.4.gh5','process1',1,1)
#
mplus.plot.sample_proportions <-function(file,var,series=FALSE,cat1=1,cat2=1,lposition='top',ptype='o') {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}
	
	if (missing(var)) {
		stop("- variable or process name must be given")
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	if (series) {
		# check that the series exists
		if (!("process_data" %in% names(gh5))) {
			stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith sample proportions.\n")
		}
		
		procstr <- var
		
		# if cat2 is missing and cat1 is given, then we should assign cat2 to cat1.
		if (missing(cat2)) {
			if (!(missing(cat1))) {
				cat2 <- cat1
			}
		}
		
		allpnames <- attr(gh5$process_data,"names")
		pind <- pmatch(procstr, allpnames, nomatch=0)
		if (pind == 0) {
			cstr <- paste("- process does not exist:",procstr,"\n\n")
			cat(cstr)
			return(invisible(cstr))
		}
		
		# get the process
		proc <- gh5$process_data[[procstr]]
		
		# get the series type in properties
		cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
		prop <- mplus.get.group.attribute(file,cstr2,'properties')
		
		series_type <- prop[1]
		if ( !(series_type == 3) ) {
			cstr <- paste("- process does not have sample proportions:",procstr,"\n\n")
			stop(cstr)
		}
		
		# get the time scores
		xx <- proc$time_scores
		
		# set up the array for the sample proportions
		dims <- attr(proc$time_scores,"dim")
		# dims[1] is the number of time points, dims[2] is the number of classes
		yy <- mplus.get.sample_proportions(file,procstr,series=TRUE,cat1,cat2)
		cstr <- paste("Sample proportions for",procstr)
	} else {
		catvars <- mplus.get.group.attribute(file,'categorical_data','var_names')
		vartypes <- as.integer(mplus.get.group.attribute(file,'categorical_data','vtype'))
		categories <- as.integer(mplus.get.group.attribute(file,'categorical_data','categories'))
		
		var <- toupper(var)
		cat_index <- as.integer(pmatch(var, catvars, nomatch=0))
		if (cat_index == 0) {
			cstr <- paste("- variable not found:",var,"\n\n")
			stop(cstr)
		}
		if (vartypes[cat_index] < 0) {
			cstr <- paste("- variable does not have sample proportions:",var,"\n\n")
			stop(cstr)
		}
		if (categories[cat_index] == 0) {
			cstr <- paste("- variable does not have sample proportions:",var,"\n\n")
			stop(cstr)
		}
		dims <- attr(yy,'dim')
		yy <- mplus.get.sample_proportions(file,var)
		xx <- array(c(1:categories[cat_index]), c(dims[1],dims[2]))
		
		cstr <- paste("Sample proportions for",var)
	}

	# plot the proportions
	plot(xx,yy,xlab="",ylab="",main=cstr,type='n',ylim=c(0:1))
	symb <- array(c(21,22,23,24,25),c(dims[1]))
	colors <- rainbow(dims[2])
	for (k in c(1:dims[2])) {
		lines(xx[,k],yy[,k],type=ptype,pch=symb[k],col=colors[k])
	}

	glabels <- mplus.get.file.dataset(file,'model_group_labels')
	glabels <- gsub("(^\\s+|\\s+$)", "", glabels, perl=TRUE)

	ldesc <- array(0,c(dims[2]))
	lty <- array(0,c(dims[2]))
	lwd <- array(0,c(dims[2]))
	lcol <- array(0,c(dims[2]))
	for (i in c(1:dims[2])) {
		ldesc[i] <- glabels[i] #sprintf("Class %d", i)
		lty[i] = 1
		lwd[i] = 2.5
		lcol[i] <- colors[i]
	}
	legend(lposition,col=lcol,pch=symb,ldesc,lty=lty,lwd=lwd)
}


##########################################################################
#
# mplus.plot.sample_proportions_and_estimated_probabilities - plot sample proportions for the
# quoted process, summing up proportions of the first to the last category
# chosen
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	procstr - the quoted name of a series
#	cat1 - the first category to include
#	cat2 - the last category to include
#
# eg. mplus.plot.sample_proportions_and_estimated_probabilities('ex8.4.gh5','process1',1,1)
#
mplus.plot.sample_proportions_and_estimated_probabilities <-function(file,var,series=FALSE,cat1=1,cat2=1,lposition='top',ptype='o') {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}
	
	if (missing(var)) {
		stop("- variable or process name must be given")
	}
	
	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)
	
	if (series) {
		# check that the series exists
		if (!("process_data" %in% names(gh5))) {
			stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith sample proportions.\n")
		}
		
		procstr <- var

		# if cat2 is missing and cat1 is given, then we should assign cat2 to cat1.
		if (missing(cat2)) {
			if (!(missing(cat1))) {
				cat2 <- cat1
			}
		}
		
		allpnames <- attr(gh5$process_data,"names")
		pind <- pmatch(procstr, allpnames, nomatch=0)
		if (pind == 0) {
			cstr <- paste("- process does not exist:",procstr,"\n\n")
			cat(cstr)
			return(invisible(cstr))
		}
		
		# get the process
		proc <- gh5$process_data[[procstr]]
		
		# get the series type in properties
		cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
		prop <- mplus.get.group.attribute(file,cstr2,'properties')
		
		series_type <- prop[1]
		if ( !(series_type == 3) ) {
			cstr <- paste("- process does not have sample proportions:",procstr,"\n\n")
			stop(cstr)
		}
		
		# set up the array for the sample proportions
		dims <- attr(proc$time_scores,"dim")
		xx <- array(0, c(dims[1],2*dims[2]))
		yy <- array(0, c(dims[1],2*dims[2]))
		
		# dims[1] is the number of time points, dims[2] is the number of classes
		samp <- mplus.get.sample_proportions(file,procstr,series=TRUE,cat1,cat2)
		eprob <- mplus.get.estimated_probabilities(file,procstr,series=TRUE,cat1,cat2)

		for (i in c(1:dims[1])) {
			for (j in c(1:dims[2])) {
				# set the time scores and pick up sample means
				xx[i,2*j-1] <- proc$time_scores[i,j]
				yy[i,2*j-1] <- samp[i,j]
				
				# set the time scores and pick up estimated means
				xx[i,2*j] <- proc$time_scores[i,j]
				yy[i,2*j] <- eprob[i,j]
			}
		}

		cstr <- paste("Sample proportions and estimated probabilities for",procstr)
	} else {
		catvars <- mplus.get.group.attribute(file,'categorical_data','var_names')
		vartypes <- as.integer(mplus.get.group.attribute(file,'categorical_data','vtype'))
		categories <- as.integer(mplus.get.group.attribute(file,'categorical_data','categories'))
		
		var <- toupper(var)
		cat_index <- as.integer(pmatch(var, catvars, nomatch=0))
		if (cat_index == 0) {
			cstr <- paste("- variable not found:",var,"\n\n")
			stop(cstr)
		}
		if (vartypes[cat_index] < 0) {
			cstr <- paste("- variable does not have sample proportions:",var,"\n\n")
			stop(cstr)
		}
		num_cat <- categories[cat_index]
		if (num_cat == 0) {
			cstr <- paste("- variable does not have sample proportions:",var,"\n\n")
			stop(cstr)
		}
		num_groups <- length(mplus.get.file.dataset(file,'model_group_labels'))
		xx <- array(0, c(num_cat,2*num_groups))
		yy <- array(0, c(num_cat,2*num_groups))

		samp <- mplus.get.sample_proportions(file,var)
		eprop <- mplus.get.estimated_probabilities(file,var)

		for (i in c(1:num_cat)) {
			for (j in c(1:num_groups)) {
				# set the time scores and pick up sample means
				xx[i,2*j-1] <- i
				yy[i,2*j-1] <- samp[i,j]
				
				# set the time scores and pick up estimated means
				xx[i,2*j] <- i
				yy[i,2*j] <- eprop[i,j]
			}
		}

		cstr <- paste("Sample proportions and estimated probabilities for",var)
		dims <- c(num_cat, num_groups)
	}
	
	# plot the proportions
	plot(xx,yy,xlab="",ylab="",main=cstr,type='n',ylim=c(0:1))
	symb <- array(c(21,22,23,24,25),c(dims[1]))
	colors <- rainbow(dims[2])
	for (k in c(1:dims[2])) {
		lines(xx[,2*k-1],yy[,2*k-1],type=ptype,pch=symb[k],col=colors[k])
		lines(xx[,2*k],yy[,2*k],type=ptype,pch=symb[k],col=colors[k])
	}
	
	glabels <- mplus.get.file.dataset(file,'model_group_labels')
	glabels <- gsub("(^\\s+|\\s+$)", "", glabels, perl=TRUE)
	
	ldesc <- array(0,c(2*dims[2]))
	lty <- array(0,c(2*dims[2]))
	lwd <- array(0,c(2*dims[2]))
	lcol <- array(0,c(2*dims[2]))
	lsymb <- array(0,c(2*dims[2]))
	for (i in c(1:dims[2])) {
		ldesc[2*i-1] <- sprintf("Sample proportions, %s", glabels[i]) #sprintf("Class %d", i)
		lty[2*i-1] = 1
		lwd[2*i-1] = 2.5
		lcol[2*i-1] <- colors[i]
		lsymb[2*i-1] <- symb[1]

		ldesc[2*i] <- sprintf("Estimated probabilities, %s", glabels[i]) #sprintf("Class %d", i)
		lty[2*i] = 1
		lwd[2*i] = 2.5
		lcol[2*i] <- colors[i]
		lsymb[2*i] <- symb[2]
	}
	legend(lposition,col=lcol,pch=lsymb,ldesc,lty=lty,lwd=lwd)
}


##########################################################################
#
# mplus.get.estimated_means - plot estimated means for the quoted process
#
# arguments:
#	file - the quoted name of an existing GH5 file, required
#	procstr - the quoted name of a series, not required.  Defaults to 'process1' (the first process)
#	classidx - the class index, not required - 0 for all classes.  Default to 0.
#
# eg. mplus.get.estimated_means('ex8.1.gh5','process1',3)
#
mplus.get.estimated_means <-function(file,procstr='process1',classidx=0) {
	if (missing(file)) {
		stop(" - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith estimated means.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	# Replace the line below with series of low-level function calls
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file,cstr2,'properties')

	series_type <- prop[1]
	if ( ! (series_type == 1 || series_type == 2) ) {
		cstr <- paste("- process does not have estimated means:",procstr,"\n\n")
		stop(cstr)
	}

	# set up the array for the estimated means
	dims <- attr(proc$time_scores,"dim")
	# if all classes, dimension it by number of classes.  Otherwise, just dimension by 1.
	if (classidx == 0) {
		yy <- array(0, c(dims[1],dims[2]))
	} else {
		# check that the classidx is within range.
		if (classidx < 0 || classidx > dims[2]) {
			cstr <- paste("- classidx is out of range, 1 to ",dims[2],": ",classidx,"\n\n")
			stop(cstr)
		}
		yy <- array(0, c(dims[1],1))
	}

	# get the indices of variables in the series
	var_names <- mplus.get.group.attribute(file,cstr2,'var_names')

	if (series_type == 1) {
		mean_vars <- mplus.get.group.attribute(file,'means_and_variances_data/y_estimated_means','variables')
	} else {
		mean_vars <- mplus.get.group.attribute(file,'means_and_variances_data/latent_estimated_means','variables')
	}
	var_indices <- pmatch(var_names, mean_vars, nomatch=0)

	# type 1 is estimated means for observed variables
	if (series_type == 1) {
		if (classidx == 0) {
			for (i in c(1:dims[2])) {
				for (j in c(1:dims[1])) {
					yy[j,i] <- gh5$means_and_variances_data$y_estimated_means$values[var_indices[j],i]
				}
			}
		} else {
			for (j in c(1:dims[1])) {
				yy[j,i] <- gh5$means_and_variances_data$y_estimated_means$values[var_indices[j],classidx]
			}
		}
	}

	# type 2 is estimated means for latent variables
	if (series_type == 2) {
		if (classidx == 0) {
			for (i in c(1:dims[2])) {
				for (j in c(1:dims[1])) {
					yy[j,i] <- gh5$means_and_variances_data$latent_estimated_means$values[var_indices[j],i]
				}
			}
		} else {
			for (j in c(1:dims[1])) {
				yy[j,i] <- gh5$means_and_variances_data$latent_estimated_means$values[var_indices[j],classidx]
			}
		}
	}

	# return the means
	return(yy)
}


##########################################################################
#
# mplus.get.sample_means - return sample means for the quoted process
#
# arguments:
#	file - the quoted name of an existing GH5 file, required
#	procstr - the quoted name of a series, not required.  Defaults to 'process1' (the first process)
#	classidx - the class index, not required - 0 for all classes.  Default to 0.
#
# eg. mplus.get.sample_means('ex8.1.gh5','process1',3)
#
mplus.get.sample_means <- function(file,procstr='process1',classidx=0) {
	if (missing(file)) {
		stop("- the name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information.\n\nUse the SERIES option in Mplus to specify series information for processes\nwith sample means.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	# Replace the line below with series of low-level function calls
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file,cstr2,'properties')

	series_type <- prop[1]

	if ( ! (series_type == 1) ) {
		cstr <- paste("- process does not have sample means:",procstr,"\n\n")
		stop(cstr)
	}

	# get the time scores
	xx <- proc$time_scores

	# set up the array for the estimated means
	dims <- attr(proc$time_scores,"dim")
	# if all classes, dimension it by number of classes.  Otherwise, just dimension by 1.
	if (classidx == 0) {
		yy <- array(0, c(dims[1],dims[2]))
	} else {
		# check that the classidx is within range.
		if (classidx < 0 || classidx > dims[2]) {
			cstr <- paste("- classidx is out of range, 1 to ",dims[2],": ",classidx,"\n\n")
			stop(cstr)
		}
		yy <- array(0, c(dims[1],1))
	}

	# get the indices of variables in the series
	var_names <- mplus.get.group.attribute(file,cstr2,'var_names')

	mean_vars <- mplus.get.group.attribute(file,'means_and_variances_data/y_observed_means','variables')
	var_indices <- pmatch(var_names, mean_vars, nomatch=0)

	# only type 1 has sample means
	if (classidx == 0) {
		for (i in c(1:dims[2])) {
			for (j in c(1:dims[1])) {
				yy[j,i] <- gh5$means_and_variances_data$y_observed_means$values[var_indices[j],i]
			}
		}
	} else {
		for (j in c(1:dims[1])) {
			yy[j,i] <- gh5$means_and_variances_data$y_observed_means$values[var_indices[j],classidx]
		}
	}

	# return the means
	return(yy)
}


##########################################################################
#
# mplus.get.estimated_modes - plot estimated modes for the quoted process
#
# arguments:
#	file - the quoted name of an existing GH5 file, required
#	procstr - the quoted name of a series, not required.  Defaults to 'process1' (the first process)
#	classidx - the class index, not required - 0 for all classes.  Default to 0.
#
# eg. mplus.get.estimated_modes('ex8.1.gh5','process1',3)
#
mplus.get.estimated_modes <-function(file,procstr='process1',classidx=0) {
	if (missing(file)) {
		stop(" - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith estimated modes.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	# Replace the line below with series of low-level function calls
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file,cstr2,'properties')

	series_type <- prop[1]
	if ( ! (series_type == 1 || series_type == 2) ) {
		cstr <- paste("- process does not have estimated modes:",procstr,"\n\n")
		stop(cstr)
	}

	# set up the array for the estimated modes
	dims <- attr(proc$time_scores,"dim")
	# if all classes, dimension it by number of classes.  Otherwise, just dimension by 1.
	if (classidx == 0) {
		yy <- array(0, c(dims[1],dims[2]))
	} else {
		# check that the classidx is within range.
		if (classidx < 0 || classidx > dims[2]) {
			cstr <- paste("- classidx is out of range, 1 to ",dims[2],": ",classidx,"\n\n")
			stop(cstr)
		}
		yy <- array(0, c(dims[1],1))
	}

	# get the indices of variables in the series
	var_names <- mplus.get.group.attribute(file,cstr2,'var_names')

	if (series_type == 1) {
		mean_vars <- mplus.get.group.attribute(file,'means_and_variances_data/y_estimated_modes','variables')
	} else {
		mean_vars <- mplus.get.group.attribute(file,'means_and_variances_data/e_estimated_modes','variables')
	}
	var_indices <- pmatch(var_names, mean_vars, nomatch=0)

	# type 1 is estimated means for observed variables
	if (series_type == 1) {
		if (classidx == 0) {
			for (i in c(1:dims[2])) {
				for (j in c(1:dims[1])) {
					yy[j,i] <- gh5$means_and_variances_data$y_estimated_modes$values[var_indices[j],i]
				}
			}
		} else {
			for (j in c(1:dims[1])) {
				yy[j,i] <- gh5$means_and_variances_data$y_estimated_modes$values[var_indices[j],classidx]
			}
		}
	}

	# type 2 is estimated means for latent variables
	if (series_type == 2) {
		if (classidx == 0) {
			for (i in c(1:dims[2])) {
				for (j in c(1:dims[1])) {
					yy[j,i] <- gh5$means_and_variances_data$e_estimated_modes$values[var_indices[j],i]
				}
			}
		} else {
			for (j in c(1:dims[1])) {
				yy[j,i] <- gh5$means_and_variances_data$e_estimated_modes$values[var_indices[j],classidx]
			}
		}
	}

	# return the modes
	return(yy)
}



##########################################################################
#
# mplus.get.estimated_medians - plot estimated medians for the quoted process
#
# arguments:
#	file - the quoted name of an existing GH5 file, required
#	procstr - the quoted name of a series, not required.  Defaults to 'process1' (the first process)
#	classidx - the class index, not required - 0 for all classes.  Default to 0.
#
# eg. mplus.get.estimated_medians('ex8.1.gh5','process1',3)
#
mplus.get.estimated_medians <-function(file,procstr='process1',classidx=0) {
	if (missing(file)) {
		stop(" - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith estimated medians.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	# Replace the line below with series of low-level function calls
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file,cstr2,'properties')

	series_type <- prop[1]
	if ( ! (series_type == 1 || series_type == 2) ) {
		cstr <- paste("- process does not have estimated medians:",procstr,"\n\n")
		stop(cstr)
	}

	# set up the array for the estimated medians
	dims <- attr(proc$time_scores,"dim")
	# if all classes, dimension it by number of classes.  Otherwise, just dimension by 1.
	if (classidx == 0) {
		yy <- array(0, c(dims[1],dims[2]))
	} else {
		# check that the classidx is within range.
		if (classidx < 0 || classidx > dims[2]) {
			cstr <- paste("- classidx is out of range, 1 to ",dims[2],": ",classidx,"\n\n")
			stop(cstr)
		}
		yy <- array(0, c(dims[1],1))
	}

	# get the indices of variables in the series
	var_names <- mplus.get.group.attribute(file,cstr2,'var_names')

	if (series_type == 1) {
		mean_vars <- mplus.get.group.attribute(file,'means_and_variances_data/y_estimated_medians','variables')
	} else {
		mean_vars <- mplus.get.group.attribute(file,'means_and_variances_data/e_estimated_medians','variables')
	}
	var_indices <- pmatch(var_names, mean_vars, nomatch=0)

	# type 1 is estimated means for observed variables
	if (series_type == 1) {
		if (classidx == 0) {
			for (i in c(1:dims[2])) {
				for (j in c(1:dims[1])) {
					yy[j,i] <- gh5$means_and_variances_data$y_estimated_medians$values[var_indices[j],i]
				}
			}
		} else {
			for (j in c(1:dims[1])) {
				yy[j,i] <- gh5$means_and_variances_data$y_estimated_medians$values[var_indices[j],classidx]
			}
		}
	}

	# type 2 is estimated means for latent variables
	if (series_type == 2) {
		if (classidx == 0) {
			for (i in c(1:dims[2])) {
				for (j in c(1:dims[1])) {
					yy[j,i] <- gh5$means_and_variances_data$e_estimated_medians$values[var_indices[j],i]
				}
			}
		} else {
			for (j in c(1:dims[1])) {
				yy[j,i] <- gh5$means_and_variances_data$e_estimated_medians$values[var_indices[j],classidx]
			}
		}
	}

	# return the modes
	return(yy)
}



##########################################################################
#
# mplus.get.time_scores - return time scores for the quoted process
#
# arguments:
#	procstr - the quoted name of a series
#
# eg. mplus.get.time_scores('ex6.1.gh5', 'process1')
#
mplus.get.time_scores <- function(file,procstr='process1') {
	if (missing(file)) {
		stop("- the name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	# check that the series exists
	if (!("process_data" %in% names(gh5))) {
		stop("- requires series information.\n\nUse the SERIES option in Mplus to specify series information for processes\nwith sample means.\n")
	}

	allpnames <- attr(gh5$process_data,"names")
	pind <- pmatch(procstr, allpnames, nomatch=0)
	if (pind == 0) {
		cstr <- paste("- process does not exist:",procstr,"\n\n")
		stop(cstr)
	}

	# get the process
	proc <- gh5$process_data[[procstr]]

	# get the series type in properties
	# Replace the line below with series of low-level function calls
	cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
	prop <- mplus.get.group.attribute(file,cstr2,'properties')

	series_type <- prop[1]

	# get the time scores
	xx <- proc$time_scores
	return(xx)
}



##########################################################################
#
# mplus.get.estimated_probabilities - return estimated probabilities for the
# quoted process, summing up probabilities of the first to the last category
# chosen
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	procstr - the quoted name of a series
#	cat1 - the first category to include
#	cat2 - the last category to include
#
# eg. mplus.get.estimated_probabilities('ex8.4.gh5','process1',1,1)
#
mplus.get.estimated_probabilities <- function(file,var,series=FALSE,cat1=1,cat2=1) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	if (missing(var)) {
		stop("- variable or process name must be given")
	}
	
	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	# get categorical data information then look up the variables in the process
	# in categorical_data so we can get the number of categories for each variable in the process
	# this would be achieved by categories[cat_indices[i]] for variable i in the process
	categories <- as.integer(mplus.get.group.attribute(file,'categorical_data','categories'))
	
	catvars <- mplus.get.group.attribute(file,'categorical_data','var_names')
	vartypes <- as.integer(mplus.get.group.attribute(file,'categorical_data','vtype'))

	if (series) {
		# check that the series exists
		if (!("process_data" %in% names(gh5))) {
			stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith estimated probabilities.\n")
		}

		procstr <- var

		# if cat2 is missing and cat1 is given, then we should assign cat2 to cat1.
		if (missing(cat2)) {
			if (!(missing(cat1))) {
				cat2 <- cat1
			}
		}
		
		allpnames <- attr(gh5$process_data,"names")
		pind <- pmatch(procstr, allpnames, nomatch=0)
		if (pind == 0) {
			cstr <- paste("- process does not exist:",procstr,"\n\n")
			stop(cstr)
		}
		
		# get the process
		proc <- gh5$process_data[[procstr]]
		
		# get the series type in properties
		cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
		prop <- mplus.get.group.attribute(file,cstr2,'properties')
		
		series_type <- prop[1]
		if ( !(series_type == 3) ) {
			cstr <- paste("- process does not have estimated probabilities:",procstr,"\n\n")
			stop(cstr)
		}
		
		# set up the array for the estimated probabilities
		dims <- attr(proc$time_scores,"dim")
		yy <- array(0, c(dims[1],dims[2]))
		
		# get indices and names of the variables in the series
		var_indices <- mplus.get.group.attribute(file,cstr2,'var_indices')
		var_names <- mplus.get.group.attribute(file,cstr2,'var_names')
		
		cat_indices <- pmatch(var_names, catvars, nomatch=0)
		cat_indices <- as.integer(cat_indices)

		# get the probabilities
		for (i in c(1:dims[1])) {
			for (j in c(1:dims[2])) {
				start_index <- 0
				if (i > 1) {
					for (k in c(1:c(cat_indices[i]-1))) {
						if (vartypes[k] == 0) {
							start_index <- start_index + categories[k]
						}
					}
				}
				
				startk <- cat1 + start_index
				endk <- cat2 + start_index
				yy[i,j] <- sum(gh5$means_and_variances_data$estimated_probs$values[startk:endk,j])
			}
		}
	} else {
		var <- toupper(var)
		cat_index <- as.integer(pmatch(var, catvars, nomatch=0))
		if (cat_index == 0) {
			cstr <- paste("- variable not found:",var,"\n\n")
			stop(cstr)
		}
		if (vartypes[cat_index] < 0) {
			cstr <- paste("- variable does not have estimated probabilities:",var,"\n\n")
			stop(cstr)
		}
		num_cat <- categories[cat_index]
		if (num_cat == 0) {
			cstr <- paste("- variable does not have estimated probabilities:",var,"\n\n")
			stop(cstr)
		}
		num_groups <- attr(gh5$model_group_labels,'dim')
		start_index <- 0
		if (cat_index > 1) {
			for (k in c(1:(cat_index-1))) {
				if (vartypes[k] == 0) {
					start_index <- start_index + categories[k]
				}
			}
		}
		
		yy <- array(0, c(num_cat,num_groups))
		for (i in c(1:num_groups)) {
			for (j in c(1:num_cat)){
				yy[j,i] <- gh5$means_and_variances_data$estimated_probs$values[start_index+j,i]
			}
		}
	}

	# return the probabilities
	return(yy);
}




##########################################################################
#
# mplus.get.sample_proportions - return sample proportions for the
# quoted process, summing up proportions of the first to the last category
# chosen
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	procstr - the quoted name of a series
#	cat1 - the first category to include
#	cat2 - the last category to include
#
# eg. mplus.get.sample_proportions('ex8.4.gh5','process1',1,1)
#
mplus.get.sample_proportions <-function(file,var,series=FALSE,cat1=1,cat2=1) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	if (missing(var)) {
		stop("- variable or process name must be given")
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	# get categorical data information then look up the variables in the process
	# in categorical_data so we can get the number of categories for each variable in the process
	# this would be achieved by categories[cat_indices[i]] for variable i in the process
	categories <- as.integer(mplus.get.group.attribute(file,'categorical_data','categories'))

	catvars <- mplus.get.group.attribute(file,'categorical_data','var_names')
	vartypes <- as.integer(mplus.get.group.attribute(file,'categorical_data','vtype'))
	
	if (series) {
		# check that the series exists
		if (!("process_data" %in% names(gh5))) {
			stop("- requires series information\n\nUse the SERIES option in Mplus to specify series information for processes\nwith sample proportions.\n")
		}

		procstr <- var

		# if cat2 is missing and cat1 is given, then we should assign cat2 to cat1.
		if (missing(cat2)) {
			if (!(missing(cat1))) {
				cat2 <- cat1
			}
		}
	
		allpnames <- attr(gh5$process_data,"names")
		pind <- pmatch(procstr, allpnames, nomatch=0)
		if (pind == 0) {
			cstr <- paste("- process does not exist:",procstr,"\n\n")
			stop(cstr)
		}
	
		# get the process
		proc <- gh5$process_data[[procstr]]
	
		# get the series type in properties
		cstr2 <- paste(c("process_data"),"/",procstr,"", sep="")
		prop <- mplus.get.group.attribute(file,cstr2,'properties')
	
		series_type <- prop[1]
		if ( ! (series_type == 3) ) {
			cstr <- paste("- process does not have sample proportions:",procstr,"\n\n")
			stop(cstr)
		}
	
		# set up the array for the sample proportions
		dims <- attr(proc$time_scores,"dim")
		# dims[1] is the number of time points, dims[2] is the number of classes
		yy <- array(0, c(dims[1],dims[2]))
	
		# get indices and names of the variables in the series
		var_indices <- mplus.get.group.attribute(file,cstr2,'var_indices')
		var_names <- mplus.get.group.attribute(file,cstr2,'var_names')
	
		cat_indices <- as.integer(pmatch(var_names, catvars, nomatch=0))

		# get the proportions
		for (i in c(1:dims[1])) {
			for (j in c(1:dims[2])) {
				start_index <- 0
				if (i > 1) {
					for (k in c(1:(cat_indices[i]-1))) {
						if (vartypes[k] == 0) {
							start_index <- start_index + as.integer(categories[k])
						}
					}
				}
	
				startk <- cat1 + start_index
				endk <- cat2 + start_index
				yy[i,j] <- sum(gh5$means_and_variances_data$observed_probs$values[startk:endk,j])
			}
		}
	} else {
		var <- toupper(var)
		cat_index <- as.integer(pmatch(var, catvars, nomatch=0))
		if (cat_index == 0) {
			cstr <- paste("- variable not found:",var,"\n\n")
			stop(cstr)
		}
		if (vartypes[cat_index] < 0) {
			cstr <- paste("- variable does not have estimated probabilities:",var,"\n\n")
			stop(cstr)
		}
		num_cat <- categories[cat_index]
		if (num_cat == 0) {
			cstr <- paste("- variable does not have estimated probabilities:",var,"\n\n")
			stop(cstr)
		}
		num_groups <- attr(gh5$model_group_labels,'dim')
		start_index <- 0
		if (cat_index > 1) {
			for (k in c(1:(cat_index-1))) {
				if (vartypes[k] == 0) {
					start_index <- start_index + categories[k]
				}
			}
		}
		
		yy <- array(0, c(num_cat,num_groups))
		for (i in c(1:num_groups)) {
			for (j in c(1:num_cat)){
				yy[j,i] <- gh5$means_and_variances_data$observed_probs$values[start_index+j,i]
			}
		}
	}

	# return the proportions
	return(yy)
}



##########################################################################
#
# mplus.list.variables - list the variables in individual data
#
# arguments: none
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.variables('ex8.1.gh5')
#
mplus.list.variables <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if individual data exists
	if ( !("individual_data" %in% names(gh5)) ) {
		stop("- requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data.")
	}

	cat(c("\nList of variable names to use in the following functions:\n"))
	cat(c(" - mplus.plot.histogram\n"))
	cat(c(" - mplus.plot.densityplot\n"))
	cat(c(" - mplus.plot.qqnorm\n"))
	cat(c(" - mplus.plot.scatterplot\n"))
	cat(c(" - mplus.get.data\n"))

	cat(c("\nVariables:\n"))

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')
	var_names <- gsub("(^\\s+|\\s+$)", "", var_names, perl=TRUE)
	var_names
}


##########################################################################
#
# mplus.get.data - return the individual data for the quoted variable
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	v - name of variable to plot
#
# eg. mplus.get.data('ex8.1.gh5','y1')
#
mplus.get.data <- function(file,v) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	if (!("individual_data" %in% names(gh5))) {
		stop("- requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data.")
	}

	if (missing(v)) {
		stop("- requires the name of a variable.\n\nUse mplus.list.variables() to get the list of variable names.")
	}

	# variables are stored in uppercase
	var <- toupper(v)

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')

	index <- pmatch(var, var_names, nomatch=0)

	if (index == 0) {
		cstr <- paste(c("Unknown variable:"),var,"\n")
		stop(cstr)
	}

	# get the data for the variable
	xx <- gh5$individual_data$raw_data[index,]
	xx[xx == 999] <- NA
	xx
}


##########################################################################
#
# mplus.list.cluster.idnums - list the idnums in individual data
#
# arguments: none
#	file - the quoted name of an existing GH5 file
#	clusvar - the cluster variable
#
# eg. mplus.list.cluster.idnums('ex8.1.gh5','cluster')
#
mplus.list.cluster.idnums <- function(file,clusvar,fprint=FALSE) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if individual data exists
	if ( !("individual_data" %in% names(gh5)) ) {
		stop("- requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data.")
	}

	# check if cluster information exists
	if ( !(mplus.check.group.attribute(file, 'individual_data', 'cluster'))) {
		stop("- requires cluster information.\n\nThe CLUSTER option must be used.")
	}

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')

	index <- pmatch(clusvar, var_names, nomatch=0)

	if (index == 0) {
		cstr <- paste(c("Unknown cluster variable:"),clusvar,"\n")
		stop(cstr)
	}

	ids <- mplus.get.data(file, clusvar)
	ids <- sort(unique(ids))

#	cat(c("\nList of variable names to use in the following functions:\n"))
#	cat(c(" - mplus.plot.timeseries\n"))

	if (fprint) {
		cstr <- sprintf("IDs for %s:", clusvar)
		cat(cstr,sep="\n")
	}

	ids
}


##########################################################################
#
# mplus.plot.scatterplot - plot the scatterplot for the 2 quoted variables
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	xv - name of variable on the x-axis
#	yv - name of variable on the y-axis
#
# eg. mplus.plot.scatterplot('ex8.1.gh5','y1','y2')
#
mplus.plot.scatterplot <- function(file, xv, yv) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if individual data exists
	if ( !("individual_data" %in% names(gh5)) ) {
		stop("mplus.plot.scatterplot requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data\nfor scatterplots.")
	}

	if (missing(xv) || missing(yv)) {
		stop("mplus.plot.scatterplot requires the names of two variables.\n\nUse mplus.list.variables() to get the list of variable names.")
	}

	# variables are stored in uppercase
	xvar <- toupper(xv)
	yvar <- toupper(yv)

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')

	xindex <- pmatch(xvar, var_names, nomatch=0)
	yindex <- pmatch(yvar, var_names, nomatch=0)

	if (xindex == 0) {
		cstr <- paste(c("Unknown x-variable:"),xvar,"\n")
		stop(cstr)
	}
	if (yindex == 0) {
		cstr <- paste(c("Unknown y-variable:"),yvar,"\n")
		stop(cstr)
	}

	# get the data for the 2 variables
	xx <- mplus.get.data(file,xvar)
	yy <- mplus.get.data(file,yvar)

	plot(xx,yy,xlab=xvar,ylab=yvar)
}


##########################################################################
#
# mplus.plot.histogram - plot the histogram for the quoted variable, using the
# specified number of bins (the default is 20 bins)
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	v - name of variable to plot
#	bins - the number of bins to use
#
# eg. mplus.plot.histogram('y1',5)
#
mplus.plot.histogram <- function(file,v,bins=20) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if individual data exists
	if ( !("individual_data" %in% names(gh5)) ) {
		stop("mplus.plot.histogram requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data\nfor histograms.")
	}

	if (missing(v)) {
		stop("mplus.plot.histogram requires the name of a variable.\n\nUse mplus.list.variables() to get the list of variable names.")
	}

	# the number of bins should be greater than 0
	if (bins <= 0) {
		stop("The number of bins should be greater than 0.")
	}

	# variables are stored in uppercase
	var <- toupper(v)

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')

	index <- pmatch(var, var_names, nomatch=0)

	if (index == 0) {
		cstr <- paste(c("Unknown variable:"),var,"\n")
		stop(cstr)
	}

	xx <- mplus.get.data(file,v)
	cstr <- paste(c("Histogram of"),var)
	hist(xx,breaks=seq(min(xx),max(xx),length=bins+1),col="red",main=cstr,xlab=var,right=TRUE)
}


##########################################################################
#
# mplus.plot.densityplot - plot the histogram with density plot 
#   for the quoted variable, using the specified number of bins (the default is 20 bins)
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	v - name of variable to plot
#	bins - the number of bins to use
#
# eg. mplus.plot.densityplot('y1',5)
#
mplus.plot.densityplot <- function(file,v,bins=20) {
	if (missing(file)) {
		stop("name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if individual data exists
	if ( !("individual_data" %in% names(gh5)) ) {
		stop("requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data for density plots.")
	}

	if (missing(v)) {
		stop("requires the name of a variable.\n\nUse mplus.list.variables() to get the list of variable names.")
	}

	# the number of bins should be greater than 0
	if (bins <= 0) {
		stop("the number of bins should be greater than 0")
	}

	# variables are stored in uppercase
	var <- toupper(v)

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')

	index <- pmatch(var, var_names, nomatch=0)

	if (index == 0) {
		cstr <- paste(c("unknown variable '"),var,"'\n",sep="")
		stop(cstr)
	}

	xx <- mplus.get.data(file,v)
	cstr <- paste(c("Density plot of"),var)
	hist(xx,freq=FALSE,breaks=seq(min(xx),max(xx),length=bins+1),col="red",main=cstr,xlab=var,right=TRUE)
	mn <- mean(xx)
	std <- sd(xx)
	curve(dnorm(x,mean=mn,sd=std),add=TRUE)
}


##########################################################################
#
# mplus.plot.qqnorm - plot the normal QQ plot for the quoted variable
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	v - name of variable to plot
#   between - TRUE if a between histogram should be shown
#   level - if between, level number - 2 or 3
#   fvariance - for level 1 variables, TRUE if variance over Within should be plotted
#        otherwise, average over within is plotted - default is FALSE
#
# eg. mplus.plot.qqnormplot('y1',5)
#
mplus.plot.qqnorm <- function(file,v,datax=FALSE,line=TRUE,main="Normal Q-Q Plot",xlab="Theoretical Quantiles",ylab="Sample Quantiles") {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if individual data exists
	if ( !("individual_data" %in% names(gh5)) ) {
		stop("mplus.plot.qqnorm requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data\nfor histograms.")
	}

	if (missing(v)) {
		stop("mplus.plot.qqnorm requires the name of a variable.\n\nUse mplus.list.variables() to get the list of variable names.")
	}

	# variables are stored in uppercase
	var <- toupper(v)

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')

	index <- pmatch(var, var_names, nomatch=0)

	if (index == 0) {
		cstr <- paste(c("Unknown variable:"),var,"\n")
		stop(cstr)
	}

	xx <- mplus.get.data(file,v)
	cstr <- paste(c("Normal QQ plot of"),var)

	qqnorm(xx,datax=datax,main=main,xlab=xlab,ylab=ylab)

	if (line) {
		qqline(xx,datax=datax)
	}
}



######################################################################################################
# Functions for BAYESIAN plots
######################################################################################################


#=========================================================================
#
# mplus.list.bayesian.parameters - list the parameters in bayesian data
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.bayesian.parameters('ex8.1.gh5')
#
mplus.list.bayesian.parameters <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data.\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	cat(c("\nList of parameters to use in the following functions:\n"))
	cat(c(" - mplus.plot.bayesian.trace_plots\n"))
	cat(c(" - mplus.plot.bayesian.distribution\n"))
	cat(c(" - mplus.plot.bayesian.prior.distribution\n"))
	cat(c(" - mplus.plot.bayesian.autocorrelation\n"))
	cat(c(" - mplus.get.bayesian.parameter.data\n"))
	cat(c(" - mplus.get.bayesian.prior.parameter.data\n"))
	cat(c(" - mplus.get.bayesian.autocorrelation\n"))

	cat(c("\nParameters:\n"))

	# get the parameter statements from bayesian_data and lookup the indices
	statements <- mplus.get.group.dataset(file, 'bayesian_data/parameters_autocorr', 'statements')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	nplaus <- length(statements)
	for (i in c(1:nplaus)) {
		cstr <- sprintf("[%d] %s", i, statements[i])
		cat(cstr,sep="\n")
	}
	invisible(statements)
}

#=========================================================================
#
# mplus.get.bayesian.parameter.data - get the bayesian data for the given parameter/chain
#
# arguments:
#	file - the quoted name of an existing GH5 file
#   paramstr - the quoted name of a parameter or the parameter index
#	chainnum - the chain number
#
# eg. mplus.get.bayesian.parameter.data('ex8.1.gh5','parameter 1',1)
#
mplus.get.bayesian.parameter.data <- function(file,paramstr,chainnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data.\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(paramstr)) {
		stop("- requires the parameter label or index.\n\nUse mplus.list.bayesian.parameters to get the list of parameters.")
	}

	if (is.character(paramstr)) {
		statements <- mplus.get.group.dataset(file, 'bayesian_data/parameters_autocorr', 'statements')
		statements <- tolower(statements)
		paramstr <- tolower(paramstr)
		paramidx <- pmatch(paramstr, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("Unknown parameter:"),paramstr,"\n")
			stop(cstr)
		}
	} else {
		# first dimension is the number of parameters
		# second dimension is the number of iterations
		# third dimension is the number of chains
		dims <- attr(gh5$bayesian_data$parameters_autocorr$parameters,"dim")

		paramidx <- paramstr
		if (paramidx < 1 || paramidx > dims[1]) {
			cstr <- paste("- parameter index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	}

	xx <- gh5$bayesian_data$parameters_autocorr$parameters[paramidx,,chainnum]
	xx
}


#=========================================================================
#
# mplus.get.bayesian.prior.parameter.data - get the prior data for the given parameter
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	paramstr - the quoted parameter label or the parameter index
#
# eg. mplus.get.bayesian.prior.parameter.data('ex8.1.gh5',1)
#
mplus.get.bayesian.prior.parameter.data <- function(file,paramstr) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data.\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	# first dimension is the number of parameters
	# second dimension is the number of priors
	dims <- attr(gh5$bayesian_data$parameters_autocorr$priors,"dim")

	if (missing(paramstr)) {
		stop("- requires the parameter label or index.\n\nUse mplus.list.bayesian.parameters to get the list of parameters.")
	}

	if (is.character(paramstr)) {
		statements <- mplus.get.group.dataset(file, 'bayesian_data/parameters_autocorr', 'statements')
		statements <- tolower(statements)
		paramstr <- tolower(paramstr)
		paramidx <- pmatch(paramstr, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown parameter:"),paramstr,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		# first dimension is the number of parameters
		# second dimension is the number of priors
		dims <- attr(gh5$bayesian_data$parameters_autocorr$priors,"dim")

		paramidx <- paramstr
		if (paramidx < 1 || paramidx > dims[1]) {
			cstr <- paste("- parameter index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	}

	xx <- gh5$bayesian_data$parameters_autocorr$priors[,paramidx]
	xx
}


#=========================================================================
#
# mplus.get.bayesian.autocorrelation - get the autocorrelation data for the given parameter
#
# arguments:
#	file - the quoted name of an existing GH5 file
#   paramidx - the quoted parameter label
#   chainnum - the chain number
#
# eg. mplus.get.bayesian.autocorrelation('ex8.1.gh5','parameter 1',1)
#
mplus.get.bayesian.autocorrelation <- function(file,paramstr,chainnum=1) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data.\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(paramstr)) {
		stop("- requires the parameter label or index.\n\nUse mplus.list.bayesian.parameters to get the list of parameters.")
	}

	# first dimension is the number of autocorrelation
	# second dimension is the number of parameters
	# third dimension is the number of chains
	dims <- attr(gh5$bayesian_data$parameters_autocorr$autocorrelation,"dim")
	if (is.character(paramstr)) {
		statements <- mplus.get.group.dataset(file, 'bayesian_data/parameters_autocorr', 'statements')
		statements <- tolower(statements)
		paramstr <- tolower(paramstr)
		paramidx <- pmatch(paramstr, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("Unknown parameter:"),paramstr,"\n")
			stop(cstr)
		}
	} else {
		paramidx <- paramstr
		if (paramidx < 1 || paramidx > dims[2]) {
			cstr <- paste("- parameter index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	}

	if (chainnum < 1 && chainnum > dims[3]) {
		cstr <- paste("- invalid chain number: ", chainnum,"\n\nThe chain number must be between 1 and ", dims[3], ".")
		stop(cstr)
	}

	xx <- gh5$bayesian_data$parameters_autocorr$autocorrelation[,paramidx,chainnum]
	xx
}

#=========================================================================
#
# mplus.plot.bayesian.traceplot - list the parameters in bayesian data
#
# arguments:
#	file - the quoted name of an existing GH5 file
#   paramstr - the quoted name of a parameter
#
# eg. mplus.plot.bayesian.traceplot('ex8.1.gh5','parameter 1')
#
mplus.plot.bayesian.traceplot <- function(file,paramstr) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(paramstr)) {
		stop("- requires the parameter label or index.\n\nUse mplus.list.bayesian.parameters to get the list of parameters.")
	}

	# get the dimensions of parameters array
	# first dimension is the number of parameters
	# second dimension is the number of iterations
	# third dimension is the number of chains
	dims <- attr(gh5$bayesian_data$parameters_autocorr$parameters,"dim")

	statements <- mplus.get.group.dataset(file, 'bayesian_data/parameters_autocorr', 'statements')

	if (is.character(paramstr)) {
		lcstatements <- tolower(statements)
		paramstr <- tolower(paramstr)
		paramidx <- pmatch(paramstr, lcstatements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown parameter:"),paramstr,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		paramidx <- paramstr
		if (paramidx < 1 || paramidx > dims[1]) {
			cstr <- paste("- parameter index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	}
	label <- statements[paramidx]
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	xx <- array(0, c(dims[2],dims[3]))
	yy <- array(0, c(dims[2],dims[3]))

	for (i in c(1:dims[3])) {
		yy[,i] <- mplus.get.bayesian.parameter.data(file, paramidx, i)
	}
	for (i in c(1:dims[2])) {
		xx[i,] <- i
	}

	colors <- rainbow(dims[3])


	ndist <- mplus.get.dataset.attribute(file, 'bayesian_data/parameters_autocorr/parameters', 'ndistribution')

	# plot the traceplot
	cstr <- paste("Trace plot of:",label)
	plot(xx,yy,xlab="",ylab="",main=cstr,type='n')
	for (i in c(1:dims[3])) {
		lines(xx[,i],yy[,i],col=colors[i])
	}
	abline(v=ndist,untf=FALSE,col='red')
}


#=========================================================================
#
# mplus.plot.bayesian.distribution - plot the histogram for the parameter, using the
# specified number of bins (the default is 100 bins)
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	paramstr - the quoted name of the parameter
#	bins - the number of bins to use
#
# eg. mplus.plot.bayesian.distribution('bayes.gh5','parameter 1',50)
#
mplus.plot.bayesian.distribution <- function(file,paramstr,bins=100) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(paramstr)) {
		stop("- requires the parameter label or index.\n\nUse mplus.list.bayesian.parameters to get the list of parameters.")
	}

	# the number of bins should be greater than 0
	if (bins <= 0) {
		stop("The number of bins should be greater than 0.")
	}

	# get the dimensions of parameters array
	# first dimension is the number of parameters
	# second dimension is the number of iterations
	# third dimension is the number of chains
	dims <- attr(gh5$bayesian_data$parameters_autocorr$parameters,"dim")
	#print(dims)

	statements <- mplus.get.group.dataset(file, 'bayesian_data/parameters_autocorr', 'statements')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	if (is.character(paramstr)) {
		lcstatements <- tolower(statements)
		paramstr <- tolower(paramstr)
		paramidx <- pmatch(paramstr, lcstatements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown parameter:"),paramstr,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		paramidx <- paramstr
		if (paramidx < 1 || paramidx > dims[1]) {
			cstr <- paste(" - parameter index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	}
	label <- statements[paramidx]

	ndist <- mplus.get.dataset.attribute(file, 'bayesian_data/parameters_autocorr/parameters', 'ndistribution')

	yy <- array(0, c(dims[2],dims[3]))
	if (ndist == dims[2]) {
		xx <- array(0, c(dims[2]*dims[3]))
	} else {
		xx <- array(0, c((dims[2]-ndist)*dims[3]))
	}

	for (i in c(1:dims[3])) {
		yy[,i] <- mplus.get.bayesian.parameter.data(file, paramidx, i)
	}
	start <- 0
	for (i in c(1:dims[3])) {
		if (ndist == dims[2]) {
			for (j in c(1:dims[2])) {
				start <- start + 1
				#cstr <- paste(start, j, i)
				#print(cstr)
				#print(xxc[j,i])
				xx[start] <- yy[j,i]
			}
		} else {
			for (j in c((ndist+1):dims[2])) {
				start <- start + 1
				#cstr <- paste(start, j, i)
				#print(cstr)
				#print(xxc[j,i])
				xx[start] <- yy[j,i]
			}
		}
	}

	cstr <- paste(c("Distribution of:"),label)
	h <- hist(xx,breaks=seq(min(xx),max(xx),length=bins+1),col="red",main=cstr,xlab='Estimate',ylab='Count')

	xxmode <- h$mids[h$counts == max(h$counts)]
	xxmean <- mean(xx)
	xxsd <- sd(xx)
	xxmedian <- median(xx)

	left <- quantile(xx, 0.025)
	right <- quantile(xx, 0.975)
	
	abline(v=xxmode,untf=FALSE,col='green')
	abline(v=xxmean,untf=FALSE,col='brown')
	abline(v=xxmedian,untf=FALSE,col='purple')
	abline(v=left,untf=FALSE,col='blue')
	abline(v=right,untf=FALSE,col='blue')

	modestr <- sprintf("Mode = %0.5f", xxmode)
	meanstr <- sprintf("Mean = %0.5f, Std Dev = %0.5f", xxmean, xxsd)
	medianstr <- sprintf("Median = %0.5f", xxmedian)
	lowci <- sprintf("95%% Lower CI = %0.5f", left)
	uppci <- sprintf("95%% Upper CI = %0.5f", right)
	ldesc <- c(meanstr, medianstr, modestr, lowci, uppci)

	lcol <- c('brown','purple','green','blue','blue')
	legend("topright",ldesc,col=lcol,lty=c(1,1,1,1,1),lwd=c(2.5,2.5,2.5,2.5,2.5))

	#invisible(xx)
}



#=========================================================================
#
# mplus.plot.bayesian.prior.distribution - plot the histogram for the parameter, using the
# specified number of bins (the default is 100 bins)
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	paramstr - the quoted name of the parameter
#	bins - the number of bins to use
#
# eg. mplus.plot.bayesian.prior.distribution('bayes.gh5','parameter 1',50)
#
mplus.plot.bayesian.prior.distribution <- function(file,paramstr,bins=100) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(paramstr)) {
		stop("- requires the parameter label or index.\n\nUse mplus.list.bayesian.parameters to get the list of parameters.")
	}

	# the number of bins should be greater than 0
	if (bins <= 0) {
		stop("- the number of bins should be greater than 0")
	}

	statements <- mplus.get.group.dataset(file, 'bayesian_data/parameters_autocorr', 'statements')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	# get the dimensions of parameters array
	# first dimension is the number of parameters
	# second dimension is the number of priors
	dims <- attr(gh5$bayesian_data$parameters_autocorr$priors,"dim")

	if (is.character(paramstr)) {
		lcstatements <- tolower(statements)
		paramstr <- tolower(paramstr)
		paramidx <- pmatch(paramstr, lcstatements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown parameter:"),paramstr,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		paramidx <- paramstr
		if (paramidx < 1 || paramidx > dims[1]) {
			cstr <- paste("- parameter index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	}
	plabel <- statements[paramidx]

	xx <- mplus.get.bayesian.prior.parameter.data(file, paramidx)

	if (min(xx) == 999 && max(xx) == 999) {
		stop("- prior distributions for this parameter cannot be displayed because the prior is improper")
	} else if (min(xx) == 998 && max(xx) == 998) {
		stop("- prior distributions for this parameter are not available")
	}

	cstr <- paste(c("Prior distribution of:"),plabel)
	h <- hist(xx,breaks=seq(min(xx),max(xx),length=bins+1),col="red",main=cstr,xlab='Estimate',ylab='Count')

	xxmode <- h$mids[h$counts == max(h$counts)]
	xxmean <- mean(xx)
	xxsd <- sd(xx)
	xxmedian <- median(xx)

	left <- quantile(xx, 0.025)
	right <- quantile(xx, 0.975)
	
	abline(v=xxmode,untf=FALSE,col='green')
	abline(v=xxmean,untf=FALSE,col='brown')
	abline(v=xxmedian,untf=FALSE,col='purple')
	abline(v=left,untf=FALSE,col='blue')
	abline(v=right,untf=FALSE,col='blue')

	modestr <- sprintf("Mode = %0.5f", xxmode)
	meanstr <- sprintf("Mean = %0.5f, Std Dev = %0.5f", xxmean, xxsd)
	medianstr <- sprintf("Median = %0.5f", xxmedian)
	lowci <- sprintf("95%% Lower CI = %0.5f", left)
	uppci <- sprintf("95%% Upper CI = %0.5f", right)
	ldesc <- c(meanstr, medianstr, modestr, lowci, uppci)

	lcol <- c('brown','purple','green','blue','blue')
	legend("topright",ldesc,col=lcol,lty=c(1,1,1,1,1),lwd=c(2.5,2.5,2.5,2.5,2.5))

	invisible(xx)
}



#=========================================================================
#
# mplus.plot.bayesian.autocorrelation - plot the autocorrelation histogram for the parameter
#	for the given chain
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	paramstr - the quoted name of the parameter
#	chainnum - the chain number
#
# eg. mplus.plot.bayesian.autocorrelation('bayes.gh5','parameter 1',1)
#
mplus.plot.bayesian.autocorrelation <- function(file,paramstr,chainnum=1) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian dat.\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(paramstr)) {
		stop("- requires the parameter label or index.\n\nUse mplus.list.bayesian.parameters to get the list of parameters.")
	}

	# get the dimensions of parameters array
	# first dimension is the number of autocorrelations
	# second dimension is the number of parameters
	# third dimension is the number of chains
	dims <- attr(gh5$bayesian_data$parameters_autocorr$autocorrelation,"dim")

	statements <- mplus.get.group.dataset(file, 'bayesian_data/parameters_autocorr', 'statements')

	if (is.character(paramstr)) {
		lcstatements <- tolower(statements)
		paramstr <- tolower(paramstr)
		paramidx <- pmatch(paramstr, lcstatements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown parameter:"),paramstr,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		paramidx <- paramstr
		if (paramidx < 1 || paramidx > dims[2]) {
			cstr <- paste("- parameter index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	}
	plabel <- statements[paramidx]

	if (chainnum < 1 && chainnum > dims[3]) {
		cstr <- paste("- invalid chain number: ", chainnum,"\n\nThe chain number must be between 1 and ", dims[3], ".")
		stop(cstr)
	}

	yy <- mplus.get.bayesian.autocorrelation(file,paramidx,chainnum)
	xx <- as.character(1:dims[1])

	cstr <- paste(c("Autocorrelation (chain "),format(chainnum),c("): "),plabel)
	barplot(yy,ylim=c(-1,1),names.arg=xx,col='red',main=cstr)

	invisible(xx)
}


#=========================================================================
#
# mplus.list.bayesian.predictive.labels - list the parameters in bayesian data
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.bayesian.predictive.labels('ex8.1.gh5')
#
mplus.list.bayesian.predictive.labels <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	cat(c("\nList of parameters to use in the following functions:\n"))
	cat(c(" - mplus.plot.bayesian.predictive.scatterplot\n"))
	cat(c(" - mplus.plot.bayesian.predictive.distribution\n"))
	cat(c(" - mplus.get.bayesian.predictive.observed\n"))
	cat(c(" - mplus.get.bayesian.predictive.replicated\n"))
	cat(c(" - mplus.get.bayesian.predictive.lowerci\n"))
	cat(c(" - mplus.get.bayesian.predictive.upperci\n"))
	cat(c(" - mplus.get.bayesian.predictive.pvalue\n"))
	cat(c(" - mplus.get.bayesian.predictive.pvalue_type\n"))

	cat(c("\nPredictive labels:\n"))

	# get the parameter statements from bayesian_data and lookup the indices
	statements <- mplus.get.group.dataset(file, 'bayesian_data/predictive', 'labels')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)
	statements
}


#=========================================================================
#
# mplus.get.bayesian.predictive.observed - get the predictive observed data
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	plabel - the quoted name of the parameter
#
# eg. mplus.get.bayesian.predictive.observed('bayes.gh5','parameter 1')
#
mplus.get.bayesian.predictive.observed <- function(file,plabel) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(plabel)) {
		stop("- requires the predictive label or index.\n\nUse mplus.list.bayesian.predictive.labels to get the list of parameters.")
	}

	if (is.character(plabel)) {
		statements <- mplus.get.group.dataset(file, 'bayesian_data/predictive', 'labels')
		statements <- tolower(statements)
		plabel <- tolower(plabel)
		paramidx <- pmatch(plabel, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown predictive label:"),plabel,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of parameters array
		# first dimension is the number of ???
		# second dimension is the number of predictive labels
		dims <- attr(gh5$bayesian_data$predictive$observed,"dim")

		paramidx <- plabel
		if (paramidx < 1 || paramidx > dims[2]) {
			cstr <- paste("- predictive label index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	}

	xx <- gh5$bayesian_data$predictive$observed[,paramidx]
	xx
}



#=========================================================================
#
# mplus.get.bayesian.predictive.replicated - get the predictive replicated data
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	plabel - the quoted name of the parameter
#
# eg. mplus.get.bayesian.predictive.replicated('bayes.gh5','parameter 1')
#
mplus.get.bayesian.predictive.replicated <- function(file,plabel) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(plabel)) {
		stop("- requires the predictive label or index.\n\nUse mplus.list.bayesian.predictive.labels to get the list of parameters.")
	}

	if (is.character(plabel)) {
		statements <- mplus.get.group.dataset(file, 'bayesian_data/predictive', 'labels')
		statements <- tolower(statements)
		plabel <- tolower(plabel)
		paramidx <- pmatch(plabel, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown predictive label:"),plabel,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of parameters array
		# first dimension is the number of ???
		# second dimension is the number of predictive labels
		dims <- attr(gh5$bayesian_data$predictive$replicated,"dim")

		paramidx <- plabel
		if (paramidx < 1 || paramidx > dims[2]) {
			cstr <- paste("- predictive label index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	}

	xx <- gh5$bayesian_data$predictive$replicated[,paramidx]
	xx
}


#=========================================================================
#
# mplus.get.bayesian.predictive.lowerci - get the predictive lower CI
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	plabel - the quoted name of the parameter
#
# eg. mplus.get.bayesian.predictive.lowerci('bayes.gh5','parameter 1')
#
mplus.get.bayesian.predictive.lowerci <- function(file,plabel) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(plabel)) {
		stop("- requires the predictive label or index.\n\nUse mplus.list.bayesian.predictive.labels to get the list of parameters.")
	}

	if (is.character(plabel)) {
		statements <- mplus.get.group.dataset(file, 'bayesian_data/predictive', 'labels')
		statements <- tolower(statements)
		plabel <- tolower(plabel)
		paramidx <- pmatch(plabel, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown predictive label:"),plabel,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of parameters array
		# first dimension is the number of pvalues
		# second dimension is the number of predictive labels
		dims <- attr(gh5$bayesian_data$predictive$pvalues,"dim")

		paramidx <- plabel
		if (paramidx < 1 || paramidx > dims[2]) {
			cstr <- paste("- predictive label index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	}

	gh5$bayesian_data$predictive$pvalues[1,paramidx]
}



#=========================================================================
#
# mplus.get.bayesian.predictive.upperci - get the predictive upper CI
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	plabel - the quoted name of the parameter
#
# eg. mplus.get.bayesian.predictive.upperci('bayes.gh5','parameter 1')
#
mplus.get.bayesian.predictive.upperci <- function(file,plabel) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(plabel)) {
		stop("- requires the predictive label or index.\n\nUse mplus.list.bayesian.predictive.labels to get the list of parameters.")
	}

	if (is.character(plabel)) {
		statements <- mplus.get.group.dataset(file, 'bayesian_data/predictive', 'labels')
		statements <- tolower(statements)
		plabel <- tolower(plabel)
		paramidx <- pmatch(plabel, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown predictive label:"),plabel,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of parameters array
		# first dimension is the number of pvalues
		# second dimension is the number of predictive labels
		dims <- attr(gh5$bayesian_data$predictive$pvalues,"dim")

		paramidx <- plabel
		if (paramidx < 1 || paramidx > dims[2]) {
			cstr <- paste("- predictive label index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	}

	gh5$bayesian_data$predictive$pvalues[2,paramidx]
}




#=========================================================================
#
# mplus.get.bayesian.predictive.pvalue - get the predictive pvalue
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	plabel - the quoted name of the parameter
#
# eg. mplus.get.bayesian.predictive.pvalue('bayes.gh5','parameter 1')
#
mplus.get.bayesian.predictive.pvalue <- function(file,plabel) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(plabel)) {
		stop("- requires the predictive label or index.\n\nUse mplus.list.bayesian.predictive.labels to get the list of parameters.")
	}

	if (is.character(plabel)) {
		statements <- mplus.get.group.dataset(file, 'bayesian_data/predictive', 'labels')
		statements <- tolower(statements)
		plabel <- tolower(plabel)
		paramidx <- pmatch(plabel, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown predictive label:"),plabel,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of parameters array
		# first dimension is the number of pvalues
		# second dimension is the number of predictive labels
		dims <- attr(gh5$bayesian_data$predictive$pvalues,"dim")

		paramidx <- plabel
		if (paramidx < 1 || paramidx > dims[2]) {
			cstr <- paste("- predictive label index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	}

	gh5$bayesian_data$predictive$pvalues[3,paramidx]
}




#=========================================================================
#
# mplus.get.bayesian.predictive.pvalue_type - get the predictive pvalue type
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	plabel - the quoted name of the parameter
#
# eg. mplus.get.bayesian.predictive.pvalue_type('bayes.gh5','parameter 1')
#
mplus.get.bayesian.predictive.pvalue_type <- function(file,plabel) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(plabel)) {
		stop("- requires the predictive label or index.\n\nUse mplus.list.bayesian.predictive.labels to get the list of parameters.")
	}

	ptypes <- mplus.get.group.dataset(file,'bayesian_data/predictive','types')

	if (is.character(plabel)) {
		statements <- mplus.get.group.dataset(file, 'bayesian_data/predictive', 'labels')
		statements <- tolower(statements)
		plabel <- tolower(plabel)
		paramidx <- pmatch(plabel, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown predictive label:"),plabel,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of parameters array
		# first dimension is the number of pvalues
		dims <- attr(ptypes,"dim")

		paramidx <- plabel
		if (paramidx < 1 || paramidx > dims[1]) {
			cstr <- paste("- predictive label index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	}

	ptypes[paramidx]
}




#=========================================================================
#
# mplus.plot.bayesian.predictive.scatterplot - plot the predictive checking scatterplot
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	plabel - the predictive label
#
# eg. mplus.plot.bayesian.predictive.scatterplot('bayes.gh5','label 1')
#
mplus.plot.bayesian.predictive.scatterplot <- function(file,plabel) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(plabel)) {
		stop("- requires the predictive label or index.\n\nUse mplus.list.bayesian.predictive.labels to get the list of parameters.")
	}

	statements <- mplus.get.group.dataset(file, 'bayesian_data/predictive', 'labels')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	dims <- attr(statements,"dim")

	if (is.character(plabel)) {
		lcstatements <- tolower(statements)
		plabel <- tolower(plabel)
		paramidx <- pmatch(plabel, lcstatements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown predictive label:"),plabel,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		paramidx <- plabel
		if (paramidx < 1 || paramidx > dims[1]) {
			cstr <- paste("- predictive label index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	}

	rep <- mplus.get.bayesian.predictive.replicated(file,paramidx)
	obs <- mplus.get.bayesian.predictive.observed(file,paramidx)

	omin <- min(obs)
	omax <- max(obs)
	
	rmin <- min(rep)
	rmax <- max(rep)
	
	if (omin < rmin) {
		rmin <- omin
	}
	if (omax > rmax) {
		rmax <- omax
	}
	
	plot(obs,rep,xlab='Observed',ylab='Replicated',xlim=c(rmin,rmax),ylim=c(rmin,rmax))
#	print(rmin)
#	print(rmax)
	xx=c(rmin,rmax)
	yy=c(rmin,rmax)
	lines(xx,yy,col='green')
	#text(50,50,"test")

	lowci <- mplus.get.bayesian.predictive.lowerci(file,paramidx)
	uppci <- mplus.get.bayesian.predictive.upperci(file,paramidx)
	pval <- mplus.get.bayesian.predictive.pvalue(file,paramidx)
	ptype <- mplus.get.bayesian.predictive.pvalue_type(file,paramidx)

	if (ptype == -1) {
		text2 <- "(Proportion of Points in the Lower Right Half)";
	}
	else if (ptype == 1) {
		text2 <- "(Proportion of Points in the Upper Left Half)";
	} else {
		text2 <- "(Smallest Proportion of Points in the Upper versus Lower Halves)";
	}

	#ldesc <- sprintf("95%% Confidence Interval for the Difference\n%0.3f     %0.3f\nPosterior Predictive P-Value %0.3f\n%s",
	#		lowci, uppci, pval, text2)

	#mtext(ldesc, side=3)

	line1 <- sprintf("95%% Confidence Interval for the Difference")
	line2 <- sprintf("            %0.3f     %0.3f                ", lowci, uppci)
	line3 <- sprintf("")
	line4 <- sprintf("   Posterior Predictive P-Value %0.3f      ", pval)
	line5 <- sprintf("")
	line6 <- text2

	ldesc <- c(line1,line2,line3,line4,line5,line6)
	legend('topleft',ldesc,xjust=1)

	title(statements[paramidx])
}




#=========================================================================
#
# mplus.plot.bayesian.predictive.distribution - plot the predictive checking distribution
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	plabel - the predictive label
#	bins - the number of bins, default is 10
#
# eg. mplus.plot.bayesian.predictive.distribution('bayes.gh5','label 1')
#
mplus.plot.bayesian.predictive.distribution <- function(file,plabel,bins=100) {
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data\n\nUse TYPE=PLOT2 setting in Mplus with a Bayesian analysis.")
	}

	if (missing(plabel)) {
		stop("- requires the index of the predictive label\n\nUse mplus.list.bayesian.predictive.labels to get the list of parameters.")
	}

	statements <- mplus.get.group.dataset(file, 'bayesian_data/predictive', 'labels')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	dims <- attr(statements,"dim")

	if (is.character(plabel)) {
		lcstatements <- tolower(statements)
		plabel <- tolower(plabel)
		paramidx <- pmatch(plabel, lcstatements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown predictive label:"),plabel,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		paramidx <- plabel
		if (paramidx < 1 || paramidx > dims[1]) {
			cstr <- paste("- predictive label index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.predictive.labels to see the list of parameters.\n")
			stop(cstr)
		}
	}

	rep <- mplus.get.bayesian.predictive.replicated(file,paramidx)
	obs <- mplus.get.bayesian.predictive.observed(file,paramidx)

	omin <- min(obs)
	omax <- max(obs)
	
	rmin <- min(rep)
	rmax <- max(rep)
	
	if (omin < rmin) {
		rmin <- omin
	}
	if (omax > rmax) {
		rmax <- omax
	}

	npred <- length(rep)
	vals <- array(c(npred))
	for (i in c(1:npred)) {
		vals[i] <- obs[i] - rep[i]
	}
	hist(vals,breaks=seq(min(vals),max(vals),length=bins+1),col="red",main=statements[paramidx],xlab='Observed - Replicated',ylab='Count')

	xxmedian <- median(vals)
	abline(v=xxmedian,untf=FALSE,col='purple')

#	print(rmin)
#	print(rmax)
	xx=c(rmin,rmax)
	yy=c(rmin,rmax)
	lines(xx,yy,col='green')
	#text(50,50,"test")

	lowci <- mplus.get.bayesian.predictive.lowerci(file,paramidx)
	uppci <- mplus.get.bayesian.predictive.upperci(file,paramidx)
	pval <- mplus.get.bayesian.predictive.pvalue(file,paramidx)

	#ldesc <- sprintf("95%% Confidence Interval for the Difference\n%0.3f     %0.3f\nPosterior Predictive P-Value %0.3f\n%s",
	#		lowci, uppci, pval, text2)

	#mtext(ldesc, side=3)

	line1 <- sprintf("95%% Confidence Interval for the Difference")
	line2 <- sprintf("            %0.3f     %0.3f                ", lowci, uppci)
	line3 <- sprintf("")
	line4 <- sprintf("   Posterior Predictive P-Value %0.3f      ", pval)

	ldesc <- c(line1,line2,line3,line4)
	legend('topleft',ldesc,xjust=1)
}



#=========================================================================
#
# mplus.list.bayesian.plausible.labels - list the plausible labels in bayesian data
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.bayesian.plausible.labels('ex8.1.gh5')
#
mplus.list.bayesian.plausible.labels <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data and factor scores.\n\nUse TYPE=PLOT3 and the FACTORS option in Mplus with a Bayesian analysis.")
	}

	# check if plausible exists
	if ( !("plausible" %in% names(gh5$bayesian_data)) ) {
		stop("- requires bayesian data factor scores.\n\nUse TYPE=PLOT3 and the FACTORS option in Mplus with a Bayesian analysis.")
	}

	cat(c("\nList of labels to use in the following functions:\n"))
	cat(c(" - mplus.plot.bayesian.plausible.distribution\n"))
	cat(c(" - mplus.get.bayesian.plausible.data\n"))

	cat(c("\nPlausible labels:\n"))

	# get the parameter statements from bayesian_data and lookup the indices
	statements <- mplus.get.group.attribute(file, 'bayesian_data/plausible', 'plauslabels')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	nplaus <- length(statements)
	for (i in c(1:nplaus)) {
		cstr <- sprintf("[%d] %s", i, statements[i])
		cat(cstr,sep="\n")
	}
#	cat(statements,sep="\n")
	invisible(statements)
}



#=========================================================================
#
# mplus.get.bayesian.plausible.data - get plausible data for the given plausible label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	plauslabel - the plausible label or the index of the plausible label
#	obs - the observation index or 0 for overall
#
# eg. mplus.get.bayesian.plausible.data('ex8.1.gh5',1,obs)
#
mplus.get.bayesian.plausible.data <- function(file,plauslabel,obs=0) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data and factor scores\n\nUse TYPE=PLOT3 and the FACTORS option in Mplus with a Bayesian analysis.")
	}

	# check if plausible exists
	if ( !("plausible" %in% names(gh5$bayesian_data)) ) {
		stop("- requires bayesian data and factor scores\n\nUse TYPE=PLOT3 and the FACTORS option in Mplus with a Bayesian analysis.")
	}

	if (missing(plauslabel)) {
		stop("- requires the plausible label or index.\n\nUse mplus.list.bayesian.plausible.labels to get the list of plausible labels.")
	}

	if (is.character(plauslabel)) {
		labels <- mplus.get.group.attribute(file,'bayesian_data/plausible','plauslabels')
		labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
		labels <- tolower(labels)
		plauslabel <- tolower(plauslabel)
		paramidx <- pmatch(plauslabel, labels, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown plausible label:"),plauslabel,"\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of plausible array
		# first dimension is the number of observations
		# second dimension is the number of imputations
		# third dimension is the number of labels
		dims <- attr(gh5$bayesian_data$plausible$plausible,"dim")

		paramidx <- plauslabel
		if (paramidx < 1 || paramidx > dims[3]) {
			cstr <- paste("- plausible label index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.plausible.labels to see the list of plausible labels.\n")
			stop(cstr)
		}
	}

	if (obs == 0) {
		xx <- array(0, c(dims[1]*dims[2]))
		start <- 0
		for (i in c(1:dims[1])) {
			for (j in c(1:dims[2])) {
				start <- start + 1
				xx[start] <- gh5$bayesian_data$plausible$plausible[i,j,paramidx]
			}
		}
	} else {
		xx <- gh5$bayesian_data$plausible$plausible[obs,,paramidx]
	}
	xx
}



#=========================================================================
#
# mplus.plot.bayesian.plausible.distribution - plot the histogram for the plausible label, using the
# specified number of bins (the default is 100 bins for overall and 10 for a specific observation)
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	paramstr - name or index of variable to plot
#	obs - the observation number or 0
#	bins - the number of bins to use
#
# eg. mplus.plot.bayesian.plausible.distribution('bayes.gh5',1,0)
#
mplus.plot.bayesian.plausible.distribution <- function(file,plauslabel,obs=0,bins=100) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bayesian data exists
	if ( !("bayesian_data" %in% names(gh5)) ) {
		stop("- requires bayesian data and factor scores\n\nUse TYPE=PLOT3 and the FACTORS option in Mplus with a Bayesian analysis.")
	}

	# check if plausible exists
	if ( !("plausible" %in% names(gh5$bayesian_data)) ) {
		stop("- requires bayesian data and factor scores\n\nUse TYPE=PLOT3 and the FACTORS option in Mplus with a Bayesian analysis.")
	}

	if (missing(plauslabel)) {
		stop("- requires the index of the plausible label or index.\n\nUse mplus.list.bayesian.plausible.labels to get the list of plausible labels.")
	}

	if (missing(bins)) {
		if (obs == 0) {
			bins = 100
		} else {
			bins = 10
		}
	}

	# the number of bins should be greater than 0
	if (bins <= 0) {
		stop("- the number of bins should be greater than 0")
	}

	labels <- mplus.get.group.attribute(file,'bayesian_data/plausible','plauslabels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
	adim <- attr(labels,'dim')

	if (is.character(plauslabel)) {
		lclabels <- tolower(labels)
		plauslabel <- tolower(plauslabel)
		paramidx <- pmatch(plauslabel, lclabels, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown plausible label:"),plauslabel,"\n")
			stop(cstr)
		}
	} else {
		paramidx <- plauslabel
		if (paramidx < 1 || paramidx > adim[1]) {
			cstr <- paste("- plausible index is out of range: ",paramidx,"\n\nUse mplus.list.bayesian.plausible.labels to see the list of plausible labels.\n")
			stop(cstr)
		}
	}

	xx <- mplus.get.bayesian.plausible.data(file,paramidx,obs)

	xxmax <- max(xx)
	xxmin <- min(xx)
#	print(xxmax)
#	print(xxmin)

	if (obs == 0) {
		cstr <- paste(c("Overall distribution of"),labels[paramidx])
	} else {
		cstr <- sprintf("Distribution of %s for Individual %d", labels[paramidx], obs)
	}
	h <- hist(xx,breaks=seq(min(xx),max(xx),length=bins+1),col="red",main=cstr,xlab='Estimate',ylab='Count')

	xxmode <- h$mids[h$counts == max(h$counts)]
	xxmean <- mean(xx)
	xxsd <- sd(xx)
	xxmedian <- median(xx)

	left <- quantile(xx, 0.025,type=3)
	right <- quantile(xx, 0.975,type=3)
	
	abline(v=xxmode,untf=FALSE,col='green')
	abline(v=xxmean,untf=FALSE,col='brown')
	abline(v=xxmedian,untf=FALSE,col='purple')
	abline(v=left,untf=FALSE,col='blue')
	abline(v=right,untf=FALSE,col='blue')

	modestr <- sprintf("Mode = %0.5f", xxmode)
	meanstr <- sprintf("Mean = %0.5f, Std Dev = %0.5f", xxmean, xxsd)
	medianstr <- sprintf("Median = %0.5f", xxmedian)
	lowci <- sprintf("95%% Lower CI = %0.5f", left)
	uppci <- sprintf("95%% Upper CI = %0.5f", right)
	ldesc <- c(meanstr, medianstr, modestr, lowci, uppci)

	lcol <- c('brown','purple','green','blue','blue')
	legend("topleft",ldesc,col=lcol,lty=c(1,1,1,1,1),lwd=c(2.5,2.5,2.5,2.5,2.5))

	invisible(xx)
}

######################################################################################################
# Functions for LOOP PLOT
######################################################################################################

#========================================================================
#
# mplus.list.loop.labels - list the loop variables
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.loop.labels('ex8.1.gh5')
#
mplus.list.loop.labels <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if loop data exists
	if ( !("loop_data" %in% names(gh5)) ) {
		stop("- requires loop data.\n\nUse TYPE=PLOT2 and the PLOT/LOOP keywords in MODEL CONSTRAINT.")
	}

	cat(c("\nList of loop labels to use in the following functions:\n"))
	cat(c(" - mplus.plot.loop\n"))
	cat(c(" - mplus.get.loop.estimates\n"))
	cat(c(" - mplus.get.loop.lowerci\n"))
	cat(c(" - mplus.get.loop.upperci\n"))
	cat(c(" - mplus.get.loop.xvalues\n"))

	cat(c("\nLoop labels:\n"))

	# get the parameter statements from loop_data and lookup the indices
	statements <- mplus.get.group.attribute(file, 'loop_data', 'labels')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	nplaus <- length(statements)
	for (i in c(1:nplaus)) {
		cstr <- sprintf("[%d] %s", i, statements[i])
		cat(cstr,sep="\n")
	}
#	cat(statements,sep="\n")
	invisible(statements)
}



#========================================================================
#
# mplus.get.loop.estimates - get the estimates for the given loop label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	label - the quoted label or index of the label in the list
#
# eg. mplus.get.loop.estimates('ex8.1.gh5','indirect')
#
mplus.get.loop.estimates <- function(file,label=1) {
	if (missing(file)) {
		stop("- - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste(" - file does not exist:",file)
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if loop data exists
	if ( !("loop_data" %in% names(gh5)) ) {
		stop(" - requires loop data\n\nUse TYPE=PLOT2 and the PLOT/LOOP keywords in MODEL CONSTRAINT.")
	}

	if (is.character(label)) {
		labels <- mplus.get.group.attribute(file,'loop_data','labels')
		labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
		labels <- tolower(labels)
		label <- tolower(label)
		loopidx <- pmatch(label, labels, nomatch=0)

		if (loopidx == 0) {
			cstr <- paste(c(" - unknown label:"),label,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of the estimates dataset
		# first dimension is the number of loop labels
		# second dimension is the number of x points
		dims <- attr(gh5$loop_data$estimates,'dim')

		if (label <= 0 || label > dims[1]) {
			cstr <- paste(" - index is out of range: ",label,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
			stop(cstr)
		}
		loopidx <- label
	}

	gh5$loop_data$estimates[loopidx,]
}



#========================================================================
#
# mplus.get.loop.lowerci - get the lower CI values for the given loop label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	label - the quoted label or index of the label in the list
#
# eg. mplus.get.loop.lowerci('ex8.1.gh5','indirect')
#
mplus.get.loop.lowerci <- function(file,label=1) {
	if (missing(file)) {
		stop("- - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste(" - file does not exist:",file)
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if loop data exists
	if ( !("loop_data" %in% names(gh5)) ) {
		stop(" - requires loop data\n\nUse TYPE=PLOT2 and the PLOT/LOOP keywords in MODEL CONSTRAINT.")
	}

	if (is.character(label)) {
		labels <- mplus.get.group.attribute(file,'loop_data','labels')
		labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
		labels <- tolower(labels)
		label <- tolower(label)
		loopidx <- pmatch(label, labels, nomatch=0)

		if (loopidx == 0) {
			cstr <- paste(c(" - unknown label:"),label,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of the estimates dataset
		# first dimension is the number of loop labels
		# second dimension is the number of x points
		dims <- attr(gh5$loop_data$estimates,'dim')

		if (label <= 0 || label > dims[1]) {
			cstr <- paste(" - index is out of range: ",label,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
			stop(cstr)
		}
		loopidx <- label
	}

	gh5$loop_data$lowerci[loopidx,]
}



#========================================================================
#
# mplus.get.loop.upperci - get the upper CI values for the given loop label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	label - the quoted label or index of the label in the list
#
# eg. mplus.get.loop.upperci('ex8.1.gh5','indirect')
#
mplus.get.loop.upperci <- function(file,label=1) {
	if (missing(file)) {
		stop("- - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste(" - file does not exist:",file)
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if loop data exists
	if ( !("loop_data" %in% names(gh5)) ) {
		stop(" - requires loop data\n\nUse TYPE=PLOT2 and the PLOT/LOOP keywords in MODEL CONSTRAINT.")
	}

	if (is.character(label)) {
		labels <- mplus.get.group.attribute(file,'loop_data','labels')
		labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
		labels <- tolower(labels)
		label <- tolower(label)
		loopidx <- pmatch(label, labels, nomatch=0)

		if (loopidx == 0) {
			cstr <- paste(c("- unknown label:"),label,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		# get the dimensions of the estimates dataset
		# first dimension is the number of loop labels
		# second dimension is the number of x points
		dims <- attr(gh5$loop_data$estimates,'dim')

		if (label <= 0 || label > dims[1]) {
			cstr <- paste(" - index is out of range: ",label,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
			stop(cstr)
		}
		loopidx <- label
	}

	gh5$loop_data$upperci[loopidx,]
}



#========================================================================
#
# mplus.get.loop.xvalues - get the x points for the loop plots
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.get.loop.xvalues('ex8.1.gh5')
#
mplus.get.loop.xvalues <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if loop data exists
	if ( !("loop_data" %in% names(gh5)) ) {
		stop("- requires loop data.\n\nUse TYPE=PLOT2 and the PLOT/LOOP keywords in MODEL CONSTRAINT.")
	}

	gh5$loop_data$xvalues
}



#========================================================================
#
# mplus.plot.loop - plot the loop label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	label - the quoted label or index of the label in the list
#	showgrid - option to turn off grid lines, default is to show grid
#
# eg. mplus.plot.loop('ex8.1.gh5',1)
#
mplus.plot.loop <- function(file,label=1,showgrid=TRUE,ylim,linecolors,linetype,noci=FALSE,xlabel,ylabel) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if loop data exists
	if ( !("loop_data" %in% names(gh5)) ) {
		stop("- requires loop data.\n\nUse TYPE=PLOT2 and the PLOT/LOOP keywords in MODEL CONSTRAINT.")
	}

	if (!missing(showgrid)) {
		if (!is.logical(showgrid)) {
			cstr <- paste(" - specify TRUE or FALSE for showgrid\n")
			stop(cstr)
		}
	}

	# get the dimensions of the estimates dataset
	# first dimension is the number of loop labels
	# second dimension is the number of x points
	props <- mplus.get.group.attribute(file,'loop_data','properties')

	# get the parameter statements from loop_data and lookup the indices
	labels <- mplus.get.group.attribute(file, 'loop_data', 'labels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
	labels <- tolower(labels)

	if (length(label) > 1) {
		loopindices <- vector()
		num_loop <- length(label)
		for (r in c(1:num_loop)) {
			var <- label[r]
			if (is.character(var)) {
				var <- tolower(var)
				index <- pmatch(var, labels, nomatch=0)
				if (index == 0) {
					cstr <- paste(c("- unknown label:"),var,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
					stop(cstr)
				}
				loopindices[r] = index
			} else {
				if (var <= 0 || var > props[1]) {
					cstr <- paste(" - index is out of range: ",var,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
					stop(cstr)
				}
				loopindices[r] = var
			}
		}

		labels <- mplus.get.group.attribute(file, 'loop_data', 'labels')
		labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)

		loopvar <- mplus.get.group.attribute(file,'loop_data','loop_variable')
		loopvar <- gsub("(^\\s+|\\s+$)", "", loopvar, perl=TRUE)

		if (noci) {
		  xx <- array(0,c(num_loop,props[2]))
		  yy <- array(0,c(num_loop,props[2]))
		} else {
		  xx <- array(0,c(3*num_loop,props[2]))
		  yy <- array(0,c(3*num_loop,props[2]))
		}

		for (r in c(1:num_loop)) {
			loopidx <- loopindices[r]
			if (noci) {
			  xx[(r-1)+1,] <- mplus.get.loop.xvalues(file)

			  yy[(r-1)+1,] <- mplus.get.loop.estimates(file,loopidx)
			} else {
			  xx[3*(r-1)+1,] <- mplus.get.loop.xvalues(file)
			  xx[3*(r-1)+2,] <- mplus.get.loop.xvalues(file)
			  xx[3*(r-1)+3,] <- mplus.get.loop.xvalues(file)
			  
			  yy[3*(r-1)+1,] <- mplus.get.loop.estimates(file,loopidx)
			  yy[3*(r-1)+2,] <- mplus.get.loop.lowerci(file,loopidx)
			  yy[3*(r-1)+3,] <- mplus.get.loop.upperci(file,loopidx)
			}
		}

		# plot the loop
		cstr <- paste("Loop plots")
		if (missing(ylim)) {
			ylim <- c(min(yy),max(yy))
		}
		
		if (missing(xlabel)) {
		  xlabel <- loopvar
		}
		if (missing(ylabel)) {
		  ylabel <- c("Labels")
		}
		plot(xx,yy,xlab=xlabel,ylab=ylabel,main=cstr,type='n',ylim=ylim)

		if (missing(linecolors)) {
			linecolors <- rainbow(num_loop)
		}
		if (missing(linetype)) {
			linetype <- array(2,c(num_loop))
		}
		plotchar <- seq(18,18+num_loop,1)

		for (r in c(1:num_loop)) {
			lines(xx[3*(r-1)+1,],yy[3*(r-1)+1,],col=linecolors[r]) #, pch=plotchar[r])
			lines(xx[3*(r-1)+2,],yy[3*(r-1)+2,],type='l',lty=linetype[r], col=linecolors[r]) #, pch=plotchar[r])
			lines(xx[3*(r-1)+3,],yy[3*(r-1)+3,],type='l',lty=linetype[r], col=linecolors[r]) #, pch=plotchar[r])
		}

		if (showgrid) {
			grid(NULL, NULL, lty=6, col='cornsilk2')
		}
		
		ldesc <- array(0,c(num_loop))
		lty <- array(0,c(num_loop))
		lwd <- array(0,c(num_loop))
		for (i in c(1:num_loop)) {
			ldesc[i] <- sprintf("%s", labels[loopindices[i]])
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend("top",ldesc,col=linecolors,lty=lty,lwd=lwd)

		return(invisible())
	} else if (is.character(label)) {
		label <- tolower(label)
		loopidx <- pmatch(label, labels, nomatch=0)

		if (loopidx == 0) {
			cstr <- paste(c(" - unknown label:"),label,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		if (label <= 0 || label > props[1]) {
			cstr <- paste(" - index is out of range: ",label,"\n\nUse mplus.list.loop.labels to see the list of labels.\n")
			stop(cstr)
		}
		loopidx <- label
	}

	labels <- mplus.get.group.attribute(file, 'loop_data', 'labels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)

	loopvar <- mplus.get.group.attribute(file,'loop_data','loop_variable')
	loopvar <- gsub("(^\\s+|\\s+$)", "", loopvar, perl=TRUE)

	xx <- array(0,c(3,props[2]))
	xx[1,] <- mplus.get.loop.xvalues(file)
	xx[2,] <- mplus.get.loop.xvalues(file)
	xx[3,] <- mplus.get.loop.xvalues(file)

	yy <- array(0,c(3,props[2]))
	yy[1,] <- mplus.get.loop.estimates(file,loopidx)
	yy[2,] <- mplus.get.loop.lowerci(file,loopidx)
	yy[3,] <- mplus.get.loop.upperci(file,loopidx)

	# plot the loop
	cstr <- paste("Loop plot for",labels[loopidx])

	if (missing(ylim)) {
		ylim <- c(min(yy),max(yy))
	}

	if (missing(xlabel)) {
	  xlabel <- loopvar
	}
	if (missing(ylabel)) {
	  ylabel <- labels[loopidx]
	}
	plot(xx,yy,xlab=xlabel,ylab=ylabel,main=cstr,type='n',ylim=ylim)

	if (missing(linecolors)) {
		linecolors <- rainbow(1)
	}
	if (missing(linetype)) {
		linetype <- array(2,c(1))
	}
	
	lines(xx[1,],yy[1,],col=linecolors[1])
	lines(xx[2,],yy[2,],col=linecolors[1],lty=linetype[1])
	lines(xx[3,],yy[3,],col=linecolors[1],lty=linetype[1])

	if (showgrid) {
		grid(NULL, NULL, lty=6, col='cornsilk2')
	}
}



######################################################################################################
# Functions for MODERATION PLOT
######################################################################################################

#========================================================================
#
# mplus.list.moderation.labels - list the moderation parameters
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.moderation.labels('ex8.1.gh5')
#
mplus.list.moderation.labels <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if moderation data exists
	if ( !("moderation_data" %in% names(gh5)) ) {
		stop("- requires moderation data.\n\nUse TYPE=PLOT2 and the MOD keyword in MODEL INDIRECT.")
	}

	cat(c("\nList of moderation labels to use in the following functions:\n"))
	cat(c(" - mplus.plot.moderation\n"))
	cat(c(" - mplus.get.moderation.estimates\n"))
	cat(c(" - mplus.get.moderation.lowerci\n"))
	cat(c(" - mplus.get.moderation.upperci\n"))
	cat(c(" - mplus.get.moderation.xvalues\n"))

	cat(c("\nModeration labels:\n"))

	# get the parameter statements from loop_data and lookup the indices
	statements <- mplus.get.group.attribute(file, 'moderation_data', 'labels')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	nplaus <- length(statements)
	for (i in c(1:nplaus)) {
		cstr <- sprintf("[%d] %s", i, statements[i])
		cat(cstr,sep="\n")
	}
#	cat(statements,sep="\n")
	invisible(statements)
}



#========================================================================
#
# mplus.get.moderation.estimates - get the estimates for the given moderation label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	loopstr - the quoted moderation label
#	grp - group index, default to 1
#
# eg. mplus.get.moderation.estimates('ex8.1.gh5','indirect',1)
#
mplus.get.moderation.estimates <- function(file,loopstr=1,grp=1) {
	if (missing(file)) {
		stop("- - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste(" - file does not exist:",file)
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if moderation data exists
	if ( !("moderation_data" %in% names(gh5)) ) {
		stop("- requires moderation data.\n\nUse TYPE=PLOT2 and the MOD keyword in MODEL INDIRECT.")
	}

	if (missing(loopstr)) {
		loopstr=1
	}

	# get the dimensions of the estimates dataset
	# first dimension is the number of loop labels
	# second dimension is the number of x points
	# third dimension is the number of groups
	dims <- attr(gh5$moderation_data$estimates,'dim')

	if (is.character(loopstr)) {
		labels <- mplus.get.group.attribute(file,'moderation_data','labels')
		labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
		labels <- tolower(labels)
		loopstr <- tolower(loopstr)
		loopidx <- pmatch(loopstr, labels, nomatch=0)

		if (loopidx == 0) {
			cstr <- paste(c("- unknown moderation label:"),loopstr,"\n\nUse mplus.list.moderation.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		loopidx <- loopstr
		if (loopidx <= 0 || loopidx > dims[1]) {
			cstr <- paste(" - moderation index is out of range: ",loopidx,"\n\nUse mplus.list.moderation.labels to see the list of labels.\n")
			stop(cstr)
		}
	}

	if (grp <= 0 || grp > dims[3]) {
		cstr <- paste(" - group index is out of range: ",grp,"\n\nMaximum number of groups: ",dims[3],"\n")
		stop(cstr)
	}

	gh5$moderation_data$estimates[loopidx,,grp]
}



#========================================================================
#
# mplus.get.moderation.lowerci - get the lower CI values for the given moderation label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	loopstr - the quoted moderation label
#	grp - group index, default to 1
#
# eg. mplus.get.moderation.lowerci('ex8.1.gh5','indirect',1)
#
mplus.get.moderation.lowerci <- function(file,loopstr=1,grp=1) {
	if (missing(file)) {
		stop("- - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste(" - file does not exist:",file)
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if moderation data exists
	if ( !("moderation_data" %in% names(gh5)) ) {
		stop("- requires moderation data.\n\nUse TYPE=PLOT2 and the MOD keyword in MODEL INDIRECT.")
	}

	if (missing(loopstr)) {
		loopstr=1
	}

	# get the dimensions of the estimates dataset
	# first dimension is the number of loop labels
	# second dimension is the number of x points
	# third dimension is the number of groups
	dims <- attr(gh5$moderation_data$lowerci,'dim')

	if (is.character(loopstr)) {
		labels <- mplus.get.group.attribute(file,'moderation_data','labels')
		labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
		labels <- tolower(labels)
		loopstr <- tolower(loopstr)
		loopidx <- pmatch(loopstr, labels, nomatch=0)

		if (loopidx == 0) {
			cstr <- paste(c("- unknown moderation label:"),loopstr,"\n\nUse mplus.list.moderation.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		loopidx <- loopstr
		if (loopidx <= 0 || loopidx > dims[1]) {
			cstr <- paste(" - moderation index is out of range: ",loopidx,"\n\nUse mplus.list.moderation.labels to see the list of labels.\n")
			stop(cstr)
		}
	}

	if (grp <= 0 || grp > dims[3]) {
		cstr <- paste(" - group index is out of range: ",grp,"\n\nMaximum number of groups: ",dims[3],"\n")
		stop(cstr)
	}

	gh5$moderation_data$lowerci[loopidx,,grp]
}



#========================================================================
#
# mplus.get.moderation.upperci - get the upper CI values for the given moderation label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	loopstr - the quoted moderation label
#	grp - group index, default to 1
#
# eg. mplus.get.moderation.upperci('ex8.1.gh5','indirect',1)
#
mplus.get.moderation.upperci <- function(file,loopstr=1,grp=1) {
	if (missing(file)) {
		stop("- - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste(" - file does not exist:",file)
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if moderation data exists
	if ( !("moderation_data" %in% names(gh5)) ) {
		stop("- requires moderation data.\n\nUse TYPE=PLOT2 and the MOD keyword in MODEL INDIRECT.")
	}

	if (missing(loopstr)) {
		loopstr=1
	}

	# get the dimensions of the estimates dataset
	# first dimension is the number of loop labels
	# second dimension is the number of x points
	# third dimension is the number of groups
	dims <- attr(gh5$moderation_data$upperci,'dim')

	if (is.character(loopstr)) {
		labels <- mplus.get.group.attribute(file,'moderation_data','labels')
		labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
		labels <- tolower(labels)
		loopstr <- tolower(loopstr)
		loopidx <- pmatch(loopstr, labels, nomatch=0)

		if (loopidx == 0) {
			cstr <- paste(c("- unknown moderation label:"),loopstr,"\n\nUse mplus.list.moderation.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		loopidx <- loopstr
		if (loopidx <= 0 || loopidx > dims[1]) {
			cstr <- paste(" - moderation index is out of range: ",loopidx,"\n\nUse mplus.list.moderation.labels to see the list of labels.\n")
			stop(cstr)
		}
	}

	if (grp <= 0 || grp > dims[3]) {
		cstr <- paste(" - group index is out of range: ",grp,"\n\nMaximum number of groups: ",dims[3],"\n")
		stop(cstr)
	}

	gh5$moderation_data$upperci[loopidx,,grp]
}



#========================================================================
#
# mplus.get.moderation.xvalues - get the x points for the moderation plots
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.get.moderation.xvalues('ex8.1.gh5')
#
mplus.get.moderation.xvalues <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if moderation data exists
	if ( !("moderation_data" %in% names(gh5)) ) {
		stop("- requires moderation data.\n\nUse TYPE=PLOT2 and the MOD keyword in MODEL INDIRECT.")
	}

	gh5$moderation_data$xvalues
}



#========================================================================
#
# mplus.plot.moderation - plot the moderation label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	label - the index of the loop label
#	group - group index, default to 1
#	allgroups - TRUE if all groups are plotted in the same window, default to FALSE
#	showgrid - option to turn off grid lines, default is to show grid
#	lloc - location of legend
#
# eg. mplus.plot.moderation('ex8.1.gh5',1,1)
#
mplus.plot.moderation <- function(file,label=1,group=1,allgroups=FALSE,showgrid=TRUE,lloc="top") {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if moderation data exists
	if ( !("moderation_data" %in% names(gh5)) ) {
		stop("- requires moderation data.\n\nUse TYPE=PLOT2 and the MOD keyword in MODEL INDIRECT.")
	}

	if (missing(label)) {
		label=1
	}

	# get the dimensions of the properties attribute
	# first value is the number of loop labels
	# second value is the number of x points
	# third value is the number of groups
	props <- mplus.get.group.attribute(file,'moderation_data','properties')
	nlabels <- as.integer(props[1])
	npoints <- as.integer(props[2])
	ngroups <- as.integer(props[3])

	# get the parameter statements from loop_data and lookup the indices
	labels <- mplus.get.group.attribute(file, 'moderation_data', 'labels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
	labels <- tolower(labels)

	if (identical(FALSE, allgroups)) {
		if (group <= 0 || group > ngroups) {
			cstr <- paste(" - group index is out of range: ",group,"\n\nMaximum number of groups: ",ngroups,"\n")
			stop(cstr)
		}
		gidx <- group
	} else {
		gidx <- 1
	}

	if (length(label) > 1) {
		cstr <- paste("- multiple parameters cannot be plotted together\n")
		stop(cstr)
	} else if (is.character(label)) {
		label <- tolower(label)
		loopidx <- pmatch(label, labels, nomatch=0)

		if (loopidx == 0) {
			cstr <- paste(c("- unknown moderation label:"),label,"\n\nUse mplus.list.moderation.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		if (label <= 0 || label > nlabels) {
			cstr <- paste("- moderation index is out of range: ",label,"\n\nUse mplus.list.moderation.labels to see the list of labels.\n")
			stop(cstr)
		}
		loopidx <- label
	}

	labels <- mplus.get.group.attribute(file, 'moderation_data', 'labels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)

	loopvar <- mplus.get.group.attribute(file,'moderation_data','loop_variable')
	loopvar <- gsub("(^\\s+|\\s+$)", "", loopvar, perl=TRUE)

	if (allgroups && ngroups > 1) {
		xx <- array(0,c(3*ngroups,npoints))
		yy <- array(0,c(3*ngroups,npoints))
		for (gidx in c(1:ngroups)) {
			xx[3*(gidx-1)+1,] <- mplus.get.moderation.xvalues(file)
			xx[3*(gidx-1)+2,] <- mplus.get.moderation.xvalues(file)
			xx[3*(gidx-1)+3,] <- mplus.get.moderation.xvalues(file)
			
			yy[3*(gidx-1)+1,] <- mplus.get.moderation.estimates(file,loopidx,gidx)
			yy[3*(gidx-1)+2,] <- mplus.get.moderation.lowerci(file,loopidx,gidx)
			yy[3*(gidx-1)+3,] <- mplus.get.moderation.upperci(file,loopidx,gidx)
		}

		# plot the loop
		cstr <- paste("Moderation plot for",labels[loopidx])
		plot(xx,yy,xlab=loopvar,ylab=labels[loopidx],main=cstr,type='n')
		
		colors <- rainbow(ngroups)
		for (gidx in c(1:ngroups)) {
			lines(xx[3*(gidx-1)+1,],yy[3*(gidx-1)+1,],col=colors[gidx],lwd=2)
			lines(xx[3*(gidx-1)+2,],yy[3*(gidx-1)+2,],col=colors[gidx])
			lines(xx[3*(gidx-1)+3,],yy[3*(gidx-1)+3,],col=colors[gidx])
		}
		
		glabels <- mplus.get.file.dataset(file, 'data_group_labels')
		glabels <- gsub("(^\\s+|\\s+$)", "", glabels, perl=TRUE)

		lty <- array(0,c(ngroups))
		lwd <- array(0,c(ngroups))
		for (i in c(1:ngroups)) {
			lty[i] = 1
			lwd[i] = 1
		}
		legend(lloc,glabels,col=colors,lty=lty,lwd=lwd)
	} else {
		xx <- array(0,c(3,props[2]))
		xx[1,] <- mplus.get.moderation.xvalues(file)
		xx[2,] <- mplus.get.moderation.xvalues(file)
		xx[3,] <- mplus.get.moderation.xvalues(file)
		
		yy <- array(0,c(3,props[2]))
		yy[1,] <- mplus.get.moderation.estimates(file,loopidx,gidx)
		yy[2,] <- mplus.get.moderation.lowerci(file,loopidx,gidx)
		yy[3,] <- mplus.get.moderation.upperci(file,loopidx,gidx)
		
		# plot the loop
		if (ngroups > 1) {
			glabels <- mplus.get.file.dataset(file, 'data_group_labels')
			glabels <- gsub("(^\\s+|\\s+$)", "", glabels, perl=TRUE)
			cstr <- paste("Moderation plot for ",labels[loopidx],", ",glabels[gidx],sep="")
		} else {
			cstr <- paste("Moderation plot for",labels[loopidx])
		}
		plot(xx,yy,xlab=loopvar,ylab=labels[loopidx],main=cstr,type='n')
		
		lines(xx[1,],yy[1,],col='red')
		lines(xx[2,],yy[2,],col='blue')
		lines(xx[3,],yy[3,],col='blue')
	}

	if (!missing(showgrid)) {
		if (!is.logical(showgrid)) {
			cstr <- paste(" - specify TRUE or FALSE for showgrid\n")
			stop(cstr)
		}
	}
	if (showgrid) {
		grid(NULL, NULL, lty=6, col='cornsilk2')
	}
}



######################################################################################################
# Functions for SENSITIVITY PLOT
######################################################################################################

#========================================================================
#
# mplus.list.sensitivity.labels - list the sensitivity parameters
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.sensitivity.labels('ex8.1.gh5')
#
mplus.list.sensitivity.labels <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}
	
	gh5 <- h5dump(file, load=TRUE)
	
	# check if sensitivity data exists
	if ( !("sensitivity_data" %in% names(gh5)) ) {
		stop("- requires sensitivity data.\n\nUse TYPE=PLOT2 with TYPE = SENSITIVITY and the MOD keyword in MODEL INDIRECT.")
	}
	
	cat(c("\nList of sensitivity labels to use in the following functions:\n"))
	cat(c(" - mplus.plot.sensitivity\n"))
	cat(c(" - mplus.get.sensitivity.estimates\n"))
	cat(c(" - mplus.get.sensitivity.lowerci\n"))
	cat(c(" - mplus.get.sensitivity.upperci\n"))

	cat(c("\nSensitivity labels:\n"))
	
	# get the parameter statements from loop_data and lookup the indices
	statements <- mplus.get.group.attribute(file, 'sensitivity_data', 'labels')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)
	
	nplaus <- length(statements)
	for (i in c(1:nplaus)) {
		cstr <- sprintf("[%d] %s", i, statements[i])
		cat(cstr,sep="\n")
	}
	#	cat(statements,sep="\n")
	invisible(statements)
}



#========================================================================
#
# mplus.get.sensitivity.estimates - get the estimates for the given moderation label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	label - the quoted moderation label
#	zvalue - index of z value, default to 1
#	group - group index, default to the first value
#	zdecimals - the number of decimals for matching the z value
#
# eg. mplus.get.sensitivity.estimates('ex8.1.gh5','indirect',1)
#
mplus.get.sensitivity.estimates <- function(file,label=1,zvalue=1,group=1,zdecimals=3) {
	if (missing(file)) {
		stop("- - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste(" - file does not exist:",file)
		stop(cstr)
	}
	
	#H5close()
	gh5 <- h5dump(file, load=TRUE)
	
	# check if sensitivity data exists
	if ( !("sensitivity_data" %in% names(gh5)) ) {
		stop("- requires sensitivity data.\n\nUse TYPE=PLOT2 and TYPE=SENSITIVITY and the MOD keyword in MODEL INDIRECT.")
	}
	
	# get the dimensions of the estimates dataset
	# first dimension is the number of loop labels
	# second dimension is the number of x points
	# third dimension is the number of z values
	# fourth dimension is the number of groups
	dims <- attr(gh5$sensitivity_data$estimates,'dim')
	nlabels <- as.integer(dims[1])
	npoints <- as.integer(dims[2])
	nzvalues <- as.integer(dims[3])
	ngroups <- as.integer(dims[4])
	
	labels <- mplus.get.group.attribute(file,'sensitivity_data','labels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)

	if (is.character(label)) {
		labels <- tolower(labels)
		label <- tolower(label)
		loopidx <- pmatch(label, labels, nomatch=0)
		
		if (loopidx == 0) {
			cstr <- paste(c("- unknown sensitivity label:"),label,"\n\nUse mplus.list.sensitivity.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		if (label <= 0 || label > nlabels) {
			cstr <- paste(" - sensitivity index is out of range: ",label,"\n\nUse mplus.list.sensitivity.labels to see the list of labels.\n")
			stop(cstr)
		}
		loopidx <- label
	}
	
	if (group <= 0 || group > ngroups) {
		cstr <- paste(" - group index is out of range: ",group,"\n\nMaximum number of groups: ",ngroups,"\n")
		stop(cstr)
	}
	
	zflags <- mplus.get.group.attribute(file,'sensitivity_data','zflags')
	zflags <- as.integer(zflags)
	
	if (missing(zvalue)) {
		zindex <- 1
	} else {
		zvalue <- as.numeric(zvalue)
		if (zflags[loopidx] == 0) {
			cstr <- sprintf(" - no Z values for label index:  %d\n\nThe indirect effect '%s' has no Z values.\n", loopidx, labels[loopidx])
			stop(cstr)
		}
		zvalue <- as.numeric(zvalue)

		zvalues <- mplus.get.group.dataset(file,'sensitivity_data','zvalues')
		zvalues <- as.numeric(zvalues)
	
		zindex <- pmatch(round(zvalue,zdecimals), round(zvalues,zdecimals), nomatch=0)
		if (zindex == 0) {
			cstr <- sprintf(" - unknown value for Z:  %0.3f\n\nUse mplus.list.sensitivity.zvalues to see a list of the Z values.\n", zvalue)
			stop(cstr)
		}
	}

	gh5$sensitivity_data$estimates[loopidx,,zindex,group]
}



#========================================================================
#
# mplus.get.sensitivity.lowerci - get the lower CI values for the given sensitivity label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	label - the quoted sensitivity label
#	group - group index, default to 1
#	zvalue - value of Z variable, default to the first value
#	zdecimals - the number of decimals for matching the z value
#
# eg. mplus.get.sensitivity.lowerci('ex8.1.gh5','indirect',1)
#
mplus.get.sensitivity.lowerci <- function(file,label=1,zvalue=1,group=1,zdecimals=3) {
	if (missing(file)) {
		stop("- - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste(" - file does not exist:",file)
		stop(cstr)
	}
	
	#H5close()
	gh5 <- h5dump(file, load=TRUE)
	
	# check if sensitivity data exists
	if ( !("sensitivity_data" %in% names(gh5)) ) {
		stop("- requires sensitivity data.\n\nUse TYPE=PLOT2 and TYPE=SENSITIVITY and the MOD keyword in MODEL INDIRECT.")
	}
	
	# get the dimensions of the estimates dataset
	# first dimension is the number of loop labels
	# second dimension is the number of x points
	# third dimension is the number of z values
	# fourth dimension is the number of groups
	dims <- attr(gh5$sensitivity_data$lowerci,'dim')
	nlabels <- as.integer(dims[1])
	npoints <- as.integer(dims[2])
	nzvalues <- as.integer(dims[3])
	ngroups <- as.integer(dims[4])
	
	labels <- mplus.get.group.attribute(file,'sensitivity_data','labels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)

	if (is.character(label)) {
		labels <- tolower(labels)
		label <- tolower(label)
		loopidx <- pmatch(label, labels, nomatch=0)
		
		if (loopidx == 0) {
			cstr <- paste(c("- unknown sensitivity label:"),label,"\n\nUse mplus.list.sensitivity.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		if (label <= 0 || label > nlabels) {
			cstr <- paste(" - sensitivity index is out of range: ",label,"\n\nUse mplus.list.sensitivity.labels to see the list of labels.\n")
			stop(cstr)
		}
		loopidx <- label
	}
	
	if (group <= 0 || group > ngroups) {
		cstr <- paste(" - group index is out of range: ",group,"\n\nMaximum number of groups: ",ngroups,"\n")
		stop(cstr)
	}
	
	zflags <- mplus.get.group.attribute(file,'sensitivity_data','zflags')
	zflags <- as.integer(zflags)
	
	if (missing(zvalue)) {
		zindex <- 1
	} else {
		zvalue <- as.numeric(zvalue)
		if (zflags[loopidx] == 0) {
			cstr <- sprintf(" - no Z values for label index:  %d\n\nThe indirect effect '%s' has no Z values.\n", loopidx, labels[loopidx])
			stop(cstr)
		}
		zvalue <- as.numeric(zvalue)
	
		zvalues <- mplus.get.group.dataset(file,'sensitivity_data','zvalues')
		zvalues <- as.numeric(zvalues)

		zindex <- pmatch(round(zvalue,zdecimals), round(zvalues,zdecimals), nomatch=0)
		if (zindex == 0) {
			cstr <- sprintf(" - unknown value for Z:  %0.3f\n\nUse mplus.list.sensitivity.zvalues to see a list of the Z values.\n", zvalue)
			stop(cstr)
		}
	}
	
	gh5$sensitivity_data$lowerci[loopidx,,zindex,group]
}



#========================================================================
#
# mplus.get.sensitivity.upperci - get the upper CI values for the given sensitivity label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	label - the quoted sensitivity label
#	zvalue - value of the Z variable, default to first value
#	group - group index, default to 1
#	zdecimals - the number of decimals for matching the z value
#
# eg. mplus.get.sensitivity.upperci('ex8.1.gh5','indirect',1)
#
mplus.get.sensitivity.upperci <- function(file,label=1,zvalue=1,group=1,zdecimals=3) {
	if (missing(file)) {
		stop("- - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste(" - file does not exist:",file)
		stop(cstr)
	}
	
	#H5close()
	gh5 <- h5dump(file, load=TRUE)
	
	# check if sensitivity data exists
	if ( !("sensitivity_data" %in% names(gh5)) ) {
		stop("- requires sensitivity data.\n\nUse TYPE=PLOT2 and TYPE=SENSITIVITY and the MOD keyword in MODEL INDIRECT.")
	}
	
	# get the dimensions of the estimates dataset
	# first dimension is the number of loop labels
	# second dimension is the number of x points
	# third dimension is the number of z values
	# fourth dimension is the number of groups
	dims <- attr(gh5$sensitivity_data$upperci,'dim')
	nlabels <- as.integer(dims[1])
	npoints <- as.integer(dims[2])
	nzvalues <- as.integer(dims[3])
	ngroups <- as.integer(dims[4])
	
	labels <- mplus.get.group.attribute(file,'sensitivity_data','labels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)

	if (is.character(label)) {
		labels <- tolower(labels)
		label <- tolower(label)
		loopidx <- pmatch(label, labels, nomatch=0)
		
		if (loopidx == 0) {
			cstr <- paste(c("- unknown sensitivity label:"),label,"\n\nUse mplus.list.sensitivity.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		if (label <= 0 || label > nlabels) {
			cstr <- paste(" - sensitivity index is out of range: ",label,"\n\nUse mplus.list.sensitivity.labels to see the list of labels.\n")
			stop(cstr)
		}
		loopidx <- label
	}
	
	if (group <= 0 || group > ngroups) {
		cstr <- paste(" - group index is out of range: ",group,"\n\nMaximum number of groups: ",ngroups,"\n")
		stop(cstr)
	}
	
	zflags <- mplus.get.group.attribute(file,'sensitivity_data','zflags')
	zflags <- as.integer(zflags)
	
	if (missing(zvalue)) {
		zindex <- 1
	} else {
		zvalue <- as.numeric(zvalue)
		if (zflags[loopidx] == 0) {
			cstr <- sprintf(" - no Z values for label index:  %d\n\nThe indirect effect '%s' has no Z values.\n", loopidx, labels[loopidx])
			stop(cstr)
		}
		zvalue <- as.numeric(zvalue)

		zvalues <- mplus.get.group.dataset(file,'sensitivity_data','zvalues')
		zvalues <- as.numeric(zvalues)
	
		zindex <- pmatch(round(zvalue,zdecimals), round(zvalues,zdecimals), nomatch=0)
		if (zindex == 0) {
			cstr <- sprintf(" - unknown value for Z:  %0.3f\n\nUse mplus.list.sensitivity.zvalues to see a list of the Z values.\n", zvalue)
			stop(cstr)
		}
	}
	
	gh5$sensitivity_data$upperci[loopidx,,zindex,group]
}



#========================================================================
#
# mplus.get.sensitivity.xvalues - get the x points for the sensitivity plots
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.get.sensitivity.xvalues('ex8.1.gh5')
#
mplus.get.sensitivity.xvalues <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}
	
	#H5close()
	gh5 <- h5dump(file, load=TRUE)
	
	# check if sensitivity data exists
	if ( !("sensitivity_data" %in% names(gh5)) ) {
		stop("- requires sensitivity data.\n\nUse TYPE=PLOT2 and TYPE=SENSITIVITY and the MOD keyword in MODEL INDIRECT.")
	}
	
	gh5$sensitivity_data$xvalues
}



#========================================================================
#
# mplus.plot.sensitivity - plot the sensitivity label
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	label - the index of the loop label
#	zvalue - the value of the Z variable, default to the first one
#	group - group index, default to 1
#	allgroups - TRUE if all groups are plotted in the same window, default to FALSE
#	xrange - the custom xrange, default is to not have custom range and display the full range
#	showgrid - option to turn off grid lines, default is to show grid
#	lloc - location of legend
#	zdecimals - the number of decimals for matching the z value
#
# eg. mplus.plot.sensitivity('ex8.1.gh5',1,1)
#
mplus.plot.sensitivity <- function(file,label=1,zvalue=1,group=1,allgroups=FALSE,xrange=c(-1,1),showgrid=TRUE,lloc="top",zdecimals=3) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}
	
	#H5close()
	gh5 <- h5dump(file, load=TRUE)
	
	# check if sensitivity data exists
	if ( !("sensitivity_data" %in% names(gh5)) ) {
		stop("- requires sensitivity data.\n\nUse TYPE=PLOT2 and TYPE=SENSITIVITY and the MOD keyword in MODEL INDIRECT.")
	}
	
	# get the dimensions of the properties attribute
	# first value is the number of loop labels
	# second value is the number of x points
	# third value is the number of groups
	# fourth value is the number of z values
	props <- mplus.get.group.attribute(file,'sensitivity_data','properties')
	nlabels <- as.integer(props[1])
	npoints <- as.integer(props[2])
	ngroups <- as.integer(props[3])
	nzvalues <- as.integer(props[4])
	
		# get the parameter statements from sensitivity_data and lookup the indices
	labels <- mplus.get.group.attribute(file, 'sensitivity_data', 'labels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
	
	if (identical(FALSE, allgroups)) {
		if (group <= 0 || group > ngroups) {
			cstr <- paste(" - group index is out of range: ",group,"\n\nMaximum number of groups: ",ngroups,"\n")
			stop(cstr)
		}
		gidx <- group
	} else {
		gidx <- 1
	}

	if (length(label) > 1) {
		cstr <- paste("- multiple parameters cannot be plotted together.\n")
		stop(cstr)
	} else if (is.character(label)) {
		labels <- tolower(labels)
		label <- tolower(label)
		loopidx <- pmatch(label, labels, nomatch=0)
		
		if (loopidx == 0) {
			cstr <- paste(c("- unknown sensitivity label:"),label,"\n\nUse mplus.list.sensitivity.labels to see the list of labels.\n")
			stop(cstr)
		}
	} else {
		if (label <= 0 || label > props[1]) {
			cstr <- paste("- sensitivity index is out of range: ",label,"\n\nUse mplus.list.sensitivity.labels to see the list of labels.\n")
			stop(cstr)
		}
		loopidx <- label
	}
	
	labels <- mplus.get.group.attribute(file, 'sensitivity_data', 'labels')
	labels <- gsub("(^\\s+|\\s+$)", "", labels, perl=TRUE)
	
	loopvar <- mplus.get.group.attribute(file,'sensitivity_data','loop_variable')
	loopvar <- gsub("(^\\s+|\\s+$)", "", loopvar, perl=TRUE)
	
	zflags <- mplus.get.group.attribute(file,'sensitivity_data','zflags')
	zflags <- as.integer(zflags)

	if (missing(zvalue)) {
		if (zflags[loopidx] == 1) {
			zvalue <- zvalues[1]
		}
	} else {
		zvalue <- as.numeric(zvalue)
		if (zflags[loopidx] == 0) {
			cstr <- sprintf(" - no Z values for label index:  %d\n\nThe indirect effect '%s' has no Z values.\n", loopidx, labels[loopidx])
			stop(cstr)
		}
		zvalue <- as.numeric(zvalue)

		zvalues <- mplus.get.group.dataset(file,'sensitivity_data','zvalues')
		zvalues <- as.numeric(zvalues)

		zindex <- match(round(zvalue,zdecimals), round(zvalues,zdecimals), nomatch=0)
		if (zindex == 0) {
			cstr <- sprintf(" - unknown value for Z:  %0.3f\n\nUse mplus.list.sensitivity.zvalues to see a list of the Z values.\n", zvalue)
			stop(cstr)
		}
	}
	
	if (!missing(xrange)) {
		if (!is.vector(xrange)) {
			cstr <- paste(" - xrange argument must be a vector with 2 values\n")
			stop(cstr)
		}
		xlength <- length(xrange)
		if (xlength < 2 || xlength > 2) {
			cstr <- paste(" - xrange argument must be a vector with 2 values\n")
			stop(cstr)
		}
		if (xrange[1] < -1) {
			cstr <- paste(" - custom xrange should be between -1 and 1\n")
			stop(cstr)
		}
		if (xrange[2] > 1) {
			cstr <- paste(" - custom xrange should be between -1 and 1\n")
			stop(cstr)
		}
		if (xrange[1] > xrange[2]) {
			cstr <- paste(" - custom xrange should be increasing from first value to second value\n")
			stop(cstr)
		}
	}

	if (allgroups) {
		xvalues <- array(0,c(npoints))
		xvalues <- mplus.get.sensitivity.xvalues(file)
		
		estimates <- array(0,c(ngroups,npoints))
		lowerci <- array(0,c(ngroups,npoints))
		upperci <- array(0,c(ngroups,npoints))

		for (gidx in c(1:ngroups)) {
			if (missing(zvalue)) {
				estimates[gidx,] <- mplus.get.sensitivity.estimates(file,loopidx,group=gidx)
				lowerci[gidx,] <- mplus.get.sensitivity.lowerci(file,loopidx,group=gidx)
				upperci[gidx,] <- mplus.get.sensitivity.upperci(file,loopidx,group=gidx)
			} else {
				estimates[gidx,] <- mplus.get.sensitivity.estimates(file,loopidx,zvalue,gidx)
				lowerci[gidx,] <- mplus.get.sensitivity.lowerci(file,loopidx,zvalue,gidx)
				upperci[gidx,] <- mplus.get.sensitivity.upperci(file,loopidx,zvalue,gidx)
			}
		}

		if (missing(xrange)) {
			xx <- array(0,c(3*ngroups,npoints))
			yy <- array(0,c(3*ngroups,npoints))
			for (gidx in c(1:ngroups)) {
				
				xx[3*(gidx-1)+1,] <- xvalues
				xx[3*(gidx-1)+2,] <- xvalues
				xx[3*(gidx-1)+3,] <- xvalues
				
				yy[3*(gidx-1)+1,] <- estimates[gidx,]
				yy[3*(gidx-1)+2,] <- lowerci[gidx,]
				yy[3*(gidx-1)+3,] <- upperci[gidx,]
			}
		} else {
			dffull <- data.frame(xvalues)
			for (gidx in c(1:ngroups)) {
				gname <- sprintf("est%d",gidx)
				dffull[,gname] <- estimates[gidx,]
				gname <- sprintf("lc%d",gidx)
				dffull[,gname] <- lowerci[gidx,]
				gname <- sprintf("uc%d",gidx)
				dffull[,gname] <- upperci[gidx,]
			}
			dfcustom <- subset(dffull, xvalues >= xrange[1] & xvalues <= xrange[2])
			xpoints <- nrow(dfcustom$xvalues)

			xx <- array(0,c(3*ngroups,xpoints))
			yy <- array(0,c(3*ngroups,xpoints))
			for (gidx in c(1:ngroups)) {
				xx[3*(gidx-1)+1,] <- dfcustom$xvalues
				xx[3*(gidx-1)+2,] <- dfcustom$xvalues
				xx[3*(gidx-1)+3,] <- dfcustom$xvalues
				
				gname <- sprintf("est%d",gidx)
				yy[3*(gidx-1)+1,] <- dfcustom[,gname]
				gname <- sprintf("lc%d",gidx)
				yy[3*(gidx-1)+2,] <- dfcustom[,gname]
				gname <- sprintf("uc%d",gidx)
				yy[3*(gidx-1)+3,] <- dfcustom[,gname]
			}
		}

		
		# plot the loop
		if (zflags[loopidx] == 1){
			zvar <- mplus.get.group.attribute(file, 'sensitivity_data', 'zlabel')
			zvar <- gsub("(^\\s+|\\s+$)", "", zvar, perl=TRUE)
			zvalues <- mplus.get.group.dataset(file, 'sensitivity_data','zvalues')
			cstr <- sprintf("Sensitivity plots for %s for %s = %0.3f",labels[loopidx],zvar,zvalues[zindex])
		} else {
			cstr <- paste("Sensitivity plots for",labels[loopidx])
		}
		
		plot(xx,yy,xlab=loopvar,ylab=labels[loopidx],main=cstr,type='n')
		
		colors <- rainbow(ngroups)
		for (gidx in c(1:ngroups)) {
			lines(xx[3*(gidx-1)+1,],yy[3*(gidx-1)+1,],col=colors[gidx],lwd=2)
			lines(xx[3*(gidx-1)+2,],yy[3*(gidx-1)+2,],col=colors[gidx])
			lines(xx[3*(gidx-1)+3,],yy[3*(gidx-1)+3,],col=colors[gidx])
		}
		
		glabels <- mplus.get.file.dataset(file, 'data_group_labels')
		glabels <- gsub("(^\\s+|\\s+$)", "", glabels, perl=TRUE)
		
		lty <- array(0,c(ngroups))
		lwd <- array(0,c(ngroups))
		for (i in c(1:ngroups)) {
			lty[i] = 1
			lwd[i] = 1
		}
		legend(lloc,glabels,col=colors,lty=lty,lwd=lwd)
	} else {
		xvalues <- array(0,c(npoints))
		xvalues <- mplus.get.sensitivity.xvalues(file)
		
		estimates <- array(0,c(npoints))
		lowerci <- array(0,c(npoints))
		upperci <- array(0,c(npoints))

		if (missing(zvalue)) {
			estimates <- mplus.get.sensitivity.estimates(file,loopidx,group=gidx)
			lowerci <- mplus.get.sensitivity.lowerci(file,loopidx,group=gidx)
			upperci <- mplus.get.sensitivity.upperci(file,loopidx,group=gidx)
		} else {
			estimates <- mplus.get.sensitivity.estimates(file,loopidx,zvalue,gidx)
			lowerci <- mplus.get.sensitivity.lowerci(file,loopidx,zvalue,gidx)
			upperci <- mplus.get.sensitivity.upperci(file,loopidx,zvalue,gidx)
		}

		if (missing(xrange)) {
			xpoints <- npoints
			dfcustom <- data.frame(xvalues,estimates,lowerci,upperci)
		} else {
			dffull <- data.frame(xvalues,estimates,lowerci,upperci)
			dfcustom <- subset(dffull, xvalues >= xrange[1] & xvalues <= xrange[2])
			xpoints <- nrow(dfcustom$xvalues)
		}

		xx <- array(0,c(3,xpoints))
		xx[1,] <- dfcustom$xvalues
		xx[2,] <- dfcustom$xvalues
		xx[3,] <- dfcustom$xvalues
		
		yy <- array(0,c(3,xpoints))
		yy[1,] <- dfcustom$estimates
		yy[2,] <- dfcustom$lowerci
		yy[3,] <- dfcustom$upperci
		
		# plot the loop
		if (zflags[loopidx] == 1){
			zvar <- mplus.get.group.attribute(file, 'sensitivity_data', 'zlabel')
			zvar <- gsub("(^\\s+|\\s+$)", "", zvar, perl=TRUE)
			if (ngroups > 1) {
				glabels <- mplus.get.file.dataset(file, 'data_group_labels')
				glabels <- gsub("(^\\s+|\\s+$)", "", glabels, perl=TRUE)
				cstr <- sprintf("Sensitivity plot for %s for %s = %0.3f, %s",labels[loopidx],zvar,zvalues[zindex],glabels[gidx])
			} else {
				cstr <- sprintf("Sensitivity plot for %s for %s = %0.3f",labels[loopidx],zvar,zvalues[zindex])
			}
		} else {
			if (ngroups > 1) {
				glabels <- mplus.get.file.dataset(file, 'data_group_labels')
				glabels <- gsub("(^\\s+|\\s+$)", "", glabels, perl=TRUE)
				cstr <- sprintf("Sensitivity plot for %s, %s",labels[loopidx],glabels[gidx])
			} else {
				cstr <- paste("Sensitivity plot for",labels[loopidx])
			}
		}
		plot(xx,yy,xlab=loopvar,ylab=labels[loopidx],main=cstr,type='n')
		
		lines(xx[1,],yy[1,],col='red')
		lines(xx[2,],yy[2,],col='blue')
		lines(xx[3,],yy[3,],col='blue')
	}
	
	if (!missing(showgrid)) {
		if (!is.logical(showgrid)) {
			cstr <- paste(" - specify TRUE or FALSE for showgrid\n")
			stop(cstr)
		}
	}
	if (showgrid) {
		grid(NULL, NULL, lty=6, col='cornsilk2')
	}
}


#========================================================================
#
# mplus.list.sensitivity.zvalues - list the values for Z
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.sensitivity.zvalues('ex8.1.gh5')
#
mplus.list.sensitivity.zvalues <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}
	
	gh5 <- h5dump(file, load=TRUE)
	
	# check if sensitivity data exists
	if ( !("sensitivity_data" %in% names(gh5)) ) {
		stop("- requires sensitivity data.\n\nUse TYPE=PLOT2 with TYPE = SENSITIVITY and the MOD keyword in MODEL INDIRECT.")
	}
	
	cat(c("\nValues of Z:\n"))
	
	# get the parameter statements from loop_data and lookup the indices
	zvalues <- mplus.get.group.dataset(file, 'sensitivity_data', 'zvalues')
	zvalues <- as.numeric(zvalues)

	nvalues <- length(zvalues)
	for (i in c(1:nvalues)) {
		cstr <- sprintf("[%d] %f", i, zvalues[i])
		cat(cstr,sep="\n")
	}
	invisible(zvalues)
}




######################################################################################################
# Functions for IRT plots
######################################################################################################

#========================================================================
# mplus.list.irt.variables
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.irt.variables('ex7.27.gh5')
#
mplus.list.irt.variables <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if irt data exists
	if ( !("irt_data" %in% names(gh5)) ) {
		stop("IRT data is required.\n\nUse TYPE=PLOT2.")
	}

	cat(c("\nList of variables to use in the following functions:\n"))
	cat(c(" - mplus.compute.irt.icc\n"))
	cat(c(" - mplus.plot.irt.icc\n"))

	cat(c("\nVariables for 'uvar' argument:\n"))

	ulabels <- mplus.get.group.attribute(file,'irt_data','ulabels')
	ulabels <- gsub("(^\\s+|\\s+$)", "", ulabels, perl=TRUE)

	nvar <- length(ulabels)
	for (i in c(1:nvar)) {
		cstr <- sprintf("[%d] %s", i, ulabels[i])
		cat(cstr,sep="\n")
	}
	invisible(ulabels)
}


#========================================================================
# mplus.list.irt.xvariables
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.irt.xvariables('ex7.27.gh5')
#
mplus.list.irt.xvariables <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if irt data exists
	if ( !("irt_data" %in% names(gh5)) ) {
		stop("IRT data is required.\n\nUse TYPE=PLOT2.")
	}

	cat(c("\nList of variables to use in the following functions:\n"))
	cat(c(" - mplus.compute.irt.icc\n"))
	cat(c(" - mplus.plot.irt.icc\n"))

	cat(c("\nVariables for the 'xvar' argument:\n"))

	flabels <- mplus.get.group.attribute(file,'irt_data','flabels')
	flabels <- gsub("(^\\s+|\\s+$)", "", flabels, perl=TRUE)

	nvar <- length(flabels)
	for (i in c(1:nvar)) {
		cstr <- sprintf("[%d] %s", i, flabels[i])
		cat(cstr,sep="\n")
	}
	invisible(flabels)
}

#========================================================================
# mplus.compute.irt.icc
#
# arguments:
#	file - the quoted name of an existing GH5 file (required)
#	group - the group number (required)
#	xvar - the variable for the x-axis, can be the variable index or quoted variable name (required)
#	uvar - the indicator variable, can be the variable index or the quoted variable name (required)
#	cat - the category number (required)
#	xvector -> the vector containing x values to use (required)
#	covariates -> the vector containing values for all the other covariates (not required, sample mean used if not given)
#
# eg. mplus.compute.irt.icc('ex7.27.gh5',1,'F','U1',1,seq(-3,3,0.2))
#
mplus.compute.irt.icc <- function(file,group,xvar,uvar,cat,xvector,covariates) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if irt data exists
	if ( !("irt_data" %in% names(gh5)) ) {
		stop("IRT data is required.\n\nUse TYPE=PLOT2.")
	}

	#	properties[1] - number of factors
	#	properties[2] - number of factors/covariates
	#	properties[3] - number of indicators
	#	properties[4] - number of classes
	#	properties[5] - maximum number of categories 
	props <- mplus.get.group.attribute(file,'irt_data','properties')

	num_fx <- as.integer(props[2])
	num_r <- as.integer(props[3])
	max_num_cat <- as.integer(props[5])

	flabels <- mplus.get.group.attribute(file,'irt_data','flabels')
	flabels <- gsub("(^\\s+|\\s+$)", "", flabels, perl=TRUE)

	ulabels <- mplus.get.group.attribute(file,'irt_data','ulabels')
	ulabels <- gsub("(^\\s+|\\s+$)", "", ulabels, perl=TRUE)

	if (missing(xvar)) {
		stop("The x-axis variable (xvar) is required.")
	} else {
		if (is.character(xvar)) {
			xvar <- toupper(xvar)
			index <- pmatch(xvar, flabels, nomatch=0)
			if (index == 0) {
				cstr <- sprintf("Unknown x-variable:  %s\n", xvar)
				stop(cstr)
			}
			fidx = index
		} else {
			if (xvar <= 0 || xvar > num_fx) {
				stop("The index for the x-variable (xvar) is out of range.")
			}
			fidx = xvar
		}
	}
	if (missing(uvar)) {
		stop("The indicator variable (uvar) is required.")
	} else {
		if (is.character(uvar)) {
			uvar <- toupper(uvar)
			index <- pmatch(uvar, ulabels, nomatch=0)
			if (index == 0) {
				cstr <- sprintf("Unknown indicator:  %s\n", uvar)
				stop(cstr)
			}
			ridx = index
		} else {
			if (uvar <= 0 || uvar > num_r) {
				stop("The index for the indicator (uvar) is out of range.")
			}
			ridx = uvar
		}
	}
	if (missing(group)) {
		stop("The group index (group) is required.")
	} else {
		if (group <= 0 || group > props[4]) {
			stop("The group index (group) is out of range.")
		}
	}
	if (missing(xvector)) {
		stop("The vector (xvector) containing values for the x-axis is required.")
	}
	if (missing(covariates)) {
		means <- mplus.get.group.dataset(file,'irt_data','mean')
		covariates <- means[,group]
	} else {
		if (length(covariates) != num_fx) {
			cstr <- sprintf("The length of the covariates vector should be %d.\nFound: %d", num_fx, length(covariates))
			stop(cstr)
		}
	}

	links <- mplus.get.group.attribute(file,'categorical_data','link')
	shift <- 0.0
	for (i in c(1:num_fx)) {
		if (i != fidx) {
			shift <- shift + covariates[i]*gh5$irt_data$loading[ridx,i,group]
		}
	}

	prob <- array(0,c(length(xvector)))
	for (i in c(1:length(xvector))) {
		x <- xvector[i]
		if (cat == 1) {
			p <- gh5$irt_data$tau[cat,ridx,group] - shift - x * gh5$irt_data$loading[fidx,ridx,group]
			p <- p * gh5$irt_data$scale[ridx,group]
			prob[i] <- lin(p,links[ridx])
		} else if (cat == max_num_cat) {
			p = gh5$irt_data$tau[cat-1,ridx,group] - shift - x * gh5$irt_data$loading[fidx,ridx,group]
			p = p * gh5$irt_data$scale[ridx,group]
			prob[i] = 1.0 - lin(p,links[ridx])
		} else {
			p = gh5$irt_data$tau[cat,ridx,group] - shift - x * gh5$irt_data$loading[fidx,ridx,group]
			p = p * gh5$irt_data$scale[ridx,group]

			p2 = gh5$irt_data$tau[cat-1,ridx,group] - shift - x * gh5$irt_data$loading[fidx,ridx,group]
			p2 = p2 * gh5$irt_data$scale[ridx,group]

			prob[i] = lin(p,links[ridx]) - lin(p2,links[ridx])
		}
	}

	prob
}


#========================================================================
# mplus.compute.irt.iic
#
# arguments:
#	file - the quoted name of an existing GH5 file (required)
#	group - the group number (required)
#	xvar - the variable for the x-axis, can be the variable index or quoted variable name (required)
#	uvar - the indicator variable, can be the variable index or the quoted variable name (required)
#	xvector -> the vector containing x values to use (required)
#	covariates -> the vector containing values for all the other covariates (not required, sample mean used if not given)
#
# eg. mplus.compute.irt.iic('ex7.27.gh5',1,'F','U1',seq(-3,3,0.2))
#
mplus.compute.irt.iic <- function(file,group,xvar,uvar,xvector,covariates) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if irt data exists
	if ( !("irt_data" %in% names(gh5)) ) {
		stop("IRT data is required.\n\nUse TYPE=PLOT2.")
	}

	#	properties[1] - number of factors
	#	properties[2] - number of factors/covariates
	#	properties[3] - number of indicators
	#	properties[4] - number of classes
	#	properties[5] - maximum number of categories 
	props <- mplus.get.group.attribute(file,'irt_data','properties')

	num_fx <- as.integer(props[2])
	num_r <- as.integer(props[3])
	max_num_cat <- as.integer(props[5])

	if (missing(xvar)) {
		stop("The x-axis variable (xvar) is required.")
	} else {
		if (is.character(xvar)) {
			index <- pmatch(xvar, flabels, nomatch=0)
			if (index == 0) {
				cstr <- sprintf("Unknown x-variable:  %s\n", xvar)
				stop(cstr)
			}
			fidx = index
		} else {
			if (xvar <= 0 || xvar > num_fx) {
				stop("The index for the x-variable (xvar) is out of range.")
			}
			fidx = xvar
		}
	}
	if (missing(uvar)) {
		stop("The indicator variable (uvar) is required.")
	} else {
		if (is.character(uvar)) {
			index <- pmatch(uvar, ulabels, nomatch=0)
			if (index == 0) {
				cstr <- sprintf("Unknown indicator:  %s\n", uvar)
				stop(cstr)
			}
			ridx = index
		} else {
			if (uvar <= 0 || uvar > num_r) {
				stop("The index for the indicator (uvar) is out of range.")
			}
			ridx = uvar
		}
	}
	if (missing(group)) {
		stop("The group index (group) is required.")
	} else {
		if (group <= 0 || group > props[4]) {
			stop("The group index (group) is out of range.")
		}
	}
	if (missing(xvector)) {
		stop("The vector (xvector) containing values for the x-axis is required.")
	}
	if (missing(covariates)) {
		covariates <- mplus.get.group.dataset(file,'irt_data','mean')
	} else {
		if (length(covariates) != num_fx) {
			cstr <- sprintf("The length of the covariates vector should be %d.\nFound: %d", num_fx, length(covariates))
			stop(cstr)
		}
	}

	categories <- mplus.get.group.attribute(file,'irt_data','categories')
	links <- mplus.get.group.attribute(file,'categorical_data','link')

	shift <- 0.0
	for (i in c(1:num_fx)) {
		if (i != fidx) {
			shift <- shift + covariates[i]*gh5$irt_data$loading[ridx,i,group]
		}
	}

	categories <- as.numeric(categories)

	probvec <- array(0, c(length(xvector),categories[ridx]+1))
	for (i in c(1:length(xvector))) {
		x <- xvector[i]
		probvec[1] <- 0
		for (j in c(2:c(categories[ridx]))) {
			fp = gh5$irt_data$tau[j-1,ridx,group] - shift - x * gh5$irt_data$loading[fidx,ridx,group]
			fp = fp * gh5$irt_data$scale[ridx,group]
			dp = lin(fp,links[ridx])
			probvec[i,j] <- dp
		}
		probvec[i,categories[ridx]+1]=1.0
	}

	prob <- array(0,c(length(xvector)))
	for (i in c(1:length(xvector))) {
		x <- xvector[i]
		for (j in c(2:c(categories[ridx]+1))) {
			r <- 10**(-10)
			ep = probvec[i,j] - probvec[i,j-1]
			if (ep < r) { ep <- r }
			dp = gh5$irt_data$scale[ridx,group] * gh5$irt_data$loading[fidx,ridx,group] * gh5$irt_data$scale[ridx,group] * gh5$irt_data$loading[fidx,ridx,group];
			p = (probvec[i,j] * (1-probvec[i,j])) - (probvec[i,j-1] * (1-probvec[i,j-1]))
			prob[i] <- prob[i] + p * p * dp / ep
		}
	}

	prob
}

#========================================================================
# mplus.plot.irt.icc
#
# arguments:
#	file - the quoted name of an existing GH5 file (required)
#	group - the group number (not required) -- 1 if not specified
#	xvar - the variable for the x-axis, can be the variable index or quoted variable name (not required, uses the first x)
#	uvar - the indicator variable or vector containing more than one indicator variable
#		 - can be the variable index or the quoted variable name
#		 - if not given, assume all indicator variables but cat must be given (not required)
#	cat - the category number
#		- if not given, assume all categories for the given indicator variables
#		- required if uvar not given
#	cat2 - the second category number if range of categories is desired (not required)
#	covariates -> the vector containing values for all the other covariates (not required, sample mean used if not given)
#	xrange - the type of range for the x-axis (not required)
#		- xrange=1: -1 s.d to +1 s.d of xvar
#		- xrange=2: -2 s.d to +2 s.d of xvar
#		- xrange=3: -3 s.d to +3 s.d of xvar (default)
#		- xrange=4: -4 s.d to +4 s.d of xvar
#		- xrange=5: -5 s.d to +5 s.d of xvar
#		- xrange=6: -6 s.d to +6 s.d of xvar
#	xstep - the step increment for the x-axis range (not required)
#		- xstep=1: 1.0
#		- xstep=2: 0.5
#		- xstep=3: 0.1
#		- xstep=4: 0.05
#		- xstep=5: 1/2 s.d of xvar
#		- xstep=6: 1/4 s.d of xvar
#		- xstep=7: 1/5 s.d of xvar (default)
#		- xstep=8: 1/10 s.d of xvar
#		- xstep=9: 1/20 s.d of xvar
#		- xstep=10: 1/50 s.d of xvar
#		- xstep=11: 1/100 s.d of xvar
#
# eg. mplus.plot.irt.icc('ex7.27.gh5',1,'F','U1',)
#
mplus.plot.irt.icc <- function(file,group=1,xvar=1,uvar,cat,cat2,covariates,xrange=3,xstep=7,lloc="top") {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if irt data exists
	if ( !("irt_data" %in% names(gh5)) ) {
		stop("This function requires IRT data.\n\nUse TYPE=PLOT2.")
	}

	#	properties[1] - number of factors
	#	properties[2] - number of factors/covariates
	#	properties[3] - number of indicators
	#	properties[4] - number of classes
	#	properties[5] - maximum number of categories 
	props <- mplus.get.group.attribute(file,'irt_data','properties')

	flabels <- mplus.get.group.attribute(file,'irt_data','flabels')
	flabels <- gsub("(^\\s+|\\s+$)", "", flabels, perl=TRUE)
	ulabels <- mplus.get.group.attribute(file,'irt_data','ulabels')
	ulabels <- gsub("(^\\s+|\\s+$)", "", ulabels, perl=TRUE)

	num_fx <- as.integer(props[2])
	num_r <- as.integer(props[3])
	max_num_cat <- as.integer(props[5])

	if (is.character(xvar)) {
		xvar <- toupper(xvar)
		index <- pmatch(xvar, flabels, nomatch=0)
		if (index == 0) {
			cstr <- sprintf("Unknown variable for the x-axis:  %s\n", xvar)
			stop(cstr)
		}
		fidx = index
	} else {
		if (xvar <= 0 || xvar > num_fx) {
			stop("The index for the x-variable (xvar) is out of range.")
		}
		fidx = xvar
	}
	if (missing(uvar)) {
	} else if (length(uvar) > 1) {
		ridx <- vector()
		for (r in c(1:length(uvar))) {
			var <- uvar[r]
			if (is.character(var)) {
				var <- toupper(var)
				index <- pmatch(var, ulabels, nomatch=0)
				if (index == 0) {
					cstr <- sprintf("Unknown indicator:  %s\n", var)
					stop(cstr)
				}
				ridx[r] = index
			} else {
				if (var <= 0 || var > num_r) {
					stop("The index for the indicator in uvar is out of range.")
				}
				ridx[r] = var
			}
		}
	} else {
		if (is.character(uvar)) {
			uvar <- toupper(uvar)
			index <- pmatch(uvar, ulabels, nomatch=0)
			if (index == 0) {
				cstr <- sprintf("Unknown indicator:  %s\n", uvar)
				stop(cstr)
			}
			ridx <- index
		} else {
			if (uvar <= 0 || uvar > num_r) {
				stop("The index for the indicator (uvar) is out of range.")
			}
			ridx <- uvar
		}
	}
	if (group <= 0 || group > props[4]) {
		stop("The group index (group) is out of range.")
	}
	if (missing(covariates)) {
		xmean <- mplus.get.group.dataset(file,'irt_data','mean')
		covariates <- xmean[,group]
	} else {
		if (length(covariates) != num_fx) {
			cstr <- sprintf("The length of the covariates vector should be %d.", num_fx)
			stop(cstr)
		}
	}

	categories <- mplus.get.group.attribute(file,'irt_data','categories')
	if (missing(uvar)) {
		# case 1: uvar not specified, we plot ICC for all variables.  The category number must be given.
		if (missing(cat)) {
			stop("The category number (cat) is required when plotting ICCs for all variables.")
		}
		for (i in c(1:num_r)) {
			if (cat <= 0 || cat > categories[i]) {
				cstr <- sprintf("The category number (cat) is out of range for variable %s.", ulabels[i])
				stop(cstr)
			}
		}
		if (!(missing(cat2))) {
			if (cat > cat2) {
				cstr <- sprintf("The first category number (cat2=%d) must be smaller than the second category number (cat2=%d).", cat, cat2)
				stop(cstr)
			}
			for (i in c(1:num_r)) {
				if (cat2 <= 0 || cat2 > categories[i]) {
					cstr <- sprintf("The second category number (cat2) is out of range for variable %s.", ulabels[i])
					stop(cstr)
				}
			}
		}
	} else if (length(uvar) > 1) {
		for (r in c(1:length(ridx))) {
			if (!(missing(cat))) {
				if (cat <= 0 || cat > categories[ridx[r]]) {
					cstr <- sprintf("The category (cat) is out of range for variable %s.", ulabels[ridx[r]])
					stop(cstr)
				}
			} else {
				# cat is missing but cat2 isn't!
				if (!(missing(cat2))) {
					stop("The first category (cat) is required if the second category (cat2) is given.")
				}
			}
			if (!(missing(cat2))) {
				if (cat2 <= 0 || cat2 > categories[ridx[r]]) {
					cstr <- sprintf("The category (cat2) is out of range for variable %s.", ulabels[ridx[r]])
					stop(cstr)
				}
				if (cat > cat2) {
					cstr <- sprintf("The first category (cat2=%d) must be smaller than the second category (cat2=%d).", cat, cat2)
					stop(cstr)
				}
			}
		}
	} else {
		if (!(missing(cat))) {
			if (cat <= 0 || cat > categories[ridx]) {
				cstr <- sprintf("The category (cat) is out of range for variable %s.", ulabels[ridx])
				stop(cstr)
			}
		} else {
			# cat is missing but cat2 isn't!
			if (!(missing(cat2))) {
				stop("The first category (cat) is required if the second category (cat2) is given.")
			}
		}
		if (!(missing(cat2))) {
			if (cat2 <= 0 || cat2 > categories[ridx]) {
				cstr <- sprintf("The category (cat2) is out of range for variable %s.", ulabels[ridx])
				stop(cstr)
			}
			if (cat > cat2) {
				cstr <- sprintf("The first category (cat2=%d) must be smaller than the second category (cat2=%d).", cat, cat2)
				stop(cstr)
			}
		}
	}
	if (!(missing(xrange))) {
		if (xrange <= 0 || xrange > 6) {
			stop("The xrange type should be between 1 and 6.")
		}
	}
	if (!(missing(xstep))) {
		if (xstep <= 0 || xstep > 11) {
			stop("The xstep type should be between 1 and 11.")
		}
	}

	variances <- mplus.get.group.dataset(file,'irt_data','variance')
	means <- mplus.get.group.dataset(file,'irt_data','mean')
	fsd = sqrt(variances[fidx])

	xmult <- switch(xrange, 1, 2, 3, 4, 5, 6)
	vmin <- means[fidx,group] + (-1) * xmult * fsd
	vmax <- means[fidx,group] + xmult * fsd

	vstep <- switch(xstep, 1.0, 0.5, 0.1, 0.05, 0.5*fsd, 0.25*fsd, 0.2*fsd, 0.1*fsd, 0.05*fsd, 0.02*fsd, 0.01*fsd)
	steps <- seq(vmin,vmax,by=vstep)

	print(steps)

	# if cat is missing, then we plot all categories
	if (missing(uvar)) {
		prob <- array(0,c(num_r,length(steps)))
		xx <- array(0,c(num_r,length(steps)))
		if (missing(cat2)) {
			for (r in c(1:num_r)) {
				prob[r,] <- mplus.compute.irt.icc(file,group,fidx,r,cat,xvector=steps,covariates=covariates)
				xx[r,] <- steps
			}
		} else {
			for (r in c(1:num_r)) {
				for (c in c(cat:cat2)) {
					prob[r,] <- prob[r,] + mplus.compute.irt.icc(file,group,fidx,r,c,xvector=steps,covariates=covariates)
				}
				xx[r,] <- steps
			}
		}

		# plot the icc
		cstr <- sprintf("Item characteristic curves as a function of %s, Class %d", flabels[fidx], group)
		colors <- rainbow(num_r)
		plot(xx,prob,xlab=flabels[fidx],ylab="Probability",main=cstr,type='n')
		for (i in c(1:num_r)) {
			lines(xx[i,],prob[i,],col=colors[i])
		}

		ldesc <- array(0,c(num_r))
		lty <- array(0,c(num_r))
		lwd <- array(0,c(num_r))
		for (i in c(1:num_r)) {
			if (missing(cat2)) {
				ldesc[i] <- sprintf("%s, Category %d", ulabels[i], cat)
			} else {
				ldesc[i] <- sprintf("%s, Cat %d to %d", ulabels[i], cat, cat2)
			}
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend(lloc,ldesc,col=colors,lty=lty,lwd=lwd)
	} else if (length(ridx) > 1) {
		prob <- array(0,c(length(ridx),length(steps)))
		xx <- array(0,c(length(ridx),length(steps)))
		if (missing(cat)) {
			for (j in c(1:categories[ridx])) {
				prob[j,] <- mplus.compute.irt.icc(file,group,fidx,ridx,j,xvector=steps,covariates=covariates)
				xx[j,] <- steps
			}
		} else if (missing(cat2)) {
			for (r in c(1:length(ridx))) {
				prob[r,] <- mplus.compute.irt.icc(file,group,fidx,ridx[r],cat,xvector=steps,covariates=covariates)
				xx[r,] <- steps
			}
		} else {
			for (r in c(1:length(ridx))) {
				for (c in c(cat:cat2)) {
					prob[r,] <- prob[r,] + mplus.compute.irt.icc(file,group,fidx,ridx[r],c,xvector=steps,covariates=covariates)
				}
				xx[r,] <- steps
			}
		}

		# plot the icc
		cstr <- sprintf("Item characteristic curves as a function of %s, Class %d", flabels[fidx], group)
		colors <- rainbow(length(ridx))
		plot(xx,prob,xlab=flabels[fidx],ylab="Probability",main=cstr,type='n')
		for (i in c(1:length(ridx))) {
			lines(xx[i,],prob[i,],col=colors[i])
		}

		ldesc <- array(0,c(length(ridx)))
		lty <- array(0,c(length(ridx)))
		lwd <- array(0,c(length(ridx)))
		for (i in c(1:length(ridx))) {
			if (missing(cat2)) {
				ldesc[i] <- sprintf("%s, Category %d", ulabels[ridx[i]], cat)
			} else {
				ldesc[i] <- sprintf("%s, Cat %d to %d", ulabels[ridx[i]], cat, cat2)
			}
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend(lloc,ldesc,col=colors,lty=lty,lwd=lwd)
	} else if (missing(cat)) {
		prob <- array(0,c(categories[ridx],length(steps)))
		xx <- array(0,c(categories[ridx],length(steps)))
		for (j in c(1:categories[ridx])) {
			prob[j,] <- mplus.compute.irt.icc(file,group,fidx,ridx,j,steps,covariates)
			xx[j,] <- steps
		}

		# plot the icc
		cstr <- sprintf("Item characteristic curve for %s (all categories)\n as a function of %s, Class %d", ulabels[ridx], flabels[fidx], group)
		colors <- rainbow(categories[ridx])
		plot(xx,prob,xlab=flabels[fidx],ylab="Probability",main=cstr,type='n',ylim=c(0,1))
		for (i in c(1:categories[ridx])) {
			lines(xx[i,],prob[i,],col=colors[i])
		}

		ldesc <- vector()
		for (i in c(1:categories[ridx])) {
			ldesc[i] <- sprintf("%s, Category %d", ulabels[ridx], i)
		}

		legend(lloc,ldesc,col=colors,lty=c(1,1,1,1,1),lwd=c(2.5,2.5,2.5,2.5,2.5))
	} else if (missing(cat2)) {
		# if cat2 is missing, then we plot only the given category

		prob <- mplus.compute.irt.icc(file,group,fidx,ridx,cat,steps,covariates)

		# plot the icc
		cstr <- sprintf("Item characteristic curve for %s (category %d)\n as a function of %s, Class %d", ulabels[ridx], cat, flabels[fidx], group)
		plot(steps,prob,xlab=flabels[fidx],ylab="Probability",main=cstr,type='n',ylim=c(0,1))
		lines(steps,prob,col='red')
	} else {
		# if cat and cat2 are given, then we plot the sum from cat to cat2

		prob <- array(0,c(length(steps)))
		for (c in c(cat:cat2)) {
			prob <- prob + mplus.compute.irt.icc(file,group,fidx,ridx,c,steps,covariates)
		}

		# plot the icc
		cstr <- sprintf("Item characteristic curve for %s\n(sum from category %d to category %d)\nas a function of %s, Class %d", ulabels[ridx], cat, cat2, flabels[fidx], group)

		plot(steps,prob,xlab=flabels[fidx],ylab="Probability",main=cstr,type='n',ylim=c(0,1))
		lines(steps,prob,col='red')
	}

    steps
}


#========================================================================
# mplus.plot.irt.iic
#
# arguments:
#	file - the quoted name of an existing GH5 file (required)
#	group - the group number (not required)
#		 - if not given, group=1 will be used
#	xvar - the variable for the x-axis, can be the variable index or quoted variable name (not required, uses the first x)
#	uvar - the indicator variable or vector containing more than one indicator variable
#		 - can be the variable index or the quoted variable name
#		 - if not given, assume all indicator variables (not required)
#	covariates -> the vector containing values for all the other covariates (not required, sample mean used if not given)
#	xrange - the type of range for the x-axis (not required)
#		- xrange=1: -1 s.d to +1 s.d of xvar
#		- xrange=2: -2 s.d to +2 s.d of xvar
#		- xrange=3: -3 s.d to +3 s.d of xvar (default)
#		- xrange=4: -4 s.d to +4 s.d of xvar
#		- xrange=5: -5 s.d to +5 s.d of xvar
#		- xrange=6: -6 s.d to +6 s.d of xvar
#	xstep - the step increment for the x-axis range (not required)
#		- xstep=1: 1.0
#		- xstep=2: 0.5
#		- xstep=3: 0.1
#		- xstep=4: 0.05
#		- xstep=5: 1/2 s.d of xvar
#		- xstep=6: 1/4 s.d of xvar
#		- xstep=7: 1/5 s.d of xvar (default)
#		- xstep=8: 1/10 s.d of xvar
#		- xstep=9: 1/20 s.d of xvar
#		- xstep=10: 1/50 s.d of xvar
#		- xstep=11: 1/100 s.d of xvar
#
# eg. mplus.plot.irt.iic('ex7.27.gh5',1,'F','U1',)
#
mplus.plot.irt.iic <- function(file,group=1,xvar=1,uvar,covariates,xrange=3,xstep=7,lloc="top") {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if irt data exists
	if ( !("irt_data" %in% names(gh5)) ) {
		stop("This function requires IRT data.\n\nUse TYPE=PLOT2.")
	}

	#	properties[1] - number of factors
	#	properties[2] - number of factors/covariates
	#	properties[3] - number of indicators
	#	properties[4] - number of classes
	#	properties[5] - maximum number of categories 
	props <- mplus.get.group.attribute(file,'irt_data','properties')

	flabels <- mplus.get.group.attribute(file,'irt_data','flabels')
	flabels <- gsub("(^\\s+|\\s+$)", "", flabels, perl=TRUE)
	flabels <- tolower(flabels)
	ulabels <- mplus.get.group.attribute(file,'irt_data','ulabels')
	ulabels <- gsub("(^\\s+|\\s+$)", "", ulabels, perl=TRUE)
	ulabels <- tolower(ulabels)

	num_fx <- as.integer(props[2])
	num_r <- as.integer(props[3])
	max_num_cat <- as.integer(props[5])

	if (is.character(xvar)) {
		xvar <- tolower(xvar)
		index <- pmatch(xvar, flabels, nomatch=0)
		if (index == 0) {
			cstr <- sprintf("Unknown variable for the x-axis:  %s\n", xvar)
			stop(cstr)
		}
		fidx = index
	} else {
		if (xvar <= 0 || xvar > num_fx) {
			stop("The index for the x-variable (xvar) is out of range.")
		}
		fidx = xvar
	}
	if (missing(uvar)) {
	} else if (length(uvar) > 1) {
		ridx <- vector()
		for (r in c(1:length(uvar))) {
			var <- uvar[r]
			if (is.character(var)) {
				index <- pmatch(var, ulabels, nomatch=0)
				if (index == 0) {
					cstr <- sprintf("Unknown indicator:  %s\n", var)
					stop(cstr)
				}
				ridx[r] = index
			} else {
				if (var <= 0 || var > num_r) {
					stop("The index for the indicator in uvar is out of range.")
				}
				ridx[r] = var
			}
		}
	} else {
		if (is.character(uvar)) {
			uvar <- tolower(uvar)
			index <- pmatch(uvar, ulabels, nomatch=0)
			if (index == 0) {
				cstr <- sprintf("Unknown indicator:  %s\n", uvar)
				stop(cstr)
			}
			ridx = index
		} else {
			if (uvar <= 0 || uvar > num_r) {
				stop("The index for the indicator (uvar) is out of range.")
			}
			ridx = uvar
		}
	}
	if (group <= 0 || group > props[4]) {
		stop("The group index (group) is out of range.")
	}
	if (missing(covariates)) {
		xmean <- mplus.get.group.dataset(file,'irt_data','mean')
		covariates <- xmean[,group]
	} else {
		if (length(covariates) != num_fx) {
			cstr <- sprintf("The length of the covariates vector should be %d.", num_fx)
			stop(cstr)
		}
	}

	categories <- mplus.get.group.attribute(file,'irt_data','categories')
	if (!(missing(xrange))) {
		if (xrange <= 0 || xrange > 6) {
			stop("The xrange type should be between 1 and 6.")
		}
	}
	if (!(missing(xstep))) {
		if (xstep <= 0 || xstep > 11) {
			stop("The xstep type should be between 1 and 11.")
		}
	}

	variances <- mplus.get.group.dataset(file,'irt_data','variance')
	means <- mplus.get.group.dataset(file,'irt_data','mean')
	fsd = sqrt(variances[fidx])

	xmult <- switch(xrange, 1, 2, 3, 4, 5, 6)
	vmin <- means[fidx,group] + (-1) * xmult * fsd
	vmax <- means[fidx,group] + xmult * fsd

	vstep <- switch(xstep, 1.0, 0.5, 0.1, 0.05, 0.5*fsd, 0.25*fsd, 0.2*fsd, 0.1*fsd, 0.05*fsd, 0.02*fsd, 0.01*fsd)
	steps <- seq(vmin,vmax,by=vstep)

	# if cat is missing, then we plot all categories
	if (missing(uvar)) {
		prob <- array(0,c(num_r,length(steps)))
		xx <- array(0,c(num_r,length(steps)))
		for (r in c(1:num_r)) {
			prob[r,] <- mplus.compute.irt.iic(file,group,fidx,r,xvector=steps,covariates=covariates)
			xx[r,] <- steps
		}

		# plot the iic
		cstr <- sprintf("Item information curves as a function of %s, Class %d", flabels[fidx], group)
		colors <- rainbow(num_r)
		plot(xx,prob,xlab=flabels[fidx],ylab="Information",main=cstr,type='n')
		for (i in c(1:num_r)) {
			lines(xx[i,],prob[i,],col=colors[i])
		}

		ldesc <- array(0,c(num_r))
		lty <- array(0,c(num_r))
		lwd <- array(0,c(num_r))
		for (i in c(1:num_r)) {
			ldesc[i] <- sprintf("%s", ulabels[i])
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend(lloc,ldesc,col=colors,lty=lty,lwd=lwd)
	} else if (length(ridx) > 1) {
		prob <- array(0,c(length(ridx),length(steps)))
		xx <- array(0,c(length(ridx),length(steps)))
		for (r in c(1:length(ridx))) {
			prob[r,] <- mplus.compute.irt.iic(file,group,fidx,ridx[r],xvector=steps,covariates=covariates)
			xx[r,] <- steps
		}

		# plot the iic
		cstr <- sprintf("Item information curves as a function of %s, Class %d", flabels[fidx], group)
		colors <- rainbow(length(ridx))
		plot(xx,prob,xlab=flabels[fidx],ylab="Information",main=cstr,type='n')
		for (i in c(1:length(ridx))) {
			lines(xx[i,],prob[i,],col=colors[i])
		}

#		for (i in c(1:length(steps))) {
#			cstr <- sprintf("x = %0.3f, probx = %0.3f", xx[1,i], prob[1,i])
#			print(cstr)
#		}

		ldesc <- array(0,c(length(ridx)))
		lty <- array(0,c(length(ridx)))
		lwd <- array(0,c(length(ridx)))
		for (i in c(1:length(ridx))) {
			ldesc[i] <- sprintf("%s", ulabels[ridx[i]])
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend(lloc,ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		prob <- mplus.compute.irt.iic(file,group,fidx,ridx,steps,covariates)

#		for (i in c(1:length(steps))) {
#			cstr <- sprintf("x = %0.3f, probx = %0.3f", steps[i], prob[i])
#			print(cstr)
#		}

		# plot the iic
		cstr <- sprintf("Item information curve for %s as a function of %s, Class %d", ulabels[ridx], flabels[fidx], group)
		plot(steps,prob,xlab=flabels[fidx],ylab="Information",main=cstr,type='n')
		lines(steps,prob,col='red')
	}
}


#========================================================================
# mplus.plot.irt.tic
#
# arguments:
#	file - the quoted name of an existing GH5 file (required)
#	group - the group number (not required)
#		 - if not given, group=1 will be shown
#	xvar - the variable for the x-axis, can be the variable index or quoted variable name (not required, uses the first x)
#	uvar - the indicator variable or vector containing more than one indicator variable
#		 - can be the variable index or the quoted variable name
#		 - if not given, assume all indicator variables (not required)
#	covariates -> the vector containing values for all the other covariates (not required, sample mean used if not given)
#	xrange - the type of range for the x-axis (not required)
#		- xrange=1: -1 s.d to +1 s.d of xvar
#		- xrange=2: -2 s.d to +2 s.d of xvar
#		- xrange=3: -3 s.d to +3 s.d of xvar (default)
#		- xrange=4: -4 s.d to +4 s.d of xvar
#		- xrange=5: -5 s.d to +5 s.d of xvar
#		- xrange=6: -6 s.d to +6 s.d of xvar
#	xstep - the step increment for the x-axis range (not required)
#		- xstep=1: 1.0
#		- xstep=2: 0.5
#		- xstep=3: 0.1
#		- xstep=4: 0.05
#		- xstep=5: 1/2 s.d of xvar
#		- xstep=6: 1/4 s.d of xvar
#		- xstep=7: 1/5 s.d of xvar (default)
#		- xstep=8: 1/10 s.d of xvar
#		- xstep=9: 1/20 s.d of xvar
#		- xstep=10: 1/50 s.d of xvar
#		- xstep=11: 1/100 s.d of xvar
#
# eg. mplus.plot.irt.tic('ex7.27.gh5',1,'F','U1',)
#
mplus.plot.irt.tic <- function(file,group=1,xvar=1,uvar,covariates,xrange=3,xstep=7) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if irt data exists
	if ( !("irt_data" %in% names(gh5)) ) {
		stop("This function requires IRT data.\n\nUse TYPE=PLOT2.")
	}

	#	properties[1] - number of factors
	#	properties[2] - number of factors/covariates
	#	properties[3] - number of indicators
	#	properties[4] - number of classes
	#	properties[5] - maximum number of categories 
	props <- mplus.get.group.attribute(file,'irt_data','properties')

	flabels <- mplus.get.group.attribute(file,'irt_data','flabels')
	flabels <- gsub("(^\\s+|\\s+$)", "", flabels, perl=TRUE)
	flabels <- tolower(flabels)
	ulabels <- mplus.get.group.attribute(file,'irt_data','ulabels')
	ulabels <- gsub("(^\\s+|\\s+$)", "", ulabels, perl=TRUE)
	ulabels <- tolower(ulabels)

	num_fx <- as.integer(props[2])
	num_r <- as.integer(props[3])
	max_num_cat <- as.integer(props[5])

	if (is.character(xvar)) {
		index <- pmatch(xvar, flabels, nomatch=0)
		if (index == 0) {
			cstr <- sprintf("Unknown variable for the x-axis:  %s\n", xvar)
			stop(cstr)
		}
		fidx = index
	} else {
		if (xvar <= 0 || xvar > num_fx) {
			stop("The index for the x-variable (xvar) is out of range.")
		}
		fidx = xvar
	}
	if (missing(uvar)) {
	} else if (length(uvar) > 1) {
		ridx <- vector()
		for (r in c(1:length(uvar))) {
			var <- uvar[r]
			if (is.character(var)) {
				index <- pmatch(var, ulabels, nomatch=0)
				if (index == 0) {
					cstr <- sprintf("Unknown indicator:  %s\n", var)
					stop(cstr)
				}
				ridx[r] = index
			} else {
				if (var <= 0 || var > num_r) {
					stop("The index for the indicator in uvar is out of range.")
				}
				ridx[r] = var
			}
		}
	} else {
		if (is.character(uvar)) {
			index <- pmatch(uvar, ulabels, nomatch=0)
			if (index == 0) {
				cstr <- sprintf("Unknown indicator:  %s\n", uvar)
				stop(cstr)
			}
			ridx = index
		} else {
			if (uvar <= 0 || uvar > num_r) {
				stop("The index for the indicator (uvar) is out of range.")
			}
			ridx = uvar
		}
	}
	if (group <= 0 || group > props[4]) {
		stop("The group index (group) is out of range.")
	}
	if (missing(covariates)) {
		xmean <- mplus.get.group.dataset(file,'irt_data','mean')
		covariates <- xmean[,group]
	} else {
		if (length(covariates) != num_fx) {
			cstr <- sprintf("The length of the covariates vector should be %d.", num_fx)
			stop(cstr)
		}
	}

	categories <- mplus.get.group.attribute(file,'irt_data','categories')
	if (!(missing(xrange))) {
		if (xrange <= 0 || xrange > 6) {
			stop("The xrange type should be between 1 and 6.")
		}
	}
	if (!(missing(xstep))) {
		if (xstep <= 0 || xstep > 11) {
			stop("The xstep type should be between 1 and 11.")
		}
	}

	variances <- mplus.get.group.dataset(file,'irt_data','variance')
	means <- mplus.get.group.dataset(file,'irt_data','mean')
	fsd = sqrt(variances[fidx])

	xmult <- switch(xrange, 1, 2, 3, 4, 5, 6)
	vmin = means[fidx] + (-1) * xmult * fsd
	vmax = means[fidx] + xmult * fsd

	vstep = switch(xstep, 1.0, 0.5, 0.1, 0.05, 0.5*fsd, 0.25*fsd, 0.2*fsd, 0.1*fsd, 0.05*fsd, 0.02*fsd, 0.01*fsd)
	steps <- seq(vmin,vmax,by=vstep)

	# if cat is missing, then we plot all categories
	if (missing(uvar)) {
		prob <- array(0,c(length(steps)))
		for (r in c(1:num_r)) {
			prob <- prob + mplus.compute.irt.iic(file,group,fidx,r,xvector=steps,covariates=covariates)
		}
        prob <- prob + 1 / gh5$irt_data$variance[fidx,group]
		# plot the tic
		cstr <- sprintf("Total information curve as a function of %s, Class %d", flabels[fidx], group)
		plot(steps,prob,xlab=flabels[fidx],ylab="Information",main=cstr,type='n')
		lines(steps,prob,col='red')
	} else if (length(ridx) > 1) {
		prob <- array(0,c(length(steps)))
		for (r in c(1:length(ridx))) {
			prob <- prob + mplus.compute.irt.iic(file,group,fidx,ridx[r],xvector=steps,covariates=covariates)
		}

		# plot the iic
		cstr <- sprintf("Partial total information curve as a function of %s, Class %d", flabels[fidx], group)
		plot(steps,prob,xlab=flabels[fidx],ylab="Information",main=cstr,type='n')
		lines(steps,prob,col='red')
	} else {
		prob <- mplus.compute.irt.iic(file,group,fidx,ridx,steps,covariates)

		# plot the tic
		cstr <- sprintf("Partial total information curve as a function of %s, Class %d", flabels[fidx], group)
		plot(steps,prob,xlab=flabels[fidx],ylab="Information",main=cstr,type='n')
		lines(steps,prob,col='red')
	}

#	for (i in c(1:length(steps))) {
#		cstr <- sprintf("x = %0.3f, probx = %0.5f", steps[i], prob[i])
#		print(cstr)
#	}
}



######################################################################################################
# Functions for Survival plots
######################################################################################################

#========================================================================
# mplus.list.survival.variables
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.survival.variables('ex6.21.gh5')
#
mplus.list.survival.variables <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

#	cat(c("\nList of variables to use in the following functions:\n"))
#	cat(c(" - mplus.compute.irt.icc\n"))
#	cat(c(" - mplus.plot.irt.icc\n"))

	cat(c("\nList of survival variables:\n"))

	for (i in c(1:props[1])) {
		cstr <- sprintf("survival_data/survival%d", i)
		label <- mplus.get.group.attribute(file,cstr,'label')
		cstr <- sprintf("%s", label)
		cstr <- gsub("(^\\s+|\\s+$)", "", cstr, perl=TRUE)
		print(cstr)
	}
}


#========================================================================
# mplus.get.survival.kaplanmeier.values
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (required)
#	classnum - the group number (not required)
#
# eg. mplus.get.survival.kaplanmeier.values('ex6.21.gh5','T')
#
mplus.get.survival.kaplanmeier.values <- function(file,survvar,classnum,time) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	if (missing(survvar)) {
		stop("The survival variable must be given.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	if (missing(classnum)) {
		datastr <- sprintf("kaplan_meier1")
	} else {
		classes <- mplus.get.group.dataset(file,'/','model_group_labels')
		dims <- attr(classes,'dim')
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}
		datastr <- sprintf("kaplan_meier%d", classnum)
	}
	kmvals <- mplus.get.group.dataset(file,groupstr,datastr)

	if (missing(time)) {
		return(kmvals[,2])
	} else {
		return(kmvals[,1])
	}
}



#========================================================================
# mplus.compute.survival.sample.logcumulative.values
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (required)
#	classnum - the group number (not required)
#
# eg. mplus.compute.survival.sample.logcumulative.values('ex6.21.gh5','T')
#
mplus.compute.survival.sample.logcumulative.values <- function(file,survvar,classnum,time) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	if (missing(survvar)) {
		stop("The survival variable must be given.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	if (missing(classnum)) {
		datastr <- sprintf("kaplan_meier1")
	} else {
		classes <- mplus.get.group.dataset(file,'/','model_group_labels')
		dims <- attr(classes,'dim')
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}
		datastr <- sprintf("kaplan_meier%d", classnum)
	}
	kmvals <- mplus.get.group.dataset(file,groupstr,datastr)

	y <- log(-log(kmvals[,2]))
	return(y)
}


#========================================================================
# mplus.get.survival.baseline.values
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (required)
#	survvar2 - ending survival variable for getting sequential time
#	classnum - the group number (not required)
#
# eg. mplus.get.survival.baseline.values('ex6.21.gh5','T')
#
mplus.get.survival.baseline.values <- function(file,survvar,survvar2,classnum,time) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	if (missing(survvar)) {
		stop("The survival variable must be given.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	if (!(missing(survvar2))) {
		if (is.character(survvar2)) {
			surv_idx2 <- 0
			for (i in c(1:props[1])) {
				cstr <- sprintf("survival_data/survival%d", i)
				label <- mplus.get.group.attribute(file,cstr,'label')
				label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
				if (label == survvar2) {
					surv_idx2 = i
					break
				}
			}
			if (surv_idx2 == 0) {
				stop("- unknown survival variable:  ", survvar2)
			}
		} else {
			if (survvar2 <= 0 || survvar2 > props[1]) {
				stop("- index for the survival variable is out of range")
			}
			surv_idx2 = survvar2
		}
	}

	if (missing(survvar2)) {
		groupstr <- sprintf("survival_data/survival%d", surv_idx)
		if (missing(classnum)) {
			datastr <- sprintf("estimated_survival")
		} else {
			classes <- mplus.get.group.dataset(file,'/','model_group_labels')
			dims <- attr(classes,'dim')
			if (classnum <= 0 || classnum > dims[1]) {
				stop("Class number is out of range.")
			}
			datastr <- sprintf("estimated_survival%d", classnum)
		}
		esvals <- mplus.get.group.dataset(file,groupstr,datastr)

		if (missing(time)) {
			return(esvals[,2])
		} else {
			return(esvals[,1])
		}
	} else {
		# ending survival variable given so we need to link them sequentially
		ylast <- 1
		xlast <- 0
		data <- vector()
		time <- vector()
		count <- 0
		for (s in c(surv_idx:surv_idx2)) {
			groupstr <- sprintf("survival_data/survival%d", s)
			if (missing(classnum)) {
				datastr <- sprintf("estimated_survival")
			} else {
				classes <- mplus.get.group.dataset(file,'/','model_group_labels')
				dims <- attr(classes,'dim')
				if (classnum <= 0 || classnum > dims[1]) {
					stop("Class number is out of range.")
				}
				datastr <- sprintf("estimated_survival%d", classnum)
			}
			esvals1 <- mplus.get.group.dataset(file,groupstr,datastr)
			
			if (s == surv_idx) {
				count <- length(esvals1[,1])
				data[1:count] <- esvals1[,1]
				time[1:count] <- estvals[,2]
			} else {
				n <- length(estvals1[,1])
			}
		}
	}
}



#========================================================================
# mplus.compute.survival.estimated.logcumulative.values
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (required)
#	classnum - the group number (not required)
#
# eg. mplus.compute.survival.estimated.logcumulative.values('ex6.21.gh5','T')
#
mplus.compute.survival.estimated.logcumulative.values <- function(file,survvar,classnum,time) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	if (missing(survvar)) {
		stop("The survival variable must be given.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	if (missing(classnum)) {
		datastr <- sprintf("estimated_survival1")
	} else {
		classes <- mplus.get.group.dataset(file,'/','model_group_labels')
		dims <- attr(classes,'dim')
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}
		datastr <- sprintf("estimated_survival%d", classnum)
	}
	esvals <- mplus.get.group.dataset(file,groupstr,datastr)

	y <- log(-log(esvals[,2]))
	return(y)
}



#========================================================================
# mplus.get.survival.basehazard.values
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (required)
#	classnum - the group number (not required)
#
# eg. mplus.get.survival.basehazard.values('ex6.21.gh5','T')
#
mplus.get.survival.basehazard.values <- function(file,survvar,classnum,time) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	if (missing(survvar)) {
		stop("The survival variable must be given.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	if (missing(classnum)) {
		datastr <- sprintf("basehazard")
	} else {
		classes <- mplus.get.group.dataset(file,'/','model_group_labels')
		dims <- attr(classes,'dim')
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}
		datastr <- sprintf("basehazard%d", classnum)
	}
	bhvals <- mplus.get.group.dataset(file,groupstr,datastr)

	if (missing(time)) {
		return(bhvals[,2])
	} else {
		return(bhvals[,1])
	}
}


#========================================================================
# mplus.plot.survival.kaplanmeier
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.survival.kaplanmeier('ex6.21.gh5','T')
#
mplus.plot.survival.kaplanmeier <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Kaplan-Meier curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i,0)
			npoints[i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(dims[1],maxpoints))
		yall <- array(NA, c(dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[i,1:npoints[i]] <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i,0)
			yall[i,1:npoints[i]] <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i)
		}
		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(dims[1])
		for (i in c(1:dims[1])) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(dims[1]))
		lty <- array(0,c(dims[1]))
		lwd <- array(0,c(dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[i] <- sprintf("Class %d", i)
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("- class number is out of range")
		}

		xx <- mplus.get.survival.kaplanmeier.values(file,surv_idx,classnum,0)
		yy <- mplus.get.survival.kaplanmeier.values(file,surv_idx,classnum)

		plot(xx,yy,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))
		lines(xx,yy,col='red')
	}
}



#========================================================================
# mplus.plot.survival.baseline
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.survival.baseline('ex6.21.gh5','T')
#
mplus.plot.survival.baseline <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Estimated baseline survival curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			npoints[i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(dims[1],maxpoints))
		yall <- array(NA, c(dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[i,1:npoints[i]] <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			yall[i,1:npoints[i]] <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i)
		}
		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(dims[1])
		for (i in c(1:dims[1])) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(dims[1]))
		lty <- array(0,c(dims[1]))
		lwd <- array(0,c(dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[i] <- sprintf("Class %d", i)
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}

		xx <- mplus.get.survival.baseline.values(file,surv_idx,classnum=classnum,time=0)
		yy <- mplus.get.survival.baseline.values(file,surv_idx,classnum=classnum)

		plot(xx,yy,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))
		lines(xx,yy,col='red')
	}
}


#========================================================================
# mplus.plot.survival.basehazard
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.survival.basehazard('ex6.21.gh5','T')
#
mplus.plot.survival.basehazard <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required.\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Estimated baseline hazard curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.survival.basehazard.values(file,surv_idx,i,0)
			npoints[i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(dims[1],maxpoints))
		yall <- array(NA, c(dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[i,1:npoints[i]] <- mplus.get.survival.basehazard.values(file,surv_idx,i,0)
			yall[i,1:npoints[i]] <- mplus.get.survival.basehazard.values(file,surv_idx,i)
		}
		plot(xall,yall,xlab="Time",ylab="",main=cstr,type='n')

		colors <- rainbow(dims[1])
		for (i in c(1:dims[1])) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(dims[1]))
		lty <- array(0,c(dims[1]))
		lwd <- array(0,c(dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[i] <- sprintf("Class %d", i)
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}

		xx <- mplus.get.survival.basehazard.values(file,surv_idx,classnum,0)
		yy <- mplus.get.survival.basehazard.values(file,surv_idx,classnum)

		plot(xx,yy,xlab="Time",ylab="",main=cstr,type='n')
		lines(xx,yy,col='red')
	}
}


#========================================================================
# mplus.plot.survival.sample.logcumulative
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.survival.sample.logcumulative('ex6.21.gh5','T')
#
mplus.plot.survival.sample.logcumulative <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Sample log cumulative hazard curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i,0)
			npoints[i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(dims[1],maxpoints))
		yall <- array(NA, c(dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[i,1:npoints[i]] <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i,0)
			yall[i,1:npoints[i]] <- mplus.compute.survival.sample.logcumulative.values(file,surv_idx,i)
			for (j in c(1:npoints[i])) {
				if (is.infinite(yall[j])) {
					xall[j] = NA
				}
			}
		}
		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(dims[1])
		for (i in c(1:dims[1])) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(dims[1]))
		lty <- array(0,c(dims[1]))
		lwd <- array(0,c(dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[i] <- sprintf("Class %d", i)
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}

		xx <- mplus.get.survival.kaplanmeier.values(file,surv_idx,classnum,0)
		yy <- mplus.compute.survival.sample.logcumulative.values(file,surv_idx,classnum)
		for (j in c(1:length(xx))) {
			if (is.infinite(yy[j])) {
				xx[j] = NA
			}
		}

		plot(xx,yy,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))
		lines(xx,yy,col='red')
	}
}



#========================================================================
# mplus.plot.survival.estimated.logcumulative
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.survival.estimated.logcumulative('ex6.21.gh5','T')
#
mplus.plot.survival.estimated.logcumulative <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Estimated log cumulative hazard curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			npoints[i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(dims[1],maxpoints))
		yall <- array(NA, c(dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[i,1:npoints[i]] <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			yall[i,1:npoints[i]] <- mplus.compute.survival.estimated.logcumulative.values(file,surv_idx,i)
			for (j in c(1:npoints[i])) {
				if (is.infinite(yall[j])) {
					xall[j] = NA
				}
			}
		}
		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(dims[1])
		for (i in c(1:dims[1])) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(dims[1]))
		lty <- array(0,c(dims[1]))
		lwd <- array(0,c(dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[i] <- sprintf("Class %d", i)
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}

		xx <- mplus.get.survival.baseline.values(file,surv_idx,classnum=classnum,time=0)
		yy <- mplus.compute.survival.estimated.logcumulative.values(file,surv_idx,classnum)
		for (j in c(1:length(xx))) {
			if (is.infinite(yy[j])) {
				xx[j] = NA
			}
		}

		plot(xx,yy,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))
		lines(xx,yy,col='red')
	}
}



#========================================================================
# mplus.plot.survival.kaplanmeier.vs.baseline
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.survival.kaplanmeier.vs.baseline('ex6.21.gh5','T')
#
mplus.plot.survival.kaplanmeier.vs.baseline <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Kaplan-Meier curve compared with\nestimated baseline survival curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(2*dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i,0)
			npoints[2*(i-1)+1] = length(xx)
			xx <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			npoints[2*i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(2*dims[1],maxpoints))
		yall <- array(NA, c(2*dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[2*(i-1)+1,1:npoints[2*(i-1)+1]] <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i,0)
			yall[2*(i-1)+1,1:npoints[2*(i-1)+1]] <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i)

			xall[2*i,1:npoints[2*i]] <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			yall[2*i,1:npoints[2*i]] <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i)
		}
		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(2*dims[1])
		for (i in c(1:(2*dims[1]))) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(2*dims[1]))
		lty <- array(0,c(2*dims[1]))
		lwd <- array(0,c(2*dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[2*(i-1)+1] <- sprintf("KM for Class %d", i)
			lty[2*(i-1)+1] = 1
			lwd[2*(i-1)+1] = 2.5

			ldesc[2*i] <- sprintf("ES for Class %d", i)
			lty[2*i] = 1
			lwd[2*i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}

		npoints <- array(0, c(2))
		xx <- mplus.get.survival.kaplanmeier.values(file,surv_idx,classnum,0)
		npoints[1] = length(xx)
		xx <- mplus.get.survival.baseline.values(file,surv_idx,classnum=classnum,time=0)
		npoints[2] = length(xx)
		maxpoints = max(npoints)

		xall <- array(NA, c(2,maxpoints))
		yall <- array(NA, c(2,maxpoints))

		xall[1,1:npoints[1]] <- mplus.get.survival.kaplanmeier.values(file,surv_idx,classnum,0)
		yall[1,1:npoints[1]] <- mplus.get.survival.kaplanmeier.values(file,surv_idx,classnum)

		xall[2,1:npoints[2]] <- mplus.get.survival.baseline.values(file,surv_idx,classnum=classnum,time=0)
		yall[2,1:npoints[2]] <- mplus.get.survival.baseline.values(file,surv_idx,classnum=classnum)

		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(2)
		for (i in c(1:2)) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(2))
		lty <- array(0,c(2))
		lwd <- array(0,c(2))

		ldesc[1] <- sprintf("KM for Class %d", classnum)
		lty[1] = 1
		lwd[1] = 2.5

		ldesc[2] <- sprintf("ES for Class %d", classnum)
		lty[2] = 1
		lwd[2] = 2.5

		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	}
}



#========================================================================
# mplus.plot.survival.sample.vs.estimated.logcumulative
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.survival.sample.vs.estimated.logcumulative('ex6.21.gh5','T')
#
mplus.plot.survival.sample.vs.estimated.logcumulative <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Sample log cumulative hazard curve compared with\nestimated log cumulative baseline hazard curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(2*dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i,0)
			npoints[2*(i-1)+1] = length(xx)
			xx <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			npoints[2*i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(2*dims[1],maxpoints))
		yall <- array(NA, c(2*dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[2*(i-1)+1,1:npoints[2*(i-1)+1]] <- mplus.get.survival.kaplanmeier.values(file,surv_idx,i,0)
			yall[2*(i-1)+1,1:npoints[2*(i-1)+1]] <- mplus.compute.survival.sample.logcumulative.values(file,surv_idx,i)

			xall[2*i,1:npoints[2*i]] <- mplus.get.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			yall[2*i,1:npoints[2*i]] <- mplus.compute.survival.estimated.logcumulative.values(file,surv_idx,i)
		}
		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(2*dims[1])
		for (i in c(1:(2*dims[1]))) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(2*dims[1]))
		lty <- array(0,c(2*dims[1]))
		lwd <- array(0,c(2*dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[2*(i-1)+1] <- sprintf("LC for Class %d", i)
			lty[2*(i-1)+1] = 1
			lwd[2*(i-1)+1] = 2.5

			ldesc[2*i] <- sprintf("ELC for Class %d", i)
			lty[2*i] = 1
			lwd[2*i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}

		npoints <- array(0, c(2))
		xx <- mplus.get.survival.kaplanmeier.values(file,surv_idx,classnum,0)
		npoints[1] = length(xx)
		xx <- mplus.get.survival.baseline.values(file,surv_idx,classnum=classnum,time=0)
		npoints[2] = length(xx)
		maxpoints = max(npoints)

		xall <- array(NA, c(2,maxpoints))
		yall <- array(NA, c(2,maxpoints))

		xall[1,1:npoints[1]] <- mplus.get.survival.kaplanmeier.values(file,surv_idx,classnum,0)
		yall[1,1:npoints[1]] <-mplus.compute.survival.sample.logcumulative.values(file,surv_idx,classnum)

		xall[2,1:npoints[2]] <- mplus.get.survival.baseline.values(file,surv_idx,classnum=classnum,time=0)
		yall[2,1:npoints[2]] <- mplus.compute.survival.estimated.logcumulative.values(file,surv_idx,classnum)

		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(2)
		for (i in c(1:2)) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(2))
		lty <- array(0,c(2))
		lwd <- array(0,c(2))

		ldesc[1] <- sprintf("LC for Class %d", classnum)
		lty[1] = 1
		lwd[1] = 2.5

		ldesc[2] <- sprintf("ELC for Class %d", classnum)
		lty[2] = 1
		lwd[2] = 2.5

		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	}
}




######################################################################################################
# Functions for Discrete survival plots
######################################################################################################

#========================================================================
# mplus.list.discrete.survival.variables
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.discrete.survival.variables('ex6.21.gh5')
#
mplus.list.discrete.survival.variables <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("discrete_survival_data" %in% names(gh5)) ) {
		stop("- discrete survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'discrete_survival_data','properties')

#	cat(c("\nList of variables to use in the following functions:\n"))
#	cat(c(" - mplus.compute.irt.icc\n"))
#	cat(c(" - mplus.plot.irt.icc\n"))

	cat(c("\nList of survival variables:\n"))

	for (i in c(1:props[1])) {
		cstr <- sprintf("discrete_survival_data/survival%d", i)
		label <- mplus.get.group.attribute(file,cstr,'label')
		cstr <- sprintf("%s", label)
		cstr <- gsub("(^\\s+|\\s+$)", "", cstr, perl=TRUE)
		print(cstr)
	}
}


#========================================================================
# mplus.get.discrete.survival.kaplanmeier.values
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (required)
#	classnum - the group number (not required)
#
# eg. mplus.get.discrete.survival.kaplanmeier.values('ex6.21.gh5','T')
#
mplus.get.discrete.survival.kaplanmeier.values <- function(file,survvar,classnum,time) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("discrete_survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	if (missing(survvar)) {
		stop("The survival variable must be given.")
	}

	props <- mplus.get.group.attribute(file,'discrete_survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("discrete_survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("discrete_survival_data/survival%d", surv_idx)
	if (missing(classnum)) {
		datastr <- sprintf("kaplan_meier1")
	} else {
		classes <- mplus.get.group.dataset(file,'/','model_group_labels')
		dims <- attr(classes,'dim')
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}
		datastr <- sprintf("kaplan_meier%d", classnum)
	}
	kmvals <- mplus.get.group.dataset(file,groupstr,datastr)

	if (missing(time)) {
		return(kmvals[,2])
	} else {
		return(kmvals[,1])
	}
}

#========================================================================
# mplus.get.discrete.survival.baseline.values
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (required)
#	survvar2 - ending survival variable for getting sequential time
#	classnum - the group number (not required)
#
# eg. mplus.get.discrete.survival.baseline.values('ex6.21.gh5','T')
#
mplus.get.discrete.survival.baseline.values <- function(file,survvar,survvar2,classnum,time) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("discrete_survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	if (missing(survvar)) {
		stop("The survival variable must be given.")
	}

	props <- mplus.get.group.attribute(file,'discrete_survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("discrete_survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	if (!(missing(survvar2))) {
		if (is.character(survvar2)) {
			surv_idx2 <- 0
			for (i in c(1:props[1])) {
				cstr <- sprintf("discrete_survival_data/survival%d", i)
				label <- mplus.get.group.attribute(file,cstr,'label')
				label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
				if (label == survvar2) {
					surv_idx2 = i
					break
				}
			}
			if (surv_idx2 == 0) {
				stop("- unknown survival variable:  ", survvar2)
			}
		} else {
			if (survvar2 <= 0 || survvar2 > props[1]) {
				stop("- index for the survival variable is out of range")
			}
			surv_idx2 = survvar2
		}
	}

	if (missing(survvar2)) {
		groupstr <- sprintf("discrete_survival_data/survival%d", surv_idx)
		if (missing(classnum)) {
			datastr <- sprintf("estimated_survival")
		} else {
			classes <- mplus.get.group.dataset(file,'/','model_group_labels')
			dims <- attr(classes,'dim')
			if (classnum <= 0 || classnum > dims[1]) {
				stop("Class number is out of range.")
			}
			datastr <- sprintf("estimated_survival%d", classnum)
		}
		esvals <- mplus.get.group.dataset(file,groupstr,datastr)

		if (missing(time)) {
			return(esvals[,2])
		} else {
			return(esvals[,1])
		}
	} else {
		# ending survival variable given so we need to link them sequentially
		ylast <- 1
		xlast <- 0
		data <- vector()
		time <- vector()
		count <- 0
		for (s in c(surv_idx:surv_idx2)) {
			groupstr <- sprintf("discrete_survival_data/survival%d", s)
			if (missing(classnum)) {
				datastr <- sprintf("estimated_survival")
			} else {
				classes <- mplus.get.group.dataset(file,'/','model_group_labels')
				dims <- attr(classes,'dim')
				if (classnum <= 0 || classnum > dims[1]) {
					stop("Class number is out of range.")
				}
				datastr <- sprintf("estimated_survival%d", classnum)
			}
			esvals1 <- mplus.get.group.dataset(file,groupstr,datastr)
			
			if (s == surv_idx) {
				count <- length(esvals1[,1])
				data[1:count] <- esvals1[,1]
				time[1:count] <- estvals[,2]
			} else {
				n <- length(estvals1[,1])
			}
		}
	}
}



#========================================================================
# mplus.get.discrete.survival.basehazard.values
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (required)
#	classnum - the group number (not required)
#
# eg. mplus.get.discrete.survival.basehazard.values('ex6.21.gh5','T')
#
mplus.get.discrete.survival.basehazard.values <- function(file,survvar,classnum,time) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("discrete_survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	if (missing(survvar)) {
		stop("The survival variable must be given.")
	}

	props <- mplus.get.group.attribute(file,'discrete_survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("discrete_survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("discrete_survival_data/survival%d", surv_idx)
	if (missing(classnum)) {
		datastr <- sprintf("basehazard")
	} else {
		classes <- mplus.get.group.dataset(file,'/','model_group_labels')
		dims <- attr(classes,'dim')
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}
		datastr <- sprintf("basehazard%d", classnum)
	}
	bhvals <- mplus.get.group.dataset(file,groupstr,datastr)

	if (missing(time)) {
		return(bhvals[,2])
	} else {
		return(bhvals[,1])
	}
}


#========================================================================
# mplus.plot.discrete.survival.kaplanmeier
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.discrete.survival.kaplanmeier('ex6.21.gh5','T')
#
mplus.plot.discrete.survival.kaplanmeier <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("discrete_survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'discrete_survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("discrete_survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("discrete_survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Kaplan-Meier curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,i,0)
			npoints[i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(dims[1],maxpoints))
		yall <- array(NA, c(dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[i,1:npoints[i]] <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,i,0)
			yall[i,1:npoints[i]] <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,i)
		}
		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(dims[1])
		for (i in c(1:dims[1])) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(dims[1]))
		lty <- array(0,c(dims[1]))
		lwd <- array(0,c(dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[i] <- sprintf("Class %d", i)
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("- class number is out of range")
		}

		xx <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,classnum,0)
		yy <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,classnum)

		plot(xx,yy,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))
		lines(xx,yy,col='red')
	}
}



#========================================================================
# mplus.plot.discrete.survival.baseline
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.discrete.survival.baseline('ex6.21.gh5','T')
#
mplus.plot.discrete.survival.baseline <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("discrete_survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'discrete_survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("discrete_survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("discrete_survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Estimated baseline survival curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			npoints[i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(dims[1],maxpoints))
		yall <- array(NA, c(dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[i,1:npoints[i]] <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			yall[i,1:npoints[i]] <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=i)
		}
		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))

		colors <- rainbow(dims[1])
		for (i in c(1:dims[1])) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(dims[1]))
		lty <- array(0,c(dims[1]))
		lwd <- array(0,c(dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[i] <- sprintf("Class %d", i)
			lty[i] = 1
			lwd[i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}

		xx <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=classnum,time=0)
		yy <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=classnum)

		plot(xx,yy,xlab="Time",ylab="Probability",main=cstr,type='n',ylim=c(0,1))
		lines(xx,yy,col='red')
	}
}


#========================================================================
# mplus.plot.discrete.survival.kaplanmeier.vs.baseline
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	survvar - the quoted name of the survival variable or the index of the survival variable (not required)
#	classnum - the group number (not required)
#
# eg. mplus.plot.discrete.survival.kaplanmeier.vs.baseline('ex6.21.gh5','T')
#
mplus.plot.discrete.survival.kaplanmeier.vs.baseline <- function(file,survvar=1,classnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if survival data exists
	if ( !("discrete_survival_data" %in% names(gh5)) ) {
		stop("- survival data is required\n\nUse TYPE=PLOT2.")
	}

	props <- mplus.get.group.attribute(file,'discrete_survival_data','properties')

	if (is.character(survvar)) {
		surv_idx <- 0
		for (i in c(1:props[1])) {
			cstr <- sprintf("discrete_survival_data/survival%d", i)
			label <- mplus.get.group.attribute(file,cstr,'label')
			label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)
			if (label == survvar) {
				surv_idx = i
				break
			}
		}
		if (surv_idx == 0) {
			stop("- unknown survival variable:  ", survvar)
		}
	} else {
		if (survvar <= 0 || survvar > props[1]) {
			stop("- index for the survival variable is out of range")
		}
		surv_idx = survvar
	}

	groupstr <- sprintf("discrete_survival_data/survival%d", surv_idx)
	label <- mplus.get.group.attribute(file,groupstr,'label')
	label <- gsub("(^\\s+|\\s+$)", "", label, perl=TRUE)

	classes <- mplus.get.group.dataset(file,'/','model_group_labels')
	dims <- attr(classes,'dim')

	cstr <- sprintf("Kaplan-Meier curve compared with\nestimated baseline survival curve for %s", label)

	if (missing(classnum)) {
		npoints <- array(0, c(2*dims[1]))
		for (i in c(1:dims[1])) {
			xx <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,i,0)
			npoints[2*(i-1)+1] = length(xx)
			xx <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			npoints[2*i] = length(xx)
		}
		maxpoints = max(npoints)

		xall <- array(NA, c(2*dims[1],maxpoints))
		yall <- array(NA, c(2*dims[1],maxpoints))

		for (i in c(1:dims[1])) {
			xall[2*(i-1)+1,1:npoints[2*(i-1)+1]] <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,i,0)
			yall[2*(i-1)+1,1:npoints[2*(i-1)+1]] <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,i)

			xall[2*i,1:npoints[2*i]] <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=i,time=0)
			yall[2*i,1:npoints[2*i]] <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=i)
		}
		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n')

		colors <- rainbow(2*dims[1])
		for (i in c(1:(2*dims[1]))) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(2*dims[1]))
		lty <- array(0,c(2*dims[1]))
		lwd <- array(0,c(2*dims[1]))
		for (i in c(1:dims[1])) {
			ldesc[2*(i-1)+1] <- sprintf("KM for Class %d", i)
			lty[2*(i-1)+1] = 1
			lwd[2*(i-1)+1] = 2.5

			ldesc[2*i] <- sprintf("ES for Class %d", i)
			lty[2*i] = 1
			lwd[2*i] = 2.5
		}
		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	} else {
		if (classnum <= 0 || classnum > dims[1]) {
			stop("Class number is out of range.")
		}

		npoints <- array(0, c(2))
		xx <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,classnum,0)
		npoints[1] = length(xx)
		xx <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=classnum,time=0)
		npoints[2] = length(xx)
		maxpoints = max(npoints)

		xall <- array(NA, c(2,maxpoints))
		yall <- array(NA, c(2,maxpoints))

		xall[1,1:npoints[1]] <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,classnum,0)
		yall[1,1:npoints[1]] <- mplus.get.discrete.survival.kaplanmeier.values(file,surv_idx,classnum)

		xall[2,1:npoints[2]] <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=classnum,time=0)
		yall[2,1:npoints[2]] <- mplus.get.discrete.survival.baseline.values(file,surv_idx,classnum=classnum)

		plot(xall,yall,xlab="Time",ylab="Probability",main=cstr,type='n')

		colors <- rainbow(2)
		for (i in c(1:2)) {
			lines(xall[i,],yall[i,],col=colors[i])
		}

		ldesc <- array(0,c(2))
		lty <- array(0,c(2))
		lwd <- array(0,c(2))

		ldesc[1] <- sprintf("KM for Class %d", classnum)
		lty[1] = 1
		lwd[1] = 2.5

		ldesc[2] <- sprintf("ES for Class %d", classnum)
		lty[2] = 1
		lwd[2] = 2.5

		legend("top",ldesc,col=colors,lty=lty,lwd=lwd)
	}
}



######################################################################################################
# Functions for BOOTSTRAP distribution plots
######################################################################################################


#=========================================================================
#
# mplus.list.bootstrap.parameters - list the parameters in bootstrap data
#
# arguments:
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.bootstrap.parameters('ex8.1.gh5')
#
mplus.list.bootstrap.parameters <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bootstrap data exists
	if ( !("bootstrap_data" %in% names(gh5)) ) {
		stop("- requires bootstrap data.\n\nUse TYPE=PLOT2 setting in Mplus with the BOOTSTRAP option.")
	}

	cat(c("\nList of parameters to use in the following functions:\n"))
	cat(c(" - mplus.plot.bootstrap.distribution\n"))
	cat(c(" - mplus.get.bootstrap.distribution\n"))
	cat(c(" - mplus.get.bootstrap.point.estimate\n"))
	
	cat(c("\nParameters:\n"))

	# get the parameter statements from bayesian_data and lookup the indices
	statements <- mplus.get.group.dataset(file, 'bootstrap_data', 'statements')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	nplaus <- length(statements)
	for (i in c(1:nplaus)) {
		cstr <- sprintf("[%d] %s", i, statements[i])
		cat(cstr,sep="\n")
	}
	invisible(statements)
}

#=========================================================================
#
# mplus.get.bootstrap.distribution - get the bootstrap distribution for the given parameter
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	parameter - the quoted name of a parameter or the parameter index, default is the first parameter
#
# eg. mplus.get.bootstrap.distribution('ex8.1.gh5','parameter 1')
#
mplus.get.bootstrap.distribution <- function(file,parameter=1) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bootstrap data exists
	if ( !("bootstrap_data" %in% names(gh5)) ) {
		stop("- requires bootstrap data.\n\nUse TYPE=PLOT2 setting in Mplus with the BOOTSTRAP option.")
	}

	if (is.character(parameter)) {
		statements <- mplus.get.group.dataset(file, 'bootstrap_data', 'statements')
		statements <- tolower(statements)
		parameter <- tolower(parameter)
		paramidx <- pmatch(parameter, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown parameter:"),parameter,"\n\nUse mplus.list.bootstrap.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		# first dimension is the number of parameters
		# second dimension is the number of iterations
		dims <- attr(gh5$bootstrap_data$parameters,"dim")

		if (parameter < 1 || parameter > dims[1]) {
			cstr <- paste("- parameter index is out of range: ",parameter,"\n\nUse mplus.list.bootstrap.parameters to see the list of parameters.\n")
			stop(cstr)
		}
		paramidx <- parameter
	}

	xx <- gh5$bootstrap_data$parameters[paramidx,]
	xx
}


#=========================================================================
#
# mplus.get.bootstrap.point.estimate - get the bootstrap point estimate for the given parameter
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	parameter - the quoted name of a parameter or the parameter index, default is the first parameter
#
# eg. mplus.get.bootstrap.point.estimate('ex8.1.gh5','parameter 1')
#
mplus.get.bootstrap.point.estimate <- function(file,parameter=1) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bootstrap data exists
	if ( !("bootstrap_data" %in% names(gh5)) ) {
		stop("- requires bootstrap data.\n\nUse TYPE=PLOT2 setting in Mplus with the BOOTSTRAP option.")
	}

	if (is.character(parameter)) {
		statements <- mplus.get.group.dataset(file, 'bootstrap_data', 'statements')
		statements <- tolower(statements)
		parameter <- tolower(parameter)
		paramidx <- pmatch(parameter, statements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown parameter:"),parameter,"\n\nUse mplus.list.bootstrap.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		# first dimension is the number of parameters
		# second dimension is the number of iterations
		dims <- attr(gh5$bootstrap_data$parameters,"dim")

		if (parameter < 1 || parameter > dims[1]) {
			cstr <- paste("- parameter index is out of range: ",parameter,"\n\nUse mplus.list.bootstrap.parameters to see the list of parameters.\n")
			stop(cstr)
		}
		paramidx <- parameter
	}

	xx <- gh5$bootstrap_data$point_estimate[paramidx]
	xx
}


#=========================================================================
#
# mplus.plot.bootstrap.distribution - plot the histogram for the parameter, using the
# specified number of bins (the default is 100 bins)
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	parameter - the quoted name of the parameter or the parameter index, default is the first parameter
#	bins - the number of bins to use
#	colest - color for estimate marker, default is green - 'none' to leave off
#	colmed - color for median marker, default is purple - 'none' to leave off
#	colci - color for confidence interval markers, default is blue - 'none' to leave off
#	lloc - location of legend, default is 'right'
#	hcol - color of the histogram
#
# eg. mplus.plot.bootstrap.distribution('bayes.gh5','parameter 1',50)
#
mplus.plot.bootstrap.distribution <- function(file,parameter=1,bins=100,colest='green',colmed='purple',colci='blue',lloc='right',hcol='red') {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if bootstrap data exists
	if ( !("bootstrap_data" %in% names(gh5)) ) {
		stop("- requires bootstrap data.\n\nUse TYPE=PLOT2 setting in Mplus with the BOOTSTRAP option.")
	}

	# the number of bins should be greater than 0
	if (bins <= 0) {
		stop("The number of bins should be greater than 0.")
	}

	# get the dimensions of parameters array
	# first dimension is the number of parameters
	# second dimension is the number of iterations
	dims <- attr(gh5$bootstrap_data$parameters,"dim")
	#print(dims)

	statements <- mplus.get.group.dataset(file, 'bootstrap_data', 'statements')
	statements <- gsub("(^\\s+|\\s+$)", "", statements, perl=TRUE)

	if (is.character(parameter)) {
		lcstatements <- tolower(statements)
		parameter <- tolower(parameter)
		paramidx <- pmatch(parameter, lcstatements, nomatch=0)

		if (paramidx == 0) {
			cstr <- paste(c("- unknown parameter:"),parameter,"\n\nUse mplus.list.bootstrap.parameters to see the list of parameters.\n")
			stop(cstr)
		}
	} else {
		if (parameter < 1 || parameter > dims[1]) {
			cstr <- paste(" - parameter index is out of range: ",parameter,"\n\nUse mplus.list.bootstrap.parameters to see the list of parameters.\n")
			stop(cstr)
		}
		paramidx <- parameter
	}
	label <- statements[paramidx]

	xx <- array(0, c(dims[2]))

	xx <- mplus.get.bootstrap.distribution(file, paramidx)

	cstr <- paste(c("Bootstrap distribution of:"),label)
	h <- hist(xx,breaks=seq(min(xx),max(xx),length=bins+1),col=hcol,main=cstr,xlab='Estimate',ylab='Count')

	lidx <- 1
	if (colest != 'none') {
		xxestimate <- mplus.get.bootstrap.point.estimate(file, paramidx)
		eststr <- sprintf("Point estimate = %0.5f", xxestimate)
		ldesc <- c(eststr)
		lcol <- c(colest)
		lidx <- lidx + 1
		abline(v=xxestimate,untf=FALSE,col=colest)
	}
	
	if (colmed != 'none') {
		xxmedian <- median(xx)
		medianstr <- sprintf("Median = %0.5f", xxmedian)
		ldesc[lidx] <- medianstr
		lcol[lidx] <- colmed
		lidx <- lidx + 1
		abline(v=xxmedian,untf=FALSE,col=colmed)
	}

	if (colci != 'none') {
		left <- quantile(xx, 0.025)
		right <- quantile(xx, 0.975)
		lowci <- sprintf("95%% Lower CI = %0.5f", left)
		uppci <- sprintf("95%% Upper CI = %0.5f", right)
		ldesc[lidx] <- lowci
		lcol[lidx] <- colci
		lidx <- lidx + 1
		ldesc[lidx] <- lowci
		lcol[lidx] <- colci
		lidx <- lidx + 1
		abline(v=left,untf=FALSE,col=colci)
		abline(v=right,untf=FALSE,col=colci)
	}

	if (length(ldesc) > 0) {
		legend(lloc,ldesc,col=lcol,lty=c(1,1,1,1),lwd=c(2.5,2.5,2.5,2.5),bg='white')
	}

	#invisible(xx)
}


######################################################################################################
# Functions for Eigenvalue plot
######################################################################################################

##########################################################################
#
# mplus.plot.eigenvalues - plot the eigenvalues
#
# arguments:
#	file - the quoted name of an existing GH5 file, required
#
# eg. mplus.plot.eigenvalues('ex4.1.gh5')
#
mplus.plot.eigenvalues <-function(file) {
	if (missing(file)) {
		stop(" - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that efa information exists
	gh5 <- h5dump(file, load=TRUE)

	if (!("efa" %in% names(gh5))) {
		stop("- requires EFA information\n\nSpecify TYPE=EFA in Mplus to get eigenvalues.\n")
	}

	eigen <- mplus.get.group.dataset(file, 'efa', 'eigenvalues')

	xeig <- c(1:length(eigen))
	plot(xeig,eigen,xlab='',ylab='',type='o')
}


##########################################################################
#
# mplus.get.eigenvalues - get eigenvalues
#
# arguments:
#	file - the quoted name of an existing GH5 file, required
#
# eg. mplus.get.eigenvalues('ex4.1.gh5')
#
mplus.get.eigenvalues <-function(file) {
	if (missing(file)) {
		stop(" - name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that efa information exists
	gh5 <- h5dump(file, load=TRUE)

	if (!("efa" %in% names(gh5))) {
		stop("- requires EFA information\n\nSpecify TYPE=EFA in Mplus to get eigenvalues.\n")
	}

	eigen <- mplus.get.group.dataset(file, 'efa', 'eigenvalues')

	# return the eigenvalues
	return(eigen)
}



######################################################################################################
# Functions for TIMESERIES plots
######################################################################################################

##########################################################################
#
# mplus.plot.timeseries.observed - return the individual data for the quoted variable
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	v - name of variable to plot
#	idnum - the id number
#
# eg. mplus.plot.timeseries.observed('ex8.1.gh5','y1')
#
mplus.plot.timeseries.observed <- function(file,v,idnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	if (!("individual_data" %in% names(gh5))) {
		stop("- requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data.")
	}

	if (missing(v)) {
		stop("- requires the name of a variable.\n\nUse mplus.list.timeseries.variables() to get the list of variable names.")
	}

	# variables are stored in uppercase
	var <- toupper(v)

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')

	index <- pmatch(var, var_names, nomatch=0)

	if (index == 0) {
		cstr <- paste(c("Unknown variable:"),var,"\n")
		stop(cstr)
	}

	# check if timeseries information exists
	if ( !(mplus.check.group.attribute(file, 'individual_data', 'timeseries')) ) {
		stop("- requires time series information.\n\nModel must have lagged variables with & notation.")
	}

	# get the variable names from individual_data and lookup the indices
	timeseries <- mplus.get.group.attribute(file, 'individual_data', 'timeseries')
	timeseries <- as.integer(timeseries)

	if (timeseries[index] == 0) {
		cstr <- paste(c("No time series plot for this variable:"),var,"\n")
		stop(cstr)
	}

	if ( !(mplus.check.group.attribute(file, 'individual_data', 'cluster')) ) {
		cstr <- sprintf("Time series plot for %s", var_names[index])
		yy <- mplus.get.timeseries.data(file, v)
		xx <- 1:c(length(yy))
	}

	ntime <- length(which(!is.na(yy)))
	obspoint <- array(0,c(ntime))
	obstime <- array(0,c(ntime))
	jidx <- 1
	for (i in c(1:length(yy))) {
		if (is.na(yy[i])) {
#			if (i > 1) {
#				j <- i-1
#				found <- FALSE
#				while (j > 0 && !found) {
#					if (!is.na(yy[j])) {
#						found <- TRUE
#					} else {
#						j <- j -1
#					}
#				}
#				if (found) {
#					missy[jmiss] <- yy[j]
#					missx[jmiss] <- xx[j]
#					jmiss <- jmiss + 1
#				}
#			}
		} else {
			obspoint[jidx] <- yy[i]
			obstime[jidx] <- xx[i]
			jidx <- jidx + 1
		}
	}

	plot(xx,yy,xlab='Time',ylab=var_names[index],main=cstr,type='l',pch=21,col='red')
	points(obstime,obspoint,pch=21,col='red',bg='red')
}

##########################################################################
#
# mplus.get.timeseries.data - return the individual data for the quoted variable
#
# arguments:
#	file - the quoted name of an existing GH5 file
#	v - name of variable to plot
#	idnum - the id number
#
# eg. mplus.get.timeseries.data('ex8.1.gh5','y1')
#
mplus.get.timeseries.data <- function(file,v,idnum) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	# check that the series exists
	gh5 <- h5dump(file, load=TRUE)

	if (!("individual_data" %in% names(gh5))) {
		stop("- requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data.")
	}

	# check if timeseries information exists
	if ( !(mplus.check.group.attribute(file, 'individual_data', 'timeseries'))) {
		stop("- requires time series information.\n\nModel must have lagged variables with & notation.")
	}

	if (missing(v)) {
		stop("- requires the name of a variable.\n\nUse mplus.list.timeseries.variables() to get the list of variable names.")
	}

	# variables are stored in uppercase
	var <- toupper(v)

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')

	index <- pmatch(var, var_names, nomatch=0)

	if (index == 0) {
		cstr <- paste(c("Unknown variable:"),var,"\n")
		stop(cstr)
	}

	xx <- gh5$individual_data$raw_data[index,]
	xx[xx == 999] <- NA

	# if there is no cluster, then just return the entire data for this variable.
	if ( !(mplus.check.group.attribute(file, 'individual_data', 'cluster')))
	{
		# get the data for the variable
		return(xx)
	}

	if (missing(idnum)) {
		stop("- requires the id number.\n\nUse mplus.list.idnumbers() to get the list of id numbers.")
	}

	num_clusters <- mplus.get.group.attribute(file, 'individual_data', 'cluster')

	if (num_clusters == 1) {
		cluster_index <- length(var_names)
	} else {
		cluster_index <- length(var_names) - 1
	}

	clusters <- mplus.get.data(file, var_names[cluster_index])
	df <- data.frame(xx, clusters)
	df2 <- df[ which(df$clusters==idnum), ]
	return (df2$xx)
}

##########################################################################
#
# mplus.list.timeseries.variables - list the time series variables in individual data
#
# arguments: none
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.timeseries.variables('ex8.1.gh5')
#
mplus.list.timeseries.variables <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if individual data exists
	if ( !("individual_data" %in% names(gh5)) ) {
		stop("- requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data.")
	}

	# check if timeseries information exists
	if ( !(mplus.check.group.attribute(file, 'individual_data', 'timeseries'))) {
		stop("- requires time series information.\n\nModel must have lagged variables with & notation.")
	}

	cat(c("\nList of variable names to use in the following functions:\n"))
	cat(c(" - mplus.plot.timeseries\n"))

	cat(c("\nVariables:\n"))

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')
	var_names <- gsub("(^\\s+|\\s+$)", "", var_names, perl=TRUE)
	var_names <- gsub(":1", ", mean", var_names, perl=FALSE)
	var_names <- gsub(":2", ", median", var_names, perl=FALSE)

	# get the timeseries indicators from individual_data and lookup the indices
	timeseries <- mplus.get.group.attribute(file, 'individual_data', 'timeseries')
	timeseries <- as.integer(timeseries)

	ii <- 0
	for (i in c(1:length(timeseries))) {
		if (timeseries[i] == 1) {
			ii <- ii + 1;
			cstr <- sprintf("[%d] %s", ii, var_names[i])
			cat(cstr,sep="\n")
		}
	}
}


##########################################################################
#
# mplus.list.timeseries.idnums - list the idnums in individual data
#
# arguments: none
#	file - the quoted name of an existing GH5 file
#
# eg. mplus.list.timeseries.idnums('ex8.1.gh5',)
#
mplus.list.timeseries.idnums <- function(file) {
	if (missing(file)) {
		stop("- name of the GH5 file is required")
	}
	if (!(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	# check if individual data exists
	if ( !("individual_data" %in% names(gh5)) ) {
		stop("- requires individual data.\n\nUse TYPE=PLOT1 or TYPE=PLOT3 setting in Mplus to store individual data.")
	}

	# check if timeseries information exists
	if ( !(mplus.check.group.attribute(file, 'individual_data', 'timeseries'))) {
		stop("- requires time series information.\n\nModel must have lagged variables with & notation.")
	}

	# check if cluster information exists
	if ( !(mplus.check.group.attribute(file, 'individual_data', 'cluster'))) {
		stop("- requires cluster information.\n\nThe CLUSTER option must be used.")
	}

	nclusters <- mplus.get.group.attribute(file, 'individual_data', 'cluster')

	# get the variable names from individual_data and lookup the indices
	var_names <- mplus.get.group.attribute(file, 'individual_data', 'var_names')

	if (nclusters == 1) {
		clusvar <- var_names[length(var_names)]
	} else {
		clusvar <- var_names[length(var_names)-1]
	}

	ids <- mplus.get.data(file, clusvar)
	ids <- sort(unique(ids))

	cstr <- sprintf("IDs for %s:", clusvar)
	cat(cstr,sep="\n")

	ids
}

##########################################################################
#
# mplus.compute.timeseries.acf - compute the autocorrelation for a give array of values
#
# arguments:
#	vec - array containing the data
#	lagmax - the max lag number
#
# eg. mplus.compute.timeseries.acf(vec,15)
#
mplus.compute.timeseries.acf <- function(vec,lagmax) {
	if (true) {
		n <- length(vec)
		n0 <- length(vec[which(!is.na(vec))])
		m <- mean(vec, na.rm=TRUE)
		v <- var(vec, na.rm=TRUE)
		sdn <- sqrt(var(vec, na.rm=TRUE) * (n0-1)/n0)

		vecm <- vec - m

		# without missing data, pacse is a constant for all lag
		pacse <- array(1 / sqrt(n0), c(lagmax))

		ac <- array(0,c(lagmax))
		acse <- pacse

		for (lag in c(1:lagmax)) {
			vecmlag <- vecm[1:(n-lag)]   # d2(j-i)

			n0lag <- length(vecmlag[which(!is.na(vecmlag))])
			v <- sqrt(var(vecmlag,na.rm=TRUE) * (n0lag-1)/n0lag)

			vec1 <- vec[(1+lag):n]     # d(j)
			veclag <- vec[1:(n-lag)]   # d(j-i)

			obsflag <- !is.na(vec1) & !is.na(veclag)

			m0 <- mean(vec1[which(obsflag)])
			m00 <- mean(veclag[which(obsflag)])

			vec1_x_veclag <- vec1 * veclag

		}
	} else {
		n <- length(vec)
		m <- mean(vec)
		sdn <- sqrt(var(vec) * (n-1)/n)

		vecm <- vec - m

		# without missing data, pacse is a constant for all lag
		pacse <- 1 / sqrt(n)

		ac <- array(0,c(lagmax))
		acse <- array(pacse,c(lagmax))

		for (lag in c(1:lagmax)) {
			vecm1 <- array(0, c(n-lag))
			vecmlag <- array(0, c(n-lag))

			vecm1 <- vecm[(1+lag):n]
			vecmlag <- vecm[1:(n-lag)]

			vecmlagsq <- vecmlag * vecmlag
			lagsd <- sqrt(sum(vecmlagsq)/(n-lag))

			vecm1lag <- vecm1 * vecmlag
			f54 <- sum(vecm1lag)/(n-lag)
			ac[lag] <- f54/(lagsd * sdn)
			if (lag>1) {
				acse[lag] <- pacse * sqrt(1+2*(ac[1:(lag-1)] %*% ac[1:(lag-1)])[1])
			}
		}
	}

	df <- data.frame(estimate=as.vector(ac), se=as.vector(acse))
	return(df)
}





######################################################################################################
# Supporting functions
######################################################################################################


##########################################################################
#
# mplus.get.group.attribute - supporting function for getting attribute
#
# arguments:
#	file - the quoted name of an existing GH5 file
#   groupstr - the name of the group for the attribute
#   attrstr - the name of the attribute
#
# eg. mplus.get.group.attribute('ex8.1.gh5','individual_data','var_names')
#
mplus.get.group.attribute <- function(file, groupstr, attrstr) {
	if ( !(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	fid <- H5Fopen(file)
	gid <- H5Gopen(fid, groupstr)
	atid <- H5Aopen(gid, attrstr)

	attr <- H5Aread(atid)

	H5Aclose(atid)
	H5Gclose(gid)
	H5Fclose(fid)

	attr <- gsub("(^\\s+|\\s+$)", "", attr, perl=TRUE)

	return(attr)
}

##########################################################################
#
# mplus.check.group.attribute - supporting function for checking attribute
#
# arguments:
#	file - the quoted name of an existing GH5 file
#   groupstr - the name of the group for the attribute
#   attrstr - the name of the attribute
#
# eg. mplus.check.group.attribute('ex8.1.gh5','individual_data','var_names')
#
mplus.check.group.attribute <- function(file, groupstr, attrstr) {
	if ( !(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	fid <- H5Fopen(file)
	gid <- H5Gopen(fid, groupstr)
	atid <- H5Aexists(gid, attrstr)

	H5Gclose(gid)
	H5Fclose(fid)

	return(atid)
}

##########################################################################
#
# mplus.get.dataset.attribute - supporting function for getting attribute
#
# arguments:
#	file - the quoted name of an existing GH5 file
#   groupstr - the name of the group for the attribute
#   attrstr - the name of the attribute
#
# eg. mplus.get.dataset.attribute('ex8.1.gh5','individual_data','var_names')
#
mplus.get.dataset.attribute <- function(file, datastr, attrstr) {
	if ( !(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	fid <- H5Fopen(file)
	did <- H5Dopen(fid, datastr)
	atid <- H5Aopen(did, attrstr)

	attr <- H5Aread(atid)

	H5Aclose(atid)
	H5Dclose(did)
	H5Fclose(fid)

	return(attr)
}

##########################################################################
#
# mplus.check.dataset.attribute - supporting function for getting attribute
#
# arguments:
#	file - the quoted name of an existing GH5 file
#   groupstr - the name of the group for the attribute
#   attrstr - the name of the attribute
#
# eg. mplus.check.dataset.attribute('ex8.1.gh5','individual_data','var_names')
#
mplus.check.dataset.attribute <- function(file, datastr, attrstr) {
	if ( !(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	fid <- H5Fopen(file)
	did <- H5Dopen(fid, datastr)
	atid <- H5Aexists(did, attrstr)

	H5Dclose(did)
	H5Fclose(fid)

	return(atid)
}

##########################################################################
#
# mplus.get.group.dataset - supporting function for getting dataset
#
# arguments:
#	file - the quoted name of an existing GH5 file
#   groupstr - the name of the group for the attribute
#   datastr - the name of the attribute
#
# eg. mplus.get.group.dataset('ex8.1.gh5','bayesian_data','statements')
#
mplus.get.group.dataset <- function(file, groupstr, datastr) {
	if ( !(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	fid <- H5Fopen(file)
	gid <- H5Gopen(fid, groupstr)
	dtid <- H5Dopen(gid, datastr)

	data <- H5Dread(dtid)

	H5Dclose(dtid)
	H5Gclose(gid)
	H5Fclose(fid)

	return(data)
}

estimate_mode <- function(x) {
	d <- density(x)
	d$x[which.max(d$y)]
}

##########################################################################
#
# mplus.get.file.dataset - supporting function for getting dataset in the file
#
# arguments:
#	file - the quoted name of an existing GH5 file
#   datastr - the name of the attribute
#
# eg. mplus.get.file.dataset('ex8.1.gh5','model_group_labels')
#
mplus.get.file.dataset <- function(file, datastr) {
	if ( !(file.exists(file))) {
		cstr <- paste("- file does not exist:",file,"\n")
		stop(cstr)
	}

	gh5 <- h5dump(file, load=TRUE)

	fid <- H5Fopen(file)
	dtid <- H5Dopen(fid, datastr)

	data <- H5Dread(dtid)

	H5Dclose(dtid)
	H5Fclose(fid)

	return(data)
}

estimate_mode <- function(x) {
	d <- density(x)
	d$x[which.max(d$y)]
}





######################################################################################################
# Math functions
######################################################################################################

lin <- function(y, link) {
	if (link == 0) {
		x <- logistic(y)
	} else {
		x <- pnorm(y, mean=0, sd=1)
	}
	x
}

logistic <- function(y) {
	if (y > 50) {
		x = 1
	} else if (y > -50) {
		x = 1 / (1 + exp(-y))
	} else {
		x = 0
	}

	x
}



######################################################################################################
# Utility functions
######################################################################################################

zmatch <- function(zvalue, values) {
	
	nvalues <- length(values)
	index <- 0
	for (i in c(1:nvalues)) {
		cat(sprintf("%s\n", zvalue))
		cat(sprintf("%s\n", round(values[i],3)))
		if (zvalue == round(values[i],3)) {
			print("found!")
			index <- i
		}
	}
	index
}
ebardelli/mplus.R documentation built on Dec. 20, 2021, 3:12 a.m.