+ reserved_label.assign(128,1,1,1,~0U);
+ reserved_label['t'] = 17;
+ reserved_label['w'] = 18;
+ reserved_label['h'] = 19;
+ reserved_label['d'] = 20;
+ reserved_label['s'] = 21;
+ reserved_label['r'] = 22;
+ reserved_label[0] = 23; // wh
+ reserved_label[1] = 24; // whd
+ reserved_label[2] = 25; // whds
+ reserved_label[3] = 26; // pi
+ reserved_label['e'] = 27;
+ reserved_label[29] = 28; // interpolation
+ reserved_label[30] = 29; // boundary
+ reserved_label['x'] = _cimg_mp_x;
+ reserved_label['y'] = _cimg_mp_y;
+ reserved_label['z'] = _cimg_mp_z;
+ reserved_label['c'] = _cimg_mp_c;
+ // reserved_label[4-30] store also two-char variables:
+ // [4] = im, [5] = iM, [6] = ia, [7] = iv, [8] = is, [9] = ip, [10] = ic,
+ // [11] = xm, [12] = ym, [13] = zm, [14] = cm, [15] = xM, [16] = yM, [17] = zM, [18]=cM, [19]=i0...[28]=i9,
+
+ // Compile expression into a serie of opcodes.
+ const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1);
+ p_code_end = code.end();
+
+ // Free resources used for parsing and prepare for evaluation.
+ mem.resize(mempos,1,1,1,-1);
+ result = mem._data + ind_result;
+ level.assign();
+ labelMpos.assign();
+ reserved_label.assign();
+ expr.assign();
+ pexpr.assign();
+ opcode._width = opcode._depth = opcode._spectrum = 1;
+ opcode._is_shared = true;
+
+ // Execute init() function if any specified.
+ p_code_begin = code._data + init_size;
+ if (init_size) {
+ mem[_cimg_mp_x] = mem[_cimg_mp_y] = mem[_cimg_mp_z] = mem[_cimg_mp_c] = 0;
+ for (p_code = code._data; p_code<p_code_begin; ++p_code) {
+ const CImg<uptrT> &op = *p_code;
+ // Allows to avoid parameter passing to evaluation functions.
+ opcode._data = op._data; opcode._height = op._height;
+ const uptrT target = opcode[1];
+ mem[target] = _cimg_mp_defunc(*this);
+ }
+ }
+ }
+
+ _cimg_math_parser():
+ code(_code),p_code_begin(0),p_code_end(0),
+ imgin(CImg<T>::const_empty()),listin(CImgList<T>::const_empty()),
+ imgout(CImg<T>::empty()),listout(CImgList<T>::empty()),
+ img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),debug_indent(0),
+ calling_function(0) {
+ mem.assign(1 + _cimg_mp_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()().
+ result = mem._data;
+ }
+
+ _cimg_math_parser(const _cimg_math_parser& mp):
+ mem(mp.mem),code(mp.code),p_code_begin(mp.p_code_begin),p_code_end(mp.p_code_end),
+ imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout),img_stats(mp.img_stats),
+ list_stats(mp.list_stats),list_median(mp.list_median),debug_indent(0),
+ result(mem._data + (mp.result - mp.mem._data)),calling_function(0) {
+#ifdef cimg_use_openmp
+ mem[17] = omp_get_thread_num();
+#endif
+ opcode._width = opcode._depth = opcode._spectrum = 1;
+ opcode._is_shared = true;
+ }
+
+ // Return 'true' is the specified mathematical expression requires the input image to be copied.
+ // Set 'is_parallelizable' to 'false' if expression should be evaluated with a single thread.
+ static bool needs_input_copy(const char *expression, bool &is_parallelizable) {
+ if (!expression || *expression=='>' || *expression=='<') return is_parallelizable = false;
+ for (const char *s = expression; *s; ++s)
+ if ((*s=='i' || *s=='j') && (s[1]=='(' || s[1]=='[')) {
+ if (s[2]=='#') is_parallelizable = false;
+ else {
+ const char opening = s[1], ending = opening=='('?')':']';
+ const char *ns;
+ int level = 0;
+ for (ns = s + 2; *ns; ++ns) { // Find ending ')' or ']'.
+ if (*ns==ending && !level) break;
+ if (*ns==opening) ++level; else if (*ns==ending) --level;
+ }
+ if (*ns && (ns[1]!='=' || ns[2]=='=')) return true;
+ }
+ } else if (((*s=='R' || *s=='G' || *s=='B') && s[1]!='#') ||
+ (*s=='i' && s[1]>='0' && s[1]<='7' && s[2]!='#')) return true;
+ return false;
+ }
+
+ // Compilation procedure.
+ unsigned int compile(char *ss, char *se, unsigned int *p_coords=0) {
+ const char *const ss0 = ss;
+ if (ss<se) {
+ while (*ss==' ') ++ss;
+ while (se>ss && *(se - 1)==' ') --se;
+ }
+ if (se>ss && *(se - 1)==';') --se;
+ if (se<=ss || !*ss) {
+ cimg::strellipsize(expr,64);
+ throw CImgArgumentException("[_cimg_math_parser] "
+ "CImg<%s>::%s(): Missing item in expression '%s'.",
+ pixel_type(),calling_function,
+ expr._data);
+ }
+ unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6;
+ char
+ *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3,
+ *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4,
+ *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8,
+ *s, *ps, *ns, *s0, *s1, *s2, *s3, c1, c2, c3, c4, sep, end;
+
+ if (p_coords)
+ p_coords[0] = p_coords[1] = p_coords[2] = p_coords[3] = p_coords[4] = p_coords[5] =
+ p_coords[6] = p_coords[7] = p_coords[8] = p_coords[9] = p_coords[10] = ~0U;
+
+ const char saved_char = *se; *se = 0;
+ const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1;
+ bool is_sth;
+ CImg<uintT> coords;
+ CImgList<uptrT> _opcode;
+ CImg<charT> variable_name;
+
+ // Look for a single value, pre-defined variable or a variable assignment.
+ double val, val1, val2;
+ sep = end = 0;
+ int nb = cimg_sscanf(ss,"%lf%c%c",&val,&sep,&end);
+
+#if cimg_OS==2
+ // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
+ // to read those particular values.
+ if (!nb && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) {
+ is_sth = true;
+ s = ss;
+ if (*s=='+') ++s; else if (*s=='-') { ++s; is_sth = false; }
+ if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; }
+ else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; }
+ if (nb==1 && !is_sth) val = -val;
+ }
+#endif
+ if (nb==1) _cimg_mp_constant(val);
+ if (nb==2 && sep=='%') _cimg_mp_constant(val/100);
+
+ if (ss1==se) switch (*ss) { // One-char variable.
+ case 't' : case 'w' : case 'h' : case 'd' : case 's' : case 'r' :
+ case 'x' : case 'y' : case 'z' : case 'c' : case 'e' :
+ _cimg_mp_return(reserved_label[*ss]);
+ case 'u' :
+ if (reserved_label['u']!=~0U) _cimg_mp_return(reserved_label['u']);
+ _cimg_mp_opcode2(mp_u,0,1);
+ case 'g' :
+ if (reserved_label['g']!=~0U) _cimg_mp_return(reserved_label['g']);
+ _cimg_mp_opcode0(mp_g);
+ case 'i' :
+ if (reserved_label['i']!=~0U) _cimg_mp_return(reserved_label['i']);
+ _cimg_mp_opcode0(mp_i);
+ case 'R' :
+ if (reserved_label['R']!=~0U) _cimg_mp_return(reserved_label['R']);
+ _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,0,0,0);
+ case 'G' :
+ if (reserved_label['G']!=~0U) _cimg_mp_return(reserved_label['G']);
+ _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,1,0,0);
+ case 'B' :
+ if (reserved_label['B']!=~0U) _cimg_mp_return(reserved_label['B']);
+ _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,2,0,0);
+ case 'A' :
+ if (reserved_label['A']!=~0U) _cimg_mp_return(reserved_label['A']);
+ _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,3,0,0);
+ }
+ else if (ss2==se) { // Two-chars variable.
+ arg1 = arg2 = ~0U;
+ if (*ss=='w' && *ss1=='h') _cimg_mp_return(reserved_label[0]); // wh
+ if (*ss=='p' && *ss1=='i') _cimg_mp_return(reserved_label[3]); // pi
+ if (*ss=='i') {
+ if (*ss1>='0' && *ss1<='9') { // i0...i9
+ pos = 19 + *ss1 - '0';
+ if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]);
+ _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,pos - 19,0,0);
+ }
+ switch (*ss1) {
+ case 'm' : arg1 = 4; arg2 = 0; break; // im
+ case 'M' : arg1 = 5; arg2 = 1; break; // iM
+ case 'a' : arg1 = 6; arg2 = 2; break; // ia
+ case 'v' : arg1 = 7; arg2 = 3; break; // iv
+ case 's' : arg1 = 8; arg2 = 12; break; // is
+ case 'p' : arg1 = 9; arg2 = 13; break; // is
+ case 'c' : // ic
+ if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]);
+ if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0;
+ _cimg_mp_return(mem_img_median);
+ break;
+ }
+ }
+ else if (*ss1=='m') switch (*ss) {
+ case 'x' : arg1 = 11; arg2 = 4; break; // xm
+ case 'y' : arg1 = 12; arg2 = 5; break; // ym
+ case 'z' : arg1 = 13; arg2 = 6; break; // zm
+ case 'c' : arg1 = 14; arg2 = 7; break; // cm
+ }
+ else if (*ss1=='M') switch (*ss) {
+ case 'x' : arg1 = 15; arg2 = 8; break; // xM
+ case 'y' : arg1 = 16; arg2 = 9; break; // yM
+ case 'z' : arg1 = 17; arg2 = 10; break; // zM
+ case 'c' : arg1 = 18; arg2 = 11; break; // cM
+ }
+ if (arg1!=~0U) {
+ if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]);
+ if (!img_stats) {
+ img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false);
+ mem_img_stats.assign(1,14,1,1,~0U);
+ }
+ if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]);
+ _cimg_mp_return(mem_img_stats[arg2]);
+ }
+ } else if (ss3==se) { // Three-chars variable.
+ if (*ss=='w' && *ss1=='h' && *ss2=='d') _cimg_mp_return(reserved_label[1]); // whd
+ } else if (ss4==se) { // Four-chars variable.
+ if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') _cimg_mp_return(reserved_label[2]); // whds
+ }
+
+ for (s = se2; s>ss; --s) // Separator ';'.
+ if (*s==';' && level[s - expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s + 1,se,p_coords)); }
+
+ // Variable declaration/assignment or pixel assignment.
+ for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns)
+ if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' &&
+ *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' &&
+ *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' &&
+ level[s - expr._data]==clevel) {
+ variable_name.assign(ss,(unsigned int)(s - ss + 1)).back() = 0;
+ cimg::strpare(variable_name);
+ const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name);
+
+ // Pixel assignment (fast).
+ if (l_variable_name>2 && (*ss=='i' || *ss=='j')) {
+ char *const ve1 = ss + l_variable_name - 1;
+ is_sth = *ss=='j'; // is_relative?
+ if (*ss1=='(' && *ve1==')') { // i/j(_#ind,_x,_y,_z,_c) = value
+ if (*ss2=='#') {
+ s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
+ p1 = compile(ss3,s0++);
+ } else { p1 = ~0U; s0 = ss2; }
+ arg1 = is_sth?0U:(unsigned int)_cimg_mp_x;
+ arg2 = is_sth?0U:(unsigned int)_cimg_mp_y;
+ arg3 = is_sth?0U:(unsigned int)_cimg_mp_z;
+ arg4 = is_sth?0U:(unsigned int)_cimg_mp_c;
+ arg5 = compile(s + 1,se);
+ if (s0<ve1) {
+ s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ arg1 = compile(s0,s1);
+ if (s1<ve1) {
+ s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
+ arg2 = compile(s1,s2);
+ if (s2<ve1) {
+ s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
+ arg3 = compile(s2,s3);
+ if (s3<ve1) arg4 = compile(++s3,ve1);
+ }
+ }
+ }
+ if (p_coords) {
+ if (is_sth) { p_coords[6] = arg1; p_coords[7] = arg2; p_coords[8] = arg3; p_coords[9] = arg4; }
+ else { p_coords[1] = arg1; p_coords[2] = arg2; p_coords[3] = arg3; p_coords[4] = arg4; }
+ }
+ if (*ss2=='#') _cimg_mp_opcode6(is_sth?mp_list_set_jxyzc:mp_list_set_ixyzc,p1,arg1,arg2,arg3,arg4,arg5);
+ _cimg_mp_opcode5(is_sth?mp_set_jxyzc:mp_set_ixyzc,arg1,arg2,arg3,arg4,arg5);
+ } else if (*ss1=='[' && *ve1==']') { // i/j[_#ind,offset] = value
+ if (*ss2=='#') {
+ s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
+ p1 = compile(ss3,s0++);
+ } else { p1 = ~0U; s0 = ss2; }
+ arg1 = compile(s0,ve1);
+ arg2 = compile(s + 1,se);
+ if (p_coords) { p_coords[is_sth?5:0] = arg1; p_coords[10] = p1; }
+ if (p1!=~0U) _cimg_mp_opcode3(is_sth?mp_list_set_joff:mp_list_set_ioff,p1,arg1,arg2);
+ _cimg_mp_opcode2(is_sth?mp_set_joff:mp_set_ioff,arg1,arg2);
+ }
+ }
+
+ is_sth = true; // is_valid_variable_name.
+ if (*variable_name>='0' && *variable_name<='9') is_sth = false;
+ else for (ns = variable_name._data; *ns; ++ns)
+ if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') {
+ is_sth = false; break;
+ }
+ if (!is_sth) { // Not a valid name.
+ is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator?
+ if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment.
+
+ // Pixel assignment (generic lvalue).
+ if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) {
+ coords.assign(11);
+ arg1 = compile(ss,s,coords);
+ arg2 = compile(s + 1,se);
+ if (*coords!=~0U || coords[1]!=~0U || coords[5]!=~0U || coords[6]!=~0U) {
+ p1 = coords[10];
+ if (*coords!=~0U) {// i[_#ind,_off] = value
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_ioff,arg1,p1,*coords,arg2).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_ioff,arg1,*coords,arg2).move_to(code);
+ } else if (coords[1]!=~0U) { // i(_#ind,_x,_y,_z,_c) = value
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_ixyzc,arg1,
+ p1,coords[1],coords[2],coords[3],coords[4],arg2).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_ixyzc,arg1,
+ coords[1],coords[2],coords[3],coords[4],arg2).move_to(code);
+ } else if (coords[5]!=~0U) { // j[_#ind,_off] = value
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_joff,arg1,p1,coords[5],arg2).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_joff,arg1,coords[5],arg2).move_to(code);
+ } else if (coords[6]!=~0U) { // j(_#ind,x,y,z,c) = value
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_jxyzc,arg1,
+ p1,coords[6],coords[7],coords[8],coords[9],arg2).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_jxyzc,arg1,
+ coords[6],coords[7],coords[8],coords[9],arg2).move_to(code);
+ }
+ if (p_coords) std::memcpy(p_coords,coords,coords._width*sizeof(unsigned int));
+ _cimg_mp_return(arg1);
+ }
+ }
+ *se = saved_char;
+ cimg::strellipsize(variable_name,64);
+ throw CImgArgumentException("[_cimg_math_parser] "
+ "CImg<%s>::%s(): Invalid variable name '%s' in expression "
+ "'%s%s%s'.",
+ pixel_type(),calling_function,
+ variable_name._data,
+ (ss - 8)>expr._data?"...":"",
+ (ss - 8)>expr._data?ss - 8:expr._data,
+ se<&expr.back()?"...":"");
+ }
+
+ // Check for particular case of a reserved variable assignment.
+ if (variable_name[1] && !variable_name[2]) { // Two-chars variable.
+ c1 = variable_name[0];
+ c2 = variable_name[1];
+ if (c1=='w' && c2=='h') variable_name.fill((char)0,(char)0); // wh
+ else if (c1=='p' && c2=='i') variable_name.fill(3,0); // pi
+ else if (c1=='i') {
+ if (c2>='0' && c2<='9') variable_name.fill(19 + c2 - '0',0); // i0...i9
+ else if (c2=='m') variable_name.fill(4,0); // im
+ else if (c2=='M') variable_name.fill(5,0); // iM
+ else if (c2=='a') variable_name.fill(6,0); // ia
+ else if (c2=='v') variable_name.fill(7,0); // iv
+ else if (c2=='s') variable_name.fill(8,0); // is
+ else if (c2=='p') variable_name.fill(9,0); // ip
+ else if (c2=='c') variable_name.fill(10,0); // ic
+ } else if (c2=='m') {
+ if (c1=='x') variable_name.fill(11,0); // xm
+ else if (c1=='y') variable_name.fill(12,0); // ym
+ else if (c1=='z') variable_name.fill(13,0); // zm
+ else if (c1=='c') variable_name.fill(14,0); // cm
+ } else if (c2=='M') {
+ if (c1=='x') variable_name.fill(15,0); // xM
+ else if (c1=='y') variable_name.fill(16,0); // yM
+ else if (c1=='z') variable_name.fill(17,0); // zM
+ else if (c1=='c') variable_name.fill(18,0); // cM
+ }
+ } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable.
+ c1 = variable_name[0];
+ c2 = variable_name[1];
+ c3 = variable_name[2];
+ if (c1=='w' && c2=='h' && c3=='d') variable_name.fill(1,0); // whd
+ } else if (variable_name[1] && variable_name[2] && variable_name[3] &&
+ !variable_name[4]) { // Four-chars variable.
+ c1 = variable_name[0];
+ c2 = variable_name[1];
+ c3 = variable_name[2];
+ c4 = variable_name[3];
+ if (c1=='w' && c2=='h' && c3=='d' && c4=='s') variable_name.fill(2,0); // whds
+ } else if (!std::strcmp(variable_name,"interpolation")) variable_name.fill(29,0);
+ else if (!std::strcmp(variable_name,"boundary")) variable_name.fill(30,0);
+
+ // Set new value to variable.
+ arg2 = compile(s + 1,se);
+ if (!variable_name[1] && *variable_name>=0) { // One-char variable, or variable in reserved_labels.
+ arg1 = reserved_label[*variable_name];
+ if (arg1==~0U) // New variable.
+ arg1 = reserved_label[*variable_name] = opcode1(mp_replace,arg2);
+ else // Already declared (or reserved).
+ CImg<uptrT>::vector((uptrT)mp_replace,arg1,arg2).move_to(code);
+ } else {
+ int label_pos = -1;
+ cimglist_for(labelM,i) // Check for existing variable with same name.
+ if (!std::strcmp(variable_name,labelM[i])) { label_pos = i; break; }
+ if (label_pos<0) { // New variable.
+ if (labelM._width>=labelMpos._width) labelMpos.resize(-200,1,1,1,0);
+ label_pos = labelM.width();
+ variable_name.move_to(labelM);
+ arg1 = labelMpos[label_pos] = opcode1(mp_replace,arg2);
+ } else { // Already declared.
+ arg1 = labelMpos[label_pos];
+ CImg<uptrT>::vector((uptrT)mp_replace,arg1,arg2).move_to(code);
+ }
+ }
+ mem(arg1,1) = -1; // Set variable property.
+ _cimg_mp_return(arg1);
+ }
+
+ // Look for unary/binary/ternary operators. The operator precedences should be the same as in C++.
+ for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1;
+ if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' ||
+ *ps=='&' || *ps=='^' || *ps=='|' ||
+ (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) &&
+ level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=).
+ const char *s_op;
+ mp_func op;
+ switch (*ps) {
+ case '+' : op = mp_self_add; s_op = "addition"; break;
+ case '-' : op = mp_self_sub; s_op = "subtraction"; break;
+ case '*' : op = mp_self_mul; s_op = "multiplication"; break;
+ case '/' : op = mp_self_div; s_op = "division"; break;
+ case '%' : op = mp_self_modulo; s_op = "modulo"; break;
+ case '<' : op = mp_self_bitwise_left_shift; s_op = "left bitshift"; break;
+ case '>' : op = mp_self_bitwise_right_shift; s_op = "right bitshift"; break;
+ case '&' : op = mp_self_bitwise_and; s_op = "bitwise and"; break;
+ case '|' : op = mp_self_bitwise_or; s_op = "bitwise or"; break;
+ default : op = mp_self_power; s_op = "power"; break;
+ }
+
+ coords.assign(11);
+ arg1 = compile(ss,*ps=='>' || *ps=='<'?ns:ps,coords);
+ arg2 = compile(s + 1,se);
+ CImg<uptrT>::vector((uptrT)op,arg1,arg2).move_to(code);
+ if (*coords!=~0U || coords[1]!=~0U || coords[5]!=~0U || coords[6]!=~0U) { // Modify pixel value.
+ p1 = coords[10];
+ if (*coords!=~0U) { // i[_#ind,off] + = value
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_ioff,arg1,p1,*coords,arg1).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_ioff,arg1,*coords,arg1).move_to(code);
+ } else if (coords[1]!=~0U) { // i(_#ind,_x,_y,_z,_c) += value
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_ixyzc,arg1,
+ p1,coords[1],coords[2],coords[3],coords[4],arg1).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_ixyzc,arg1,
+ coords[1],coords[2],coords[3],coords[4],arg1).move_to(code);
+ } else if (coords[5]!=~0U) { // j[_#ind,off] += value
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_joff,arg1,p1,coords[5],arg1).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_joff,arg1,coords[5],arg1).move_to(code);
+ } else if (coords[6]!=~0U) { // j(_#ind,_x,_y,_z,_c) += value
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_jxyzc,arg1,
+ p1,coords[6],coords[7],coords[8],coords[9],arg1).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_jxyzc,arg1,
+ coords[6],coords[7],coords[8],coords[9],arg1).move_to(code);
+ }
+ if (p_coords) std::memcpy(p_coords,coords,coords._width*sizeof(unsigned int));
+ } else if (mem(arg1,1)>=0) {
+ *se = saved_char;
+ variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0;
+ cimg::strellipsize(variable_name,64);
+ cimg::strellipsize(expr,64);
+ throw CImgArgumentException("[_cimg_math_parser] "
+ "CImg<%s>::%s(): Invalid self-%s of non-variable '%s' "
+ "in expression '%s%s%s'.",
+ pixel_type(),calling_function,
+ s_op,variable_name._data,
+ (ss - 8)>expr._data?"...":"",
+ (ss - 8)>expr._data?ss - 8:expr._data,
+ se<&expr.back()?"...":"");
+ }
+ _cimg_mp_return(arg1);
+ }
+
+ for (s = ss1; s<se1; ++s)
+ if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2'.
+ s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1;
+ arg1 = compile(ss,s);
+ p2 = code._width; arg2 = compile(s + 1,*s1!=':'?se:s1);
+ p3 = code._width; arg3 = *s1!=':'?0:compile(s1 + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0 && mem(arg3,1)>0) _cimg_mp_constant(mem[arg1]?mem[arg2]:mem[arg3]);
+ if (mempos>=mem._width) mem.resize(-200,2,1,1,0);
+ pos = mempos++;
+ CImg<uptrT>::vector((uptrT)mp_if,pos,arg1,arg2,arg3,
+ p3 - p2,code._width - p3).move_to(code,p2);
+ _cimg_mp_return(pos);
+ }
+
+ for (s = se3, ns = se2; s>ss; --s, --ns)
+ if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or.
+ arg1 = compile(ss,s);
+ p2 = code._width; arg2 = compile(s + 2,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1] || mem[arg2]);
+ if (mempos>=mem._width) mem.resize(-200,2,1,1,0);
+ pos = mempos++;
+ CImg<uptrT>::vector((uptrT)mp_logical_or,pos,arg1,arg2,code._width - p2).
+ move_to(code,p2);
+ _cimg_mp_return(pos);
+ }
+
+ for (s = se3, ns = se2; s>ss; --s, --ns)
+ if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and.
+ arg1 = compile(ss,s);
+ p2 = code._width; arg2 = compile(s + 2,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1] && mem[arg2]);
+ if (mempos>=mem._width) mem.resize(-200,2,1,1,0);
+ pos = mempos++;
+ CImg<uptrT>::vector((uptrT)mp_logical_and,pos,arg1,arg2,code._width - p2).
+ move_to(code,p2);
+ _cimg_mp_return(pos);
+ }
+
+ for (s = se2; s>ss; --s)
+ if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or.
+ arg1 = compile(ss,s); arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant((unsigned long)mem[arg1] | (unsigned long)mem[arg2]);
+ _cimg_mp_opcode2(mp_bitwise_or,arg1,arg2);
+ }
+
+ for (s = se2; s>ss; --s)
+ if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and.
+ arg1 = compile(ss,s); arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant((unsigned long)mem[arg1] & (unsigned long)mem[arg2]);
+ _cimg_mp_opcode2(mp_bitwise_and,arg1,arg2);
+ }
+
+ for (s = se3, ns = se2; s>ss; --s, --ns)
+ if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to.
+ arg1 = compile(ss,s); arg2 = compile(s + 2,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]!=mem[arg2]);
+ _cimg_mp_opcode2(mp_neq,arg1,arg2);
+ }
+
+ for (s = se3, ns = se2; s>ss; --s, --ns)
+ if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to.
+ arg1 = compile(ss,s); arg2 = compile(s + 2,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]==mem[arg2]);
+ _cimg_mp_opcode2(mp_eq,arg1,arg2);
+ }
+
+ for (s = se3, ns = se2; s>ss; --s, --ns)
+ if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than.
+ arg1 = compile(ss,s); arg2 = compile(s + 2,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]<=mem[arg2]);
+ _cimg_mp_opcode2(mp_lte,arg1,arg2);
+ }
+
+ for (s = se3, ns = se2; s>ss; --s, --ns)
+ if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than.
+ arg1 = compile(ss,s); arg2 = compile(s + 2,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]>=mem[arg2]);
+ _cimg_mp_opcode2(mp_gte,arg1,arg2);
+ }
+
+ for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
+ if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than.
+ arg1 = compile(ss,s); arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]<mem[arg2]);
+ _cimg_mp_opcode2(mp_lt,arg1,arg2);
+ }
+
+ for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
+ if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greather than.
+ arg1 = compile(ss,s); arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]>mem[arg2]);
+ _cimg_mp_opcode2(mp_gt,arg1,arg2);
+ }
+
+ for (s = se3, ns = se2; s>ss; --s, --ns)
+ if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift.
+ arg1 = compile(ss,s); arg2 = compile(s + 2,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant((long)mem[arg1]<<(unsigned int)mem[arg2]);
+ _cimg_mp_opcode2(mp_bitwise_left_shift,compile(ss,s),compile(s + 2,se));
+ }
+
+ for (s = se3, ns = se2; s>ss; --s, --ns)
+ if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift.
+ arg1 = compile(ss,s); arg2 = compile(s + 2,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant((long)mem[arg1]>>(unsigned int)mem[arg2]);
+ _cimg_mp_opcode2(mp_bitwise_right_shift,arg1,arg2);
+ }
+
+ for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
+ if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
+ *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
+ (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
+ *(ps - 1)<='9')))) &&
+ level[s - expr._data]==clevel) { // Addition.
+ arg1 = compile(ss,s);
+ arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1] + mem[arg2]);
+ if (arg2==1) _cimg_mp_opcode1(mp_increment,arg1);
+ if (arg1==1) _cimg_mp_opcode1(mp_increment,arg2);
+ _cimg_mp_opcode2(mp_add,arg1,arg2);
+ }
+
+ for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
+ if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
+ *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
+ (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
+ *(ps - 1)<='9')))) &&
+ level[s - expr._data]==clevel) { // Subtraction.
+ arg1 = compile(ss,s); arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1] - mem[arg2]);
+ if (arg2==1) _cimg_mp_opcode1(mp_decrement,arg1);
+ _cimg_mp_opcode2(mp_sub,arg1,arg2);
+ }
+
+ for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication.
+ arg1 = compile(ss,s); arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]*mem[arg2]);
+ _cimg_mp_opcode2(mp_mul,arg1,arg2);
+ }
+
+ for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division.
+ arg1 = compile(ss,s); arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]/mem[arg2]);
+ _cimg_mp_opcode2(mp_div,arg1,arg2);
+ }
+
+ for (s = se2, ns = se1; s>ss; --s, --ns) if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo.
+ arg1 = compile(ss,s); arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2]));
+ _cimg_mp_opcode2(mp_modulo,arg1,arg2);
+ }
+
+ if (se1>ss) {
+ if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) // Unary plus.
+ _cimg_mp_return(compile(ss1,se));
+
+ if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus.
+ arg1 = compile(ss1,se);
+ if (mem(arg1,1)>0) _cimg_mp_constant(-mem[arg1]);
+ _cimg_mp_opcode1(mp_minus,arg1);
+ }
+
+ if (*ss=='!') { // Logical not.
+ arg1 = compile(ss1,se);
+ if (mem(arg1,1)>0) _cimg_mp_constant(!mem[arg1]);
+ _cimg_mp_opcode1(mp_logical_not,arg1);
+ }
+
+ if (*ss=='~') { // Bitwise not.
+ arg1 = compile(ss1,se);
+ if (mem(arg1,1)>0) _cimg_mp_constant(~(unsigned long)mem[arg1]);
+ _cimg_mp_opcode1(mp_bitwise_not,arg1);
+ }
+ }
+
+ for (s = se2; s>ss; --s)
+ if (*s=='^' && level[s - expr._data]==clevel) { // Power.
+ arg1 = compile(ss,s);
+ arg2 = compile(s + 1,se);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(std::pow(mem[arg1],mem[arg2]));
+ switch (arg2) {
+ case 0 : _cimg_mp_return(1);
+ case 1 : _cimg_mp_return(arg1);
+ case 2 : _cimg_mp_opcode1(mp_sqr,arg1);
+ case 3 : _cimg_mp_opcode1(mp_pow3,arg1);
+ case 4 : _cimg_mp_opcode1(mp_pow4,arg1);
+ default : _cimg_mp_opcode2(mp_pow,compile(ss,s),compile(s + 1,se));
+ }
+ }
+
+ is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-decrement?
+ if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment.
+ coords.assign(11);
+ arg1 = is_sth?compile(ss2,se,coords):compile(ss,se2,coords);
+ pos = is_sth?arg1:opcode1(mp_replace,arg1);
+ CImg<uptrT>::vector((uptrT)((is_sth && *ss=='+') || (!is_sth && *se1=='+')?mp_self_increment:
+ mp_self_decrement),arg1).move_to(code);
+ if (*coords!=~0U || coords[1]!=~0U || coords[5]!=~0U || coords[6]!=~0U) { // Assign pixel.
+ p1 = coords[10];
+ if (*coords!=~0U) { // i[_#ind,off]++
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_ioff,arg1,p1,*coords,arg1).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_ioff,arg1,*coords,arg1).move_to(code);
+ } else if (coords[1]!=~0U) { // i(_#ind,_x,_y,_z,_c)++
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_ixyzc,arg1,
+ p1,coords[1],coords[2],coords[3],coords[4],arg1).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_ixyzc,arg1,
+ coords[1],coords[2],coords[3],coords[4],arg1).move_to(code);
+ } else if (coords[5]!=~0U) { // j[#_ind,off]++
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_joff,arg1,p1,coords[5],arg1).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_joff,arg1,coords[5],arg1).move_to(code);
+ } else if (coords[6]!=~0U) { // j(#_ind,_x,_y,_z,_c)++
+ if (p1!=~0U) CImg<uptrT>::vector((uptrT)mp_list_set_jxyzc,arg1,
+ p1,coords[6],coords[7],coords[8],coords[9],arg1).move_to(code);
+ else CImg<uptrT>::vector((uptrT)mp_set_jxyzc,arg1,
+ coords[6],coords[7],coords[8],coords[9],arg1).move_to(code);
+ }
+ if (p_coords && is_sth) std::memcpy(p_coords,coords,coords._width*sizeof(unsigned int));
+ } else if (mem(arg1,1)>=0) {
+ *se = saved_char;
+ if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1));
+ else variable_name.assign(ss,(unsigned int)(se1 - ss));
+ variable_name.back() = 0;
+ cimg::strellipsize(variable_name,64);
+ cimg::strellipsize(expr,64);
+ throw CImgArgumentException("[_cimg_math_parser] "
+ "CImg<%s>::%s(): Invalid %s-%s of non-variable '%s' in expression '%s%s%s'.",
+ pixel_type(),calling_function,
+ is_sth?"pre":"post",
+ is_sth?(*ss=='+'?"increment":"decrement"):
+ *se1=='+'?"increment":"decrement",
+ variable_name._data,
+ (ss - 8)>expr._data?"...":"",
+ (ss - 8)>expr._data?ss - 8:expr._data,
+ se<&expr.back()?"...":"");
+ }
+ _cimg_mp_return(pos);
+ }
+
+ // Array-like access to image values 'i[_#ind,offset,_boundary]' and 'j[_#ind,offset,_boundary]'.
+ if (*se1==']') {
+ is_sth = *ss=='j'; // is_relative?
+ if ((*ss=='i' || is_sth) && *ss1=='[') {
+ if (*ss2=='#') {
+ s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
+ p1 = compile(ss3,s0++);
+ } else { p1 = ~0U; s0 = ss2; }
+ s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ arg1 = compile(s0,s1);
+ arg2 = s1<se1?compile(s1 + 1,se1):~0U;
+ if (p_coords && arg2==~0U) { p_coords[is_sth?5:0] = arg1; p_coords[10] = p1; }
+ if (*ss2=='#') _cimg_mp_opcode3(is_sth?mp_list_joff:mp_list_ioff,p1,arg1,
+ arg2==~0U?reserved_label[30]:arg2);
+ _cimg_mp_opcode2(is_sth?mp_joff:mp_ioff,arg1,
+ arg2==~0U?0:arg2);
+ }
+ }
+
+ // Look for a function call or a parenthesis.
+ if (*se1==')') {
+ if (*ss=='(') _cimg_mp_return(compile(ss1,se1,p_coords)); // Simple parentheses.
+
+ // i(...) or j(...).
+ is_sth = *ss=='j'; // is_relative?
+ if ((*ss=='i' || is_sth) && *ss1=='(') {
+ if (*ss2=='#') {
+ s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
+ p1 = compile(ss3,s0++);
+ } else { p1 = ~0U; s0 = ss2; }
+ arg1 = is_sth?0U:(unsigned int)_cimg_mp_x;
+ arg2 = is_sth?0U:(unsigned int)_cimg_mp_y;
+ arg3 = is_sth?0U:(unsigned int)_cimg_mp_z;
+ arg4 = is_sth?0U:(unsigned int)_cimg_mp_c;
+ arg5 = arg6 = ~0U;
+ if (s0<se1) {
+ s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ arg1 = compile(s0,s1);
+ if (s1<se1) {
+ s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
+ arg2 = compile(s1,s2);
+ if (s2<se1) {
+ s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
+ arg3 = compile(s2,s3);
+ if (s3<se1) {
+ s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
+ arg4 = compile(s3,s2);
+ if (s2<se1) {
+ s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
+ arg5 = compile(s2,s3);
+ if (s3<se1) arg6 = compile(++s3,se1);
+ }
+ }
+ }
+ }
+ }
+ if (p_coords && arg5==~0U && arg6==~0U) {
+ if (is_sth) { p_coords[6] = arg1; p_coords[7] = arg2; p_coords[8] = arg3; p_coords[9] = arg4; }
+ else { p_coords[1] = arg1; p_coords[2] = arg2; p_coords[3] = arg3; p_coords[4] = arg4; }
+ p_coords[10] = p1;
+ }
+ if (*ss2=='#') _cimg_mp_opcode7(is_sth?mp_list_jxyzc:mp_list_ixyzc,p1,arg1,arg2,arg3,arg4,
+ arg5==~0U?reserved_label[29]:arg5,
+ arg6==~0U?reserved_label[30]:arg6);
+ _cimg_mp_opcode6(is_sth?mp_jxyzc:mp_ixyzc,arg1,arg2,arg3,arg4,
+ arg5==~0U?reserved_label[29]:arg5,
+ arg6==~0U?reserved_label[30]:arg6);
+ }
+
+ // Mathematical functions.
+ switch (*ss) {
+ case 'a' :
+ if (!std::strncmp(ss,"abs(",4)) { // Absolute value.
+ arg1 = compile(ss4,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(cimg::abs(mem[arg1]));
+ _cimg_mp_opcode1(mp_abs,arg1);
+ }
+
+ if (!std::strncmp(ss,"asin(",5)) { // Arcsin.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::asin(mem[arg1]));
+ _cimg_mp_opcode1(mp_asin,arg1);
+ }
+
+ if (!std::strncmp(ss,"acos(",5)) { // Arccos.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::acos(mem[arg1]));
+ _cimg_mp_opcode1(mp_acos,arg1);
+ }
+
+ if (!std::strncmp(ss,"atan(",5)) { // Arctan.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::atan(mem[arg1]));
+ _cimg_mp_opcode1(mp_atan,arg1);
+ }
+
+ if (!std::strncmp(ss,"atan2(",6)) { // Arctan2.
+ s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ arg1 = compile(ss6,s1); arg2 = compile(s1 + 1,se1);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(std::atan2(mem[arg1],mem[arg2]));
+ _cimg_mp_opcode2(mp_atan2,arg1,arg2);
+ }
+ break;
+
+ case 'c' :
+ if (!std::strncmp(ss,"cos(",4)) { // Cosine.
+ arg1 = compile(ss4,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::cos(mem[arg1]));
+ _cimg_mp_opcode1(mp_cos,arg1);
+ }
+
+ if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::cosh(mem[arg1]));
+ _cimg_mp_opcode1(mp_cosh,arg1);
+ }
+
+ if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::pow(mem[arg1],1.0/3));
+ _cimg_mp_opcode1(mp_cbrt,arg1);
+ }
+
+ if (!std::strncmp(ss,"cut(",4)) { // Cut.
+ s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ arg1 = compile(ss4,s1==se2?++s1:s1);
+ s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
+ arg2 = compile(s1 + 1,s2==se2?++s2:s2);
+ arg3 = compile(s2 + 1,se1);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0 && mem(arg3,1)>0) {
+ val = mem[arg1]; val1 = mem[arg2]; val2 = mem[arg3];
+ _cimg_mp_constant(val<val1?val1:val>val2?val2:val);
+ }
+ _cimg_mp_opcode3(mp_cut,arg1,arg2,arg3);
+ }
+ break;
+
+ case 'd' :
+ if (!std::strncmp(ss,"dowhile",7) && (*ss7=='(' || (*ss7==' ' && *ss8=='('))) { // Do..while
+ if (*ss7==' ') cimg::swap(*ss7,*ss8); // Allow space before opening brace.
+ s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ p1 = code._width; arg1 = compile(ss8,s1);
+ if (s1<se1) arg2 = compile(s1 + 1,se1);
+ else arg2 = arg1;
+ CImg<uptrT>::vector((uptrT)mp_dowhile,arg1,arg2,code._width - p1).
+ move_to(code,p1);
+ _cimg_mp_return(arg1);
+ }
+
+ if (!std::strncmp(ss,"date(",5)) { // Date and file date.
+ s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ arg1 = 0;
+ is_sth = s1!=se1; // is_fdate.
+ if (s1==se1 && ss5!=se1 && // Exactly one argument.
+ (cimg_sscanf(ss5,"%u%c",&arg1,&sep)!=2 || sep!=')')) is_sth = true;
+ if (is_sth) {
+ if (cimg_sscanf(ss5,"%u%c",&arg1,&sep)!=2 || sep!=',') { arg1 = 0; s1 = ss4; }
+ *se1 = 0; val = (double)cimg::fdate(s1 + 1,arg1); *se1 = ')';
+ } else val = (double)cimg::date(arg1);
+ _cimg_mp_constant(val);
+ }
+
+ if (!std::strncmp(ss,"debug(",6)) { // Print debug info.
+ p1 = code._width; arg1 = compile(ss6,se1,p_coords);
+ *se1 = 0;
+ ((CImg<uptrT>::vector((uptrT)mp_debug,arg1,code._width - p1),
+ CImg<uptrT>::string(ss6).unroll('y'))>'y').move_to(code,p1);
+ *se1 = ')';
+ _cimg_mp_return(arg1);
+ }
+ break;
+
+ case 'e' :
+ if (!std::strncmp(ss,"exp(",4)) { // Exponential.
+ arg1 = compile(ss4,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::exp(mem[arg1]));
+ _cimg_mp_opcode1(mp_exp,arg1);
+ }
+ break;
+
+ case 'f' :
+ if (*ss1=='o' && *ss2=='r' && (*ss3=='(' || (*ss3==' ' && *ss4=='('))) { // For..
+ if (*ss3==' ') cimg::swap(*ss3,*ss4); // Allow space before opening brace.
+ s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
+ s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
+ compile(ss4,s1);
+ p2 = code._width; arg2 = compile(s1 + 1,s2);
+ p3 = code._width;
+ if (s3<se1) { pos = compile(s3 + 1,se1); compile(s2 + 1,s3); } // Body + proc.
+ else pos = compile(s2 + 1,se1); // Proc only.
+ CImg<uptrT>::vector((uptrT)mp_whiledo,pos,arg2,p3 - p2,code._width - p3).
+ move_to(code,p2);
+ _cimg_mp_return(pos);
+ }
+ break;
+
+ case 'g' :
+ if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function.
+ s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ arg1 = compile(ss6,s1);
+ arg2 = 1;
+ if (s1<se1) {
+ s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
+ arg2 = compile(s1 + 1,s2==se2?++s2:s2);
+ }
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) {
+ val1 = mem[arg1]; val2 = mem[arg2];
+ _cimg_mp_constant(std::exp(-val1*val1/(2*val2*val2))/std::sqrt(2*val2*val2*cimg::PI));
+ }
+ _cimg_mp_opcode2(mp_gauss,arg1,arg2);
+ }
+ break;
+
+ case 'h' :
+ if (!std::strncmp(ss,"hypot(",6)) { // Hypothenuse.
+ s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ arg1 = compile(ss6,s1); arg2 = compile(s1 + 1,se1);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0) {
+ val1 = cimg::abs(mem[arg1]); val2 = cimg::abs(mem[arg2]);
+ if (val1<val2) { val = val1; val1 = val2; } else val = val2;
+ if (val1>0) { val/=val1; _cimg_mp_constant(val1*std::sqrt(1+val*val)); }
+ _cimg_mp_constant(0);
+ }
+ _cimg_mp_opcode2(mp_hypot,arg1,arg2);
+ }
+ break;
+
+ case 'i' :
+ if (!std::strncmp(ss,"int(",4)) { // Integer cast.
+ arg1 = compile(ss4,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant((long)mem[arg1]);
+ _cimg_mp_opcode1(mp_int,arg1);
+ }
+
+ if (*ss1=='f' && (*ss2=='(' || (*ss2==' ' && *ss3=='('))) { // If..then[..else.]
+ if (*ss2==' ') cimg::swap(*ss2,*ss3); // Allow space before opening brace.
+ s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
+ arg1 = compile(ss3,s1);
+ p2 = code._width; arg2 = compile(s1 + 1,s2);
+ p3 = code._width; arg3 = s2>=se1?0:compile(s2 + 1,se1);
+ if (mem(arg1,1)>0 && mem(arg2,1)>0 && mem(arg3,1)>0) _cimg_mp_constant(mem[arg1]?mem[arg2]:mem[arg3]);
+ if (mempos>=mem._width) mem.resize(-200,2,1,1,0);
+ pos = mempos++;
+ CImg<uptrT>::vector((uptrT)mp_if,pos,arg1,arg2,arg3,
+ p3 - p2,code._width - p3).move_to(code,p2);
+ _cimg_mp_return(pos);
+ }
+
+ if (!std::strncmp(ss,"init(",5)) { // Init.
+ if (ss0!=expr._data || code.width()) // (only allowed as the first instruction).
+ throw CImgArgumentException("[_cimg_math_parser] "
+ "CImg<%s>::%s(): Call to init() not done at the beginning "
+ "of expression '%s%s%s'.",
+ pixel_type(),calling_function,
+ (ss - 8)>expr._data?"...":"",
+ (ss - 8)>expr._data?ss - 8:expr._data,
+ se<&expr.back()?"...":"");
+ arg1 = compile(ss5,se1,p_coords);
+ init_size = code.width();
+ _cimg_mp_return(arg1);
+ }
+
+ if (*ss1=='s') { // Family of 'is_?()' functions.
+
+ if (!std::strncmp(ss,"isbool(",7)) { // Is boolean?
+ if (ss7==se1) _cimg_mp_return(0);
+ _cimg_mp_opcode1(mp_isbool,compile(ss7,se1));
+ }
+
+ if (!std::strncmp(ss,"isdir(",6)) { // Is directory?
+ *se1 = 0;
+ is_sth = cimg::is_directory(ss6);
+ *se1 = ')';
+ _cimg_mp_return(is_sth?1U:0U);
+ }
+
+ if (!std::strncmp(ss,"isfile(",7)) { // Is file?
+ *se1 = 0;
+ is_sth = cimg::is_file(ss7);
+ *se1 = ')';
+ _cimg_mp_return(is_sth?1U:0U);
+ }
+
+ if (!std::strncmp(ss,"isin(",5)) { // Is in sequence?
+ if (mempos>=mem._width) mem.resize(-200,2,1,1,0);
+ pos = mempos++;
+ CImg<uptrT>::vector((uptrT)mp_isin,pos).move_to(_opcode);
+ for (s = ss5; s<se; ++s) {
+ ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
+ (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
+ CImg<uptrT>::vector(compile(s,ns)).move_to(_opcode);
+ s = ns;
+ }
+ (_opcode>'y').move_to(code);
+ _cimg_mp_return(pos);
+ }
+
+ if (!std::strncmp(ss,"isinf(",6)) { // Is infinite?
+ if (ss6==se1) _cimg_mp_return(0);
+ _cimg_mp_opcode1(mp_isinf,compile(ss6,se1));
+ }
+
+ if (!std::strncmp(ss,"isint(",6)) { // Is integer?
+ if (ss6==se1) _cimg_mp_return(0);
+ _cimg_mp_opcode1(mp_isint,compile(ss6,se1));
+ }
+
+ if (!std::strncmp(ss,"isnan(",6)) { // Is Nan?
+ if (ss6==se1) _cimg_mp_return(0);
+ _cimg_mp_opcode1(mp_isnan,compile(ss6,se1));
+ }
+
+ if (!std::strncmp(ss,"isval(",6)) { // Is value?
+ val = 0;
+ if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
+ _cimg_mp_return(0);
+ }
+
+ }
+ break;
+
+ case 'l' :
+ if (!std::strncmp(ss,"log(",4)) { // Natural logarithm.
+ arg1 = compile(ss4,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::log(mem[arg1]));
+ _cimg_mp_opcode1(mp_log,arg1);
+ }
+
+ if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(cimg::log2(mem[arg1]));
+ _cimg_mp_opcode1(mp_log2,arg1);
+ }
+
+ if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm.
+ arg1 = compile(ss6,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::log10(mem[arg1]));
+ _cimg_mp_opcode1(mp_log10,arg1);
+ }
+ break;
+
+ case 'n' :
+ if (!std::strncmp(ss,"narg(",5)) { // Number of arguments.
+ if (*ss5==')') _cimg_mp_return(0);
+ arg1 = 0;
+ for (s = ss5; s<se; ++s) {
+ ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
+ (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
+ ++arg1; s = ns;
+ }
+ _cimg_mp_constant(arg1);
+ }
+
+ if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') ||
+ !std::strncmp(ss,"norminf(",8)) { // Lp norm.
+ if (mempos>=mem._width) mem.resize(-200,2,1,1,0);
+ pos = mempos++;
+ switch (arg1) {
+ case 0 : CImg<uptrT>::vector((uptrT)mp_norm0,pos).move_to(_opcode); break;
+ case 1 : CImg<uptrT>::vector((uptrT)mp_norm1,pos).move_to(_opcode); break;
+ case 2 : CImg<uptrT>::vector((uptrT)mp_norm2,pos).move_to(_opcode); break;
+ case ~0U : CImg<uptrT>::vector((uptrT)mp_norminf,pos).move_to(_opcode); break;
+ default :
+ CImg<uptrT>::vector((uptrT)mp_normp,pos,(uptrT)(arg1==~0U?-1:(int)arg1)).
+ move_to(_opcode);
+ }
+ for (s = std::strchr(ss5,'(') + 1; s<se; ++s) {
+ ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
+ (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
+ CImg<uptrT>::vector(compile(s,ns)).move_to(_opcode);
+ s = ns;
+ }
+ (_opcode>'y').move_to(code);
+ _cimg_mp_return(pos);
+ }
+ break;
+
+ case 'p' :
+ if (!std::strncmp(ss,"print(",6)) { // Print expression.
+ pos = compile(ss6,se1,p_coords);
+ *se1 = 0;
+ ((CImg<uptrT>::vector((uptrT)mp_print,pos),
+ CImg<uptrT>::string(ss6).unroll('y'))>'y').move_to(code);
+ *se1 = ')';
+ _cimg_mp_return(pos);
+ }
+ break;
+
+ case 'r' :
+ if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation.
+ s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
+ arg1 = compile(ss4,s1==se2?++s1:s1);
+ arg2 = 1;
+ if (s1<se1) {
+ s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
+ arg2 = compile(s1 + 1,se1);
+ }
+ if (mem(arg1,1)>0 && mem(arg2,1)>0)
+ _cimg_mp_constant(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]):
+ cimg::ror(mem[arg1],(unsigned int)mem[arg2]));
+ _cimg_mp_opcode2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
+ }
+
+ if (!std::strncmp(ss,"round(",6)) { // Value rounding.
+ s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ arg1 = compile(ss6,s1==se2?++s1:s1);
+ arg2 = 1; arg3 = 0;
+ if (s1<se1) {
+ s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
+ arg2 = compile(s1 + 1,s2==se2?++s2:s2);
+ if (s2<se1) arg3 = compile(s2 + 1,se1);
+ }
+ if (mem(arg1,1)>0 && mem(arg2,1)>0 && mem(arg3,1)>0)
+ _cimg_mp_constant(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3]));
+ _cimg_mp_opcode3(mp_round,arg1,arg2,arg3);
+ }
+ break;
+
+ case 's' :
+ if (!std::strncmp(ss,"sin(",4)) { // Sine.
+ arg1 = compile(ss4,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::sin(mem[arg1]));
+ _cimg_mp_opcode1(mp_sin,arg1);
+ }
+
+ if (!std::strncmp(ss,"sqr(",4)) { // Square.
+ arg1 = compile(ss4,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(cimg::sqr(mem[arg1]));
+ _cimg_mp_opcode1(mp_sqr,arg1);
+ }
+
+ if (!std::strncmp(ss,"sign(",5)) { // Sign.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(cimg::sign(mem[arg1]));
+ _cimg_mp_opcode1(mp_sign,arg1);
+ }
+
+ if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(cimg::sinc(mem[arg1]));
+ _cimg_mp_opcode1(mp_sinc,arg1);
+ }
+
+ if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::sinh(mem[arg1]));
+ _cimg_mp_opcode1(mp_sinh,arg1);
+ }
+
+ if (!std::strncmp(ss,"sqrt(",5)) { // Square root.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::sqrt(mem[arg1]));
+ _cimg_mp_opcode1(mp_sqrt,arg1);
+ }
+ break;
+
+ case 't' :
+ if (!std::strncmp(ss,"tan(",4)) { // Tangent.
+ arg1 = compile(ss4,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::tan(mem[arg1]));
+ _cimg_mp_opcode1(mp_tan,arg1);
+ }
+
+ if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent.
+ arg1 = compile(ss5,se1);
+ if (mem(arg1,1)>0) _cimg_mp_constant(std::tanh(mem[arg1]));
+ _cimg_mp_opcode1(mp_tanh,arg1);
+ }
+ break;
+
+ case 'u' :
+ if (*ss1=='(') { // Random value with uniform distribution.
+ if (*ss2==')') _cimg_mp_opcode2(mp_u,0,1);
+ s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ if (s1<se1) _cimg_mp_opcode2(mp_u,compile(ss2,s1),compile(s1 + 1,se1));
+ _cimg_mp_opcode2(mp_u,0,compile(ss2,s1));
+ }
+ break;
+
+ case 'w' :
+ if (!std::strncmp(ss,"whiledo",7) && (*ss7=='(' || (*ss7==' ' && *ss8=='('))) { // While...do
+ if (*ss7==' ') cimg::swap(*ss7,*ss8); // Allow space before opening brace.
+ s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+ p1 = code._width; arg1 = compile(ss8,s1);
+ p2 = code._width; arg2 = compile(s1 + 1,se1);
+ CImg<uptrT>::vector((uptrT)mp_whiledo,arg2,arg1,p2 - p1,code._width - p2).
+ move_to(code,p1);
+ _cimg_mp_return(arg2);
+ }
+ break;
+ }
+
+ if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) ||
+ !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) ||
+ !std::strncmp(ss,"arg(",4) ||
+ !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7)) { // Multi-argument functions.
+ if (mempos>=mem._width) mem.resize(-200,2,1,1,0);
+ pos = mempos++;
+ is_sth = *ss=='a' && ss[3]!='(';
+ CImg<uptrT>::vector((uptrT)(*ss=='a'?(ss[3]=='('?mp_arg:ss[4]=='i'?mp_argmin:mp_argmax):
+ *ss=='k'?mp_kth:ss[1]=='i'?mp_min:
+ ss[1]=='a'?mp_max:mp_med),pos).
+ move_to(_opcode);
+ for (s = is_sth?ss7:ss4; s<se; ++s) {
+ ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
+ (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
+ CImg<uptrT>::vector(compile(s,ns)).move_to(_opcode);
+ s = ns;
+ }
+ (_opcode>'y').move_to(code);
+ _cimg_mp_return(pos);
+ }
+
+ } // if (se1==')').
+
+ // Variables related to the input list of images.
+ if (*ss1=='#' && ss2<se) {
+ arg1 = compile(ss2,se);
+ p1 = (unsigned int)(listin._width && mem(arg1,1)>0?cimg::mod((int)mem[arg1],listin.width()):0);
+ switch (*ss) {
+ case 'w' : // w#ind
+ if (!listin) _cimg_mp_return(0);
+ if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._width);
+ _cimg_mp_opcode1(mp_list_width,arg1);
+ case 'h' : // h#ind
+ if (!listin) _cimg_mp_return(0);
+ if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._height);
+ _cimg_mp_opcode1(mp_list_height,arg1);
+ case 'd' : // d#ind
+ if (!listin) _cimg_mp_return(0);
+ if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._depth);
+ _cimg_mp_opcode1(mp_list_depth,arg1);
+ case 'r' : // r#ind
+ if (!listin) _cimg_mp_return(0);
+ if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._is_shared);
+ _cimg_mp_opcode1(mp_list_is_shared,arg1);
+ case 's' : // s#ind
+ if (!listin) _cimg_mp_return(0);
+ if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._spectrum);
+ _cimg_mp_opcode1(mp_list_spectrum,arg1);
+ case 'i' : // i#ind
+ if (!listin) _cimg_mp_return(0);
+ _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,_cimg_mp_c,
+ reserved_label[29],reserved_label[30]);
+ case 'R' : // R#ind
+ if (!listin) _cimg_mp_return(0);
+ _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,0,
+ reserved_label[29],reserved_label[30]);
+ case 'G' : // G#ind
+ if (!listin) _cimg_mp_return(0);
+ _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,1,
+ reserved_label[29],reserved_label[30]);
+ case 'B' : // B#ind
+ if (!listin) _cimg_mp_return(0);
+ _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,2,
+ reserved_label[29],reserved_label[30]);
+ case 'A' : // A#ind
+ if (!listin) _cimg_mp_return(0);
+ _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,3,
+ reserved_label[29],reserved_label[30]);
+ }
+ }
+
+ if (*ss1 && *ss2=='#' && ss3<se) {
+ arg1 = compile(ss3,se);
+ p1 = (unsigned int)(listin._width && mem(arg1,1)>0?cimg::mod((int)mem[arg1],listin.width()):0);
+ if (*ss=='w' && *ss1=='h') { // wh#ind
+ if (!listin) _cimg_mp_return(0);
+ if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._width*listin[p1]._height);
+ _cimg_mp_opcode1(mp_list_wh,arg1);
+ }
+ arg2 = ~0U;
+
+ if (*ss=='i') {
+ if (*ss1=='c') { // ic#ind
+ if (!listin) _cimg_mp_return(0);
+ if (mem(arg1,1)>0) {
+ if (!list_median) list_median.assign(listin._width);
+ if (!list_median[p1]) CImg<doubleT>::vector(listin[p1].median()).move_to(list_median[p1]);
+ _cimg_mp_constant(*list_median[p1]);
+ }
+ _cimg_mp_opcode1(mp_list_median,arg1);
+ }
+ if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind
+ if (!listin) _cimg_mp_return(0);
+ _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,*ss1 - '0',
+ reserved_label[29],reserved_label[30]);
+ }
+ switch (*ss1) {
+ case 'm' : arg2 = 0; break; // im#ind
+ case 'M' : arg2 = 1; break; // iM#ind
+ case 'a' : arg2 = 2; break; // ia#ind
+ case 'v' : arg2 = 3; break; // iv#ind
+ case 's' : arg2 = 12; break; // is#ind
+ case 'p' : arg2 = 13; break; // ip#ind
+ }
+ } else if (*ss1=='m') switch (*ss) {
+ case 'x' : arg2 = 4; break; // xm#ind
+ case 'y' : arg2 = 5; break; // ym#ind
+ case 'z' : arg2 = 6; break; // zm#ind
+ case 'c' : arg2 = 7; break; // cm#ind
+ } else if (*ss1=='M') switch (*ss) {
+ case 'x' : arg2 = 8; break; // xM#ind
+ case 'y' : arg2 = 9; break; // yM#ind
+ case 'z' : arg2 = 10; break; // zM#ind
+ case 'c' : arg2 = 11; break; // cM#ind
+ }
+ if (arg2!=~0U) {
+ if (!listin) _cimg_mp_return(0);
+ if (mem(arg1,1)>0) {
+ if (!list_stats) list_stats.assign(listin._width);
+ if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false);
+ _cimg_mp_constant(list_stats(p1,arg2));
+ }
+ _cimg_mp_opcode2(mp_list_stats,arg1,arg2);
+ }
+ }
+
+ if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
+ arg1 = compile(ss4,se);
+ if (!listin) _cimg_mp_return(0);
+ p1 = (unsigned int)(mem(arg1,1)>0?cimg::mod((int)mem[arg1],listin.width()):0);
+ if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth);
+ _cimg_mp_opcode1(mp_list_whd,arg1);
+ }
+ if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
+ arg1 = compile(ss5,se);
+ if (!listin) _cimg_mp_return(0);
+ p1 = (unsigned int)(mem(arg1,1)>0?cimg::mod((int)mem[arg1],listin.width()):0);
+ if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth*
+ listin[p1]._spectrum);
+ _cimg_mp_opcode1(mp_list_whds,arg1);
+ }
+
+ if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(reserved_label[29]); // interpolation
+ if (!std::strcmp(ss,"boundary")) _cimg_mp_return(reserved_label[30]); // boundary
+
+ // No known item found, assuming this is an already initialized variable.
+ variable_name.assign(ss,(unsigned int)(se - ss + 1)).back() = 0;
+ if (variable_name[1]) { // Multi-char variable.
+ cimglist_for(labelM,i) if (!std::strcmp(variable_name,labelM[i])) _cimg_mp_return(labelMpos[i]);
+ } else if (reserved_label[*variable_name]!=~0U) // Single-char variable.
+ _cimg_mp_return(reserved_label[*variable_name]);
+
+ // Reached an unknown item -> error.
+ is_sth = true; // is_valid_variable_name.
+ if (*variable_name>='0' && *variable_name<='9') is_sth = false;
+ else for (ns = variable_name._data + 1; *ns; ++ns)
+ if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') {
+ is_sth = false; break;
+ }
+
+ *se = saved_char;
+ cimg::strellipsize(variable_name,64);
+ cimg::strellipsize(expr,64);
+ if (is_sth) throw CImgArgumentException("[_cimg_math_parser] "
+ "CImg<%s>::%s(): Undefined variable '%s' in expression '%s%s%s'.",
+ pixel_type(),calling_function,
+ variable_name._data,
+ (ss - 8)>expr._data?"...":"",
+ (ss - 8)>expr._data?ss - 8:expr._data,
+ se<&expr.back()?"...":"");
+ else throw CImgArgumentException("[_cimg_math_parser] "
+ "CImg<%s>::%s(): Invalid item '%s' in expression '%s%s%s'.",
+ pixel_type(),calling_function,
+ variable_name._data,
+ (ss - 8)>expr._data?"...":"",
+ (ss - 8)>expr._data?ss - 8:expr._data,
+ se<&expr.back()?"...":"");
+ }
+
+ // Evaluation procedure.
+ double operator()(const double x, const double y, const double z, const double c) {
+ mem[_cimg_mp_x] = x; mem[_cimg_mp_y] = y; mem[_cimg_mp_z] = z; mem[_cimg_mp_c] = c;
+ for (p_code = p_code_begin; p_code<p_code_end; ++p_code) {
+ const CImg<uptrT> &op = *p_code;
+ // Allows to avoid parameter passing to evaluation functions.
+ opcode._data = op._data; opcode._height = op._height;
+ const uptrT target = opcode[1];
+ mem[target] = _cimg_mp_defunc(*this);
+ }
+ return *result;
+ }
+
+ // Insert constant value in memory.
+ unsigned int constant(const double val) {
+ if (val==(double)(int)val) {
+ if (val>=0 && val<=9) return (unsigned int)val;
+ if (val<0 && val>=-5) return (unsigned int)(10 - val);
+ }
+ if (val==0.5) return 16;
+ if (mempos>=mem._width) mem.resize(-200,2,1,1,0);
+ const unsigned int pos = mempos++;
+ mem[pos] = val; mem(pos,1) = 1; // Set constant property.
+ return pos;