void AbsoluteQuantitation::calculateBiasAndR(
    const std::vector<AbsoluteQuantitationStandards::featureConcentration> & component_concentrations,
    const String & feature_name,
    const String & transformation_model,
    const Param & transformation_model_params,
    std::vector<double> & biases,
    double & correlation_coefficient)
  {
    // reset biases
    biases.clear();

    // extract out the calibration points
    std::vector<double> concentration_ratios, feature_amounts_ratios;
    TransformationModel::DataPoints data;
    TransformationModel::DataPoint point;
    for (size_t i = 0; i < component_concentrations.size(); ++i)
    {

      // calculate the actual and calculated concentration ratios
      double calculated_concentration_ratio = applyCalibration(component_concentrations[i].feature,
        component_concentrations[i].IS_feature,
        feature_name,
        transformation_model,
        transformation_model_params);

      double actual_concentration_ratio = component_concentrations[i].actual_concentration/
        component_concentrations[i].IS_actual_concentration;
      concentration_ratios.push_back(component_concentrations[i].actual_concentration);

      // extract out the feature amount ratios
      double feature_amount_ratio = calculateRatio(component_concentrations[i].feature,
        component_concentrations[i].IS_feature,
        feature_name)/component_concentrations[i].dilution_factor;
      feature_amounts_ratios.push_back(feature_amount_ratio);

      // calculate the bias
      double bias = calculateBias(actual_concentration_ratio, calculated_concentration_ratio);
      biases.push_back(bias);

      point.first = actual_concentration_ratio;
      point.second = feature_amount_ratio;
      data.push_back(point);
    }

    // apply weighting to the feature amounts and actual concentration ratios
    TransformationModel tm(data, transformation_model_params);
    tm.weightData(data);
    std::vector<double> concentration_ratios_weighted, feature_amounts_ratios_weighted;
    for (size_t i = 0; i < data.size(); ++i)
    {
      concentration_ratios_weighted.push_back(data[i].first);
      feature_amounts_ratios_weighted.push_back(data[i].second);
    }

    // calculate the R2 (R2 = Pearson_R^2)
    correlation_coefficient = Math::pearsonCorrelationCoefficient(
      concentration_ratios_weighted.begin(), concentration_ratios_weighted.begin() + concentration_ratios_weighted.size(),
      feature_amounts_ratios_weighted.begin(), feature_amounts_ratios_weighted.begin() + feature_amounts_ratios_weighted.size()
    );
  }
  Param AbsoluteQuantitation::fitCalibration(
    const std::vector<AbsoluteQuantitationStandards::featureConcentration> & component_concentrations,
    const String & feature_name,
    const String & transformation_model,
    const Param & transformation_model_params)
  {
    // extract out the calibration points
    TransformationModel::DataPoints data;
    TransformationModel::DataPoint point;
    for (size_t i = 0; i < component_concentrations.size(); i++)
    {
      point.first = component_concentrations[i].actual_concentration/component_concentrations[i].IS_actual_concentration;
      double ratio = calculateRatio(component_concentrations[i].feature, component_concentrations[i].IS_feature,feature_name);
      point.second = ratio/component_concentrations[i].dilution_factor; // adjust based on the dilution factor
      data.push_back(point);
    }

    // fit the data to the model
    TransformationDescription tmd(data);
    // tmd.setDataPoints(data);
    tmd.fitModel(transformation_model, transformation_model_params);
    Param params = tmd.getModelParameters();
    // AbsoluteQuantitationMethod aqm;
    // Param params = aqm.fitTransformationModel(transformation_model, data, transformation_model_params);

    // store the information about the fit
    return params;
  }