@Override
  public List<ParameterType> getParameterTypes() {
    List<ParameterType> types = super.getParameterTypes();
    ParameterType type =
        new ParameterTypeInt(
            PARAMETER_DEGREE,
            "Specifies the degree of the local fitted polynomial. Please keep in mind, that a higher degree than 2 will increase calculation time extremely and probably suffer from overfitting.",
            0,
            Integer.MAX_VALUE,
            2);
    type.setExpert(false);
    types.add(type);
    type =
        new ParameterTypeDouble(
            PARAMETER_RIDGE,
            "Specifies the ridge factor. This factor is used to penalize high coefficients. In order to aviod overfitting this might be increased.",
            0,
            Double.POSITIVE_INFINITY,
            0.000000001);
    types.add(type);

    type =
        new ParameterTypeBoolean(
            PARAMETER_USE_ROBUST_ESTIMATION,
            "If checked, a reweighting of the examples is performed in order to downweight outliers",
            false);
    type.setExpert(false);
    types.add(type);

    type =
        new ParameterTypeBoolean(
            PARAMETER_USE_EXAMPLE_WEIGHTS,
            "Indicates if example weights should be used if present in the given example set.",
            true);
    type.registerDependencyCondition(
        new BooleanParameterCondition(this, PARAMETER_USE_ROBUST_ESTIMATION, false, false));
    types.add(type);

    type =
        new ParameterTypeInt(
            LocalPolynomialExampleWeightingOperator.PARAMETER_NUMBER_OF_ITERATIONS,
            "The number of iterations performed for weight calculation.",
            1,
            Integer.MAX_VALUE,
            20);
    type.registerDependencyCondition(
        new BooleanParameterCondition(this, PARAMETER_USE_ROBUST_ESTIMATION, false, true));
    types.add(type);

    types.addAll(DistanceMeasures.getParameterTypesForNumericals(this));
    types.addAll(Neighborhoods.getParameterTypes(this));
    types.addAll(SmoothingKernels.getParameterTypes(this));

    return types;
  }
  @Override
  public Model learn(ExampleSet exampleSet) throws OperatorException {
    DistanceMeasure measure = DistanceMeasures.createMeasure(this);
    measure.init(exampleSet);
    GeometricDataCollection<RegressionData> data = new LinearList<RegressionData>(measure);

    // check if weights should be used
    boolean useWeights = getParameterAsBoolean(PARAMETER_USE_EXAMPLE_WEIGHTS);
    // check if robust estimate should be performed: Then calculate weights and use it anyway
    if (getParameterAsBoolean(PARAMETER_USE_ROBUST_ESTIMATION)) {
      useWeights = true;
      LocalPolynomialExampleWeightingOperator weightingOperator;
      try {
        weightingOperator =
            OperatorService.createOperator(LocalPolynomialExampleWeightingOperator.class);
        exampleSet = weightingOperator.doWork((ExampleSet) exampleSet.clone(), this);
      } catch (OperatorCreationException e) {
        throw new UserError(this, 904, "LocalPolynomialExampleWeighting", e.getMessage());
      }
    }

    Attributes attributes = exampleSet.getAttributes();
    Attribute label = attributes.getLabel();
    Attribute weightAttribute = attributes.getWeight();
    for (Example example : exampleSet) {
      double[] values = new double[attributes.size()];
      double labelValue = example.getValue(label);
      double weight = 1d;
      if (weightAttribute != null && useWeights) {
        weight = example.getValue(weightAttribute);
      }

      // filter out examples without influence
      if (weight > 0d) {
        // copying example values
        int i = 0;
        for (Attribute attribute : attributes) {
          values[i] = example.getValue(attribute);
          i++;
        }

        // inserting into geometric data collection
        data.add(values, new RegressionData(values, labelValue, weight));
      }
    }
    return new LocalPolynomialRegressionModel(
        exampleSet,
        data,
        Neighborhoods.createNeighborhood(this),
        SmoothingKernels.createKernel(this),
        getParameterAsInt(PARAMETER_DEGREE),
        getParameterAsDouble(PARAMETER_RIDGE));
  }