public static void main(String[] args) throws IOException {
    // generate the data (100 data points)
    // GaussDataGen gdg = new GaussDataGen(100);
    GaussDataGen gdg = new GaussDataGen(DemoGaussSim.class.getResourceAsStream("GaussDataGen.txt"));

    double[] pos = gdg.positions();
    double[] meas = gdg.measurements();
    double[] var = gdg.variances();

    // create FCN function
    GaussFcn theFCN = new GaussFcn(meas, pos, var);

    // create initial starting values for parameters
    double x = 0.;
    double x2 = 0.;
    double norm = 0.;
    double dx = pos[1] - pos[0];
    double area = 0.;
    for (int i = 0; i < meas.length; i++) {
      norm += meas[i];
      x += (meas[i] * pos[i]);
      x2 += (meas[i] * pos[i] * pos[i]);
      area += dx * meas[i];
    }
    double mean = x / norm;
    double rms2 = x2 / norm - mean * mean;
    double rms = rms2 > 0. ? Math.sqrt(rms2) : 1.;

    System.out.printf("%g %g %g\n", mean, rms, area);

    {
      // demonstrate minimal required interface for minimization
      // create Minuit parameters without names

      // starting values for parameters
      double[] init_par = {mean, rms, area};

      // starting values for initial uncertainties
      double[] init_err = {0.1, 0.1, 0.1};

      // create minimizer (default constructor)
      MnMigrad migrad = new MnMigrad(theFCN, init_par, init_err);

      // minimize
      FunctionMinimum min = migrad.minimize();

      // output
      System.out.println("minimum: " + min);
    }
    {
      // demonstrate standard minimization using MIGRAD
      // create Minuit parameters with names
      MnUserParameters upar = new MnUserParameters();
      upar.add("mean", mean, 0.1);
      upar.add("sigma", rms, 0.1);
      upar.add("area", area, 0.1);

      // create MIGRAD minimizer
      MnMigrad migrad = new MnMigrad(theFCN, upar);

      // minimize
      FunctionMinimum min = migrad.minimize();

      // output
      System.out.println("minimum: " + min);
    }
    {
      // demonstrate full interaction with parameters over subsequent
      // minimizations

      // create Minuit parameters with names
      MnUserParameters upar = new MnUserParameters();
      upar.add("mean", mean, 0.1);
      upar.add("sigma", rms, 0.1);
      upar.add("area", area, 0.1);

      // access parameter by name to set limits...
      upar.setLimits("mean", mean - 0.01, mean + 0.01);

      // ... or access parameter by index
      upar.setLimits(1, rms - 0.1, rms + 0.1);

      // create Migrad minimizer
      MnMigrad migrad = new MnMigrad(theFCN, upar);

      // fix a parameter...
      migrad.fix("mean");

      // ... and minimize
      FunctionMinimum min = migrad.minimize();

      // output
      System.out.println("minimum: " + min);

      // release a parameter...
      migrad.release("mean");

      // ... and fix another one
      migrad.fix(1);

      // and minimize again
      FunctionMinimum min1 = migrad.minimize();

      // output
      System.out.println("minimum: " + min1);

      // release the parameter...
      migrad.release(1);

      // ... and minimize with all three parameters (still with limits!)
      FunctionMinimum min2 = migrad.minimize();

      // output
      System.out.println("minimum: " + min2);

      // remove all limits on parameters...
      migrad.removeLimits("mean");
      migrad.removeLimits("sigma");

      // ... and minimize again with all three parameters (now without limits!)
      FunctionMinimum min3 = migrad.minimize();

      // output
      System.out.println("minimum: " + min3);
    }
    {
      // test single sided limits
      MnUserParameters upar = new MnUserParameters();
      upar.add("mean", mean, 0.1);
      upar.add("sigma", rms - 1., 0.1);
      upar.add("area", area, 0.1);

      // test lower limits
      upar.setLowerLimit("mean", mean - 0.01);

      // test upper limits
      upar.setUpperLimit("sigma", rms - 0.5);

      // create MIGRAD minimizer
      MnMigrad migrad = new MnMigrad(theFCN, upar);

      // ... and minimize
      FunctionMinimum min = migrad.minimize();
      System.out.println("test lower limit minimim= " + min);
    }
    {
      // demonstrate MINOS error analysis

      // create Minuit parameters with names
      MnUserParameters upar = new MnUserParameters();
      upar.add("mean", mean, 0.1);
      upar.add("sigma", rms, 0.1);
      upar.add("area", area, 0.1);

      // create Migrad minimizer
      MnMigrad migrad = new MnMigrad(theFCN, upar);

      // minimize
      FunctionMinimum min = migrad.minimize();

      // create MINOS error factory
      MnMinos minos = new MnMinos(theFCN, min);

      {
        // 1-sigma MINOS errors (minimal interface)
        // output
        System.out.println("1-sigma minos errors: ");
        System.out.printf(
            "par0: %g %g %g\n", min.userState().value("mean"), minos.lower(0), minos.upper(0));
        System.out.printf(
            "par1: %g %g %g\n", min.userState().value(1), minos.lower(1), minos.upper(1));
        System.out.printf(
            "par2: %g %g %g\n", min.userState().value("area"), minos.lower(2), minos.upper(2));
      }

      {
        // 2-sigma MINOS errors (rich interface)
        MinosError e0 = minos.minos(0, 4.);
        MinosError e1 = minos.minos(1, 4.);
        MinosError e2 = minos.minos(2, 4.);

        // output
        System.out.println("2-sigma minos errors: ");
        System.out.println(e0);
        System.out.println(e1);
        System.out.println(e2);
      }
    }

    {
      // demonstrate how to use the CONTOURs

      // create Minuit parameters with names
      MnUserParameters upar = new MnUserParameters();
      upar.add("mean", mean, 0.1);
      upar.add("sigma", rms, 0.1);
      upar.add("area", area, 0.1);

      // create Migrad minimizer
      MnMigrad migrad = new MnMigrad(theFCN, upar);

      // minimize
      FunctionMinimum min = migrad.minimize();

      // create contours factory with FCN and minimum
      MnContours contours = new MnContours(theFCN, min);

      // 70% confidence level for 2 parameters contour around the minimum
      // (minimal interface)
      List<Point> cont = contours.points(0, 1, 2.41, 20);

      // 95% confidence level for 2 parameters contour
      // (rich interface)
      ContoursError cont4 = contours.contour(0, 1, 5.99, 20);

      // plot the contours
      MnPlot plot = new MnPlot();
      cont.addAll(cont4.points());
      plot.plot(min.userState().value("mean"), min.userState().value("sigma"), cont);

      // print out one contour
      System.out.println(cont4);
    }
  }
  public void testPaul4Chi2() {
    // create Chi2 FCN function
    PaulTest4.PowerLawChi2FCN theFCN = new PaulTest4.PowerLawChi2FCN(m, p, v);

    MnUserParameters upar = new MnUserParameters();
    upar.add("p0", -2.3, 0.2);
    upar.add("p1", 1100., 10.);

    MnMigrad migrad = new MnMigrad(theFCN, upar);
    FunctionMinimum min = migrad.minimize();
    if (!min.isValid()) {
      migrad = new MnMigrad(theFCN, upar, 2);
      min = migrad.minimize();
    }
    assertTrue(min.isValid());
    assertEquals(102, min.nfcn());
    assertEquals(95.243, min.fval(), 1e-3);
    assertEquals(3.7209e-11, min.edm(), 1e-15);

    assertEquals(-2.10019, min.userParameters().value(0), 1e-5);
    assertEquals(999.225, min.userParameters().value(1), 1e-3);

    assertEquals(0.0001592, min.userParameters().error(0), 1e-7);
    assertEquals(0.8073, min.userParameters().error(1), 1e-4);

    assertEquals(2.53567e-08, min.userCovariance().get(0, 0), 1e-13);
    assertEquals(0.000127332, min.userCovariance().get(1, 0), 1e-9);
    assertEquals(0.651708, min.userCovariance().get(1, 1), 1e-6);
  }
  public void testPaul4LogLike() {

    // create LogLikelihood FCN function
    PaulTest4.PowerLawLogLikeFCN theFCN = new PaulTest4.PowerLawLogLikeFCN(m, p);

    MnUserParameters upar = new MnUserParameters();
    upar.add("p0", -2.1, 0.2);
    upar.add("p1", 1000., 10.);

    MnMigrad migrad = new MnMigrad(theFCN, upar);
    migrad.setErrorDef(0.5);
    FunctionMinimum min = migrad.minimize();
    if (!min.isValid()) {
      // try with higher strategy
      migrad = new MnMigrad(theFCN, upar, 2);
      min = migrad.minimize();
    }
    assertTrue(min.isValid());
    assertEquals(63, min.nfcn());
    assertEquals(-1.33678e+09, min.fval(), 1e4);
    assertEquals(0.0170964, min.edm(), 1e-4);

    assertEquals(-2.10016, min.userParameters().value(0), 1e-5);
    assertEquals(999.394, min.userParameters().value(1), 1e-3);

    assertEquals(0.0001488, min.userParameters().error(0), 1e-7);
    assertEquals(0.7544, min.userParameters().error(1), 1e-4);

    assertEquals(2.21365e-08, min.userCovariance().get(0, 0), 1e-13);
    assertEquals(0.000111025, min.userCovariance().get(1, 0), 1e-9);
    assertEquals(0.569138, min.userCovariance().get(1, 1), 1e-6);
  }
  public void testPaul4Simplex() {
    PaulTest4.PowerLawChi2FCN chi2 = new PaulTest4.PowerLawChi2FCN(m, p, v);
    PaulTest4.PowerLawLogLikeFCN mlh = new PaulTest4.PowerLawLogLikeFCN(m, p);

    MnUserParameters upar;
    double[] par = {-2.3, 1100.};
    double[] err = {1., 1.};

    MnSimplex simplex = new MnSimplex(chi2, par, err);

    FunctionMinimum min = simplex.minimize();
    assertTrue(min.isValid());
    assertEquals(105, min.nfcn());
    assertEquals(95.2506, min.fval(), 1e-4);
    assertEquals(0.0286894, min.edm(), 1e-7);

    assertEquals(-2.10018, min.userParameters().value(0), 1e-5);
    assertEquals(999.286, min.userParameters().value(1), 1e-3);

    assertEquals(0.0001122, min.userParameters().error(0), 1e-7);
    assertEquals(0.7701, min.userParameters().error(1), 1e-4);

    MnSimplex simplex2 = new MnSimplex(mlh, par, err);
    simplex2.setErrorDef(0.5);
    min = simplex2.minimize();
    assertTrue(min.isValid());
    assertEquals(84, min.nfcn());
    assertEquals(-1.337e+09, min.fval(), 1e6);
    assertEquals(0.03377, min.edm(), 1e-5);

    assertEquals(-2.10018, min.userParameters().value(0), 1e-5);
    assertEquals(999.279, min.userParameters().value(1), 1e-3);

    assertEquals(0.0001793, min.userParameters().error(0), 1e-7);
    assertEquals(0.7549, min.userParameters().error(1), 1e-4);
  }