void dds() {

        int ini_fevals = Math.max(5, (int) Math.round(0.005 * maxiter));
        int ileft = maxiter - ini_fevals;

        double fvalue = 0;
        double Ftest;

        for (int i = 0; i < ini_fevals; i++) {
            // sample an initial solution candidate (uniform random sampling):
            for (int j = 0; j < num_dec; j++) {
                double ranval = rand.nextDouble();
                stest[j] = s_min[j] + ranval * (s_max[j] - s_min[j]);
            }
            // Evaluate solution and return objective function value (fvalue), for example see grie10.f
            fvalue = obj_func(stest);
            Ftest = to_max * fvalue;  // to_max is 1.0 for MIN problems, -1 for MAX problems
            if (i == 1) {
                // Fbest must be initialized
                // track best solution found so far and corresponding obj function value
                Fbest = Ftest;
                sbest = stest;
            }
            if (Ftest <= Fbest) {
                // update current (best) solution
                Fbest = Ftest;
                sbest = stest;
            }

            // accumulate DDS initialization outputs
            f_count[i] = i;
            Ftests[i] = to_max * Ftest;  // candidate solution objective function value (untransformed)
            Fbests[i] = to_max * Fbest;  // best current solution objective function value (untransformed)
            stests[i] = stest;       // candidate solution (decision variable values)

//		write(*,*) f_count(i), to_max*Fbest ! *** user uncomment if desired ***
        }

        for (int i = 0; i < ileft; i++) {

            double Pn = 1.0 - Math.log((double) i+1) / Math.log((double) ileft); // probability each DV selected
            int dvn_count = 0; // counter for how many DVs selected for perturbation

            stest = sbest;  // define stest initially as best current solution

            for (int j = 0; j < num_dec; j++) {
                double ranval = rand.nextDouble();
                if (ranval < Pn) {
                    dvn_count++;
                    // call 1-D perturbation function to get new DV value (new_value)
                    double new_value = neigh_value(sbest[j], s_min[j], s_max[j], r_val);
                    // note that r_val is the value of the DDS r perturbation size parameter (0.2 by default)
                    stest[j] = new_value; //change relevant DV value in stest
                }
            }
            if (dvn_count == 0) {
                // no DVs selected at random, so select ONE
                double ranval = rand.nextDouble();
                int dv = (int) Math.ceil(num_dec * ranval);
                // call 1-D perturbation function to get new DV value (new_value)
                double new_value = neigh_value(sbest[dv], s_min[dv], s_max[dv], r_val);
                stest[dv] = new_value; //change relevant DV value in stest
            }

            //	Evaluate obj function value (fvalue) for stest, for example see grie10.f:
            fvalue = obj_func(stest);
            Ftest = to_max * fvalue;  // to_max handles min (=1) and max (=-1) problems,

            if (Ftest <= Fbest) { // update current best solution
                Fbest = Ftest;
                sbest = stest;
            }
            // accumulate DDS search history
            int ind1 = i + ini_fevals; // proper index for storage
            f_count[ind1] = ind1;
            Ftests[ind1] = to_max * Ftest;
            Fbests[ind1] = to_max * Fbest;
            stests[ind1] = stest;
        }
    }