14#define DEFAULT_MAXEVALS 100
15#define DEFAULT_MAXPASSES 5
19#define RCDS_ABORT 0x0001UL
20static MDB_THREAD_LOCK rcdsFlagsLock = MDB_THREAD_LOCK_INITIALIZER;
21static unsigned long rcdsFlags = 0;
23static void clearRcdsAbort(
void) {
24 mdb_thread_lock(&rcdsFlagsLock);
25 rcdsFlags &= ~RCDS_ABORT;
26 mdb_thread_unlock(&rcdsFlagsLock);
29static long rcdsAbortRequested(
void) {
32 mdb_thread_lock(&rcdsFlagsLock);
33 requested = (rcdsFlags & RCDS_ABORT) ? 1 : 0;
34 mdb_thread_unlock(&rcdsFlagsLock);
47 mdb_thread_lock(&rcdsFlagsLock);
50 rcdsFlags |= RCDS_ABORT;
52 fprintf(stderr,
"rcdsMin abort requested\n");
55 requested = rcdsFlags & RCDS_ABORT ? 1 : 0;
56 mdb_thread_unlock(&rcdsFlagsLock);
94void sort_two_arrays(
double *x,
double *y,
long n);
96void normalize_variables(
double *x0,
double *relative_x,
double *lowerLimit,
double *upperLimit,
long dimensions);
98void scale_variables(
double *x0,
double *relative_x,
double *lowerLimit,
double *upperLimit,
long dimensions);
101static MDB_THREAD_LOCAL
long DIMENSIONS;
103long bracketmin(
double (*func)(
double *x,
long *invalid),
104 double *x0,
double f0,
double *dv,
double *lowerLimit,
double *upperLimit,
long dimensions,
double noise,
double step,
double *a10,
double *a20,
double **stepList,
double **flist,
long *nflist,
double *xm,
double *fm,
double *xmin,
double *fmin);
106long linescan(
double (*func)(
double *x,
long *invalid),
double *x0,
double f0,
double *dv,
double *lowerLimit,
double *upperLimit,
long dimensions,
double alo,
double ahi,
long Np,
double **stepList,
double **fList,
long n_list,
double *xm,
double *fm,
double *xmin,
double *fmin);
108long outlier_1d(
double *x,
long n,
double mul_tol,
double perlim,
long *removed_index);
134long rcdsMin(
double *yReturn,
double *xBest,
double *xGuess,
double *dxGuess,
double *xLowerLimit,
double *xUpperLimit,
double **dmat0,
long dimensions,
double target,
136 double (*func)(
double *x,
long *invalid),
void (*report)(
double ymin,
double *xmin,
long pass,
long evals,
long dims),
long maxEvaluations,
138 double noise,
double rcdsStep,
unsigned long flags) {
139 long i, j, totalEvaluations = 0, inValid = 0, k, pass, Npmin = 6, direction;
140 long dmat0Allocated = 0;
142 double *dv = NULL, del = 0, f0, step = 0.01, f1, fm, ft, a1, a2, tmp, norm, maxp = 0, tmpf, *tmpx = NULL;
143 double *xm = NULL, *x1 = NULL, *xt = NULL, *ndv = NULL, *dotp = NULL, *x_value = NULL, *xmin = NULL, fmin;
144 double *step_list = NULL, *f_list = NULL, step_init;
148 if (rcdsStep > 0 && rcdsStep < 1)
153 DIMENSIONS = dimensions;
155 if (flags & SIMPLEX_VERBOSE_LEVEL1)
156 fprintf(stdout,
"rcdsMin dimensions: %ld\n", dimensions);
158 x0 = malloc(
sizeof(*x0) * dimensions);
159 tmpx = malloc(
sizeof(*tmpx) * dimensions);
161 normalize_variables(xGuess, x0, xLowerLimit, xUpperLimit, dimensions);
163 f0 = (*func)(xGuess, &inValid);
166 fprintf(stderr,
"error: initial guess is invalid in rcdsMin()\n");
173 dmat0 = malloc(
sizeof(*dmat0) * dimensions);
175 for (i = 0; i < dimensions; i++) {
176 dmat0[i] = calloc(dimensions,
sizeof(**dmat0));
177 for (j = 0; j < dimensions; j++)
184 for (i = 0; i < dimensions; i++) {
185 if (xLowerLimit && xUpperLimit) {
186 step += dxGuess[i] / (xUpperLimit[i] - xLowerLimit[i]);
193 if (rcdsStep > 0 && rcdsStep < 1)
196 xm = malloc(
sizeof(*xm) * dimensions);
197 xmin = malloc(
sizeof(*xmin) * dimensions);
198 memcpy(xm, x0,
sizeof(*xm) * dimensions);
199 memcpy(xmin, x0,
sizeof(*xm) * dimensions);
201 memcpy(xBest, xGuess,
sizeof(*xBest) * dimensions);
204 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
205 fprintf(stdout,
"rcdsMin: target value achieved in initial setup.\n");
208 (*report)(f0, xGuess, 0, 1, dimensions);
215 return (totalEvaluations);
219 maxPasses = DEFAULT_MAXPASSES;
221 x1 =
tmalloc(
sizeof(*x1) * dimensions);
222 xt =
tmalloc(
sizeof(*xt) * dimensions);
223 ndv =
tmalloc(
sizeof(*ndv) * dimensions);
224 dotp =
tmalloc(
sizeof(*dotp) * dimensions);
225 for (i = 0; i < dimensions; i++)
229 x_value =
tmalloc(
sizeof(*x_value) * dimensions);
231 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
232 fprintf(stdout,
"rcdsMin: starting conditions:\n");
233 for (direction = 0; direction < dimensions; direction++)
234 fprintf(stdout,
"direction %ld: guess=%le \n", direction, xGuess[direction]);
235 fprintf(stdout,
"starting funcation value %le \n", f0);
238 while (pass < maxPasses && !rcdsAbortRequested()) {
243 for (i = 0; !rcdsAbortRequested() && i < dimensions; i++) {
245 if (flags & SIMPLEX_VERBOSE_LEVEL1)
246 fprintf(stdout,
"begin iteration %ld, var %ld, nf=%ld\n", pass + 1, i + 1, totalEvaluations);
255 totalEvaluations += bracketmin(func, xm, fm, dv, xLowerLimit, xUpperLimit, dimensions, noise, step_init, &a1, &a2, &step_list, &f_list, &n_list, x1, &f1, xmin, &fmin);
256 memcpy(tmpx, x1,
sizeof(*tmpx) * dimensions);
258 if (flags & SIMPLEX_VERBOSE_LEVEL1)
259 fprintf(stdout,
"\niter %ld, dir (var) %ld: begin linescan %ld\n", pass + 1, i + 1, totalEvaluations);
260 if (rcdsAbortRequested())
262 totalEvaluations += linescan(func, tmpx, tmpf, dv, xLowerLimit, xUpperLimit, dimensions, a1, a2, Npmin, &step_list, &f_list, n_list, x1, &f1, xmin, &fmin);
264 if ((fm - f1) > del) {
267 if (flags & SIMPLEX_VERBOSE_LEVEL1)
268 fprintf(stdout,
"iteration %ld, var %ld: del= %f updated", pass + 1, i + 1, del);
270 if (flags & SIMPLEX_VERBOSE_LEVEL1)
271 fprintf(stdout,
"iteration %ld, director %ld done, fm=%f, f1=%f\n", pass + 1, i + 1, fm, f1);
273 if (flags & RCDS_USE_MIN_FOR_BRACKET) {
275 memcpy(xm, xmin,
sizeof(*xm) * dimensions);
278 memcpy(xm, x1,
sizeof(*xm) * dimensions);
281 if (flags & SIMPLEX_VERBOSE_LEVEL1)
282 fprintf(stderr,
"\niteration %ld, fm=%f fmin=%f\n", pass + 1, fm, fmin);
283 if (rcdsAbortRequested())
286 for (i = 0; i < dimensions; i++) {
287 xt[i] = 2 * xm[i] - x0[i];
288 if (fabs(xt[i]) > 1) {
294 scale_variables(x_value, xt, xLowerLimit, xUpperLimit, dimensions);
295 ft = (*func)(x_value, &inValid);
300 tmp = 2 * (f0 - 2 * fm + ft) * pow((f0 - fm - del) / (ft - f0), 2);
301 if ((f0 <= ft) || tmp >= del) {
302 if (flags & SIMPLEX_VERBOSE_LEVEL1)
303 fprintf(stdout,
"dir %ld not replaced, %d, %d\n", k, f0 <= ft, tmp >= del);
306 if (flags & SIMPLEX_VERBOSE_LEVEL1)
307 fprintf(stdout,
"compute dotp\n");
309 for (i = 0; i < dimensions; i++) {
310 norm += (xm[i] - x0[i]) * (xm[i] - x0[i]);
312 norm = pow(norm, 0.5);
313 for (i = 0; i < dimensions; i++)
314 ndv[i] = (xm[i] - x0[i]) / norm;
316 for (i = 0; i < dimensions; i++) {
319 for (j = 0; j < dimensions; j++)
320 dotp[i] += ndv[j] * dv[j];
321 dotp[i] = fabs(dotp[i]);
326 if (flags & SIMPLEX_VERBOSE_LEVEL1)
327 fprintf(stdout,
"max dot product <0.9, do bracketmin and linescan...\n");
328 if (k < dimensions - 1) {
329 for (i = k; i < dimensions - 1; i++) {
330 for (j = 0; j < dimensions; j++)
331 dmat0[i][j] = dmat0[i + 1][j];
334 for (j = 0; j < dimensions; j++)
335 dmat0[dimensions - 1][j] = ndv[j];
336 dv = dmat0[dimensions - 1];
337 totalEvaluations += bracketmin(func, xm, fm, dv, xLowerLimit, xUpperLimit, dimensions, noise, step, &a1, &a2, &step_list, &f_list, &n_list, x1, &f1, xmin, &fmin);
339 memcpy(tmpx, x1,
sizeof(*tmpx) * dimensions);
341 totalEvaluations += linescan(func, tmpx, tmpf, dv, xLowerLimit, xUpperLimit, dimensions, a1, a2, Npmin, &step_list, &f_list, n_list, x1, &f1, xmin, &fmin);
342 memcpy(xm, x1,
sizeof(*xm) * dimensions);
344 if (flags & SIMPLEX_VERBOSE_LEVEL1)
345 fprintf(stderr,
"fm=%le \n", fm);
347 if (flags & SIMPLEX_VERBOSE_LEVEL1)
348 fprintf(stdout,
" , skipped new direction %ld, max dot product %f\n", k, maxp);
352 if (totalEvaluations > maxEvaluations) {
353 fprintf(stderr,
"Terminated, reaching function evaluation limit %ld > %ld\n", totalEvaluations, maxEvaluations);
356 if (2.0 * fabs(f0 - fmin) < tolerance * (fabs(f0) + fabs(fmin)) && tolerance > 0) {
357 if (flags & SIMPLEX_VERBOSE_LEVEL1)
358 fprintf(stdout,
"Reach tolerance, terminated, f0=%le, fmin=%le, f0-fmin=%le\n", f0, fmin, f0 - fmin);
361 if (fmin <= target) {
362 if (flags & SIMPLEX_VERBOSE_LEVEL1)
363 fprintf(stdout,
"Reach target, terminated, fm=%le, target=%le\n", fm, target);
367 memcpy(x0, xm,
sizeof(*x0) * dimensions);
372 scale_variables(xBest, xmin, xLowerLimit, xUpperLimit, dimensions);
389 return (totalEvaluations);
412long bracketmin(
double (*func)(
double *x,
long *invalid),
413 double *x0,
double f0,
double *dv,
double *lowerLimit,
double *upperLimit,
long dimensions,
414 double noise,
double step,
double *a10,
double *a20,
double **stepList,
double **fList,
415 long *nflist,
double *xm,
double *fm,
double *xmin,
double *fmin) {
416 long nf = 0, inValid, i, n_list, list_capacity = 100;
418 double *x1 = NULL, *x2 = NULL;
419 const double gold_r = 1.618034;
420 double *step_list = NULL, *f_list = NULL;
421 double f1, step_init, am, a1, a2, f2, tmp, step0;
422 double *x_value = NULL;
425 memcpy(xm, x0,
sizeof(*xm) * dimensions);
428 x1 =
tmalloc(
sizeof(*x1) * dimensions);
429 f_list =
tmalloc(
sizeof(*f_list) * list_capacity);
430 step_list =
tmalloc(
sizeof(*step_list) * list_capacity);
437 for (i = 0; i < dimensions; i++) {
438 x1[i] = x0[i] + dv[i] * step;
439 if (fabs(x1[i]) > 1) {
444 x_value =
tmalloc(
sizeof(*x_value) * dimensions);
449 scale_variables(x_value, x1, lowerLimit, upperLimit, dimensions);
451 f1 = (*func)(x_value, &inValid);
458 step_list[n_list] = step;
463 memcpy(xm, x1,
sizeof(*xm) * dimensions);
468 memcpy(xmin, x1,
sizeof(*xmin) * dimensions);
471 while (f1 < *fm + noise * 3 && !rcdsAbortRequested()) {
474 if (fabs(step) < 0.1)
475 step = step * (1.0 + gold_r);
480 for (i = 0; i < dimensions; i++) {
481 x1[i] = x0[i] + dv[i] * step;
482 if (fabs(x1[i]) > 1) {
490 scale_variables(x_value, x1, lowerLimit, upperLimit, dimensions);
491 f1 = (*func)(x_value, &inValid);
496 if (n_list >= list_capacity) {
497 list_capacity = n_list + 100;
498 f_list =
trealloc(f_list,
sizeof(*f_list) * list_capacity);
499 step_list =
trealloc(step_list,
sizeof(*step_list) * list_capacity);
506 step_list[n_list] = step;
511 memcpy(xm, x1,
sizeof(*xm) * dimensions);
515 memcpy(xmin, x1,
sizeof(*xmin) * dimensions);
520 if (f0 > *fm + noise * 3) {
524 for (i = 0; i < n_list; i++)
530 *stepList = step_list;
540 x2 =
tmalloc(
sizeof(*x2) * dimensions);
542 step = -1 * step_init;
544 for (i = 0; i < dimensions; i++) {
545 x2[i] = x0[i] + dv[i] * step;
546 if (fabs(x2[i]) > 1) {
554 scale_variables(x_value, x2, lowerLimit, upperLimit, dimensions);
555 f2 = (*func)(x_value, &inValid);
560 if (n_list >= list_capacity) {
561 list_capacity = n_list + 100;
562 f_list =
trealloc(f_list,
sizeof(*f_list) * list_capacity);
563 step_list =
trealloc(step_list,
sizeof(*step_list) * list_capacity);
566 step_list[n_list] = step;
572 memcpy(xm, x2,
sizeof(*xm) * dimensions);
576 memcpy(xmin, x2,
sizeof(*xmin) * dimensions);
579 while (f2 < *fm + noise * 3 && !rcdsAbortRequested()) {
581 if (fabs(step) < 0.1)
582 step = step * (1.0 + gold_r);
586 for (i = 0; i < dimensions; i++) {
587 x2[i] = x0[i] + dv[i] * step;
588 if (fabs(x2[i]) > 1) {
596 scale_variables(x_value, x2, lowerLimit, upperLimit, dimensions);
597 f2 = (*func)(x_value, &inValid);
608 if (n_list >= list_capacity) {
609 list_capacity = n_list + 100;
610 f_list =
trealloc(f_list,
sizeof(*f_list) * list_capacity);
611 step_list =
trealloc(step_list,
sizeof(*step_list) * list_capacity);
614 step_list[n_list] = step;
620 memcpy(xm, x2,
sizeof(*xm) * dimensions);
624 memcpy(xmin, x2,
sizeof(*xmin) * dimensions);
636 for (i = 0; i < n_list; i++)
639 sort_two_arrays(step_list, f_list, n_list);
643 *stepList = step_list;
655void scale_variables(
double *x0,
double *relative_x,
double *lowerLimit,
double *upperLimit,
long dimensions) {
657 if (lowerLimit && upperLimit) {
658 for (i = 0; i < dimensions; i++) {
659 x0[i] = relative_x[i] * (upperLimit[i] - lowerLimit[i]) + lowerLimit[i];
662 memcpy(x0, relative_x,
sizeof(*x0) * dimensions);
666void normalize_variables(
double *x0,
double *relative_x,
double *lowerLimit,
double *upperLimit,
long dimensions) {
668 if (lowerLimit && upperLimit) {
669 for (i = 0; i < dimensions; i++) {
670 relative_x[i] = (x0[i] - lowerLimit[i]) / (upperLimit[i] - lowerLimit[i]);
673 memcpy(relative_x, x0,
sizeof(*relative_x) * dimensions);
696long linescan(
double (*func)(
double *x,
long *invalid),
double *x0,
double f0,
double *dv,
double *lowerLimit,
double *upperLimit,
long dimensions,
double alo,
double ahi,
long Np,
double **stepList,
double **fList,
long n_list,
double *xm,
double *fm,
double *xmin,
double *fmin) {
697 long nf = 0, i, j, k, MP, n_new, terms, *order;
700 long *is_outlier, outliers;
701 double a1, f1, tmp_min, tmp_max, tmp, delta, delta2, mina;
702 double *step_list, *f_list;
703 double *x1 = NULL, *aNew = NULL, *fNew = NULL, *av = NULL, *x_value = NULL;
704 double *coef, *coefSigma, chi, *diff, *tmpa = NULL, *tmpf = NULL, *fv = NULL;
706 step_list = *stepList;
709 fprintf(stderr,
"high bound should be larger than the low bound\n");
714 delta = (ahi - alo) / (Np - 1);
715 delta2 = delta / 2.0;
716 x1 =
tmalloc(
sizeof(*x1) * dimensions);
719 aNew =
tmalloc(
sizeof(*aNew) * Np);
720 fNew =
tmalloc(
sizeof(*fNew) * Np);
721 x_value =
tmalloc(
sizeof(*x_value) * dimensions);
723 for (i = 0; i < Np && !rcdsAbortRequested(); i++) {
724 a1 = alo + delta * i;
725 mina = fabs(a1 - step_list[0]);
726 for (j = 1; j < n_list; j++) {
727 tmp = fabs(a1 - step_list[j]);
729 mina = fabs(a1 - step_list[j]);
734 if (mina + 1.0e-16 > delta2) {
735 for (k = 0; k < dimensions; k++) {
736 x1[k] = x0[k] + dv[k] * a1;
738 scale_variables(x_value, x1, lowerLimit, upperLimit, dimensions);
740 f1 = (*func)(x_value, &inValid);
747 memcpy(xmin, x1,
sizeof(*xmin) * dimensions);
755 if (rcdsAbortRequested()) {
765 if (n_new + n_list > 100) {
766 f_list =
trealloc(f_list,
sizeof(*f_list) * (n_list + n_new + 1));
767 step_list =
trealloc(step_list,
sizeof(*step_list) * (n_list + n_new + 1));
769 *stepList = step_list;
771 for (i = 0; i < n_new; i++) {
772 step_list[n_list + i] = aNew[i];
773 f_list[n_list + i] = fNew[i];
784 sort_two_arrays(step_list, f_list, n_list);
787 for (i = 0; i < dimensions; i++)
788 xm[i] = x0[i] + step_list[imin] * dv[i];
792 memcpy(xmin, xm,
sizeof(*xmin) * dimensions);
800 tmp_min = MAX(step_list[0], step_list[imin] - 6 * delta);
801 tmp_max = MIN(step_list[n_list - 1], step_list[imin] + 6 * delta);
804 av =
tmalloc(
sizeof(*av) * MP);
805 for (i = 0; i < MP; i++)
806 av[i] = tmp_min + (tmp_max - tmp_min) * i * 1.0 / MP;
807 fv =
tmalloc(
sizeof(*fv) * MP);
811 coef =
tmalloc(
sizeof(*coef) * terms);
812 coefSigma =
tmalloc(
sizeof(*coefSigma) * terms);
813 order =
tmalloc(
sizeof(*order) * terms);
814 for (i = 0; i < terms; i++)
816 diff =
tmalloc(
sizeof(*diff) * n_list);
818 lsfp(step_list, f_list, NULL, n_list, terms, order, coef, coefSigma, &chi, diff);
821 is_outlier =
tmalloc(
sizeof(*is_outlier) * n_list);
822 for (i = 0; i < n_list; i++)
823 diff[i] = -1.0 * diff[i];
825 outliers = outlier_1d(diff, n_list, 3.0, 0.25, is_outlier);
827 for (i = 0; i < n_list; i++) {
828 diff[i] = -1 * diff[i];
832 tmpa =
tmalloc(
sizeof(*tmpa) * n_list);
833 tmpf =
tmalloc(
sizeof(*tmpf) * n_list);
835 for (j = 0; j < n_list; j++)
836 if (!is_outlier[j]) {
837 tmpa[n_new] = step_list[j];
838 tmpf[n_new] = f_list[j];
842 lsfp(tmpa, tmpf, NULL, n_new, terms, order, coef, coefSigma, &chi, diff);
847 for (i = 0; i < MP; i++) {
848 fv[i] = coef[0] + av[i] * coef[1] + coef[2] * av[i] * av[i];
851 for (i = 0; i < dimensions; i++) {
852 x1[i] = xm[i] = x0[i] + av[imin] * dv[i];
854 scale_variables(x_value, x1, lowerLimit, upperLimit, dimensions);
855 memcpy(xm, x1,
sizeof(*xm) * dimensions);
858 f1 = (*func)(x_value, &inValid);
865 memcpy(xmin, x1,
sizeof(*xmin) * dimensions);
891void sort_two_arrays(
double *x,
double *y,
long n) {
895 tmpx =
tmalloc(
sizeof(*tmpx) * n);
896 memcpy(tmpx, x,
sizeof(*tmpx) * n);
897 tmpy =
tmalloc(
sizeof(*tmpy) * n);
900 for (i = 0; i < n; i++) {
901 for (j = 0; j < n; j++) {
907 for (i = 0; i < n; i++) {
932long outlier_1d(
double *x,
long n,
double mul_tol,
double perlim,
long *is_outlier) {
933 long i, j, outlier = 0, *index = NULL, upl, dnl, upcut, dncut;
934 double *tmpx = NULL, *diff = NULL, ave1 = 0, ave2 = 0;
938 index =
tmalloc(
sizeof(*index) * n);
939 tmpx =
tmalloc(
sizeof(*tmpx) * n);
940 diff =
tmalloc(
sizeof(*diff) * n);
943 memcpy(tmpx, x,
sizeof(*tmpx) * n);
946 for (i = 0; i < n; i++) {
948 for (j = 0; j < n; j++) {
956 for (i = 1; i < n; i++) {
957 diff[i - 1] = tmpx[i] - tmpx[i - 1];
962 for (i = 0; i < n - 2; i++)
964 for (i = 1; i < n - 1; i++)
968 if (diff[n - 2] > mul_tol * ave1) {
969 is_outlier[index[n - 2]] = 1;
972 if (diff[0] > mul_tol * ave2) {
973 is_outlier[index[0]] = 1;
981 upl = MAX((
int)(n * (1 - perlim)), 3) - 1;
982 dnl = MAX((
int)(n * perlim), 2) - 1;
984 for (i = dnl; i <= upl; i++)
986 ave1 /= upl - dnl + 1;
991 for (i = upl; i < n - 1; i++) {
992 if (diff[i] > mul_tol * ave1) {
997 for (i = dnl; i >= 0; i--) {
998 if (diff[i] > mul_tol * ave1) {
1002 for (i = upcut; i < n; i++) {
1003 is_outlier[index[i]] = 1;
1006 for (i = dncut; i >= 0; i--) {
1007 is_outlier[index[i]] = 1;
void * trealloc(void *old_ptr, uint64_t size_of_block)
Reallocates a memory block to a new size.
int free_zarray_2d(void **array, uint64_t n1, uint64_t n2)
Frees a 2D array and its associated memory.
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
int index_min_max(int64_t *imin, int64_t *imax, double *list, int64_t n)
Finds the indices of the minimum and maximum values in a list of doubles.
long rcdsMin(double *yReturn, double *xBest, double *xGuess, double *dxGuess, double *xLowerLimit, double *xUpperLimit, double **dmat0, long dimensions, double target, double tolerance, double(*func)(double *x, long *invalid), void(*report)(double ymin, double *xmin, long pass, long evals, long dims), long maxEvaluations, long maxPasses, double noise, double rcdsStep, unsigned long flags)
Performs minimization using the RCDS (Robust Conjugate Direction Search) algorithm.
long rcdsMinAbort(long abort)
Sets or queries the abort flag for the RCDS minimization.
int double_cmpasc(const void *a, const void *b)
Compare two doubles in ascending order.