def ppxfmontecarlo(datum, wavelengths=None, fluxes=None, ivars=None, outputdir='./', nsimulations=0, inversevariance=True, mask=sky_mask, verbose=1, plot=False, rv=0, ident='', keep=True, template_files=None):
    '''
    Measure CaT strength and optionally calculate uncertainty
    
    This function measures the CaT index strength by masking sky lines, fitting a
    linear combination of template spectra using pPXF, normalising the fitted
    spectrum, and measuring the CaT index strength on the normalised spectrum. pPXF
    also fits the radial velocity and velocity dispersion. The function can also
    compute confidence intervals on the CaT strength, radial velocity and velocity
    dispersion by using the fitted spectrum and the error array to generating a 
    series of Monte Carlo realisations of the input spectrum and repeating the 
    measurement process for each. 
    
    
    
    '''
        
    if datum is None:
        
        datum = gcppxfdata.gcppxfdata()
        datum.rv = rv
        
        if ident == '':
            ident = str(np.random.randint(1e10))
        datum.id = ident
        datum.file = ident
        
    start = datetime.datetime.now()

    if wavelengths is None or fluxes is None:
        wavelengths, fluxes = onedspec.getspectrum(datum.scispectrum)
        
    zp1 = 1 + datum.rv * 1e3 / constants.c        
    catrange = (wavelengths > 8425 * zp1) & (wavelengths < 8850 * zp1)
    
    if ivars is None:
        wavelengths, sigmas = onedspec.getspectrum(datum.errorspectrum)
    else:
        sigmas = ivars
        
    if inversevariance:
        np.putmask(sigmas, sigmas <= 0, 1e-10)
        sigmas = sigmas**-.5

    cat_wavelengths = wavelengths[catrange]
    cat_fluxes = fluxes[catrange]        
    datum.catwavelengths = cat_wavelengths
    datum.catfluxes = cat_fluxes
    cat_sigmas = sigmas[catrange]
    datum.catsigmas = cat_sigmas


    s2nregion = (wavelengths > 8400) & (wavelengths < 8500)
    means2n = (fluxes[s2nregion] / sigmas[s2nregion]).mean()
    datum.s2n = means2n / (wavelengths[1] - wavelengths[0])**.5

    log_wavelengths, log_fluxes = interp.lineartolog(cat_wavelengths, cat_fluxes, function='nearest', ratio=True)
    log_wavelengths, log_sigmas = interp.lineartolog(cat_wavelengths, cat_sigmas, function='nearest', ratio=True)
    

    logDispersion = np.log10(log_wavelengths[1]) - np.log10(log_wavelengths[0])
    
    if template_files is None:
        template_files = default_templates
        
        
    if not len(template_files):
        raise Exception('No templates')
    
    log_templates = []
    for template_file in template_files:
        
        template_wavelengths, template_fluxes = onedspec.getspectrum(template_file)
        log_template_wavelengths, log_template_fluxes = interp.lineartolog(template_wavelengths, template_fluxes, function='nearest', ratio=True, logDispersion=logDispersion)
        log_templates.append(log_template_fluxes)
        
    log_templates = np.vstack(log_templates).T
    delta_v = (np.log(log_template_wavelengths[0]) - np.log(log_wavelengths[0])) * constants.c / 1e3


    regionmask = np.ones(log_wavelengths.size, dtype=np.bool_)
    if mask is not None:
        for maskregion in mask:
            regionmask = regionmask & ~((log_wavelengths > maskregion[0]) & (log_wavelengths < maskregion[1]))
        goodpixels = np.nonzero(regionmask)[0]
    else:
        goodpixels = np.arange(log_wavelengths.size)
    
    if verbose > 1:
        quiet = False
    else:
        quiet = True
    
    vel_scale = logDispersion * np.log(10) * constants.c / 1e3
    log_fit_fluxes, datum.fitwavelengths, datum.fitfluxes, datum.normfluxes, datum.fitrv, datum.fitsigma, datum.CaT = ppxf_CaT(log_wavelengths, log_templates, log_fluxes, log_sigmas, vel_scale, delta_v, datum.rv, 10, goodpixels, quiet)

    if plot:
        import matplotlib.pyplot as plt
        
        plt.figure()
        plt.title(datum.file)
        
        plt.plot()
        plt.plot(datum.catwavelengths / (1 + datum.fitrv * 1e3 / constants.c), datum.catfluxes, 'k-')
        plt.plot(datum.fitwavelengths, datum.fitfluxes, 'r-', lw=1.5)
