/** Constructor */
  public SampleStateAndCategoryModel(
      Parameter muParameter, Parameter categoriesParameter, Vector substitutionModels) {

    super(SampleStateAndCategoryModelParser.SAMPLE_STATE_AND_CATEGORY_MODEL);

    this.substitutionModels = substitutionModels;

    for (int i = 0; i < substitutionModels.size(); i++) {
      addModel((SubstitutionModel) substitutionModels.elementAt(i));
    }

    this.categoryCount = substitutionModels.size();
    sitesInCategory = new int[categoryCount];
    //	stateCount =
    // ((SubstitutionModel)substitutionModels.elementAt(0)).getDataType().getStateCount();

    this.muParameter = muParameter;
    addVariable(muParameter);
    muParameter.addBounds(new Parameter.DefaultBounds(1000.0, 0.0, 1));

    this.categoriesParameter = categoriesParameter;
    addVariable(categoriesParameter);

    if (categoryCount > 1) {
      for (int i = 0; i < categoryCount; i++) {
        Parameter p = (Parameter) ((YangCodonModel) substitutionModels.elementAt(i)).getVariable(0);
        Parameter lower = null;
        Parameter upper = null;

        if (i == 0) {
          upper = (Parameter) ((YangCodonModel) substitutionModels.elementAt(i + 1)).getVariable(0);
          p.addBounds(new omegaBounds(lower, upper));
        } else {
          if (i == (categoryCount - 1)) {
            lower =
                (Parameter) ((YangCodonModel) substitutionModels.elementAt(i - 1)).getVariable(0);
            p.addBounds(new omegaBounds(lower, upper));
          } else {
            upper =
                (Parameter) ((YangCodonModel) substitutionModels.elementAt(i + 1)).getVariable(0);
            lower =
                (Parameter) ((YangCodonModel) substitutionModels.elementAt(i - 1)).getVariable(0);
            p.addBounds(new omegaBounds(lower, upper));
          }
        }
      }
    }
  }
  public MixtureModelBranchRates(
      TreeModel tree,
      Parameter rateCategoryQuantilesParameter,
      ParametricDistributionModel[] models,
      Parameter distributionIndexParameter,
      boolean useQuantilesForRates,
      boolean normalize,
      double normalizeBranchRateTo) {
    super(MixtureModelBranchRatesParser.MIXTURE_MODEL_BRANCH_RATES);

    this.useQuantilesForRates = useQuantilesForRates;

    this.rateCategoryQuantiles =
        new TreeParameterModel(tree, rateCategoryQuantilesParameter, false);

    rates = new double[tree.getNodeCount()];

    this.normalize = normalize;

    this.treeModel = tree;
    this.distributionModels = models;
    this.normalizeBranchRateTo = normalizeBranchRateTo;

    this.tree = new SimpleTree(tree);

    this.distributionIndexParameter = distributionIndexParameter;
    addVariable(this.distributionIndexParameter);

    // Force the boundaries of rateCategoryParameter to match the category count
    // d Parameter.DefaultBounds bound = new Parameter.DefaultBounds(categoryCount - 1, 0,
    // rateCategoryParameter.getDimension());
    // d rateCategoryParameter.addBounds(bound);
    // rateCategoryQuantilesParameter.;

    Parameter.DefaultBounds bound =
        new Parameter.DefaultBounds(1.0, 0.0, rateCategoryQuantilesParameter.getDimension());
    rateCategoryQuantilesParameter.addBounds(bound);

    Parameter.DefaultBounds bound2 = new Parameter.DefaultBounds(models.length, 0.0, 1);
    distributionIndexParameter.addBounds(bound2);
    distributionIndexParameter.setParameterValue(0, 0);

    // Parameter distributionIndexParameter;

    for (ParametricDistributionModel distributionModel : distributionModels) {
      addModel(distributionModel);
    }
    // AR - commented out: changes to the tree are handled by model changed events fired by
    // rateCategories
    //        addModel(tree);
    // d addModel(rateCategories);

    addModel(rateCategoryQuantiles);

    // addModel(treeModel); // Maybe
    // AR - commented out: changes to rateCategoryParameter are handled by model changed events
    // fired by rateCategories
    //        addVariable(rateCategoryParameter);

    if (normalize) {
      tree.addModelListener(
          new ModelListener() {

            public void modelChangedEvent(Model model, Object object, int index) {
              computeFactor();
            }

            public void modelRestored(Model model) {
              computeFactor();
            }
          });
    }

    setupRates();
  }