// Performs a binary search to satisfy the Wolfe conditions
  // returns alpha where next x =should be x0 + alpha*d
  // guarantees convergence as long as search direction is bounded away from being orthogonal with
  // gradient
  // x0 is starting point, d is search direction, alpha is starting step size, maxit is max
  // iterations
  // c1 and c2 are the constants of the Wolfe conditions (0.1 and 0.9 can work)
  // uses an approximate directional gradient since a true gradient is not defined for the optical
  // flow error function
  public double stepSize(double[] x0, double[] d, double alpha, int maxit, double c1, double c2) {

    // get error and gradient at starting point
    double fx0 = error(x0);
    double gx0 = dotGradient(x0, d);

    // bound the solution
    double alphaL = 0;
    double alphaR = 100;

    for (int iter = 1; iter <= maxit; iter++) {
      double[] xp = Utility.add(x0, Utility.scale(d, alpha)); // get the point at this alpha
      double erroralpha = error(xp); // get the error at that point
      if (erroralpha >= fx0 + alpha * c1 * gx0) { // if error is not sufficiently reduced
        alphaR = alpha; // move halfway between current alpha and lower alpha
        alpha = (alphaL + alphaR) / 2.0;
      } else { // if error is sufficiently decreased
        double slopealpha = dotGradient(xp, d); // then get slope along search direction
        if (slopealpha <= c2 * Math.abs(gx0)) { // if slope sufficiently closer to 0
          return alpha; // then this is an acceptable point
        } else if (slopealpha
            >= c2 * gx0) { // if slope is too steep and positive then go to the left
          alphaR = alpha; // move halfway between current alpha and lower alpha
          alpha = (alphaL + alphaR) / 2;
        } else { // if slope is too steep and negative then go to the right of this alpha
          alphaL = alpha; // move halfway between current alpha and upper alpha
          alpha = (alphaL + alphaR) / 2;
        }
      }
    }

    // if ran out of iterations then return the best thing we got
    return alpha;
  }
  // returns the gradient of the function at x dotted with d
  // used with line-search which never requires the full gradient
  public double dotGradient(double x[], double d[]) {
    double p1[] = Utility.subtract(x, Utility.scaleTo(d, gradientepsilon));
    double p2[] = Utility.add(x, Utility.scaleTo(d, gradientepsilon));
    double f1 = error(p1);
    double f2 = error(p2);

    return (f2 - f1) / (gradientepsilon * 2);
  }
  /*
   * Constructor
   *
   * Create the dialog but don't display it (gets displayed when user selects "Settings..." menu item)
   *
   */
  public CTsettings(JFrame parentI) {

    super(parentI, "CTtext Settings", true);

    parent = parentI;

    setFont(new Font("Dialog", Font.PLAIN, 12));
    GridBagLayout gbl = new GridBagLayout();
    JPanel guiPanel = new JPanel(gbl);

    GridBagConstraints gbc = new GridBagConstraints();
    gbc.anchor = GridBagConstraints.WEST;
    gbc.fill = GridBagConstraints.NONE;
    gbc.weightx = 0;
    gbc.weighty = 0;

    // Create GUI components
    outputDirTF = new JTextField(25);
    chanNameTF = new JTextField(10);
    bUseFTPCheckB = new JCheckBox("Use FTP");
    bUseFTPCheckB.addItemListener(this);
    ftpHostLabel = new JLabel("Host", SwingConstants.LEFT);
    ftpHostTF = new JTextField(20);
    ftpUserLabel = new JLabel("Username", SwingConstants.LEFT);
    ftpUserTF = new JTextField(15);
    ftpPasswordLabel = new JLabel("Password", SwingConstants.LEFT);
    ftpPasswordTF = new JPasswordField(15);
    autoFlushRB = new JRadioButton("Automatic flush at interval");
    autoFlushRB.addItemListener(this);
    manualFlushRB = new JRadioButton("Manual flush on button click");
    flushButtonGroup = new ButtonGroup();
    flushButtonGroup.add(autoFlushRB);
    flushButtonGroup.add(manualFlushRB);
    flushIntervalLabel = new JLabel("Flush interval", SwingConstants.LEFT);
    flushIntervalComboB = new JComboBox<String>(flushIntervalStrings);
    flushIntervalComboB.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXX");
    flushIntervalComboB.setEditable(false);
    okButton = new JButton("OK");
    okButton.addActionListener(this);
    cancelButton = new JButton("Cancel");
    cancelButton.addActionListener(this);

    // Add components to the guiPanel
    int row = 0;

    // ROW 1
    gbc.fill = GridBagConstraints.NONE;
    gbc.weightx = 0;
    gbc.weighty = 0;
    JLabel tempLabel = new JLabel("Output folder", SwingConstants.LEFT);
    gbc.insets = new Insets(15, 15, 0, 10);
    Utility.add(guiPanel, tempLabel, gbl, gbc, 0, row, 1, 1);
    gbc.insets = new Insets(15, 0, 0, 15);
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.weightx = 100;
    gbc.weighty = 0;
    Utility.add(guiPanel, outputDirTF, gbl, gbc, 1, row, 1, 1);
    row++;

    // ROW 2
    gbc.fill = GridBagConstraints.NONE;
    gbc.weightx = 0;
    gbc.weighty = 0;
    tempLabel = new JLabel("Channel name", SwingConstants.LEFT);
    gbc.insets = new Insets(10, 15, 0, 10);
    Utility.add(guiPanel, tempLabel, gbl, gbc, 0, row, 1, 1);
    gbc.insets = new Insets(10, 0, 0, 15);
    gbc.fill = GridBagConstraints.NONE;
    gbc.weightx = 0;
    gbc.weighty = 0;
    Utility.add(guiPanel, chanNameTF, gbl, gbc, 1, row, 1, 1);
    row++;

    // ROW 3
    gbc.fill = GridBagConstraints.NONE;
    gbc.weightx = 0;
    gbc.weighty = 0;
    gbc.insets = new Insets(10, 15, 0, 15);
    Utility.add(guiPanel, bUseFTPCheckB, gbl, gbc, 0, row, 2, 1);
    row++;

    // ROW 4: A panel containing the FTP parameters (host, username, password)
    GridBagLayout panel_gbl = new GridBagLayout();
    JPanel ftpPanel = new JPanel(panel_gbl);
    GridBagConstraints panel_gbc = new GridBagConstraints();
    panel_gbc.anchor = GridBagConstraints.WEST;
    panel_gbc.fill = GridBagConstraints.NONE;
    panel_gbc.weightx = 0;
    panel_gbc.weighty = 0;
    int panel_row = 0;
    // Panel row 1: host
    panel_gbc.insets = new Insets(0, 0, 0, 10);
    Utility.add(ftpPanel, ftpHostLabel, panel_gbl, panel_gbc, 0, panel_row, 1, 1);
    panel_gbc.insets = new Insets(0, 0, 0, 0);
    Utility.add(ftpPanel, ftpHostTF, panel_gbl, panel_gbc, 1, panel_row, 1, 1);
    panel_row++;
    // Panel row 2: username
    panel_gbc.insets = new Insets(2, 0, 0, 10);
    Utility.add(ftpPanel, ftpUserLabel, panel_gbl, panel_gbc, 0, panel_row, 1, 1);
    panel_gbc.insets = new Insets(2, 0, 0, 0);
    Utility.add(ftpPanel, ftpUserTF, panel_gbl, panel_gbc, 1, panel_row, 1, 1);
    panel_row++;
    // Panel row 3: password
    panel_gbc.insets = new Insets(2, 0, 0, 10);
    Utility.add(ftpPanel, ftpPasswordLabel, panel_gbl, panel_gbc, 0, panel_row, 1, 1);
    panel_gbc.insets = new Insets(2, 0, 0, 0);
    Utility.add(ftpPanel, ftpPasswordTF, panel_gbl, panel_gbc, 1, panel_row, 1, 1);
    // Add the panel to guiPanel
    gbc.fill = GridBagConstraints.NONE;
    gbc.weightx = 0;
    gbc.weighty = 0;
    gbc.insets = new Insets(2, 50, 0, 15);
    Utility.add(guiPanel, ftpPanel, gbl, gbc, 0, row, 2, 1);
    row++;

    // ROW 5
    JPanel rbPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 5, 0));
    rbPanel.add(autoFlushRB);
    rbPanel.add(manualFlushRB);
    gbc.fill = GridBagConstraints.NONE;
    gbc.weightx = 0;
    gbc.weighty = 0;
    gbc.insets = new Insets(10, 5, 0, 15);
    Utility.add(guiPanel, rbPanel, gbl, gbc, 0, row, 2, 1);
    row++;

    // ROW 6
    gbc.fill = GridBagConstraints.NONE;
    gbc.weightx = 0;
    gbc.weighty = 0;
    gbc.insets = new Insets(10, 15, 0, 10);
    Utility.add(guiPanel, flushIntervalLabel, gbl, gbc, 0, row, 1, 1);
    gbc.insets = new Insets(10, 0, 0, 15);
    gbc.fill = GridBagConstraints.NONE;
    gbc.weightx = 0;
    gbc.weighty = 0;
    Utility.add(guiPanel, flushIntervalComboB, gbl, gbc, 1, row, 1, 1);
    row++;

    // ROW 7
    // Put the command buttons in a JPanel so they are all the same size
    JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 15, 0));
    buttonPanel.add(okButton);
    buttonPanel.add(cancelButton);
    // Don't have the buttons resize if the dialog is resized
    gbc.weightx = 0;
    gbc.weighty = 0;
    gbc.ipadx = 20;
    gbc.insets = new Insets(15, 25, 15, 25);
    gbc.anchor = GridBagConstraints.CENTER;
    Utility.add(guiPanel, buttonPanel, gbl, gbc, 0, row, 2, 1);

    // Add guiPanel to the content pane of the parent JFrame
    gbl = new GridBagLayout();
    getContentPane().setLayout(gbl);
    gbc = new GridBagConstraints();
    gbc.anchor = GridBagConstraints.CENTER;
    gbc.fill = GridBagConstraints.BOTH;
    gbc.weightx = 100;
    gbc.weighty = 100;
    gbc.insets = new Insets(0, 0, 0, 0);
    Utility.add(getContentPane(), guiPanel, gbl, gbc, 0, 0, 1, 1);

    pack();

    // Handle the close operation in the windowClosing() method of the
    // registered WindowListener object.  This will get around
    // JFrame's default behavior of automatically hiding the window when
    // the user clicks on the '[x]' button.
    setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);

    addWindowListener(
        new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
            cancelAction();
          }
        });
  }