#        residules = datum.catfluxes - datum.fitfluxes
#        plt.plot(datum.fitwavelengths, residules / datum.catsigmas)
#        plt.plot(datum.catwavelengths / (1 + datum.fitrv * 1e3 / constants.c), datum.catsigmas)

        plt.xlabel(u'Wavelength (\u00C5)')
        plt.ylabel('Counts')
    
    datum.samplewavelengths = datum.fitwavelengths
    
    if nsimulations:
    
        samples = []
        for i in range(nsimulations):
            noise = log_sigmas * np.random.normal(size=log_sigmas.size)
            sample_flux = log_fit_fluxes + noise
            
            samples.append((log_wavelengths, log_templates, sample_flux, log_sigmas, vel_scale, delta_v, datum.rv, 10, goodpixels, quiet))
        
        workers = min(multiprocessing.cpu_count(), 12)
        pool = multiprocessing.Pool(processes=workers)        
    
        if verbose > 1:
            print 'Using', workers, 'workers'
            
        sample_results = pool.map(_boot_ppxf_CaT, samples)
        
        sample_fit_fluxes = []
        sample_normed_fluxes = []
        sample_rvs = []
        sample_sigmas = []
        sample_CaTs = []
        
        for i in range(len(sample_results)):
            sample_result = sample_results[i]
            sample_fit_fluxes.append(sample_result[2])
            sample_normed_fluxes.append(sample_result[3])
            sample_rvs.append(sample_result[4])
            sample_sigmas.append(sample_result[5])
            sample_CaTs.append(sample_result[6])
        
        sample_fit_fluxes = np.vstack(sample_fit_fluxes)
        sample_normed_fluxes = np.vstack(sample_normed_fluxes)
        sample_rvs = np.asarray(sample_rvs)
        sample_sigmas = np.asarray(sample_sigmas)
        sample_CaTs = np.asarray(sample_CaTs)
        
        print sample_fit_fluxes.shape

        for i in range(sample_normed_fluxes.shape[1]):
            sample_fit_fluxes[:,i].sort()
            sample_normed_fluxes[:,i].sort()
        lowindex = int(.16 * nsimulations) 
        highindex = int(.84 * nsimulations) - 1

        datum.lowsample = sample_fit_fluxes[lowindex]
        datum.highsample = sample_fit_fluxes[highindex]
        datum.lownormsample = sample_normed_fluxes[lowindex]
        datum.highnormsample = sample_normed_fluxes[highindex]
        
        datum.CaT_samples = sample_CaTs

        rv_peak, rv_lower, rv_upper, rv_spline = boot.kde_interval(sample_rvs)
        datum.fitrvle = datum.fitrv - rv_lower
        datum.fitrvue = rv_upper - datum.fitrv

        sigma_peak, sigma_lower, sigma_upper, sigma_spline = boot.kde_interval(sample_sigmas)
        datum.fitsigmale = datum.fitsigma - sigma_lower
        datum.fitsigmaue = sigma_upper - datum.fitsigma

        CaT_peak, CaT_lower, CaT_upper, CaT_spline = boot.kde_interval(sample_CaTs)
        datum.CaTle = datum.CaT - CaT_lower
        datum.CaTue = CaT_upper - datum.CaT



        if plot:
            plt.figure()
            plt.title(datum.file)
            plt.hist(sample_rvs, histtype='step', normed=True, bins=int(2 * sample_rvs.size**0.5))        
            rvs = np.linspace(2 * sample_rvs.min() - sample_rvs.mean(), 2 * sample_rvs.max() - sample_rvs.mean(), 512)
            plt.plot(rvs, rv_spline(rvs))        
            plt.xlabel(u'rv (km s$^{\\mathregular{\u22121}}$)')
            print 'rv', round(datum.fitrv, 1), round(-datum.fitrvle, 1), round(datum.fitrvue, 1)
    
            plt.figure()
            plt.title(datum.file)            
            plt.hist(sample_sigmas, histtype='step', normed=True, bins=int(2 * sample_sigmas.size**0.5))
            sigmas = np.linspace(2 * sample_sigmas.min() - sample_sigmas.mean(), 2 * sample_sigmas.max() - sample_sigmas.mean(), 512)
            plt.plot(sigmas, sigma_spline(sigmas))
            plt.xlabel(u'\u03C3 (km s$^{\\mathregular{\u22121}}$)')
            print 'sigma', round(datum.fitsigma, 1), round(-datum.fitsigmale, 1), round(datum.fitsigmaue, 1)
    
            plt.figure()
            plt.title(datum.file)            
            plt.hist(sample_CaTs, histtype='step', normed=True, bins=int(2 * sample_CaTs.size**0.5))
            CaTs = np.linspace(2 * sample_CaTs.min() - sample_CaTs.mean(), 2 * sample_CaTs.max() - sample_CaTs.mean(), 512)
            plt.plot(CaTs, CaT_spline(CaTs))
            plt.xlabel(u'CaT (\u00C5)')
            print 'CaT', round(datum.CaT, 2), round(-datum.CaTle, 2), round(datum.CaTue, 2)
            print '[Z/H]', round(datum.CaT * 0.46058111 - 3.75039355, 2), round(-datum.CaTle * 0.46058111, 2), round(datum.CaTue * 0.46058111, 2)

    
    
    else:
        datum.CaTle = 0
        datum.CaTue = 0
        
        datum.lowsample = np.zeros(datum.samplewavelengths.size)
        datum.highsample = np.zeros(datum.samplewavelengths.size)
        datum.lownormsample = np.zeros(datum.samplewavelengths.size)
        datum.highnormsample = np.zeros(datum.samplewavelengths.size)
            
                
    datum.restframemask = sky_mask / zp1
    
    end = datetime.datetime.now()
    datum.runtime = end - start    
    if verbose:
        print outputdir + datum.file + '.pickle'
        print round(datum.CaT, 3), round(datum.CaTle, 3), round(datum.CaTue, 3), round(datum.s2n, 1), datum.runtime, datum.runtime.total_seconds() / (nsimulations + 1)
        print    
    if keep:    
        pickle.dump(datum, open(outputdir + datum.file + '.pickle', 'w'))
    if plot:    
        plt.show()
    return datum    
