Последний раз редактировалось Dukales 17.06.2011, 23:02, всего редактировалось 8 раз(а).
Небольшие изменения (давно писал то, что выше): hooke_and_jeeves.h:
#ifndef hooke_and_jeevesH
#define hooke_and_jeevesH
/*
R. Hooke and T.A. Jeeves. Direct search solution of numerical and statistical
problems. Journal of the Association for Computing Machinery (ACM), Volume 8 Issue 2, p. 212-229, April 1961
*/
typedef long double (* OBJECTIVE_FUNCTION)(double p[], void * param);
#pragma pack(1)
typedef struct _BOUNDARY
{
double min;
double max;
} BOUNDARY, * pBOUNDARY;
#pragma pack()
#define HJD_SEARCH_ENABLED 0x00000000
#define HJD_SEARCH_DISABLED 0x00000004
#define HJD_SEARCH_HOLD 0x00000000
#define HJD_SEARCH_INC 0x00000001
#define HJD_SEARCH_DEC 0x00000002
typedef struct _HJD
{
bool R; // restriction on
bool VP; // various precisions on
OBJECTIVE_FUNCTION of; // Целевая функция
void * param; // Дополнительный параметр для целевой функции
clock_t timeout; // Таймаут
double factor; // Множитель для уменьшения значений шагов < 1
unsigned nc; // Количество параметров-констант целевой функции
double * BPV; // Именно в BPV записан стартовый и конечный вектор
double * UPV;
unsigned * mask; // Маски: в каком направлении искать, блокировка поиска
double * steps; // Значения шагов. Можно после таймаута изменить
double * precs; // Предельные значения шагов. if VP
double prec; // Общее значение предельного значения шага для всех переменных. if !VP
BOUNDARY * boundaries; // Ограничения
long double UOFV, BOFV; // Значение целевой функции в точке UPV и BPV
unsigned OFCC; // Количество вызовов целевой функции
} HJD, * pHJD;
void hjd_init(HJD & hjd); // Задаёт начальные значения параметров корректные для случайного вызова
void hjd_free(HJD & hjd); // Освобождает память
void hjd_setfunc(HJD & hjd, OBJECTIVE_FUNCTION of, void * param, unsigned nc, bool R, bool VP); // Назначает целевую функцию и вариант метода
void hjd_setperformance(HJD & hjd, clock_t timeout, double div, double prec = 0.0); // Задаёт параметры, от которых зависит сходимость метода
#define HJD_SET_STARTVAR(_HJD, i, START) {(_HJD).BPV[(i)] = (START);}
#define HJD_SET_STARTVECTOR(_HJD, START) {memmove((_HJD).BPV, (START), (_HJD).nc * sizeof(double));}
#define HJD_SET_STEP(_HJD, i, STEP) {(_HJD).steps[(i)] = (STEP);}
#define HJD_SET_ALLSTEPS(_HJD, STEP) {for (unsigned i = 0; i < (_HJD).nc; i++) (_HJD).steps[i] = (STEP);} // Устанавливает значения всех шагов в одно и то же значение
#define HJD_IMPROVE_STEPS(_HJD, MULTIPLIER) {for (unsigned i = 0; i < (_HJD).nc; i++) (_HJD).steps[i] *= (MULTIPLIER);}
#define HJD_SET_STEPS(_HJD, STEPS) {memmove((_HJD).steps, (STEPS), (_HJD).nc * sizeof(double));}
#define HJD_SET_PRECISION(_HJD, i, PRECISION) {(_HJD).precs[(i)] = (PRECISION);}
#define HJD_SET_PRECISIONS(_HJD, PRECISIONS) {if (!(_HJD).VP) throw; memmove((_HJD).precs, (PRECISIONS), (_HJD).nc * sizeof(double));}
#define HJD_SET_BOUNDARY(_HJD, i, MIN, MAX) {(_HJD).boundaries[(i)].min = (MIN); (_HJD).boundaries[(i)].max = (MAX);}
#define HJD_SET_BOUNDARIES(_HJD, BOUNDARIES) {memmove((_HJD).boundaries, (BOUNDARIES), (_HJD).nc * sizeof(BOUNDARY));}
#define HJD_MASK_BLOCK_POSITION(_HJD, POSITION) {(_HJD).mask[(POSITION)] = HJD_SEARCH_DISABLED;} // Блокирует способность метода изменять параметр-константу
#define HJD_MASK_UNBLOCK_POSITION(_HJD, POSITION) {(_HJD).mask[(POSITION)] = HJD_SEARCH_ENABLED;}
#define HJD_SET_MASK_UNBLOCKALL(_HJD) {for (unsigned i = 0; i < (_HJD).nc; i++) (_HJD).mask[i] = HJD_SEARCH_ENABLED;}
clock_t HookeAndJeevesTechnique(HJD & hjd);
#endif
hooke_and_jeeves.cpp:
#include "commonapi.h"
#pragma hdrstop
#include "hooke_and_jeeves.h"
#define objective_function(point, param) (hjd.OFCC++, hjd.of((point), (param)))
static bool BestNearby_R(HJD & hjd)
{
hjd.UOFV = hjd.BOFV;
for (unsigned i = 0; i < hjd.nc; i++) {
hjd.UPV[i] = hjd.BPV[i];
}
for (unsigned i = 0; i < hjd.nc; i++) {
if ((hjd.mask[i] & HJD_SEARCH_DISABLED) == 0) {
long double OFV;
double CCV = hjd.UPV[i];
//++
double D = CCV + hjd.steps[i];
if (D < hjd.boundaries[i].max) {
hjd.UPV[i] = D;
OFV = objective_function(hjd.UPV, hjd.param);
if (OFV < hjd.UOFV) {
hjd.UOFV = OFV;
hjd.mask[i] = HJD_SEARCH_INC;
continue;
}
}
//--
D = CCV - hjd.steps[i];
if (D > hjd.boundaries[i].min) {
hjd.UPV[i] = D;
OFV = objective_function(hjd.UPV, hjd.param);
if (OFV < hjd.UOFV) {
hjd.UOFV = OFV;
hjd.mask[i] = HJD_SEARCH_DEC;
continue;
}
}
hjd.UPV[i] = CCV;
hjd.mask[i] = HJD_SEARCH_HOLD;
}
}
return (hjd.BOFV > hjd.UOFV);
}
static bool BestNearby_U(HJD & hjd)
{
hjd.UOFV = hjd.BOFV;
for (unsigned i = 0; i < hjd.nc; i++) {
hjd.UPV[i] = hjd.BPV[i];
}
for (unsigned i = 0; i < hjd.nc; i++) {
if ((hjd.mask[i] & HJD_SEARCH_DISABLED) == 0) {
long double OFV;
double CCV = hjd.UPV[i];
//++
hjd.UPV[i] += hjd.steps[i];
OFV = objective_function(hjd.UPV, hjd.param);
if (OFV < hjd.UOFV) {
hjd.UOFV = OFV;
hjd.mask[i] = HJD_SEARCH_INC;
} else {
//--
hjd.UPV[i] = CCV - hjd.steps[i];
OFV = objective_function(hjd.UPV, hjd.param);
if (OFV < hjd.UOFV) {
hjd.UOFV = OFV;
hjd.mask[i] = HJD_SEARCH_DEC;
} else {
hjd.UPV[i] = CCV;
hjd.mask[i] = HJD_SEARCH_HOLD;
}
}
}
}
return (hjd.BOFV > hjd.UOFV);
}
static void PatternStep_R(HJD & hjd)
{
hjd.BOFV = hjd.UOFV;
for (unsigned i = 0; i < hjd.nc; i++) {
hjd.BPV[i] = hjd.UPV[i];
}
for (unsigned i = 0; i < hjd.nc; i++) {
switch (hjd.mask[i]) {
case HJD_SEARCH_INC : {
hjd.UPV[i] += hjd.steps[i];
if (hjd.boundaries[i].max < hjd.UPV[i]) {
hjd.UPV[i] = hjd.boundaries[i].max;
}
} break;
case HJD_SEARCH_DEC : {
hjd.UPV[i] -= hjd.steps[i];
if (hjd.boundaries[i].min > hjd.UPV[i]) {
hjd.UPV[i] = hjd.boundaries[i].min;
}
}
}
}
hjd.UOFV = objective_function(hjd.UPV, hjd.param);
}
static void PatternStep_U(HJD & hjd)
{
hjd.BOFV = hjd.UOFV;
for (unsigned i = 0; i < hjd.nc; i++) {
hjd.BPV[i] = hjd.UPV[i];
}
for (unsigned i = 0; i < hjd.nc; i++) {
switch (hjd.mask[i]) {
case HJD_SEARCH_INC : {
hjd.UPV[i] += hjd.steps[i];
} break;
case HJD_SEARCH_DEC : {
hjd.UPV[i] -= hjd.steps[i];
}
}
}
hjd.UOFV = objective_function(hjd.UPV, hjd.param);
}
static bool DecreaseSteps_V(HJD & hjd)
{
unsigned nc = 0;
for (unsigned i = 0; i < hjd.nc; i++) {
hjd.steps[i] *= hjd.factor;
if (hjd.steps[i] <= hjd.precs[i]) {
hjd.steps[i] = hjd.precs[i];
nc++;
}
}
return (nc == hjd.nc);
}
static bool DecreaseSteps_G(HJD & hjd)
{
unsigned nc = 0;
for (unsigned i = 0; i < hjd.nc; i++) {
hjd.steps[i] *= hjd.factor;
if (hjd.steps[i] <= hjd.prec) {
hjd.steps[i] = hjd.prec;
nc++;
}
}
return (nc == hjd.nc);
}
clock_t HookeAndJeevesTechnique(HJD & hjd)
{
void (* PatternStep)(HJD & hjd) = hjd.R ? PatternStep_R : PatternStep_U;
bool (* BestNearby)(HJD & hjd) = hjd.R ? BestNearby_R : BestNearby_U;
bool (* DecreaseSteps)(HJD & hjd) = hjd.VP ? DecreaseSteps_V : DecreaseSteps_G;
hjd.OFCC = 0;
clock_t start = clock();
hjd.UOFV = DBL_MAX;
hjd.BOFV = objective_function(hjd.BPV, hjd.param);
bool last = false;
while (clock() - start < hjd.timeout) {
if (hjd.BOFV > hjd.UOFV) {
PatternStep(hjd);
} else {
while (!BestNearby(hjd)) {
if (last) {
return (clock() - start);
} else {
last = DecreaseSteps(hjd);
}
}
}
}
return hjd.timeout;
}
#pragma warn -rvl
#pragma argsused
static __declspec(naked) long double _retz(double p[], void * param)
{
asm {
FLDZ
RET
}
}
#pragma warn .rvl
void hjd_init(HJD & hjd)
{
hjd.of = _retz;
hjd.nc = 0;
hjd.BPV = NULL;
hjd.UPV = NULL;
{
hjd.steps = NULL;
hjd.precs = NULL;
hjd.boundaries = NULL;
}
hjd.mask = NULL;
}
void hjd_setfunc(HJD & hjd, OBJECTIVE_FUNCTION of, void * param, unsigned nc, bool R, bool VP)
{
hjd.R = R;
hjd.VP = VP;
hjd.of = of;
hjd.param = param;
hjd.nc = nc;
unsigned size = nc * sizeof(double);
hjd.BPV = (double *)realloc(hjd.BPV, size);
hjd.UPV = (double *)realloc(hjd.UPV, size);
{
hjd.steps = (double *)realloc(hjd.steps, size);
if (VP) {
hjd.precs = (double *)realloc(hjd.precs, size);
} else if (hjd.precs != NULL) {
free(hjd.precs); hjd.precs = NULL;
}
if (R) {
hjd.boundaries = (BOUNDARY *)realloc(hjd.boundaries, sizeof(BOUNDARY) * nc);
} else if (hjd.boundaries != NULL) {
free(hjd.boundaries); hjd.boundaries = NULL;
}
}
hjd.mask = (unsigned *)realloc(hjd.mask, sizeof(unsigned) * nc);
}
void hjd_setperformance(HJD & hjd, clock_t timeout, double div, double prec)
{
hjd.timeout = timeout;
hjd.prec = prec;
hjd.factor = 1.0 / div;
}
void hjd_free(HJD & hjd)
{
free(hjd.BPV); hjd.BPV = NULL;
free(hjd.UPV); hjd.UPV = NULL;
{
free(hjd.steps); hjd.steps = NULL;
free(hjd.precs); hjd.precs = NULL;
free(hjd.boundaries); hjd.boundaries = NULL;
}
free(hjd.mask); hjd.mask = NULL;
}
main.cpp:
#include "commonapi.h"
#pragma hdrstop
#include "hooke_and_jeeves.h"
#pragma pack(1)
typedef struct _DATAPOINT
{
double _y,
_x,
_yc;
} DATAPOINT, * pDATAPOINT;
#pragma pack()
long double _fastcall parabola(double c[], void * dp)
{
DATAPOINT & datapoint = *(DATAPOINT *)dp;
double temp = datapoint._x;
datapoint._yc = c[2] + (c[1] + c[0] * temp) * temp;
temp = datapoint._yc - datapoint._y;
return temp * temp;
}
typedef struct _SEARCHSET
{
void * points;
unsigned pts;
unsigned vsize;
} SEARCHSET, pSEARCHSET;
template <typename VARPOINT> inline void setvarvec(SEARCHSET & ss, VARPOINT * vv, unsigned pts)
{
ss.vsize = sizeof(VARPOINT) / sizeof(double);
ss.pts = pts;
ss.points = vv;
}
static long double PARABOLA(double p[], void * param)
{
SEARCHSET & ss = *(SEARCHSET *)param;
long double P = 0.0;
for (unsigned i = 0; i < ss.pts; i++) {
P += parabola(p, (double *)ss.points + i * ss.vsize);
}
return P;
}
DATAPOINT points[] = {{ 5.0, -1.0},
{ 2.0, 0.0},
{ 1.0, 1.0},
{ 2.0, 2.0},
{ 5.0, 3.0}};
const double parabola_BP[] = {0.0, 0.0, 0.0};
const double precisions[] = {1E-6, 1E-6, 1E-5};
const BOUNDARY boundaries[] = {{-10.0, 10.0},
{-10.0, 10.0},
{-10.0, 10.0}};
/*
Michael Powell,
An Iterative Method for Finding Stationary Values of a Function
of Several Variables,
Computer Journal,
Volume 5, 1962, pages 147-151.
Powell's quartic function
f(x) = (x1 + 10 * x2) ^ 2 + 5 * (x3 - x4) ^ 2 + (x2 - 2 * x3) ^ 4 + 10 * (x1 - x4) ^ 4
start point: f(3, -1, 0, 1) = 215
minimum: f(0, 0, 0, 0) = 0
*/
const double powell_BP[] = {3.0, -1.0, 0.0, 1.0};
#pragma argused
static long double powell(double p[], void * param)
{
double a, b, c, d, t1, t2;
a = p[0];
b = p[1];
c = p[2];
d = p[3] ;
t1 = a + 10.0 * b;
t1 *= t1;
t2 = c - d;
t1 += 5.0 * t2 * t2;
t2 = b - 2.0 * c;
t2 *= t2;
t1 += t2 * t2;
t2 = a - d;
t2 *= t2;
return t1 + 10.0 * t2 * t2;
}
// Rosenbrocks parabolic valley (banana function)
/*
Rosenbrocks parabolic (banana) function
f(x) = (1 - x1) ^ 2 + 100 * (x2 - x1 ^ 2) ^ 2
start point: f(-1.2, 1) = 24.20
minimum: f(1, 1) = 0
*/
const double rosen_BP[] = {-1.2, 1.0};
#pragma argused
static long double rosen(double p[], void * param)
{
double a, b;
a = p[0];
b = p[1] - a * a;
a = 1.0 - a;
return 100.0 * b * b + a * a;
}
// Schittowski problem 210: very difficult banana function
const double prob210_BP[] = {-1.2, 1.0};
#pragma argused
static long double prob210(double p[], void * param)
{
double a, b;
a = p[0];
b = p[1] - a * a;
a = 1.0 - a;
return 1.0E6 * b * b + a * a;
}
// Schittowski problem 281: summation of ten cubics
const double prob281_BP[] = { 1.7528323e-02,
2.2503207e+00,
1.0325044e+00,
1.1047138e+00,
6.7813668e-01,
8.6220218e-01,
9.6336051e-01,
9.3734542e-01,
9.9586705e-01,
9.5568268e-01};
#pragma argused
static long double prob281(double p[], void * param)
{
double summ = 0.0;
for(unsigned i = 1; i <= 10; i++)
{
double temp = (p[i] - 1.0);
summ += (i * i * i) * temp * temp;
}
return pow(summ, 1.0 / 3.0);
}
#pragma argsused
int main(int argc, char * argv[], char * env[])
{
printf("start hj\n");
HJD hjd;
hjd_init(hjd);
#if false
SEARCHSET ss;
setvarvec(ss, points, ARRAYSIZE(points));
hjd_setfunc(hjd, PARABOLA, &ss, ARRAYSIZE(parabola_BP), true, true);
HJD_SET_PRECISIONS(hjd, precisions);
HJD_SET_BOUNDARIES(hjd, boundaries);
HJD_SET_MASK_UNBLOCKALL(hjd);
hjd_setperformance(hjd, LONG_MAX, 56.399);
HJD_SET_STARTVECTOR(hjd, parabola_BP);
HJD_SET_ALLSTEPS(hjd, 1E-1);
clock_t elapsed = HookeAndJeevesTechnique(hjd);
printf("finish hj\n");
printf("elapsed time = %d ms\n", elapsed);
printf("OFCC = %d\n", hjd.OFCC);
printf("OFV = %Lg\n", hjd.BOFV);
printf("constants:\n");
for (unsigned i = 0; i < hjd.nc; i++) {
printf("\t% 3d: % 7lf +/- % 7lf\n", i, hjd.BPV[i], hjd.precs[i]);
}
#elif true
SEARCHSET ss;
setvarvec(ss, points, ARRAYSIZE(points));
hjd_setfunc(hjd, PARABOLA, &ss, ARRAYSIZE(parabola_BP), false, false);
HJD_SET_MASK_UNBLOCKALL(hjd);
FILE * f = fopen("divisor", "wb");
for (double r = 2.0; r <= 100.0; r += 0.1) {
hjd_setperformance(hjd, LONG_MAX, r, 0.0);
HJD_SET_STARTVECTOR(hjd, parabola_BP);
HJD_SET_ALLSTEPS(hjd, 1E-1);
HookeAndJeevesTechnique(hjd);
printf("%d\n", hjd.OFCC);
fprintf(f, "%lg %d\n", r, hjd.OFCC);
}
fclose(f);
putenv("PATH=C:\\Octave\\3.2.4_gcc-4.4.0\\bin");
FILE * gnuplot = _popen("gnuplot.exe", "wb");
fprintf(gnuplot, "%s\n", "plot 'divisor' using 1:2 with lines;");
fflush(gnuplot);
while (!kbhit()) continue;
fprintf(gnuplot, "%s\n", "exit;");
fflush(gnuplot);
_pclose(gnuplot);
#elif false
SEARCHSET ss;
setvarvec(ss, points, ARRAYSIZE(points));
hjd_setfunc(hjd, PARABOLA, &ss, ARRAYSIZE(parabola_BP), false, false);
HJD_SET_MASK_UNBLOCKALL(hjd);
FILE * f = fopen("precision", "wb");
for (signed i = -1; i >= DBL_MIN_10_EXP; i--) {
hjd_setperformance(hjd, LONG_MAX, 10.836, exp(M_LN10 * i));
HJD_SET_STARTVECTOR(hjd, parabola_BP);
HJD_SET_ALLSTEPS(hjd, 1E-1);
clock_t elapsed = HookeAndJeevesTechnique(hjd);
//printf("elapsed time = %d ms\n", elapsed);
printf("\tOFCC = %d\n", hjd.OFCC);
//printf("OFV = %Lg\n", hjd.BOFV);
//printf("general precision = %lg\n", hjd.prec);
fprintf(f, "%i %d\n", -i, hjd.OFCC);
}
fclose(f);
putenv("PATH=C:\\Octave\\3.2.4_gcc-4.4.0\\bin");
FILE * gnuplot = _popen("gnuplot.exe", "wb");
fprintf(gnuplot, "%s\n", "plot 'precision' using 1:2 with lines;");
fflush(gnuplot);
while (!kbhit()) continue;
fprintf(gnuplot, "%s\n", "exit;");
fflush(gnuplot);
_pclose(gnuplot);
#elif false
hjd_setfunc(hjd, powell, NULL, ARRAYSIZE(powell_BP), true, false);
HJD_SET_BOUNDARIES(hjd, boundaries);
HJD_SET_MASK_UNBLOCKALL(hjd);
#define TIMEOUT 4000
hjd_setperformance(hjd, TIMEOUT, 56.399, 1E-12);
HJD_SET_STARTVECTOR(hjd, powell_BP);
HJD_SET_ALLSTEPS(hjd, 1E-1);
clock_t elapsed = HookeAndJeevesTechnique(hjd);
while (elapsed >= TIMEOUT) {
printf("OFCC = %d\n", hjd.OFCC);
printf("OFV = %Lg\n", hjd.BOFV);
printf("general precision = %lg\n", hjd.prec);
printf("constants:\n");
for (unsigned i = 0; i < hjd.nc; i++) {
printf("\t% 3d: % 6lg\n", i, hjd.BPV[i]);
}
HJD_IMPROVE_STEPS(hjd, 2E2);
printf("timeout, steps improved\n");
elapsed = HookeAndJeevesTechnique(hjd);
}
printf("\tfinish!\n");
if (TIMEOUT == LONG_MAX) {
printf("elapsed time = %d ms\n", elapsed);
}
printf("OFCC = %d\n", hjd.OFCC);
printf("OFV = %Lg\n", hjd.BOFV);
printf("general precision = %lg\n", hjd.prec);
printf("constants:\n");
for (unsigned i = 0; i < hjd.nc; i++) {
printf("\t% 3d: % 6lg\n", i, hjd.BPV[i]);
}
#elif false
hjd_setfunc(hjd, rosen, NULL, ARRAYSIZE(rosen_BP), false, false);
HJD_SET_MASK_UNBLOCKALL(hjd);
hjd_setperformance(hjd, LONG_MAX, 56.399, 0.0);
HJD_SET_STARTVECTOR(hjd, rosen_BP);
HJD_SET_ALLSTEPS(hjd, 1E-1);
clock_t elapsed = HookeAndJeevesTechnique(hjd);
printf("elapsed time = %d ms\n", elapsed);
printf("OFCC = %d\n", hjd.OFCC);
printf("OFV = %Lg\n", hjd.BOFV);
printf("constants:\n");
for (unsigned i = 0; i < hjd.nc; i++) {
printf("\t% 3d: %lg +/- %lg\n", i, hjd.BPV[i], hjd.prec);
}
#elif false
hjd_setfunc(hjd, prob210, NULL, ARRAYSIZE(prob210_BP), false, false);
HJD_SET_MASK_UNBLOCKALL(hjd);
hjd_setperformance(hjd, LONG_MAX, 56.399, 1.0E-9);
HJD_SET_STARTVECTOR(hjd, prob210_BP);
HJD_SET_ALLSTEPS(hjd, 1E-1);
clock_t elapsed = HookeAndJeevesTechnique(hjd);
printf("elapsed time = %d ms\n", elapsed);
printf("OFCC = %d\n", hjd.OFCC);
printf("OFV = %Lg\n", hjd.BOFV);
printf("constants:\n");
for (unsigned i = 0; i < hjd.nc; i++) {
printf("\t% 3d: %lg +/- %lg\n", i, hjd.BPV[i], hjd.prec);
}
#elif false
hjd_setfunc(hjd, prob281, NULL, ARRAYSIZE(prob281_BP), false, false);
HJD_SET_MASK_UNBLOCKALL(hjd);
hjd_setperformance(hjd, LONG_MAX, 56.399, 1.0E-9);
HJD_SET_STARTVECTOR(hjd, prob281_BP);
HJD_SET_ALLSTEPS(hjd, 1E-1);
clock_t elapsed = HookeAndJeevesTechnique(hjd);
printf("elapsed time = %d ms\n", elapsed);
printf("OFCC = %d\n", hjd.OFCC);
printf("OFV = %Lg\n", hjd.BOFV);
printf("constants:\n");
for (unsigned i = 0; i < hjd.nc; i++) {
printf("\t% 3d: %lg +/- %lg\n", i, hjd.BPV[i], hjd.prec);
}
#endif
while (!kbhit()) continue;
hjd_free(hjd);
return EXIT_SUCCESS;
}
commonapi.h:
#pragma once
#ifndef commonapiH
#define commonapiH
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <mem.h>
#include <time.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#define ARRAYSIZE(ar) (sizeof(ar)/sizeof(ar[0]))
#endif
|