parser.add_argument('-v', '--rv', type=float, help='radial velocity')
parser.add_argument('-N', '--num-simulations', type=int, default=0, help='Number of Monte Carlo simulations')
parser.add_argument('-o', '--output-dir', help='output directory')
#parser.add_argument('-l', '--idl', default='64', help='IDL version')
parser.add_argument('-s', '--sigma', action='store_true', default=False, help='Error array is sigmas rather than ivars')
parser.add_argument('-n', '--name', help='object name')
parser.add_argument('-p', '--plot', action='store_true', default=False, help='Plot fitted spectra and Monte Carlo results')
parser.add_argument('--colour')
parser.add_argument('--coloure')
parser.add_argument('--radius')



if __name__ == "__main__":
    args = parser.parse_args()
    
    if not args.output_dir:
        args.output_dir = os.getcwd() + '/'
    if not args.name:
        args.name = args.spectrum[0]
    if not args.rv:
        args.rv = 0
    
    datum = gcppxfdata.gcppxfdata()
    datum.id = args.name
    datum.file = args.name
    datum.rv = args.rv
    datum.scispectrum = args.spectrum[0]
    datum.errorspectrum = args.error[0]
    
    gcppxf.ppxfmontecarlo(datum, outputdir=args.output_dir, nsimulations=args.num_simulations, inversevariance=(not args.sigma), plot=args.plot)