From 2016fd6f5c3d0576722ff49e1aa34fdb69eaf8a9 Mon Sep 17 00:00:00 2001
From: Leon Styhre <leon@leonstyhre.com>
Date: Sat, 5 Aug 2023 10:49:38 +0200
Subject: [PATCH] Squashed 'external/CImg/' changes from 4d061dcd6..049267da1

049267da1 .
d974f83e1 .
143c6f781 .
8b641fa30 .
79ab52e6f .
593667dcf Auto-commit for release 3.2.6_pre
a5305a976 .
6fa06681b .
cdc9ee87a .
5f40398d1 Start work on next version 3.2.6.
78d1f0d7d Final release 3.2.5
620e4155c .
be214a006 .
1f9896c3a Add support for 'id#ind' in math parser.
44800102a Add support for 'id#ind' in math parser.
a6657d3d1 Auto-commit for release 3.2.5_pre
70e9a2cc3 .
a77287fe0 .
05fd7a869 .
81c22bb46 .
1f14f5eff .
bd1228f0f .
19e44e564 .
3f19980fc Start work on v.3.2.5.
504f5c653 .
26862961d Fix line width.
860b3747d Optimize cimg_math_parser::mp_vector_norm() by avoiding copies.
44eb192e9 .
c8d8710ee .
1cf8f5c27 .
7b6ec0809 .
99871e252 Simplify CImg<T>::draw_polygon() and CImg<T>::draw_line().
30ec22fde .
a2c50471e .
d2f63a9f8 Fix possible issue in 'CImg::draw_polygon()' (better fix).
e0675260c Fix possible issue in 'CImg::draw_polygon()'.
bb9980723 .
c431612a1 .
3606c5f5b Auto-commit for release 3.2.4_pre
d5d63e21f .
acea91b06 .
9a3c772fa .
ccb519bc0 .
9bf62d16b .
0936bc4b7 math evaluator: Add function 'unitnorm(V,p).
5236bd690 .
95ff538e1 .
3285740e9 .
7d01d9b70 Start work on 3.2.4
c7868e298 .
903cc805d .
f03139316 math_parser(): Add function 'c2o()' (coordinates to offset).
ff49ec20f math_parser(): Add function 'o2c()' (offset to coordinates).
6cbef2c1d Start working on version 3.2.3
ba9bb02a8 Final release 3.2.2
998d0157c .
1bb2d4f00 .
722646c42 Attempt to fix compilation on Windows.
2f3c64511 .
113a16334 .
e67bdf5d9 .
31fe9269c .
64839222d Better support of 'CImg<unsigned long long>' and 'CImg<long long>'.
32b3bb6ff Add specific cimg::type() traits for 'unsigned long long' and 'long long'.
806ef2861 Start work on 3.2.2
b558be730 Final release 3.2.1
85aa2ac2e .
bcafdc4d9 .
a6961a5bd .
33b78048e .
3c063517e .
9c76c1842 .
87863667a .
d60290f8d .
3b75fa436 .
f1815c88a .
6f575e662 .
5d6be92b4 Allow dynamic arrays to have larger size (32bits integers, i.e 2G elements).
c7d1772b8 .
977a1bb81 .
2cf046326 .
54d2b753c .
f4a0f772d .
d11a0380e .
4828190aa math_parser: Check that specified '#index' of functions are not NaN.
3b71fa0ce Function 'v2s()': Add option to 0-pad integer values.
652e6604b .
ea3197ce4 Fix #374
c88e03819 math_parser: Add function  that computes the square root of complex numbers.
b58775209 Add detection of the not operator in fast pre-evaluation of math expressions.
5e3585553 Add detection of the not operator in fast pre-evaluation of math expressions.
209c38da9 .
6ba3d0a11 Fix #319.
4e9412e8d Start work on next version 3.2.1
57028920e .
9772e0204 .
446c39411 Math evaluator: Add new function 'abort()'.
3c2ecee1f Make fast string comparisons more robust when expressions contain blank characters.
af9ecf8d0 Optimize evaluation of string comparisons.
edfcda038 .
00cab3cea .
a0af5312c .
6a92e46f2 .
7ae52c28d cimg_math_parser(): Add 'gamma()' function.
ad516d976 .
b71e2db76 .
a9633d4ed .
9d8406e57 .
103d23625 .
6349c771f .
bef50f9d8 .
391a24013 .
d4669fbbc .
5e77a9c7e .
86440098e .
6b9eee37c .
616484469 .
e79edfdb9 .
e616c2443 .
772fa4dca .

git-subtree-dir: external/CImg
git-subtree-split: 049267da171c50ab48f93c7285f096d7d686e8f3
---
 CImg.h                              | 1667 ++++++++++++++++++---------
 examples/CImg_demo.cpp              |    2 +-
 examples/image_surface3d.cpp        |    4 +-
 html/header.html                    |    8 +-
 html/header_doxygen.html            |    8 +-
 html/img/book_cimg_en.jpg           |  Bin 0 -> 36326 bytes
 html/index.html                     |   15 +-
 resources/compile_win_visualcpp.bat |    2 +-
 8 files changed, 1123 insertions(+), 583 deletions(-)
 create mode 100644 html/img/book_cimg_en.jpg

diff --git a/CImg.h b/CImg.h
index 9ac9175b0..3d07e7834 100644
--- a/CImg.h
+++ b/CImg.h
@@ -54,7 +54,7 @@
 
 // Set version number of the library.
 #ifndef cimg_version
-#define cimg_version 320
+#define cimg_version 326
 
 /*-----------------------------------------------------------
  #
@@ -2693,6 +2693,7 @@ namespace cimg_library {
       static T min() { return ~max(); }
       static T max() { return (T)1<<(8*sizeof(T) - 1); }
       static T inf() { return max(); }
+      static T nan() { return inf(); }
       static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
       static const char* format() { return "%s"; }
       static const char* format_s() { return "%s"; }
@@ -2711,6 +2712,7 @@ namespace cimg_library {
       static bool min() { return false; }
       static bool max() { return true; }
       static bool inf() { return max(); }
+      static bool nan() { return inf(); }
       static bool is_inf() { return false; }
       static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
       static const char* format() { return "%s"; }
@@ -2727,6 +2729,7 @@ namespace cimg_library {
       static unsigned char min() { return 0; }
       static unsigned char max() { return (unsigned char)-1; }
       static unsigned char inf() { return max(); }
+      static unsigned char nan() { return inf(); }
       static unsigned char cut(const double val) {
         return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
       static const char* format() { return "%u"; }
@@ -2744,6 +2747,7 @@ namespace cimg_library {
       static char min() { return 0; }
       static char max() { return (char)-1; }
       static char inf() { return max(); }
+      static char nan() { return inf(); }
       static char cut(const double val) {
         return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
       static const char* format() { return "%u"; }
@@ -2760,6 +2764,7 @@ namespace cimg_library {
       static char min() { return ~max(); }
       static char max() { return (char)((unsigned char)-1>>1); }
       static char inf() { return max(); }
+      static char nan() { return inf(); }
       static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
       static const char* format() { return "%d"; }
       static const char* format_s() { return "%d"; }
@@ -2776,6 +2781,7 @@ namespace cimg_library {
       static signed char min() { return ~max(); }
       static signed char max() { return (signed char)((unsigned char)-1>>1); }
       static signed char inf() { return max(); }
+      static signed char nan() { return inf(); }
       static signed char cut(const double val) {
         return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
       static const char* format() { return "%d"; }
@@ -2792,6 +2798,7 @@ namespace cimg_library {
       static unsigned short min() { return 0; }
       static unsigned short max() { return (unsigned short)-1; }
       static unsigned short inf() { return max(); }
+      static unsigned short nan() { return inf(); }
       static unsigned short cut(const double val) {
         return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
       static const char* format() { return "%u"; }
@@ -2808,6 +2815,7 @@ namespace cimg_library {
       static short min() { return ~max(); }
       static short max() { return (short)((unsigned short)-1>>1); }
       static short inf() { return max(); }
+      static short nan() { return inf(); }
       static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
       static const char* format() { return "%d"; }
       static const char* format_s() { return "%d"; }
@@ -2823,6 +2831,7 @@ namespace cimg_library {
       static unsigned int min() { return 0; }
       static unsigned int max() { return (unsigned int)-1; }
       static unsigned int inf() { return max(); }
+      static unsigned int nan() { return inf(); }
       static unsigned int cut(const double val) {
         return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
       static const char* format() { return "%u"; }
@@ -2839,6 +2848,7 @@ namespace cimg_library {
       static int min() { return ~max(); }
       static int max() { return (int)(~0U>>1); }
       static int inf() { return max(); }
+      static int nan() { return inf(); }
       static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
       static const char* format() { return "%d"; }
       static const char* format_s() { return "%d"; }
@@ -2854,6 +2864,7 @@ namespace cimg_library {
       static cimg_uint64 min() { return 0; }
       static cimg_uint64 max() { return (cimg_uint64)-1; }
       static cimg_uint64 inf() { return max(); }
+      static cimg_uint64 nan() { return inf(); }
       static cimg_uint64 cut(const double val) {
         return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; }
       static const char* format() { return cimg_fuint64; }
@@ -2870,6 +2881,7 @@ namespace cimg_library {
       static cimg_int64 min() { return ~max(); }
       static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); }
       static cimg_int64 inf() { return max(); }
+      static cimg_int64 nan() { return inf(); }
       static cimg_int64 cut(const double val) {
         return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
       }
@@ -2878,6 +2890,43 @@ namespace cimg_library {
       static long format(const long val) { return (long)val; }
     };
 
+#if !(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX)))
+    template<> struct type<unsigned long long> {
+      static const char* string() { static const char *const s = "uint64"; return s; }
+      static bool is_float() { return false; }
+      static bool is_inf(const cimg_uint64) { return false; }
+      static bool is_nan(const cimg_uint64) { return false; }
+      static bool is_finite(const cimg_uint64) { return true; }
+      static cimg_uint64 min() { return 0; }
+      static cimg_uint64 max() { return (cimg_uint64)-1; }
+      static cimg_uint64 inf() { return max(); }
+      static cimg_uint64 nan() { return inf(); }
+      static cimg_uint64 cut(const double val) {
+        return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; }
+      static const char* format() { return cimg_fuint64; }
+      static const char* format_s() { return cimg_fuint64; }
+      static cimg_uint64 format(const cimg_uint64 val) { return val; }
+    };
+
+    template<> struct type<long long> {
+      static const char* string() { static const char *const s = "int64"; return s; }
+      static bool is_float() { return false; }
+      static bool is_inf(const cimg_int64) { return false; }
+      static bool is_nan(const cimg_int64) { return false; }
+      static bool is_finite(const cimg_int64) { return true; }
+      static long long min() { return ~max(); }
+      static long long max() { return (cimg_int64)((cimg_uint64)-1>>1); }
+      static long long inf() { return max(); }
+      static long long nan() { return max(); }
+      static long long cut(const double val) {
+        return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
+      }
+      static const char* format() { return cimg_fint64; }
+      static const char* format_s() { return cimg_fint64; }
+      static long format(const long val) { return (long)val; }
+    };
+#endif
+
     template<> struct type<double> {
       static const char* string() { static const char *const s = "float64"; return s; }
       static bool is_float() { return true; }
@@ -3120,6 +3169,17 @@ namespace cimg_library {
     template<> struct superset<cimg_uint64,double> { typedef double type; };
     template<> struct superset<cimg_int64,float> { typedef double type; };
     template<> struct superset<cimg_int64,double> { typedef double type; };
+#if !(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX)))
+    template<> struct superset<unsigned long long,char> { typedef cimg_int64 type; };
+    template<> struct superset<unsigned long long,signed char> { typedef cimg_int64 type; };
+    template<> struct superset<unsigned long long,short> { typedef cimg_int64 type; };
+    template<> struct superset<unsigned long long,int> { typedef cimg_int64 type; };
+    template<> struct superset<unsigned long long,cimg_int64> { typedef cimg_int64 type; };
+    template<> struct superset<unsigned long long,float> { typedef double type; };
+    template<> struct superset<unsigned long long,double> { typedef double type; };
+    template<> struct superset<long long,float> { typedef double type; };
+    template<> struct superset<long long,double> { typedef double type; };
+#endif
     template<> struct superset<float,cimg_uint64> { typedef double type; };
     template<> struct superset<float,cimg_int64> { typedef double type; };
     template<> struct superset<float,double> { typedef double type; };
@@ -5883,21 +5943,21 @@ namespace cimg_library {
       return a;
     }
 
-    // Conversion functions to get more precision when trying to store unsigned ints values as floats.
-    inline unsigned int float2uint(const float f) {
+    // Conversion functions to get more precision when trying to store 'unsigned int' values as 'float'.
+    inline unsigned int float2uint(const float value) {
       int tmp = 0;
-      std::memcpy(&tmp,&f,sizeof(float));
-      if (tmp>=0) return (unsigned int)f;
+      std::memcpy(&tmp,&value,sizeof(float));
+      if (tmp>=0) return (unsigned int)value;
       unsigned int u;
       // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
-      std::memcpy(&u,&f,sizeof(float));
+      std::memcpy(&u,&value,sizeof(float));
       return ((u)<<2)>>2; // set sign & exponent bit to 0
     }
 
-    inline float uint2float(const unsigned int u) {
-      if (u<(1U<<19)) return (float)u;  // Consider safe storage of unsigned int as floats until 19bits (i.e 524287)
+    inline float uint2float(const unsigned int value) {
+      if (value<(1U<<19)) return (float)value; // Consider 'uint32' safely stored as floats until 19bits (i.e 524287)
       float f;
-      const unsigned int v = u|(3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1
+      const unsigned int v = value | (3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1
       // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
       std::memcpy(&f,&v,sizeof(float));
       return f;
@@ -6005,7 +6065,7 @@ namespace cimg_library {
     }
 
     inline void srand(cimg_uint64 *const p_rng) {
-#if cimg_OS==1
+#if cimg_OS==1 || defined(__BORLANDC__)
       *p_rng = cimg::time() + (cimg_uint64)getpid();
 #elif cimg_OS==2
       *p_rng = cimg::time() + (cimg_uint64)_getpid();
@@ -6312,7 +6372,10 @@ namespace cimg_library {
     **/
     template<typename T>
     inline T mod(const T& x, const T& m) {
-      if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
+      if (!m) {
+        if (cimg::type<T>::is_float()) return cimg::type<T>::nan();
+        else throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
+      }
       const double dx = (double)x, dm = (double)m;
       if (!cimg::type<double>::is_finite(dm)) return x;
       if (cimg::type<double>::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm));
@@ -6937,7 +7000,7 @@ namespace cimg_library {
     //! Ellipsize a string.
     /**
        \param str C-string.
-       \param l Max number of characters.
+       \param l Max number of printed characters.
        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
     **/
     inline char *strellipsize(char *const str, const unsigned int l=64,
@@ -6959,7 +7022,7 @@ namespace cimg_library {
     /**
        \param str C-string.
        \param res output C-string.
-       \param l Max number of characters.
+       \param l Max number of printed characters. String 'res' must be a size of at least 'l+1'.
        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
     **/
     inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64,
@@ -13096,7 +13159,7 @@ namespace cimg_library {
       const unsigned int omode = cimg::exception_mode();
       cimg::exception_mode(0);
       try {
-        _fill(expression,true,1,0,0,"operator=",0);
+        _fill(expression,true,3,0,0,"operator=",0,0);
       } catch (CImgException&) {
         cimg::exception_mode(omode);
         load(expression);
@@ -13171,7 +13234,7 @@ namespace cimg_library {
          instead of assigning them.
     **/
     CImg<T>& operator+=(const char *const expression) {
-      return *this+=(+*this)._fill(expression,true,1,0,0,"operator+=",this);
+      return *this+=(+*this)._fill(expression,true,3,0,0,"operator+=",this,0);
     }
 
     //! In-place addition operator.
@@ -13292,7 +13355,7 @@ namespace cimg_library {
        Similar to operator+=(const char*), except that it performs a subtraction instead of an addition.
      **/
     CImg<T>& operator-=(const char *const expression) {
-      return *this-=(+*this)._fill(expression,true,1,0,0,"operator-=",this);
+      return *this-=(+*this)._fill(expression,true,3,0,0,"operator-=",this,0);
     }
 
     //! In-place subtraction operator.
@@ -13396,7 +13459,7 @@ namespace cimg_library {
        Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
      **/
     CImg<T>& operator*=(const char *const expression) {
-      return mul((+*this)._fill(expression,true,1,0,0,"operator*=",this));
+      return mul((+*this)._fill(expression,true,3,0,0,"operator*=",this,0));
     }
 
     //! In-place multiplication operator.
@@ -13661,7 +13724,7 @@ namespace cimg_library {
        Similar to operator+=(const char*), except that it performs a division instead of an addition.
      **/
     CImg<T>& operator/=(const char *const expression) {
-      return div((+*this)._fill(expression,true,1,0,0,"operator/=",this));
+      return div((+*this)._fill(expression,true,3,0,0,"operator/=",this,0));
     }
 
     //! In-place division operator.
@@ -13725,7 +13788,7 @@ namespace cimg_library {
        Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
     **/
     CImg<T>& operator%=(const char *const expression) {
-      return *this%=(+*this)._fill(expression,true,1,0,0,"operator%=",this);
+      return *this%=(+*this)._fill(expression,true,3,0,0,"operator%=",this,0);
     }
 
     //! In-place modulo operator.
@@ -13791,7 +13854,7 @@ namespace cimg_library {
        Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
     **/
     CImg<T>& operator&=(const char *const expression) {
-      return *this&=(+*this)._fill(expression,true,1,0,0,"operator&=",this);
+      return *this&=(+*this)._fill(expression,true,3,0,0,"operator&=",this,0);
     }
 
     //! In-place bitwise AND operator.
@@ -13857,7 +13920,7 @@ namespace cimg_library {
        Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
     **/
     CImg<T>& operator|=(const char *const expression) {
-      return *this|=(+*this)._fill(expression,true,1,0,0,"operator|=",this);
+      return *this|=(+*this)._fill(expression,true,3,0,0,"operator|=",this,0);
     }
 
     //! In-place bitwise OR operator.
@@ -13927,7 +13990,7 @@ namespace cimg_library {
        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
     **/
     CImg<T>& operator^=(const char *const expression) {
-      return *this^=(+*this)._fill(expression,true,1,0,0,"operator^=",this);
+      return *this^=(+*this)._fill(expression,true,3,0,0,"operator^=",this,0);
     }
 
     //! In-place bitwise XOR operator.
@@ -13995,7 +14058,7 @@ namespace cimg_library {
        Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
     **/
     CImg<T>& operator<<=(const char *const expression) {
-      return *this<<=(+*this)._fill(expression,true,1,0,0,"operator<<=",this);
+      return *this<<=(+*this)._fill(expression,true,3,0,0,"operator<<=",this,0);
     }
 
     //! In-place bitwise left shift operator.
@@ -14062,7 +14125,7 @@ namespace cimg_library {
        Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
     **/
     CImg<T>& operator>>=(const char *const expression) {
-      return *this>>=(+*this)._fill(expression,true,1,0,0,"operator>>=",this);
+      return *this>>=(+*this)._fill(expression,true,3,0,0,"operator>>=",this,0);
     }
 
     //! In-place bitwise right shift operator.
@@ -14144,7 +14207,7 @@ namespace cimg_library {
        \param expression Value string describing the way pixel values are compared.
     **/
     bool operator==(const char *const expression) const {
-      return *this==(+*this)._fill(expression,true,1,0,0,"operator==",this);
+      return *this==(+*this)._fill(expression,true,3,0,0,"operator==",this,0);
     }
 
     //! Test if two images have the same size and values.
@@ -16788,7 +16851,7 @@ namespace cimg_library {
         if (*(ptrs++)!=(T)-128) ptrs+=2;
         else if ((ptrs+=3)<ptre) {
           const unsigned int
-            w = (unsigned int)*(ptrs - 3),
+            w = (unsigned int)cimg::float2uint((float)*(ptrs - 3)),
             h = (unsigned int)*(ptrs - 2),
             s = (unsigned int)*(ptrs - 1);
           if (!h && !s) {
@@ -16820,7 +16883,7 @@ namespace cimg_library {
       for (unsigned int o = 0; o<nb_primitives; ++o) {
         if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
           const unsigned int
-            w = (unsigned int)*(ptrs - 3),
+            w = (unsigned int)cimg::float2uint((float)*(ptrs - 3)),
             h = (unsigned int)*(ptrs - 2),
             s = (unsigned int)*(ptrs - 1);
           if (!h && !s) {
@@ -16885,10 +16948,10 @@ namespace cimg_library {
       CImgList<charT> variable_def, macro_def, macro_body;
       char *user_macro;
 
-      unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, result_dim, break_type,
-        constcache_size;
+      unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent,
+        result_dim, result_end_dim, break_type, constcache_size;
       bool is_parallelizable, is_noncritical_run, is_end_code, is_fill, return_new_comp, need_input_copy;
-      double *result;
+      double *result, *result_end;
       cimg_uint64 rng;
       const char *const calling_function, *s_op, *ss_op;
       typedef double (*mp_func)(_cimg_math_parser&);
@@ -16901,11 +16964,13 @@ namespace cimg_library {
 #define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN)
 #define _cimg_mp_calling_function s_calling_function()._data
 #define _cimg_mp_op(s) s_op = s; ss_op = ss
-#define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char)
 #define _cimg_mp_check_const_scalar(arg,n_arg,mode) check_const_scalar(arg,n_arg,mode,ss,se,saved_char)
 #define _cimg_mp_check_const_index(arg) check_const_index(arg,ss,se,saved_char)
-#define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char)
+#define _cimg_mp_check_notnan_index(arg) check_notnan_index(arg,ss,se,saved_char)
 #define _cimg_mp_check_list() check_list(ss,se,saved_char)
+#define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char)
+#define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char)
+
 #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp)
 #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; }
 #define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan)
@@ -16945,9 +17010,10 @@ namespace cimg_library {
         p_break((CImg<ulongT>*)(cimg_ulong)-2),imgin(img_input),
         imgout(img_output?*img_output:CImg<T>::empty()),imglist(list_images?*list_images:CImgList<T>::empty()),
         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0),
-        mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),break_type(0),
-        constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(_is_fill),need_input_copy(false),
-        rng((cimg::_rand(),cimg::rng())),calling_function(funcname?funcname:"cimg_math_parser") {
+        mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),result_end_dim(0),
+        break_type(0),constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(_is_fill),
+        need_input_copy(false),result_end(0),rng((cimg::_rand(),cimg::rng())),
+        calling_function(funcname?funcname:"cimg_math_parser") {
 
 #if cimg_use_openmp!=0
         rng+=omp_get_thread_num();
@@ -17026,11 +17092,11 @@ namespace cimg_library {
               fill(cimg::type<double>::nan());
           else if (ind_result!=_cimg_mp_slot_t) mem[ind_result] = cimg::type<double>::nan();
         }
+        if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1);
+        result_dim = _cimg_mp_size(ind_result);
+        result = mem._data + ind_result;
 
         // Free resources used for compiling expression and prepare evaluation.
-        result_dim = _cimg_mp_size(ind_result);
-        if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1);
-        result = mem._data + ind_result;
         memtype.assign();
         constcache_vals.assign();
         constcache_inds.assign();
@@ -17060,8 +17126,8 @@ namespace cimg_library {
         p_code_end(0),p_break((CImg<ulongT>*)(cimg_ulong)-2),
         imgin(CImg<T>::const_empty()),imgout(CImg<T>::empty()),imglist(CImgList<T>::empty()),
         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0),
-        result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(false),
-        need_input_copy(false),rng(0),calling_function(0) {
+        result_dim(0),result_end_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),
+        is_noncritical_run(false),is_fill(false),need_input_copy(false),result_end(0),rng(0),calling_function(0) {
         mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()()
         result = mem._data;
       }
@@ -17071,9 +17137,11 @@ namespace cimg_library {
         p_code_end(mp.p_code_end),p_break(mp.p_break),
         imgin(mp.imgin),imgout(mp.imgout),imglist(mp.imglist),
         img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm),
-        debug_indent(0),result_dim(mp.result_dim),break_type(0),constcache_size(0),
+        debug_indent(0),result_dim(mp.result_dim),result_end_dim(mp.result_end_dim),break_type(0),constcache_size(0),
         is_parallelizable(mp.is_parallelizable),is_noncritical_run(mp.is_noncritical_run),is_fill(mp.is_fill),
-        need_input_copy(mp.need_input_copy),result(mem._data + (mp.result - mp.mem._data)),
+        need_input_copy(mp.need_input_copy),
+        result(mem._data + (mp.result - mp.mem._data)),
+        result_end(mp.result_end?mem._data + (mp.result_end - mp.mem._data):0),
         rng((cimg::_rand(),cimg::rng())),calling_function(0) {
 
 #if cimg_use_openmp!=0
@@ -17261,22 +17329,23 @@ namespace cimg_library {
               _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 21,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 'd' : arg1 = 8; arg2 = 3; break; // id
-            case 's' : arg1 = 9; arg2 = 12; break; // is
-            case 'p' : arg1 = 10; arg2 = 13; break; // ip
             case 'c' : // ic
               if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]);
               if (mem_img_median==~0U) mem_img_median = imgin?const_scalar(imgin.median()):0;
               _cimg_mp_return(mem_img_median);
               break;
+            case 'd' : arg1 = 8; arg2 = 3; break; // id
+            case 'm' : arg1 = 4; arg2 = 0; break; // im
+            case 'M' : arg1 = 5; arg2 = 1; break; // iM
             case 'n' : // in
               if (reserved_label[12]!=~0U) _cimg_mp_return(reserved_label[12]);
-              if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude()):0;
+              if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude(2)):0;
               _cimg_mp_return(mem_img_norm);
+              break;
+            case 'p' : arg1 = 10; arg2 = 13; break; // ip
+            case 's' : arg1 = 9; arg2 = 12; break; // is
+            case 'v' : arg1 = 7; arg2 = 3; break; // iv
             }
           }
           else if (*ss1=='m') switch (*ss) {
@@ -17350,6 +17419,7 @@ namespace cimg_library {
                 if (*ss2=='#') { // Index specified
                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
                   p1 = compile(ss3,s0++,depth1,0,block_flags);
+                  _cimg_mp_check_notnan_index(p1);
                   _cimg_mp_check_list();
                 } else { p1 = ~0U; s0 = ss2; }
                 arg1 = compile(s0,ve1,depth1,0,block_flags); // Offset
@@ -17408,6 +17478,7 @@ namespace cimg_library {
                 if (*ss2=='#') { // Index specified
                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
                   p1 = compile(ss3,s0++,depth1,0,block_flags);
+                  _cimg_mp_check_notnan_index(p1);
                   _cimg_mp_check_list();
                 } else { p1 = ~0U; s0 = ss2; }
                 arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
@@ -18757,6 +18828,7 @@ namespace cimg_library {
               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
               p1 = compile(ss3,s0++,depth1,0,block_flags);
               _cimg_mp_check_const_index(p1);
+              _cimg_mp_check_notnan_index(p1);
               _cimg_mp_check_list();
             } else { p1 = ~0U; s0 = ss2; }
             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
@@ -18801,6 +18873,7 @@ namespace cimg_library {
             if (*ss2=='#') { // Index specified
               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
               p1 = compile(ss3,s0++,depth1,0,block_flags);
+              _cimg_mp_check_notnan_index(p1);
             } else { p1 = ~0U; s0 = ss2; }
             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
             arg1 = compile(s0,s1,depth1,0,block_flags); // Offset
@@ -18895,6 +18968,7 @@ namespace cimg_library {
               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
               p1 = compile(ss3,s0++,depth1,0,block_flags);
               _cimg_mp_check_const_index(p1);
+              _cimg_mp_check_notnan_index(p1);
               _cimg_mp_check_list();
             } else { p1 = ~0U; s0 = ss2; }
             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
@@ -18971,6 +19045,7 @@ namespace cimg_library {
             if (*ss2=='#') { // Index specified
               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
               p1 = compile(ss3,s0++,depth1,0,block_flags);
+              _cimg_mp_check_notnan_index(p1);
             } else { p1 = ~0U; s0 = ss2; }
             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
             arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
@@ -19050,6 +19125,17 @@ namespace cimg_library {
           switch (*ss) {
 
           case 'a' :
+
+#ifdef cimg_mp_func_abort
+            if (!std::strncmp(ss,"abort(",6)) { // Abort
+              _cimg_mp_op("Function 'abort()'");
+              if (pexpr[se2 - expr._data]=='(') { // no arguments?
+                CImg<ulongT>::vector((ulongT)mp_abort,_cimg_mp_slot_nan).move_to(code);
+                _cimg_mp_return_nan();
+              }
+            }
+#endif
+
             if (!std::strncmp(ss,"abs(",4)) { // Absolute value
               _cimg_mp_op("Function 'abs()'");
               arg1 = compile(ss4,se1,depth1,0,block_flags);
@@ -19219,6 +19305,45 @@ namespace cimg_library {
             break;
 
           case 'c' :
+            if (!std::strncmp(ss,"c2o(",4)) { // Coordinates to offset
+              _cimg_mp_op("Function 'c2o()'");
+              if (*ss4=='#') { // Index specified
+                s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
+                p1 = compile(ss5,s0++,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
+                _cimg_mp_check_list();
+              } else { p1 = ~0U; s0 = ss4; }
+              s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+              pos = compile(s0,s1,depth1,0,block_flags);
+              p2 = _cimg_mp_size(pos);
+              if (p2) { // Coordinates specified as a vector
+                if (s1!=se1) compile(s0,se1,depth1,0,block_flags); // -> Error too much arguments
+                if (p2>4) {
+                  *s1 = 0; s1 = s0; _cimg_mp_strerr;
+                  throw CImgArgumentException("[" cimg_appname "_math_parser] "
+                                              "CImg<%s>::%s: %s: Argument '%s' is a vector of size %u (should be <=4), "
+                                              "in expression '%s'.",
+                                              pixel_type(),_cimg_mp_calling_function,s_op,s1,p2,s0);
+                }
+                arg1 = pos + 1;
+                arg2 = p2>1?pos + 2:0;
+                arg3 = p2>2?pos + 3:0;
+                arg4 = p2>3?pos + 4:0;
+              } else { // Coordinates specified as scalars
+                arg1 = pos; arg2 = arg3 = arg4 = 0;
+                if (s1<se1) {
+                  s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
+                  arg2 = compile(s1,s0,depth1,0,block_flags);
+                  if (s0<se1) {
+                    s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+                    arg3 = compile(s0,s1,depth1,0,block_flags);
+                    arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):0;
+                  } else arg3 = 0;
+                } else arg2 = 0;
+              }
+              _cimg_mp_scalar5(mp_c2o,p1,arg1,arg2,arg3,arg4);
+            }
+
             if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value
               _cimg_mp_op("Function 'cabs()'");
               arg1 = compile(ss5,se1,depth1,0,block_flags);
@@ -19306,6 +19431,17 @@ namespace cimg_library {
               _cimg_mp_return(pos);
             }
 
+            if (!std::strncmp(ss,"csqrt(",6)) { // Complex square root
+              _cimg_mp_op("Function 'csqrt()'");
+              arg1 = compile(ss6,se1,depth1,0,block_flags);
+              _cimg_mp_check_type(arg1,0,3,2);
+              pos = vector(2);
+              if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_sqrt,pos,arg1,0).move_to(code);
+              else CImg<ulongT>::vector((ulongT)mp_complex_sqrt,pos,arg1 + 1,arg1 + 2).move_to(code);
+              return_new_comp = true;
+              _cimg_mp_return(pos);
+            }
+
             if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent
               _cimg_mp_op("Function 'ctan()'");
               arg1 = compile(ss5,se1,depth1,0,block_flags);
@@ -19446,12 +19582,14 @@ namespace cimg_library {
             if (!std::strncmp(ss,"crop(",5)) { // Image or vector crop
               _cimg_mp_op("Function 'crop()'");
               is_sth = false; // is image crop ?
+              arg1 = 0;
               if (*ss5=='#') { // Index specified
                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
                 p1 = compile(ss6,s0++,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
+                _cimg_mp_check_list();
                 pos = 2;
                 is_sth = true;
-                _cimg_mp_check_list();
               } else { p1 = ~0U; s0 = ss5; need_input_copy = true; pos = 1; }
               if (s0<se1) for (s = s0; s<se; ++s, ++pos) {
                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
@@ -19822,6 +19960,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 'd()'");
               if (*ss2=='#') { // Index specified
                 p1 = compile(ss3,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss2!=se1) break; p1 = ~0U; }
               _cimg_mp_scalar1(mp_image_d,p1);
@@ -19836,6 +19975,7 @@ namespace cimg_library {
               if (*s0=='#') { // Index specified
                 s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
                 p1 = compile(s0,s1++,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
               } else { p1 = 11; s1 = s0; }
               _cimg_mp_check_list();
               _cimg_mp_check_const_scalar(p1,1,1);
@@ -19856,6 +19996,7 @@ namespace cimg_library {
               if (*s0=='#') { // Index specified
                 s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
                 p1 = compile(s0,s1++,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
               } else { p1 = 11; s1 = s0; }
               _cimg_mp_check_list();
 
@@ -19896,6 +20037,7 @@ namespace cimg_library {
               if (*s0=='#') { // Index specified
                 s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
                 p1 = compile(s0,s1++,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
               } else { p1 = 11; s1 = s0; }
               _cimg_mp_check_list();
               CImg<ulongT>::vector((ulongT)mp_da_freeze,_cimg_mp_slot_nan,p1).move_to(code);
@@ -19908,6 +20050,7 @@ namespace cimg_library {
               if (ss[10]=='#') { // Index specified
                 s0 = ss + 11; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
                 p1 = compile(ss + 11,s0++,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
               } else { p1 = 11; s0 = ss + 10; }
               _cimg_mp_check_list();
 
@@ -19927,6 +20070,7 @@ namespace cimg_library {
               if (ss[8]=='#') { // Index specified
                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
                 p1 = compile(ss + 9,s0++,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
               } else { p1 = 11; s0 = ss + 8; }
               _cimg_mp_check_list();
               _cimg_mp_scalar1(mp_da_size,p1);
@@ -20091,6 +20235,7 @@ namespace cimg_library {
               if (*ss5=='#') { // Index specified
                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
                 p1 = compile(ss6,s0++,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { p1 = ~0U; s0 = ss5; }
 
@@ -20349,12 +20494,12 @@ namespace cimg_library {
               if (*ss8=='#') { // Index specified
                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
                 p1 = compile(ss + 9,s0++,depth1,0,block_flags);
-                pos = 2;
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
-              } else { p1 = ~0U; s0 = ss8; pos = 1; }
+              } else { p1 = ~0U; s0 = ss8; }
               if (s0==se1) compile(s0,se1,depth1,0,block_flags); // 'missing' argument error
               CImg<ulongT>::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
-              for (s = s0; s<se; ++s, ++pos) {
+              for (s = s0; s<se; ++s) {
                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
                 arg2 = compile(s,ns,depth1,0,block_flags);
@@ -20449,8 +20594,10 @@ namespace cimg_library {
               if (s1!=se1) {
                 const bool is_inside_end = (bool)(block_flags&8);
                 if (!is_inside_end) code.swap(code_end);
-                compile(s1,se1,depth1,p_ref,8);
+                pos = compile(s1,se1,depth1,p_ref,8);
                 if (!is_inside_end) code.swap(code_end);
+                result_end_dim = _cimg_mp_size(pos);
+                result_end = mem._data + pos;
                 is_end_code = true;
               }
               _cimg_mp_return_nan();
@@ -20558,6 +20705,7 @@ namespace cimg_library {
               s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
               if (*ss5=='#') { // Index specified
                 p1 = compile(ss6,s0,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
                 arg1 = ~0U;
               } else { // Vector specified
@@ -20639,6 +20787,16 @@ namespace cimg_library {
             break;
 
           case 'g' :
+#if cimg_use_cpp11==1
+            if (!std::strncmp(ss,"gamma(",6)) { // Gamma
+              _cimg_mp_op("Function 'gamma()'");
+              arg1 = compile(ss6,se1,depth1,0,block_flags);
+              if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_gamma,arg1);
+              if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tgamma(mem[arg1]));
+              _cimg_mp_scalar1(mp_gamma,arg1);
+            }
+#endif
+
             if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function
               _cimg_mp_op("Function 'gauss()'");
               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
@@ -20710,6 +20868,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 'h()'");
               if (*ss2=='#') { // Index specified
                 p1 = compile(ss3,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss2!=se1) break; p1 = ~0U; }
               _cimg_mp_scalar1(mp_image_h,p1);
@@ -20721,6 +20880,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 'ic()'");
               if (*ss3=='#') { // Index specified
                 p1 = compile(ss4,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss3!=se1) break; p1 = ~0U; }
               pos = scalar();
@@ -20733,6 +20893,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 'in()'");
               if (*ss3=='#') { // Index specified
                 p1 = compile(ss4,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss3!=se1) break; p1 = ~0U; }
               pos = scalar();
@@ -21265,8 +21426,9 @@ namespace cimg_library {
               if (*ss5=='#') { // Index specified
                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
                 p1 = compile(ss6,s0++,depth1,0,block_flags);
-                is_sth = true; // is_index_specified?
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
+                is_sth = true; // is_index_specified?
               } else { s0 = ss5; p1 = get_mem_img_index(); is_sth = false; }
               arg1 = s0<se1?compile(s0,se1,depth1,0,block_flags):~0U;
               if (arg1!=~0U) {
@@ -21292,51 +21454,69 @@ namespace cimg_library {
               _cimg_mp_const_scalar((double)arg1);
             }
 
-            if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') ||
-                !std::strncmp(ss,"norminf(",8) || !std::strncmp(ss,"norm(",5) ||
-                (!std::strncmp(ss,"norm",4) && ss5<se1 && (s=std::strchr(ss5,'('))!=0)) { // Lp norm
-              _cimg_mp_op("Function 'normP()'");
-              if (*ss4=='(') { arg1 = 2; s = ss5; }
-              else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; }
-              else if (arg1==~0U) {
-                arg1 = compile(ss4,s++,depth1,0,block_flags);
-                _cimg_mp_check_const_scalar(arg1,0,2);
-                arg1 = (unsigned int)mem[arg1];
-              } else s = std::strchr(ss4,'(') + 1;
+            if (!std::strncmp(ss,"normp(",6)) { // Lp norm, with variable argument p.
+              _cimg_mp_op("Function 'normp()'");
+              s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+              arg1 = compile(ss6,s1,depth1,0,block_flags);
+              arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):2;
+              _cimg_mp_check_type(arg2,0,1,0);
+              p1 = _cimg_mp_size(arg1);
+              _cimg_mp_scalar3(mp_vector_normp,arg1,p1,arg2);
+            }
+
+            if (!std::strncmp(ss,"norm",4) && ss4<se1 && (s = std::strchr(ss4,'('))!=0) { // Lp norm (constant p)
+              _cimg_mp_op("Function 'norm()'");
+              arg1 = s!=ss4?compile(ss4,s,depth1,0,block_flags):2;
+              _cimg_mp_check_const_scalar(arg1,0,0);
+              val = mem[arg1];
               is_sth = true; // Tell if all arguments are constant
-              pos = scalar();
-              switch (arg1) {
-              case 0 : op = mp_norm0; CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode); break;
-              case 1 : op = mp_norm1; CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode); break;
-              case 2 : op = mp_norm2; CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode); break;
-              case ~0U : op = mp_norminf; CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode); break;
-              default : op = mp_normp; CImg<ulongT>::vector((ulongT)op,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)).
-                                         move_to(l_opcode);
-              }
-              for ( ; s<se; ++s) {
+              CImg<ulongT>::vector(0,0,0,arg1).move_to(l_opcode);
+              for (++s; s<se; ++s) {
                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
                 arg2 = compile(s,ns,depth1,0,block_flags);
                 if (_cimg_mp_is_vector(arg2))
-                  CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
-                                         arg2 + (ulongT)_cimg_mp_size(arg2)).
+                  CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,arg2 + (ulongT)_cimg_mp_size(arg2)).
                     move_to(l_opcode);
                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
                 is_sth&=_cimg_mp_is_const_scalar(arg2);
                 s = ns;
               }
-
               (l_opcode>'y').move_to(opcode);
+              op = val==2?_mp_vector_norm2:val==1?_mp_vector_norm1:!val?_mp_vector_norm0:
+                cimg::type<double>::is_inf(val)?_mp_vector_norminf:_mp_vector_normp;
+              opcode[0] = (ulongT)op;
               opcode[2] = opcode._height;
               if (is_sth) _cimg_mp_const_scalar(op(*this));
-              if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1
-                _cimg_mp_scalar1(mp_abs,opcode[3]);
+              if (opcode._height==5) { // Single argument
+                if (arg1) { _cimg_mp_scalar1(mp_abs,opcode[4]); }
+                else { _cimg_mp_scalar2(mp_neq,opcode[4],0); }
+              }
+              opcode[1] = pos = scalar();
               opcode.move_to(code);
               return_new_comp = true;
               _cimg_mp_return(pos);
             }
             break;
 
+          case 'o' :
+            if (!std::strncmp(ss,"o2c(",4)) { // Offset to coordinates
+              _cimg_mp_op("Function 'o2c()'");
+              if (*ss4=='#') { // Index specified
+                s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
+                p1 = compile(ss5,s0++,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
+                _cimg_mp_check_list();
+              } else { p1 = ~0U; s0 = ss4; }
+              arg1 = compile(s0,se1,depth1,0,block_flags);
+              _cimg_mp_check_type(arg1,1,1,0);
+              pos = vector(4);
+              CImg<ulongT>::vector((ulongT)mp_o2c,pos,p1,arg1).move_to(code);
+              return_new_comp = true;
+              _cimg_mp_return(pos);
+            }
+            break;
+
           case 'p' :
             if (!std::strncmp(ss,"permut(",7)) { // Number of permutations
               _cimg_mp_op("Function 'permut()'");
@@ -21359,8 +21539,9 @@ namespace cimg_library {
               if (*ss8=='#') { // Index specified
                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
                 p1 = compile(ss + 9,s0++,depth1,0,block_flags);
-                pos = 2;
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
+                pos = 2;
               } else { p1 = ~0U; s0 = ss8; pos = 1; }
               if (s0==se1) compile(s0,se1,depth1,0,block_flags); // 'missing' argument error
               CImg<ulongT>::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
@@ -21391,6 +21572,7 @@ namespace cimg_library {
               _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'");
               if (!is_sth && *s0=='#') { // Image
                 p1 = compile(ss7,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
                 CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code);
                 _cimg_mp_return_nan();
@@ -21735,6 +21917,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 's()'");
               if (*ss2=='#') { // Index specified
                 p1 = compile(ss3,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss2!=se1) break; p1 = ~0U; }
               _cimg_mp_scalar1(mp_image_s,p1);
@@ -21921,6 +22104,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 'stats()'");
               if (*ss6=='#') { // Index specified
                 p1 = compile(ss7,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss6!=se1) break; p1 = ~0U; }
               pos = vector(14);
@@ -22280,6 +22464,26 @@ namespace cimg_library {
               _cimg_mp_scalar1(mp_ui2f,arg1);
             }
 
+            if (!std::strncmp(ss,"unitnorm(",9)) { // Normalize vector to unit norm
+              _cimg_mp_op("Function 'unitnorm()'");
+              s0 = ss + 9;
+              s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
+              arg1 = compile(s0,s1,depth1,0,block_flags);
+              arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):2;
+              _cimg_mp_check_type(arg2,0,1,0);
+              p1 = _cimg_mp_size(arg1);
+              if (p1>0) pos = is_comp_vector(arg1)?arg1:((return_new_comp = true), vector(p1));
+              else {
+                pos = scalar();
+                if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) {
+                  val = mem[arg1];
+                  _cimg_mp_const_scalar(val?(mem[arg2]?1:val):0);
+                }
+              }
+              CImg<ulongT>::vector((ulongT)mp_vector_unitnorm,pos,arg1,p1,arg2).move_to(code);
+              _cimg_mp_return(pos);
+            }
+
             if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable
               _cimg_mp_op("Function 'unref()'");
               arg1=~0U;
@@ -22445,6 +22649,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 'w()'");
               if (*ss2=='#') { // Index specified
                 p1 = compile(ss3,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss2!=se1) break; p1 = ~0U; }
               _cimg_mp_scalar1(mp_image_w,p1);
@@ -22454,6 +22659,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 'wh()'");
               if (*ss3=='#') { // Index specified
                 p1 = compile(ss4,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss3!=se1) break; p1 = ~0U; }
               _cimg_mp_scalar1(mp_image_wh,p1);
@@ -22463,6 +22669,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 'whd()'");
               if (*ss4=='#') { // Index specified
                 p1 = compile(ss5,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss4!=se1) break; p1 = ~0U; }
               _cimg_mp_scalar1(mp_image_whd,p1);
@@ -22472,6 +22679,7 @@ namespace cimg_library {
               _cimg_mp_op("Function 'whds()'");
               if (*ss5=='#') { // Index specified
                 p1 = compile(ss6,se1,depth1,0,block_flags);
+                _cimg_mp_check_notnan_index(p1);
                 _cimg_mp_check_list();
               } else { if (ss5!=se1) break; p1 = ~0U; }
               _cimg_mp_scalar1(mp_image_whds,p1);
@@ -22547,7 +22755,7 @@ namespace cimg_library {
               *ss=='v'?mp_var:
               ss[1]=='i'?(ss[3]=='('?mp_min:mp_minabs):
               ss[1]=='a'?(ss[3]=='('?mp_max:mp_maxabs):
-              mp_median;
+              mp_med;
             is_sth = true; // Tell if all arguments are constant
             pos = scalar();
             CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode);
@@ -22555,11 +22763,8 @@ namespace cimg_library {
               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
               arg2 = compile(s,ns,depth1,0,block_flags);
-              if (_cimg_mp_is_vector(arg2))
-                CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
-                                       arg2 + (ulongT)_cimg_mp_size(arg2)).
-                  move_to(l_opcode);
-              else CImg<ulongT>::vector(arg2).move_to(l_opcode);
+              if (_cimg_mp_is_vector(arg2)) CImg<ulongT>::vector(arg2 + 1,_cimg_mp_size(arg2)).move_to(l_opcode);
+              else CImg<ulongT>::vector(arg2,1).move_to(l_opcode);
               is_sth&=_cimg_mp_is_const_scalar(arg2);
               s = ns;
             }
@@ -22752,6 +22957,7 @@ namespace cimg_library {
         // Variables related to the input list of images.
         if (*ss1=='#' && ss2<se) {
           arg1 = compile(ss2,se,depth1,0,block_flags);
+          _cimg_mp_check_notnan_index(arg1);
           p1 = (unsigned int)(imglist._width && _cimg_mp_is_const_scalar(arg1)?
                               cimg::mod((int)mem[arg1],imglist.width()):~0U);
           switch (*ss) {
@@ -22807,6 +23013,7 @@ namespace cimg_library {
 
         if (*ss1 && *ss2=='#' && ss3<se) {
           arg1 = compile(ss3,se,depth1,0,block_flags);
+          _cimg_mp_check_notnan_index(arg1);
           p1 = (unsigned int)(imglist._width && _cimg_mp_is_const_scalar(arg1)?
                               cimg::mod((int)mem[arg1],imglist.width()):~0U);
           if (*ss=='w' && *ss1=='h') { // wh#ind
@@ -22830,26 +23037,36 @@ namespace cimg_library {
                 if (!list_median[p1]) CImg<doubleT>::vector(imglist[p1].median()).move_to(list_median[p1]);
                 _cimg_mp_const_scalar(*list_median[p1]);
               }
-              _cimg_mp_scalar1(mp_list_median,arg1);
+              _cimg_mp_scalar1(mp_list_id,arg1);
+            }
+
+            if (*ss1=='d') { // id#ind
+              if (!imglist) _cimg_mp_return(0);
+              if (_cimg_mp_is_const_scalar(arg1)) {
+                if (!list_stats) list_stats.assign(imglist._width);
+                if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(imglist[p1].get_stats(),false);
+                _cimg_mp_const_scalar(std::sqrt(list_stats(p1,3)));
+              }
+              _cimg_mp_scalar1(mp_list_id,arg1);
             }
 
             if (*ss1=='n') { // in#ind
               if (!imglist) _cimg_mp_return(0);
               if (_cimg_mp_is_const_scalar(arg1)) {
                 if (!list_norm) list_norm.assign(imglist._width);
-                if (!list_norm[p1]) CImg<doubleT>::vector(imglist[p1].magnitude()).move_to(list_norm[p1]);
+                if (!list_norm[p1]) CImg<doubleT>::vector(imglist[p1].magnitude(2)).move_to(list_norm[p1]);
                 _cimg_mp_const_scalar(*list_norm[p1]);
               }
               _cimg_mp_scalar1(mp_list_norm,arg1);
             }
 
             switch (*ss1) {
+            case 'a' : arg2 = 2; break; // ia#ind
             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
+            case 's' : arg2 = 12; break; // is#ind
+            case 'v' : arg2 = 3; break; // iv#ind
             }
           } else if (*ss1=='m') switch (*ss) {
             case 'x' : arg2 = 4; break; // xm#ind
@@ -22875,6 +23092,7 @@ namespace cimg_library {
 
         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
           arg1 = compile(ss4,se,depth1,0,block_flags);
+          _cimg_mp_check_notnan_index(arg1);
           if (!imglist) _cimg_mp_return(0);
           p1 = (unsigned int)(_cimg_mp_is_const_scalar(arg1)?cimg::mod((int)mem[arg1],imglist.width()):~0U);
           if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._width*imglist[p1]._height*imglist[p1]._depth);
@@ -22882,6 +23100,7 @@ namespace cimg_library {
         }
         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
           arg1 = compile(ss5,se,depth1,0,block_flags);
+          _cimg_mp_check_notnan_index(arg1);
           if (!imglist) _cimg_mp_return(0);
           p1 = (unsigned int)(_cimg_mp_is_const_scalar(arg1)?cimg::mod((int)mem[arg1],imglist.width()):~0U);
           if (p1!=~0U)
@@ -23312,6 +23531,18 @@ namespace cimg_library {
         }
       }
 
+      // Check that specified constant is not nan.
+      void check_notnan_index(const unsigned int arg,
+                              char *const ss, char *const se, const char saved_char) {
+        if (arg!=~0U &&
+            (arg==_cimg_mp_slot_nan || (_cimg_mp_is_const_scalar(arg) && cimg::type<double>::is_nan(mem[arg])))) {
+          char *s0; _cimg_mp_strerr;
+          throw CImgArgumentException("[" cimg_appname "_math_parser] "
+                                      "CImg<%s>::%s: %s%s Specified index is NaN.",
+                                      pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"");
+        }
+      }
+
       // Check a matrix is square.
       void check_matrix_square(const unsigned int arg, const unsigned int n_arg,
                                char *const ss, char *const se, const char saved_char) {
@@ -23736,6 +23967,14 @@ namespace cimg_library {
 #endif
 #define _mp_arg(x) mp.mem[mp.opcode[x]]
 
+#ifdef cimg_mp_func_abort
+      static double mp_abort(_cimg_math_parser& mp) {
+        cimg::unused(mp);
+        cimg_mp_func_abort();
+        return cimg::type<double>::nan();
+      }
+#endif
+
       static double mp_abs(_cimg_math_parser& mp) {
         return cimg::abs(_mp_arg(2));
       }
@@ -23792,53 +24031,105 @@ namespace cimg_library {
 
       static double mp_argkth(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        const double val = mp_kth(mp);
-        for (unsigned int i = 4; i<i_end; ++i) if (val==_mp_arg(i)) return i - 3.;
-        return 1.;
+        CImg<double> values;
+        if (i_end==5) values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Only a single argument
+        else {
+          unsigned int siz = 0;
+          for (unsigned int i = 4; i<i_end; i+=2) siz+=(unsigned int)mp.opcode[i];
+          values.assign(siz);
+          double *ptr = values;
+          for (unsigned int i = 3; i<i_end; i+=2) {
+            const unsigned int len = (unsigned int)mp.opcode[i + 1];
+            if (len>1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double));
+            else *ptr = _mp_arg(i);
+            ptr+=len;
+          }
+        }
+        longT ind = (longT)cimg::round(_mp_arg(3));
+        ++values._data; --values._width; // Skip first value
+        if (ind<0) ind+=values.width() + 1;
+        ind = cimg::cut(ind,(longT)1,(longT)values.width());
+        const double kth = values.kth_smallest((ulongT)(ind - 1));
+        --values._data; ++values._width;
+        for (unsigned int argkth = 1; argkth<values._width; ++argkth)
+          if (values[argkth]==kth) return argkth;
+        return cimg::type<double>::nan();
       }
 
       static double mp_argmin(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3);
-        unsigned int argval = 0;
-        for (unsigned int i = 4; i<i_end; ++i) {
-          const double _val = _mp_arg(i);
-          if (_val<val) { val = _val; argval = i - 3; }
+        double val, valmin = cimg::type<double>::inf();
+        unsigned int siz = 0, argmin = 0;
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val<valmin) { valmin = val; argmin = siz + k; } }
+          } else { val = _mp_arg(i); if (val<valmin) { valmin = val; argmin = siz; } }
+          siz+=len;
         }
-        return (double)argval;
+        return (double)argmin;
       }
 
       static double mp_argminabs(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3), absval = cimg::abs(val);
-        unsigned int argval = 0;
-        for (unsigned int i = 4; i<i_end; ++i) {
-          const double _val = _mp_arg(i), _absval = cimg::abs(_val);
-          if (_absval<absval) { val = _val; absval = _absval; argval = i - 3; }
+        double val, abs_val, abs_valminabs = cimg::type<double>::inf();
+        unsigned int siz = 0, argminabs = 0;
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) {
+              val = *(ptr++);
+              abs_val = cimg::abs(val);
+              if (abs_val<abs_valminabs) { abs_valminabs = abs_val; argminabs = siz + k; }
+            }
+          } else {
+            val = _mp_arg(i);
+            abs_val = cimg::abs(val);
+            if (abs_val<abs_valminabs) { abs_valminabs = abs_val; argminabs = siz; }
+          }
+          siz+=len;
         }
-        return (double)argval;
+        return (double)argminabs;
       }
 
       static double mp_argmax(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3);
-        unsigned int argval = 0;
-        for (unsigned int i = 4; i<i_end; ++i) {
-          const double _val = _mp_arg(i);
-          if (_val>val) { val = _val; argval = i - 3; }
+        double val, valmax = -cimg::type<double>::inf();
+        unsigned int siz = 0, argmax = 0;
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val>valmax) { valmax = val; argmax = siz + k; } }
+          } else { val = _mp_arg(i); if (val>valmax) { valmax = val; argmax = siz; } }
+          siz+=len;
         }
-        return (double)argval;
+        return (double)argmax;
       }
 
       static double mp_argmaxabs(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3), absval = cimg::abs(val);
-        unsigned int argval = 0;
-        for (unsigned int i = 4; i<i_end; ++i) {
-          const double _val = _mp_arg(i), _absval = cimg::abs(_val);
-          if (_absval>absval) { val = _val; absval = _absval; argval = i - 3; }
+        double val, abs_val, abs_valmaxabs = 0;
+        unsigned int siz = 0, argmaxabs = 0;
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) {
+              val = *(ptr++);
+              abs_val = cimg::abs(val);
+              if (abs_val>abs_valmaxabs) { abs_valmaxabs = abs_val; argmaxabs = siz + k; }
+            }
+          } else {
+            val = _mp_arg(i);
+            abs_val = cimg::abs(val);
+            if (abs_val>abs_valmaxabs) { abs_valmaxabs = abs_val; argmaxabs = siz; }
+          }
+          siz+=len;
         }
-        return (double)argval;
+        return (double)argmaxabs;
       }
 
       static double mp_asin(_cimg_math_parser& mp) {
@@ -23855,9 +24146,17 @@ namespace cimg_library {
 
       static double mp_avg(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3);
-        for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
-        return val/(i_end - 3);
+        unsigned int siz = 0;
+        double sum = 0;
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) sum+=*(ptr++);
+          } else sum+=_mp_arg(i);
+          siz+=len;
+        }
+        return sum/siz;
       }
 
       static double mp_bitwise_and(_cimg_math_parser& mp) {
@@ -23902,30 +24201,18 @@ namespace cimg_library {
         return cimg::type<double>::nan();
       }
 
-#ifdef cimg_mp_func_run
-      static double mp_run(_cimg_math_parser& mp) {
-        const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
-        CImgList<charT> _str;
-        CImg<charT> it;
-        for (unsigned int n = 0; n<nb_args; ++n) {
-          const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
-          if (siz) { // Vector argument -> string
-            const double *ptr = &_mp_arg(3 + 2*n) + 1;
-            unsigned int l = 0;
-            while (l<siz && ptr[l]) ++l;
-            CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
-          } else { // Scalar argument -> number
-            it.assign(24);
-            cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
-            CImg<charT>::string(it,false,true).move_to(_str);
-          }
-        }
-        CImg(1,1,1,1,0).move_to(_str);
-        CImg<charT> str = _str>'x';
-        cimg_mp_func_run(str._data);
-        return cimg::type<double>::nan();
+      static double mp_c2o(_cimg_math_parser& mp) {
+        mp_check_list(mp,"c2o");
+        unsigned int ind = (unsigned int)mp.opcode[2];
+        if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
+        const CImg<T> &img = ind==~0U?mp.imgin:mp.imglist[ind];
+        const int
+          x = (int)_mp_arg(3),
+          y = (int)_mp_arg(4),
+          z = (int)_mp_arg(5),
+          c = (int)_mp_arg(6);
+        return (double)img.offset(x,y,z,c);
       }
-#endif
 
       static double mp_cbrt(_cimg_math_parser& mp) {
         return cimg::cbrt(_mp_arg(2));
@@ -24073,6 +24360,17 @@ namespace cimg_library {
         return cimg::type<double>::nan();
       }
 
+      static double mp_complex_sqrt(_cimg_math_parser& mp) {
+        const double
+          real = _mp_arg(2), imag = _mp_arg(3),
+          r = std::sqrt(cimg::_hypot(real,imag)),
+          theta = std::atan2(imag,real)/2;
+        double *ptrd = &_mp_arg(1) + 1;
+        ptrd[0] = r*std::cos(theta);
+        ptrd[1] = r*std::sin(theta);
+        return cimg::type<double>::nan();
+      }
+
       static double mp_complex_tan(_cimg_math_parser& mp) {
         const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag);
         double *ptrd = &_mp_arg(1) + 1;
@@ -24292,7 +24590,7 @@ namespace cimg_library {
         mp_check_list(mp,s_op);
         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
         CImg<T> &img = mp.imglist[ind];
-        int siz = img?(int)img[img._height - 1]:0;
+        int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
         if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
                                       "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
@@ -24313,7 +24611,7 @@ namespace cimg_library {
           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
         CImg<T> &img = mp.imglist[ind];
         const int
-          siz = img?(int)img[img._height - 1]:0,
+          siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0,
           pos0 = mp.opcode[3]==~0U?siz:(int)_mp_arg(3),
           pos = pos0<0?pos0 + siz:pos0;
 
@@ -24345,7 +24643,7 @@ namespace cimg_library {
             double *ptrs = &_mp_arg(6 + k) + 1;
             cimg_forC(img,c) img(0,pos + k,0,c) = ptrs[c];
           }
-        img[img._height - 1] = (T)(siz + nb_elts);
+        img[img._height - 1] = (T)cimg::uint2float(siz + nb_elts);
         return cimg::type<double>::nan();
       }
 
@@ -24353,7 +24651,7 @@ namespace cimg_library {
         mp_check_list(mp,"da_remove");
         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
         CImg<T> &img = mp.imglist[ind];
-        int siz = img?(int)img[img._height - 1]:0;
+        int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
         if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
                                       "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
@@ -24379,7 +24677,7 @@ namespace cimg_library {
         siz-=end - start + 1;
         if (img.height()>32 && siz<2*img.height()/3) // Reduce size of dynamic array
           img.resize(1,std::max(2*siz + 1,32),1,-100,0);
-        img[img._height - 1] = (T)siz;
+        img[img._height - 1] = (T)cimg::uint2float(siz);
         return cimg::type<double>::nan();
       }
 
@@ -24387,7 +24685,7 @@ namespace cimg_library {
         mp_check_list(mp,"da_size");
         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
         CImg<T> &img = mp.imglist[ind];
-        const int siz = img?(int)img[img._height - 1]:0;
+        const int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
         if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_size()': "
                                       "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
@@ -24618,10 +24916,7 @@ namespace cimg_library {
         mp_check_list(mp,"ellipse");
         const unsigned int i_end = (unsigned int)mp.opcode[2];
         unsigned int ind = (unsigned int)mp.opcode[3];
-        if (ind!=~0U) {
-          if (!mp.imglist.width()) return cimg::type<double>::nan();
-          ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
-        }
+        if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
         CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
         CImg<T> color(img._spectrum,1,1,1,0);
         bool is_invalid_arguments = false, is_outlined = false;
@@ -24911,6 +25206,12 @@ namespace cimg_library {
         return cimg::grand(&mp.rng);
       }
 
+#if cimg_use_cpp11==1
+      static double mp_gamma(_cimg_math_parser& mp) {
+        return std::tgamma(_mp_arg(2));
+      }
+#endif
+
       static double mp_gauss(_cimg_math_parser& mp) {
         const double x = _mp_arg(2), s = _mp_arg(3);
         return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1);
@@ -25081,7 +25382,7 @@ namespace cimg_library {
           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
         }
         const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
-        return (double)img.magnitude();
+        return (double)img.magnitude(2);
       }
 
       static double mp_image_print(_cimg_math_parser& mp) {
@@ -25538,13 +25839,27 @@ namespace cimg_library {
 
       static double mp_kth(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        CImg<doubleT> vals(i_end - 4);
-        double *p = vals.data();
-        for (unsigned int i = 4; i<i_end; ++i) *(p++) = _mp_arg(i);
-        longT ind = (longT)cimg::round(_mp_arg(3));
-        if (ind<0) ind+=vals.width() + 1;
-        ind = cimg::cut(ind,(longT)1,(longT)vals.width());
-        return vals.kth_smallest((ulongT)(ind - 1));
+        CImg<double> values;
+        if (i_end==5) values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Only a single argument
+        else {
+          unsigned int siz = 0;
+          for (unsigned int i = 4; i<i_end; i+=2) siz+=(unsigned int)mp.opcode[i];
+          values.assign(siz);
+          double *ptr = values;
+          for (unsigned int i = 3; i<i_end; i+=2) {
+            const unsigned int len = (unsigned int)mp.opcode[i + 1];
+            if (len>1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double));
+            else *ptr = _mp_arg(i);
+            ptr+=len;
+          }
+        }
+        longT ind = (longT)values[0];
+        ++values._data; --values._width; // Skip first value
+        if (ind<0) ind+=values.width() + 1;
+        ind = cimg::cut(ind,(longT)1,(longT)values.width());
+        const double &kth = values.kth_smallest((ulongT)(ind - 1));
+        --values._data; ++values._width;
+        return kth;
       }
 
       static double mp_lerp(_cimg_math_parser& mp) {
@@ -25872,10 +26187,27 @@ namespace cimg_library {
       static double mp_list_norm(_cimg_math_parser& mp) {
         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
         if (!mp.list_norm) mp.list_norm.assign(mp.imglist._width);
-        if (!mp.list_norm[ind]) CImg<doubleT>::vector(mp.imglist[ind].magnitude()).move_to(mp.list_norm[ind]);
+        if (!mp.list_norm[ind]) CImg<doubleT>::vector(mp.imglist[ind].magnitude(2)).move_to(mp.list_norm[ind]);
         return *mp.list_norm[ind];
       }
 
+      static double mp_list_id(_cimg_math_parser& mp) {
+        const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
+        bool get_stats = false;
+        cimg::mutex(13);
+        if (!mp.list_stats || mp.list_stats.size()!=mp.imglist._width) mp.list_stats.assign(mp.imglist._width);
+        if (!mp.list_stats[ind]) get_stats = true;
+        cimg::mutex(13,0);
+
+        if (get_stats) {
+          CImg<Tdouble> st = mp.imglist[ind].get_stats();
+          cimg::mutex(13);
+          st.move_to(mp.list_stats[ind]);
+          cimg::mutex(13,0);
+        }
+        return std::sqrt(mp.list_stats(ind,3));
+      }
+
       static double mp_list_set_ioff(_cimg_math_parser& mp) {
         if (!mp.imglist.width()) return cimg::type<double>::nan();
         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
@@ -26497,19 +26829,36 @@ namespace cimg_library {
 
       static double mp_max(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3);
-        for (unsigned int i = 4; i<i_end; ++i) val = std::max(val,_mp_arg(i));
-        return val;
+        double val, valmax = -cimg::type<double>::inf();
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val>valmax) valmax = val; }
+          } else { val = _mp_arg(i); if (val>valmax) valmax = val; }
+        }
+        return valmax;
       }
 
       static double mp_maxabs(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3), absval = cimg::abs(val);
-        for (unsigned int i = 4; i<i_end; ++i) {
-          const double _val = _mp_arg(i), _absval = cimg::abs(_val);
-          if (_absval>absval) { val = _val; absval = _absval; }
+        double val, abs_val, valmaxabs = 0, abs_valmaxabs = 0;
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) {
+              val = *(ptr++);
+              abs_val = cimg::abs(val);
+              if (abs_val>abs_valmaxabs) { valmaxabs = val; abs_valmaxabs = abs_val; }
+            }
+          } else {
+            val = _mp_arg(i);
+            abs_val = cimg::abs(val);
+            if (abs_val>abs_valmaxabs) { valmaxabs = val; abs_valmaxabs = abs_val; }
+          }
         }
-        return val;
+        return valmaxabs;
       }
 
       static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref,
@@ -26624,42 +26973,61 @@ namespace cimg_library {
 
       static double mp_min(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3);
-        for (unsigned int i = 4; i<i_end; ++i) val = std::min(val,_mp_arg(i));
-        return val;
+        double val, valmin = cimg::type<double>::inf();
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val<valmin) valmin = val; }
+          } else { val = _mp_arg(i); if (val<valmin) valmin = val; }
+        }
+        return valmin;
       }
 
       static double mp_minabs(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3), absval = cimg::abs(val);
-        for (unsigned int i = 4; i<i_end; ++i) {
-          const double _val = _mp_arg(i), _absval = cimg::abs(_val);
-          if (_absval<absval) { val = _val; absval = _absval; }
+        double val, abs_val, valminabs = cimg::type<double>::inf(), abs_valminabs = cimg::type<double>::inf();
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) {
+              val = *(ptr++);
+              abs_val = cimg::abs(val);
+              if (abs_val<abs_valminabs) { valminabs = val; abs_valminabs = abs_val; }
+            }
+          } else {
+            val = _mp_arg(i);
+            abs_val = cimg::abs(val);
+            if (abs_val<abs_valminabs) { valminabs = val; abs_valminabs = abs_val; }
+          }
         }
-        return val;
+        return valminabs;
       }
 
       static double mp_minus(_cimg_math_parser& mp) {
         return -_mp_arg(2);
       }
 
-      static double mp_median(_cimg_math_parser& mp) {
+      static double mp_med(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        switch (i_end - 3) {
-        case 1 : return _mp_arg(3);
-        case 2 : return cimg::median(_mp_arg(3),_mp_arg(4));
-        case 3 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5));
-        case 5 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7));
-        case 7 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9));
-        case 9 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9),
-                                     _mp_arg(10),_mp_arg(11));
-        case 13 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9),
-                                      _mp_arg(10),_mp_arg(11),_mp_arg(12),_mp_arg(13),_mp_arg(14),_mp_arg(15));
+        CImg<double> values;
+        if (i_end==5) { // Only a single argument
+          if ((unsigned)mp.opcode[4]==1) return _mp_arg(3); // Real value
+          else values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Vector value
+        } else {
+          unsigned int siz = 0;
+          for (unsigned int i = 4; i<i_end; i+=2) siz+=(unsigned int)mp.opcode[i];
+          values.assign(siz);
+          double *ptr = values;
+          for (unsigned int i = 3; i<i_end; i+=2) {
+            const unsigned int len = (unsigned int)mp.opcode[i + 1];
+            if (len>1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double));
+            else *ptr = _mp_arg(i);
+            ptr+=len;
+          }
         }
-        CImg<doubleT> vals(i_end - 3);
-        double *p = vals.data();
-        for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
-        return vals.median();
+        return values.median();
       }
 
       static double mp_modulo(_cimg_math_parser& mp) {
@@ -26707,65 +27075,25 @@ namespace cimg_library {
         return (double)(_mp_arg(2)!=_mp_arg(3));
       }
 
-      static double mp_norm0(_cimg_math_parser& mp) {
-        const unsigned int i_end = (unsigned int)mp.opcode[2];
-        switch (i_end - 3) {
-        case 1 : return _mp_arg(3)!=0;
-        case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0);
+      static double mp_o2c(_cimg_math_parser& mp) {
+        mp_check_list(mp,"o2c");
+        unsigned int ind = (unsigned int)mp.opcode[2];
+        if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
+        const CImg<T> &img = ind==~0U?mp.imgin:mp.imglist[ind];
+        longT offset = (longT)_mp_arg(3);
+        double *ptrd = &_mp_arg(1) + 1;
+        if (!img)
+          ptrd[0] = ptrd[1] = ptrd[2] = ptrd[3] = cimg::type<double>::nan();
+        else {
+          *(ptrd++) = (double)(offset%img.width());
+          offset/=img.width();
+          *(ptrd++) = (double)(offset%img.height());
+          offset/=img.height();
+          *(ptrd++) = (double)(offset%img.depth());
+          offset/=img.depth();
+          *ptrd = (double)(offset%img.spectrum());
         }
-        double res = 0;
-        for (unsigned int i = 3; i<i_end; ++i)
-          res+=_mp_arg(i)==0?0:1;
-        return res;
-      }
-
-      static double mp_norm1(_cimg_math_parser& mp) {
-        const unsigned int i_end = (unsigned int)mp.opcode[2];
-        switch (i_end - 3) {
-        case 1 : return cimg::abs(_mp_arg(3));
-        case 2 : return cimg::abs(_mp_arg(3)) + cimg::abs(_mp_arg(4));
-        }
-        double res = 0;
-        for (unsigned int i = 3; i<i_end; ++i)
-          res+=cimg::abs(_mp_arg(i));
-        return res;
-      }
-
-      static double mp_norm2(_cimg_math_parser& mp) {
-        const unsigned int i_end = (unsigned int)mp.opcode[2];
-        switch (i_end - 3) {
-        case 1 : return cimg::abs(_mp_arg(3));
-        case 2 : return cimg::_hypot(_mp_arg(3),_mp_arg(4));
-        }
-        double res = 0;
-        for (unsigned int i = 3; i<i_end; ++i)
-          res+=cimg::sqr(_mp_arg(i));
-        return std::sqrt(res);
-      }
-
-      static double mp_norminf(_cimg_math_parser& mp) {
-        const unsigned int i_end = (unsigned int)mp.opcode[2];
-        switch (i_end - 3) {
-        case 1 : return cimg::abs(_mp_arg(3));
-        case 2 : return std::max(cimg::abs(_mp_arg(3)),cimg::abs(_mp_arg(4)));
-        }
-        double res = 0;
-        for (unsigned int i = 3; i<i_end; ++i) {
-          const double val = cimg::abs(_mp_arg(i));
-          if (val>res) res = val;
-        }
-        return res;
-      }
-
-      static double mp_normp(_cimg_math_parser& mp) {
-        const unsigned int i_end = (unsigned int)mp.opcode[2];
-        if (i_end==4) return cimg::abs(_mp_arg(3));
-        const double p = (double)mp.opcode[3];
-        double res = 0;
-        for (unsigned int i = 4; i<i_end; ++i)
-          res+=std::pow(cimg::abs(_mp_arg(i)),p);
-        res = std::pow(res,1/p);
-        return res>0?res:0.;
+        return cimg::type<double>::nan();
       }
 
       static double mp_permutations(_cimg_math_parser& mp) {
@@ -26862,9 +27190,15 @@ namespace cimg_library {
 
       static double mp_prod(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3);
-        for (unsigned int i = 4; i<i_end; ++i) val*=_mp_arg(i);
-        return val;
+        double prod = 1;
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) prod*=*(ptr++);
+          } else prod*=_mp_arg(i);
+        }
+        return prod;
       }
 
       static double mp_rad2deg(_cimg_math_parser& mp) {
@@ -26872,7 +27206,7 @@ namespace cimg_library {
       }
 
       static double mp_repeat(_cimg_math_parser& mp) {
-        const double nb_it = _mp_arg(2);
+        const double nb_it = _mp_arg(2), nb_itm1 = nb_it - 1;
         double
           *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
           *const ptrs = &_mp_arg(1);
@@ -26880,13 +27214,13 @@ namespace cimg_library {
           *const p_body = ++mp.p_code,
           *const p_end = p_body + mp.opcode[4];
 
-        if (nb_it>0) {
+        if (nb_it>=1) {
           const unsigned int _break_type = mp.break_type;
           mp.break_type = 0;
 
           double it = 0;
           if (ptrc) { // Version with loop variable (3 arguments)
-            while (it<nb_it) {
+            while (it<=nb_itm1) {
               *ptrc = it;
               for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
                 mp.opcode._data = mp.p_code->_data;
@@ -26898,7 +27232,7 @@ namespace cimg_library {
             }
             *ptrc = it;
           } else // Version without loop variable (2 arguments)
-            while (it<nb_it) {
+            while (it<=nb_itm1) {
               for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
                 mp.opcode._data = mp.p_code->_data;
                 const ulongT target = mp.opcode[1];
@@ -26950,6 +27284,31 @@ namespace cimg_library {
         return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));
       }
 
+#ifdef cimg_mp_func_run
+      static double mp_run(_cimg_math_parser& mp) {
+        const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
+        CImgList<charT> _str;
+        CImg<charT> it;
+        for (unsigned int n = 0; n<nb_args; ++n) {
+          const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
+          if (siz) { // Vector argument -> string
+            const double *ptr = &_mp_arg(3 + 2*n) + 1;
+            unsigned int l = 0;
+            while (l<siz && ptr[l]) ++l;
+            CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
+          } else { // Scalar argument -> number
+            it.assign(24);
+            cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
+            CImg<charT>::string(it,false,true).move_to(_str);
+          }
+        }
+        CImg(1,1,1,1,0).move_to(_str);
+        CImg<charT> str = _str>'x';
+        cimg_mp_func_run(str._data);
+        return cimg::type<double>::nan();
+      }
+#endif
+
       static double mp_self_add(_cimg_math_parser& mp) {
         return _mp_arg(1)+=_mp_arg(2);
       }
@@ -27307,11 +27666,7 @@ namespace cimg_library {
       }
 
       static double mp_std(_cimg_math_parser& mp) {
-        const unsigned int i_end = (unsigned int)mp.opcode[2];
-        CImg<doubleT> vals(i_end - 3);
-        double *p = vals.data();
-        for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
-        return std::sqrt(vals.variance());
+        return std::sqrt(mp_var(mp));
       }
 
       static double mp_string_init(_cimg_math_parser& mp) {
@@ -27426,9 +27781,15 @@ namespace cimg_library {
 
       static double mp_sum(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        double val = _mp_arg(3);
-        for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
-        return val;
+        double sum = 0;
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) sum+=*(ptr++);
+          } else sum+=_mp_arg(i);
+        }
+        return sum;
       }
 
       static double mp_swap(_cimg_math_parser& mp) {
@@ -27495,10 +27856,17 @@ namespace cimg_library {
 
       static double mp_var(_cimg_math_parser& mp) {
         const unsigned int i_end = (unsigned int)mp.opcode[2];
-        CImg<doubleT> vals(i_end - 3);
-        double *p = vals.data();
-        for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
-        return vals.variance();
+        unsigned int siz = 0;
+        double val, S = 0, S2 = 0;
+        for (unsigned int i = 3; i<i_end; i+=2) {
+          const unsigned int len = (unsigned int)mp.opcode[i + 1];
+          if (len>1) {
+            const double *ptr = &_mp_arg(i);
+            for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); S+=val; S2+=val*val; }
+          } else { val = _mp_arg(i); S+=val; S2+=val*val; }
+          siz+=len;
+        }
+        return (S2 - S*S/siz)/(siz - 1);
       }
 
       static double mp_vector_copy(_cimg_math_parser& mp) {
@@ -27743,6 +28111,75 @@ namespace cimg_library {
         return !mp_vector_eq(mp);
       }
 
+      static double _mp_vector_norm0(_cimg_math_parser& mp) {
+        const unsigned int siz = (unsigned int)mp.opcode[2];
+        double res = 0;
+        for (unsigned int i = siz - 1; i>3; --i) res+=(double)(_mp_arg(i)?1:0);
+        return res;
+      }
+
+      static double _mp_vector_norm1(_cimg_math_parser& mp) {
+        const unsigned int siz = (unsigned int)mp.opcode[2];
+        double res = 0;
+        for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::abs(_mp_arg(i));
+        return res;
+      }
+
+      static double _mp_vector_norm2(_cimg_math_parser& mp) {
+        const unsigned int siz = (unsigned int)mp.opcode[2];
+        double res = 0;
+        for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::sqr(_mp_arg(i));
+        return (double)std::sqrt(res);
+      }
+
+      static double _mp_vector_norminf(_cimg_math_parser& mp) {
+        const unsigned int siz = (unsigned int)mp.opcode[2];
+        double res = 0;
+        for (unsigned int i = siz - 1; i>3; --i) {
+          const double val = (double)cimg::abs(_mp_arg(i));
+          if (val>res) res = val;
+        }
+        return res;
+      }
+
+      static double _mp_vector_normp(_cimg_math_parser& mp) {
+        const unsigned int siz = (unsigned int)mp.opcode[2];
+        const double p = _mp_arg(3);
+        double res = 0;
+        for (unsigned int i = siz - 1; i>3; --i) res+=(double)std::pow(cimg::abs(_mp_arg(i)),p);
+        res = (double)std::pow(res,1.0/p);
+        return res;
+      }
+
+      static double mp_vector_normp(_cimg_math_parser& mp) {
+        const unsigned int siz = (unsigned int)mp.opcode[3];
+        const double p = _mp_arg(4);
+        if (siz>0) { // Vector-valued argument
+          const double *ptrs = &_mp_arg(2) + 1;
+          double res = 0;
+          if (p==2) { // L2
+            for (unsigned int i = 0; i<siz; ++i) res+=(double)cimg::sqr(*(ptrs++));
+            res = (double)std::sqrt(res);
+          } else if (p==1) // L1
+            for (unsigned int i = 0; i<siz; ++i) res+=(double)cimg::abs(*(ptrs++));
+          else if (!p) // L0
+            for (unsigned int i = 0; i<siz; ++i) res+=(double)(*(ptrs++)?1:0);
+          else if (cimg::type<float>::is_inf(p)) { // L-inf
+            for (unsigned int i = 0; i<siz; ++i) {
+              const double val = (double)cimg::abs(*(ptrs++));
+              if (val>res) res = val;
+            }
+          } else { // L-p
+            for (unsigned int i = 0; i<siz; ++i) res+=(double)std::pow(cimg::abs(*(ptrs++)),p);
+            res = (double)std::pow(res,1.0/p);
+          }
+          return res>0?res:0;
+        }
+        // Scalar-valued argument.
+        const double val = _mp_arg(2);
+        return p?cimg::abs(val):(val!=0);
+      }
+
       static double mp_vector_print(_cimg_math_parser& mp) {
         const bool print_string = (bool)mp.opcode[4];
         cimg_pragma_openmp(critical(mp_vector_print))
@@ -27847,6 +28284,23 @@ namespace cimg_library {
         return _mp_arg(1);
       }
 
+      static double mp_vector_unitnorm(_cimg_math_parser& mp) {
+        const unsigned int siz = (unsigned int)mp.opcode[3];
+        const double p = _mp_arg(4);
+        if (siz>0) { // Vector-valued argument
+          double *const ptrd = &_mp_arg(1) + 1;
+          const double *const ptrs = &_mp_arg(2) + 1;
+          if (ptrd!=ptrs) std::memcpy(ptrd,ptrs,siz*sizeof(double));
+          CImg<doubleT> vec(ptrd,siz,1,1,1,true);
+          const double mag = vec.magnitude(p);
+          if (mag>0) vec/=mag;
+          return cimg::type<double>::nan();
+        }
+        // Scalar-valued argument.
+        const double val = _mp_arg(2);
+        return val?(_mp_arg(2)?1:val):0;
+      }
+
 #define _cimg_mp_vfunc(func) \
       const longT sizd = (longT)mp.opcode[2];\
       const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \
@@ -27942,15 +28396,19 @@ namespace cimg_library {
         switch (nb_digits) {
         case -1 : std::strcpy(format,"%g"); break;
         case 0 : std::strcpy(format,"%.17g"); break;
-        default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
+        default :
+          if (nb_digits>=-1) cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
+          else cimg_snprintf(format,format._width,"%%.%dld",-nb_digits);
         }
         CImg<charT> str;
         if (sizs) { // Vector expression
           const double *ptrs = &_mp_arg(3) + 1;
-          CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
+          if (nb_digits>=-1) CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
+          else CImg<longT>(ptrs,sizs,1,1,1).value_string(',',sizd + 1,format).move_to(str);
         } else { // Scalar expression
           str.assign(sizd + 1);
-          cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
+          if (nb_digits>=-1) cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
+          else cimg_snprintf(str,sizd + 1,format,(long)_mp_arg(3));
         }
         const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1);
         CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
@@ -28635,7 +29093,7 @@ namespace cimg_library {
        Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
     **/
     CImg<T>& pow(const char *const expression) {
-      return pow((+*this)._fill(expression,true,1,0,0,"pow",this));
+      return pow((+*this)._fill(expression,true,3,0,0,"pow",this,0));
     }
 
     //! Raise each pixel value to a power, specified from an expression \newinstance.
@@ -28687,7 +29145,7 @@ namespace cimg_library {
        Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
     **/
     CImg<T>& rol(const char *const expression) {
-      return rol((+*this)._fill(expression,true,1,0,0,"rol",this));
+      return rol((+*this)._fill(expression,true,3,0,0,"rol",this,0));
     }
 
     //! Compute the bitwise left rotation of each pixel value \newinstance.
@@ -28739,7 +29197,7 @@ namespace cimg_library {
        Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
     **/
     CImg<T>& ror(const char *const expression) {
-      return ror((+*this)._fill(expression,true,1,0,0,"ror",this));
+      return ror((+*this)._fill(expression,true,3,0,0,"ror",this,0));
     }
 
     //! Compute the bitwise right rotation of each pixel value \newinstance.
@@ -28821,7 +29279,7 @@ namespace cimg_library {
        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
     **/
     CImg<T>& min(const char *const expression) {
-      return min((+*this)._fill(expression,true,1,0,0,"min",this));
+      return min((+*this)._fill(expression,true,3,0,0,"min",this,0));
     }
 
     //! Pointwise min operator between an image and an expression \newinstance.
@@ -28879,7 +29337,7 @@ namespace cimg_library {
        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
     **/
     CImg<T>& max(const char *const expression) {
-      return max((+*this)._fill(expression,true,1,0,0,"max",this));
+      return max((+*this)._fill(expression,true,3,0,0,"max",this,0));
     }
 
     //! Pointwise max operator between an image and an expression \newinstance.
@@ -28938,7 +29396,7 @@ namespace cimg_library {
        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
     **/
     CImg<T>& minabs(const char *const expression) {
-      return minabs((+*this)._fill(expression,true,1,0,0,"minabs",this));
+      return minabs((+*this)._fill(expression,true,3,0,0,"minabs",this,0));
     }
 
     //! Pointwise minabs operator between an image and an expression \newinstance.
@@ -28997,7 +29455,7 @@ namespace cimg_library {
        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
     **/
     CImg<T>& maxabs(const char *const expression) {
-      return maxabs((+*this)._fill(expression,true,1,0,0,"maxabs",this));
+      return maxabs((+*this)._fill(expression,true,3,0,0,"maxabs",this,0));
     }
 
     //! Pointwise maxabs operator between an image and an expression \newinstance.
@@ -29492,67 +29950,98 @@ namespace cimg_library {
     // (return 'true' in case of success, and set value of 'res').
     template<typename t>
     bool __eval(const char *const expression, t &res) const {
-      if (!expression || !*expression) { res = (t)0; return true; }
-      const char c = *expression;
-      bool is_success = false;
-      char sep, end;
-      double val,val2;
-      int err;
-      if ((c>='0' && c<='9') || c=='.') { // Possible value
-        if (!expression[1]) { // Single digit
-          res = (t)(c - '0');
-          is_success = true;
-        } else if ((err = std::sscanf(expression,"%lf %c%lf %c",&val,&sep,&val2,&end))==1) { // Single value
-          res = (t)val;
-          is_success = true;
-        } else if (err==3) { // Value1 Operator Value2
-          switch (sep) {
-          case '+' : res = (t)(val + val2); is_success = true; break;
-          case '-' : res = (t)(val - val2); is_success = true; break;
-          case '*' : res = (t)(val*val2); is_success = true; break;
-          case '/' : res = (t)(val/val2); is_success = true; break;
-          case '%' : res = (t)cimg::mod(val,val2); is_success = true; break;
-          case '&' : res = (t)((long)val & (long)val2); is_success = true; break;
-          case '|' : res = (t)((long)val | (long)val2); is_success = true; break;
-          case '>' : res = (t)(val>val2); is_success = true; break;
-          case '<' : res = (t)(val<val2); is_success = true; break;
-          case ';' : res = (t)val2; is_success = true; break;
-          case '^' : res = (t)std::pow(val,val2); is_success = true; break;
+
+#define __eval_op(op) if (__eval_get(++ptr,val2) && !*ptr) { res = (t)(op); return true; } else return false;
+
+      double val1, val2;
+      if (!expression || !*expression || *expression==';' || *expression=='[') return false;
+      if (!expression[1]) switch (*expression) {
+        case 'w' : res = (t)_width; return true;
+        case 'h' : res = (t)_height; return true;
+        case 'd' : res = (t)_depth; return true;
+        case 's' : res = (t)_spectrum; return true;
+        case 'r' : res = (t)_is_shared; return true;
+        default : if (*expression>='0' && *expression<='9') { res = (t)(*expression - '0'); return true; }
+        }
+      if (*expression=='w' && expression[1]=='h') {
+        if (!expression[2]) { res = (t)(_width*_height); return true; }
+        if (expression[2]=='d') {
+          if (!expression[3]) { res = (t)(_width*_height*_depth); return true; }
+          if (expression[3]=='s' && !expression[4]) { res = (t)(_width*_height*_depth*_spectrum); return true; }
+        }
+        if (expression[2]=='s' && !expression[3]) { res = (t)(_width*_height*_spectrum); return true; }
+      }
+      const char *ptr = expression;
+      while (*ptr && cimg::is_blank(*ptr)) ++ptr;
+      if (*ptr=='\'' && *(++ptr)) { // Detect 'stringA' op 'stringB' (op='==' or '!=')
+        const char *ptr2 = std::strchr(ptr,'\'');
+        if (ptr2) {
+          const char *ptr3 = ptr2 + 1;
+          while (*ptr3 && cimg::is_blank(*ptr3)) ++ptr3;
+          const char *ptr4 = ptr3;
+          if ((*ptr3=='!' || *ptr3=='=') && *(++ptr4)=='=') {
+            ++ptr4;
+            while (*ptr4 && cimg::is_blank(*ptr4)) ++ptr4;
+            if (*ptr4=='\'' && *(++ptr4)) {
+              const char *const ptr5 = std::strchr(ptr4,'\'');
+              if (ptr5) {
+                const char *ptr6 = ptr5 + 1;
+                while (*ptr6 && cimg::is_blank(*ptr6)) ++ptr6;
+                if (!*ptr6) {
+                  CImg<charT> str1(ptr,ptr2 - ptr,1,1,1,true), str2(ptr4,ptr5 - ptr4,1,1,1,true);
+                  if (*ptr3=='!') res = (t)!(str1==str2); else res = (t)(str1==str2);
+                  return true;
+                }
+              }
+            }
           }
         }
-      } else if ((c=='+' || c=='-' || c=='!') && // +Value, -Value or !Value
-                 (((sep = expression[1])>='0' && sep<='9') || sep=='.')) {
-        if (!expression[2]) { // [+-!] + Single digit
-          const int ival = sep - '0';
-          res = (t)(c=='+'?ival:c=='-'?-ival:!ival);
-          is_success = true;
-        } else if ((err = std::sscanf(expression + 1,"%lf %c%lf %c",&val,&sep,&val2,&end))==1) { // [+-!] Single value
-          res = (t)(c=='+'?val:c=='-'?-val:(double)!val);
-          is_success = true;
-        } else if (err==3) { // [+-!] Value1 Operator Value2
-          const double val1 = c=='+'?val:c=='-'?-val:(double)!val;
-          switch (sep) {
-          case '+' : res = (t)(val1 + val2); is_success = true; break;
-          case '-' : res = (t)(val1 - val2); is_success = true; break;
-          case '*' : res = (t)(val1*val2); is_success = true; break;
-          case '/' : res = (t)(val1/val2); is_success = true; break;
-          case '%' : res = (t)cimg::mod(val1,val2); is_success = true; break;
-          case '&' : res = (t)((long)val1 & (long)val2); is_success = true; break;
-          case '|' : res = (t)((long)val1 | (long)val2); is_success = true; break;
-          case '>' : res = (t)(val1>val2); is_success = true; break;
-          case '<' : res = (t)(val1<val2); is_success = true; break;
-          case ';' : res = (t)val2; is_success = true; break;
-          case '^' : val = std::pow(val,val2); res = (t)(c=='+'?val:c=='-'?-val:!val); is_success = true; break;
+        return false;
+      }
+      if (__eval_get(ptr,val1)) { // Detect 'value1' op 'value2'
+        switch (*ptr) {
+        case 0 : res = (t)val1; return true;
+        case '+' : __eval_op(val1 + val2);
+        case '-' : __eval_op(val1 - val2);
+        case '*' : __eval_op(val1 * val2);
+        case '/' : __eval_op(val1 / val2);
+        case '%' : __eval_op(cimg::mod(val1,val2));
+        case '&' : if (ptr[1]=='&') { ++ptr; __eval_op(val1 && val2); } else { __eval_op((long)val1 & (long)val2); }
+        case '|' : if (ptr[1]=='|') { ++ptr; __eval_op(val1 || val2); } else { __eval_op((long)val1 | (long)val2); }
+        case '>' : if (ptr[1]=='=') { ++ptr; __eval_op(val1>=val2); } else { __eval_op(val1>val2); }
+        case '<' : if (ptr[1]=='=') { ++ptr; __eval_op(val1<=val2); } else { __eval_op(val1<val2); }
+        case ';' : __eval_op(val2);
+        case '^' : __eval_op(std::pow(val1,val2));
+        case '=' : if (*++ptr=='=') { __eval_op(val1==val2); } else return false;
+        case '!' : if (*++ptr=='=') { __eval_op(val1!=val2); } else return false;
+        }
+      }
+      return false;
+    }
+
+    // Return 'true' is a single 'value' or '!value' has been succesfully read ('value' being a double or { w,h,d,s }).
+    bool __eval_get(const char* &ptr, double &value) const {
+      int n = 0;
+      while (*ptr && cimg::is_blank(*ptr)) ++ptr;
+
+      bool is_not = false; // Detect preceding '!' operator
+      if (*ptr=='!') { is_not = true; ++ptr; while (*ptr && cimg::is_blank(*ptr)) ++ptr; }
+
+      if ((*ptr=='w' || *ptr=='h' || *ptr=='d' || *ptr=='s' || *ptr=='r') || std::sscanf(ptr,"%lf %n",&value,&n)==1) {
+        if (!n) {
+          switch (*ptr) {
+          case 'w': value = (double)_width; break;
+          case 'h': value = (double)_height; break;
+          case 'd': value = (double)_depth; break;
+          case 's': value = (double)_spectrum; break;
+          case 'r': value = (double)_is_shared; break;
           }
-        }
-      } else if (!expression[1]) switch (*expression) { // Other common single-char expressions
-        case 'w' : res = (t)_width; is_success = true; break;
-        case 'h' : res = (t)_height; is_success = true; break;
-        case 'd' : res = (t)_depth; is_success = true; break;
-        case 's' : res = (t)_spectrum; is_success = true; break;
-        case 'r' : res = (t)_is_shared; is_success = true; break;
-        }
-      return is_success;
+          ++ptr; while (*ptr && cimg::is_blank(*ptr)) ++ptr;
+        } else ptr+=n;
+        if (is_not) value = (double)!value;
+        return true;
+      }
+      return false;
     }
 
     double _eval(CImg<T> *const img_output, const char *const expression,
@@ -29737,32 +30226,36 @@ namespace cimg_library {
 
     //! Compute norm of the image, viewed as a matrix.
     /**
-       \param magnitude_type Norm type. Can be:
-       - \c -1: Linf-norm
+       \param magnitude_type Can be:
        - \c 0: L0-norm
        - \c 1: L1-norm
        - \c 2: L2-norm
+       - \c p>2 : Lp-norm
+       - \c ~0U: Linf-norm
     **/
-    double magnitude(const int magnitude_type=2) const {
+    double magnitude(const float magnitude_type=2) const {
       if (is_empty())
         throw CImgInstanceException(_cimg_instance
                                     "magnitude(): Empty instance.",
                                     cimg_instance);
       const ulongT siz = size();
       double res = 0;
-      switch (magnitude_type) {
-      case -1 : {
-        cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
-      } break;
-      case 1 : {
-        cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
-        for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]);
-      } break;
-      default : {
+      if (magnitude_type==2) { // L2
         cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
         for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]);
         res = (double)std::sqrt(res);
-      }
+      } else if (magnitude_type==1) { // L1
+        cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
+        for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]);
+      } else if (!magnitude_type) { // L0
+        cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
+        for (longT off = 0; off<(longT)siz; ++off) res+=(double)(_data[off]?1:0);
+      } else if (cimg::type<float>::is_inf(magnitude_type)) { // L-inf
+        cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
+      } else { // L-p
+        cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
+        for (longT off = 0; off<(longT)siz; ++off) res+=(double)std::pow(cimg::abs(_data[off]),magnitude_type);
+        res = (double)std::pow(res,1.0/magnitude_type);
       }
       return res;
     }
@@ -30033,7 +30526,7 @@ namespace cimg_library {
     /**
        If the instance matrix is not square, the Moore-Penrose pseudo-inverse is computed instead.
        \param use_LU Choose the inverting algorithm. Can be:
-       - \c true: LU solver (faster but less precise).
+       - \c true: LU solver (faster but sometimes less precise).
        - \c false: SVD solver (more precise but slower).
        \param lambda is used only in the Moore-Penrose pseudoinverse for estimating A^t.(A^t.A + lambda.Id)^-1.
     **/
@@ -30972,7 +31465,7 @@ namespace cimg_library {
         cimg_forX(*this,x) {
         CImg<Tfloat> S = get_column(x);
         const CImg<Tfloat> S0 = method<2?CImg<Tfloat>():S;
-        Tfloat residual = S.magnitude()/S._height;
+        Tfloat residual = S.magnitude(2)/S._height;
         const unsigned int nmax = max_iter?max_iter:D._width;
 
         for (unsigned int n = 0; n<nmax && residual>max_residual; ++n) {
@@ -31027,7 +31520,7 @@ namespace cimg_library {
               W(x,ind) = weight;
               cimg_forY(S,y) S[y]-=weight*D(ind,y);
             }
-            residual = S.magnitude()/S._height;
+            residual = S.magnitude(2)/S._height;
             is_orthoproj = true;
           }
         }
@@ -32187,103 +32680,108 @@ namespace cimg_library {
     **/
     CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
                   CImgList<T> *const list_images=0) {
-      return _fill(expression,repeat_values,allow_formula?1:0,list_images,"fill",0);
+      return _fill(expression,repeat_values,allow_formula?3:1,list_images,"fill",0,0);
     }
 
-    // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula |
-    //                    2 = allow formula and do not fill image values }.
-    CImg<T>& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode,
-                   CImgList<T> *const list_images, const char *const calling_function, const CImg<T> *provides_copy) {
+    // bits of 'mode' can enable/disable these properties:
+    // . 1 = Allow list of values.
+    // . 2 = Allow formula.
+    // . 4 = Evaluate but does not fill image values.
+    CImg<T>& _fill(const char *const expression, const bool repeat_values, const unsigned int mode,
+                   CImgList<T> *const list_images, const char *const calling_function,
+                   const CImg<T> *provides_copy, CImg<doubleT> *const result_end) {
       if (is_empty() || !expression || !*expression) return *this;
-      const unsigned int omode = cimg::exception_mode();
+      const unsigned int excmode = cimg::exception_mode();
       cimg::exception_mode(0);
       CImg<charT> is_error_expr;
-      bool is_error_seq = false, is_value_sequence = false;
+      bool is_done = false, is_value_sequence = false;
       cimg_abort_init;
+      if (result_end) result_end->assign();
 
-      if (formula_mode) {
-
-        // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser.
+      // Detect value sequence.
+      if (mode&1) {
         double value;
         char sep;
         const int err = cimg_sscanf(expression,"%lf %c",&value,&sep);
         if (err==1 || (err==2 && sep==',')) {
-          if (err==1) { if (formula_mode==2) return *this; return fill((T)value); }
+          if (err==1) { if (mode&4) return *this; return fill((T)value); }
           else is_value_sequence = true;
         }
+      }
 
-        // Try to fill values according to a formula.
+      // Try to fill values according to a formula.
+      if (mode&2 && !is_value_sequence) {
         _cimg_abort_init_openmp;
-        if (!is_value_sequence) try {
-            CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
-            _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
-                                               *expression=='*' || *expression==':'),
-                                 calling_function,base,this,list_images,true);
-            if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' &&
-                mp.need_input_copy)
-              base.assign().assign(*this,false); // Needs input copy
+        try {
+          CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
+          _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
+                                             *expression=='*' || *expression==':'),
+                               calling_function,base,this,list_images,true);
+          if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' &&
+              mp.need_input_copy)
+            base.assign().assign(*this,false); // Needs input copy
 
-            // Determine 2nd largest image dimension (used as axis for inner loop in parallelized evaluation).
-            unsigned int M;
-            if (mp.result_dim) {
-              M = cimg::max(_width,_height,_depth);
-              M = M==_width?std::max(_height,_depth):M==_height?std::max(_width,_depth):std::max(_width,_height);
-            } else {
-              M = cimg::max(_width,_height,_depth,_spectrum);
-              M = M==_width?cimg::max(_height,_depth,_spectrum):
-                M==_height?cimg::max(_width,_depth,_spectrum):
-                M==_depth?cimg::max(_width,_height,_spectrum):cimg::max(_width,_height,_depth);
-            }
+          // Determine M2, smallest image dimension (used as axis for the most inner loop in parallelized iterations).
+          // M1 is the total number of parallelized iterations.
+          unsigned int M1 = 0, M2 = 0;
+          cimg::unused(M1,M2);
+          if (mp.result_dim) {
+            M2 = cimg::min(_width,_height,_depth);
+            M1 = M2==_width?_height*_depth:M2==_height?_width*_depth:_width*_height;
+          } else {
+            M2 = cimg::min(_width,_height,_depth,_spectrum);
+            M1 = M2==_width?_height*_depth*_spectrum:M2==_height?_width*_depth*_spectrum:
+              M2==_depth?_width*_height*_spectrum:_width*_height*_depth;
+          }
 
-            bool do_in_parallel = false;
+          bool do_in_parallel = false;
 #if cimg_use_openmp!=0
-            if (mp.is_noncritical_run && (*expression=='*' || *expression==':'))
-              throw CImgArgumentException(_cimg_instance
-                                          "%s(): Cannot evaluate expression '%s' in parallel, "
-                                          "as 'run()' is used outside a 'critical()' section.",
-                                          cimg_instance,calling_function,expression);
-            cimg_openmp_if(!mp.is_noncritical_run &&
-                           (*expression=='*' || *expression==':' ||
-                            (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2)))
-              do_in_parallel = true;
+          if (mp.is_noncritical_run && (*expression=='*' || *expression==':'))
+            throw CImgArgumentException(_cimg_instance
+                                        "%s(): Cannot evaluate expression '%s' in parallel, "
+                                        "as 'run()' is used outside a 'critical()' section.",
+                                        cimg_instance,calling_function,expression);
+          cimg_openmp_if(!mp.is_noncritical_run &&
+                         (*expression=='*' || *expression==':' || (mp.is_parallelizable && M1>=2 && M1*M2>=16)))
+            do_in_parallel = true;
 #endif
-            if (mp.result_dim) { // Vector-valued expression
-              const unsigned int N = std::min(mp.result_dim,_spectrum);
-              const ulongT whd = (ulongT)_width*_height*_depth;
-              T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
+          if (mp.result_dim) { // Vector-valued expression
+            const unsigned int N = std::min(mp.result_dim,_spectrum);
+            const ulongT whd = (ulongT)_width*_height*_depth;
+            T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
 
-              if (*expression=='<') {
-                CImg<doubleT> res(1,mp.result_dim);
-                mp.begin_t();
-                cimg_rofYZ(*this,y,z) {
-                  cimg_abort_test;
-                  if (formula_mode==2) cimg_rofX(*this,x) mp(x,y,z,0);
-                  else cimg_rofX(*this,x) {
-                      mp(x,y,z,0,res._data);
-                      const double *ptrs = res._data;
-                      T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
-                    }
-                }
-                mp.end_t();
+            if (*expression=='<') {
+              CImg<doubleT> res(1,mp.result_dim);
+              mp.begin_t();
+              cimg_rofYZ(*this,y,z) {
+                cimg_abort_test;
+                if (mode&4) cimg_rofX(*this,x) mp(x,y,z,0);
+                else cimg_rofX(*this,x) {
+                    mp(x,y,z,0,res._data);
+                    const double *ptrs = res._data;
+                    T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
+                  }
+              }
+              mp.end_t();
 
-              } else if (*expression=='>' || !do_in_parallel) {
-                CImg<doubleT> res(1,mp.result_dim);
-                mp.begin_t();
-                cimg_forYZ(*this,y,z) {
-                  cimg_abort_test;
-                  if (formula_mode==2) cimg_forX(*this,x) mp(x,y,z,0);
-                  else cimg_forX(*this,x) {
-                      mp(x,y,z,0,res._data);
-                      const double *ptrs = res._data;
-                      T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
-                    }
-                }
-                mp.end_t();
+            } else if (*expression=='>' || !do_in_parallel) {
+              CImg<doubleT> res(1,mp.result_dim);
+              mp.begin_t();
+              cimg_forYZ(*this,y,z) {
+                cimg_abort_test;
+                if (mode&4) cimg_forX(*this,x) mp(x,y,z,0);
+                else cimg_forX(*this,x) {
+                    mp(x,y,z,0,res._data);
+                    const double *ptrs = res._data;
+                    T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
+                  }
+              }
+              mp.end_t();
 
-              } else {
+            } else {
 
 #if cimg_use_openmp!=0
-                cimg_pragma_openmp(parallel)
+              cimg_pragma_openmp(parallel)
                 {
                   _cimg_math_parser
                     *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
@@ -32296,7 +32794,7 @@ namespace cimg_library {
   cimg_pragma_openmp(for cimg_openmp_collapse(2)) \
   cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \
     cimg_abort_test; \
-    if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,0); \
+    if (mode&4) cimg_for##_X(*this,_x) lmp(x,y,z,0); \
     else { \
       CImg<doubleT> res(1,lmp.result_dim); \
       T *__ptrd = data(_sx,_sy,_sz,0); \
@@ -32311,8 +32809,8 @@ namespace cimg_library {
     } \
   } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
 
-                  if (M==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) }
-                  else if (M==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) }
+                  if (M2==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) }
+                  else if (M2==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) }
                   else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) }
 
                   lmp.end_t();
@@ -32320,26 +32818,26 @@ namespace cimg_library {
                   if (&lmp!=&mp) delete &lmp;
                 }
 #endif
-              }
+            }
 
-            } else { // Scalar-valued expression
-              T *ptrd = *expression=='<'?end() - 1:_data;
-              if (*expression=='<') {
-                mp.begin_t();
-                if (formula_mode==2) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); }
+          } else { // Scalar-valued expression
+            T *ptrd = *expression=='<'?end() - 1:_data;
+            if (*expression=='<') {
+              mp.begin_t();
+              if (mode&4) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); }
                 else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); }
-                mp.end_t();
+              mp.end_t();
 
-              } else if (*expression=='>' || !do_in_parallel) {
-                mp.begin_t();
-                if (formula_mode==2) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); }
-                else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
-                mp.end_t();
+            } else if (*expression=='>' || !do_in_parallel) {
+              mp.begin_t();
+              if (mode&4) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); }
+              else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
+              mp.end_t();
 
-              } else {
+            } else {
 
 #if cimg_use_openmp!=0
-                cimg_pragma_openmp(parallel)
+              cimg_pragma_openmp(parallel)
                 {
                   _cimg_math_parser
                     *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
@@ -32352,7 +32850,7 @@ namespace cimg_library {
   cimg_pragma_openmp(for cimg_openmp_collapse(3)) \
   cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \
     cimg_abort_test; \
-    if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,c); \
+    if (mode&4) cimg_for##_X(*this,_x) lmp(x,y,z,c); \
     else { \
       T *_ptrd = data(_sx,_sy,_sz,_sc); \
       const ulongT off = (ulongT)_off; \
@@ -32360,9 +32858,9 @@ namespace cimg_library {
     } \
   } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
 
-                  if (M==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) }
-                  else if (M==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) }
-                  else if (M==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) }
+                  if (M2==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) }
+                  else if (M2==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) }
+                  else if (M2==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) }
                   else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) }
 
                   lmp.end_t();
@@ -32370,24 +32868,30 @@ namespace cimg_library {
                   if (&lmp!=&mp) delete &lmp;
                 }
 #endif
-              }
             }
-            mp.end();
-          } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error_expr); }
+          }
+          mp.end();
+
+          if (result_end && mp.result_end) // Transfer result of the end() block if requested.
+            result_end->assign(mp.result_end + (mp.result_end_dim?1:0),std::max(1U,mp.result_end_dim));
+
+          is_done = true;
+        } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error_expr); }
       }
 
       // Try to fill values according to a value sequence.
-      if (!formula_mode || is_value_sequence || is_error_expr) {
-        is_error_seq = _fill_from_values(expression,repeat_values);
-        cimg::exception_mode(omode);
-        if (is_error_seq) {
-          if (is_error_expr) throw CImgArgumentException("%s",is_error_expr._data);
-          else throw CImgArgumentException(_cimg_instance
-                                           "%s(): Invalid sequence of filling values '%s'.",
-                                           cimg_instance,calling_function,expression);
-        }
+      if (!is_done && mode&1) is_done = !_fill_from_values(expression,repeat_values);
+
+      if (!is_done) {
+        cimg::exception_mode(excmode);
+        if (is_error_expr) throw CImgArgumentException(_cimg_instance
+                                                       "%s",
+                                                       cimg_instance,is_error_expr._data);
+        else throw CImgArgumentException(_cimg_instance
+                                         "%s(): Invalid sequence of filling values '%s'.",
+                                         cimg_instance,calling_function,expression);
       }
-      cimg::exception_mode(omode);
+      cimg::exception_mode(excmode);
       cimg_abort_test;
       return *this;
     }
@@ -32417,7 +32921,7 @@ namespace cimg_library {
     }
 
     // Fill image according to a value sequence, given as a string.
-    // Return 'true' if an error occured.
+    // Return 'true' if an error occured, 'false' otherwise.
     bool _fill_from_values(const char *const values, const bool repeat_values) {
       CImg<charT> item(256);
       const char *nvalues = values;
@@ -46691,7 +47195,7 @@ namespace cimg_library {
             *(ptrd++) = (float)color._spectrum;
             cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
           } else {
-            *(ptrd++) = (float)shared_ind;
+            *(ptrd++) = (float)cimg::uint2float((unsigned int)shared_ind);
             *(ptrd++) = 0;
             *(ptrd++) = 0;
           }
@@ -46723,7 +47227,7 @@ namespace cimg_library {
             *(ptrd++) = (float)opacity._spectrum;
             cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
           } else {
-            *(ptrd++) = (float)shared_ind;
+            *(ptrd++) = (float)cimg::uint2float((unsigned int)shared_ind);
             *(ptrd++) = 0;
             *(ptrd++) = 0;
           }
@@ -46835,24 +47339,32 @@ namespace cimg_library {
         const unsigned int nb_inds = (unsigned int)*(ptrs++);
         primitives[p].assign(1,nb_inds);
         tp *ptrp = primitives[p]._data;
-        for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
+        for (unsigned int i = 0; i<nb_inds; ++i)
+          *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
       }
       colors.assign(nb_primitives);
+
       cimglist_for(colors,c) {
         if (*ptrs==(T)-128) {
           ++ptrs;
-          const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
+          const unsigned int
+            w = (unsigned int)cimg::float2uint((float)*(ptrs++)),
+            h = (unsigned int)*(ptrs++),
+            s = (unsigned int)*(ptrs++);
           if (!h && !s) colors[c].assign(colors[w],true);
-          else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
+          else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=(ulongT)w*h*s; }
         } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
       }
       opacities.assign(nb_primitives);
       cimglist_for(opacities,o) {
         if (*ptrs==(T)-128) {
           ++ptrs;
-          const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
+          const unsigned int
+            w = (unsigned int)cimg::float2uint((float)*(ptrs++)),
+            h = (unsigned int)*(ptrs++),
+            s = (unsigned int)*(ptrs++);
           if (!h && !s) opacities[o].assign(opacities[w],true);
-          else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
+          else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=(ulongT)w*h*s; }
         } else opacities[o].assign(1,1,1,1,*(ptrs++));
       }
       return points;
@@ -47040,24 +47552,22 @@ namespace cimg_library {
       if (is_empty() || !opacity || !pattern ||
           std::min(y0,y1)>=height() || std::max(y0,y1)<0 ||
           std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
-
       int
         w1 = width() - 1, h1 = height() - 1,
         dx01 = x1 - x0, dy01 = y1 - y0;
 
       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
-      if (pattern==~0U && y0>y1) {
-        cimg::swap(x0,x1,y0,y1);
-        dx01*=-1; dy01*=-1;
-      }
+      if (pattern==~0U && y0>y1) { cimg::swap(x0,x1,y0,y1); dx01*=-1; dy01*=-1; }
 
       static unsigned int hatch = ~0U - (~0U>>1);
       if (init_hatch) hatch = ~0U - (~0U>>1);
       cimg_init_scanline(opacity);
       const int
-        step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2,
-        cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
+        step = y0<=y1?1:-1,
+        hdy01 = dy01*cimg::sign(dx01)/2,
+        cy0 = cimg::cut(y0,0,h1),
+        cy1 = cimg::cut(y1,0,h1) + step;
       dy01+=dy01?0:1;
 
       for (int y = cy0; y!=cy1; y+=step) {
@@ -47432,29 +47942,32 @@ namespace cimg_library {
        - This function uses several call to the single CImg::draw_line() procedure,
        depending on the vectors size in \p points.
     **/
-    template<typename t, typename tc>
-    CImg<T>& draw_line(const CImg<t>& points,
+    template<typename tp, typename tc>
+    CImg<T>& draw_line(const CImg<tp>& points,
                        const tc *const color, const float opacity=1,
                        const unsigned int pattern=~0U, const bool init_hatch=true) {
-      if (is_empty() || !points || points._width<2) return *this;
-      bool ninit_hatch = init_hatch;
-      switch (points._height) {
-      case 0 : case 1 :
+      if (is_empty() || !points) return *this;
+      if (!color)
         throw CImgArgumentException(_cimg_instance
-                                    "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).",
+                                    "draw_line(): Specified color is (null).",
+                                    cimg_instance);
+      if (points.height()!=2)
+        throw CImgArgumentException(_cimg_instance
+                                    "draw_line(): Invalid specified point set (%u,%u,%u,%u).",
                                     cimg_instance,
-                                    points._width,points._height,points._depth,points._spectrum,points._data);
+                                    points._width,points._height,points._depth,points._spectrum);
+      CImg<intT> ipoints;
+      if (cimg::type<tp>::is_float()) ipoints = points.get_round();
+      else ipoints.assign(points,cimg::type<tp>::string()==cimg::type<int>::string());
 
-      default : {
-        const int x0 = (int)points(0,0), y0 = (int)points(0,1);
-        int ox = x0, oy = y0;
-        for (unsigned int i = 1; i<points._width; ++i) {
-          const int x = (int)points(i,0), y = (int)points(i,1);
-          draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
-          ninit_hatch = false;
-          ox = x; oy = y;
-        }
-      }
+      bool ninit_hatch = init_hatch;
+      const int x0 = ipoints(0,0), y0 = ipoints(0,1);
+      int ox = x0, oy = y0;
+      for (unsigned int i = 1; i<ipoints._width; ++i) {
+        const int x = ipoints(i,0), y = ipoints(i,1);
+        draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
+        ninit_hatch = false;
+        ox = x; oy = y;
       }
       return *this;
     }
@@ -49410,45 +49923,43 @@ namespace cimg_library {
                                     "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
                                     cimg_instance,
                                     points._width,points._height,points._depth,points._spectrum);
-      if (points._width==1) return draw_point(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),color,opacity);
-      if (points._width==2) return draw_line(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),
-                                             cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),color,opacity);
-      if (points._width==3) return draw_triangle(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),
-                                                 cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),
-                                                 cimg::uiround(points(2,0)),cimg::uiround(points(2,1)),color,opacity);
+      CImg<intT> ipoints;
+      if (cimg::type<tp>::is_float()) ipoints = points.get_round();
+      else ipoints.assign(points,cimg::type<tp>::string()==cimg::type<int>::string());
+
+      if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity);
+      if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),color,opacity);
+      if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),
+                                                  ipoints(2,0),ipoints(2,1),color,opacity);
       cimg_init_scanline(opacity);
       int
         xmin = 0, ymin = 0,
-        xmax = points.get_shared_row(0).max_min(xmin),
-        ymax = points.get_shared_row(1).max_min(ymin);
+        xmax = ipoints.get_shared_row(0).max_min(xmin),
+        ymax = ipoints.get_shared_row(1).max_min(ymin);
       if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
       if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity);
 
       ymin = std::max(0,ymin);
       ymax = std::min(height() - 1,ymax);
-      CImg<intT> Xs(points._width,ymax - ymin + 1);
+      CImg<intT> Xs(ipoints._width,ymax - ymin + 1);
       CImg<uintT> count(Xs._height,1,1,1,0);
       unsigned int n = 0, nn = 1;
       bool go_on = true;
 
       while (go_on) {
-        unsigned int an = (nn + 1)%points._width;
-        const int
-          x0 = cimg::uiround(points(n,0)),
-          y0 = cimg::uiround(points(n,1));
-        if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; }
-        const int
-          x1 = cimg::uiround(points(nn,0)),
-          y1 = cimg::uiround(points(nn,1));
+        unsigned int an = (nn + 1)%ipoints._width;
+        const int x0 = ipoints(n,0), y0 = ipoints(n,1);
+        if (ipoints(nn,1)==y0) while (ipoints(an,1)==y0) { nn = an; (an+=1)%=ipoints._width; }
+        const int x1 = ipoints(nn,0), y1 = ipoints(nn,1);
         unsigned int tn = an;
-        while (points(tn,1)==y1) (tn+=1)%=points._width;
-
+        while (ipoints(tn,1)==y1) (tn+=1)%=ipoints._width;
         if (y0!=y1) {
           const int
-            y2 = cimg::uiround(points(tn,1)),
+            y2 = ipoints(tn,1),
             x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1,
             step = cimg::sign(y01),
-            tmax = std::max(1,cimg::abs(y01)), htmax = tmax*cimg::sign(x01)/2,
+            tmax = std::max(1,cimg::abs(y01)),
+            htmax = tmax*cimg::sign(x01)/2,
             tend = tmax - (step==cimg::sign(y12));
           unsigned int y = (unsigned int)y0 - ymin;
           for (int t = 0; t<=tend; ++t, y+=step)
@@ -49475,43 +49986,38 @@ namespace cimg_library {
     }
 
     //! Draw a outlined 2D or 3D polygon \overloading.
-    template<typename t, typename tc>
-    CImg<T>& draw_polygon(const CImg<t>& points,
+    template<typename tp, typename tc>
+    CImg<T>& draw_polygon(const CImg<tp>& points,
                           const tc *const color, const float opacity, const unsigned int pattern) {
       if (is_empty() || !points) return *this;
       if (!color)
         throw CImgArgumentException(_cimg_instance
                                     "draw_polygon(): Specified color is (null).",
                                     cimg_instance);
-      if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity);
-      if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1),
-                                             (int)points(1,0),(int)points(1,1),color,opacity,pattern);
-      bool ninit_hatch = true;
-      switch (points._height) {
-      case 0 : case 1 :
+      if (points.height()!=2)
         throw CImgArgumentException(_cimg_instance
                                     "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
                                     cimg_instance,
                                     points._width,points._height,points._depth,points._spectrum);
-      default : {
-        CImg<intT> npoints(points._width,2);
-        int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
-        unsigned int nb_points = 1;
-        for (unsigned int p = 1; p<points._width; ++p) {
-          const int nx = (int)points(p,0), ny = (int)points(p,1);
-          if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
-        }
-        const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
-        int ox = x0, oy = y0;
-        for (unsigned int i = 1; i<nb_points; ++i) {
-          const int _x = (int)npoints(i,0), _y = (int)npoints(i,1);
-          draw_line(ox,oy,_x,_y,color,opacity,pattern,ninit_hatch);
-          ninit_hatch = false;
-          ox = _x; oy = _y;
-        }
-        draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
-      }
+      CImg<intT> ipoints;
+      if (cimg::type<tp>::is_float()) ipoints = points.get_round();
+      else ipoints.assign(points,cimg::type<tp>::string()==cimg::type<int>::string());
+
+      if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity);
+      if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),
+                                              color,opacity,pattern);
+      if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),
+                                                  ipoints(2,0),ipoints(2,1),color,opacity,pattern);
+      bool ninit_hatch = true;
+      const int x0 = ipoints(0,0), y0 = ipoints(0,1);
+      int ox = x0, oy = y0;
+      for (unsigned int i = 1; i<ipoints._width; ++i) {
+        const int x = ipoints(i,0), y = ipoints(i,1);
+        draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
+        ninit_hatch = false;
+        ox = x; oy = y;
       }
+      draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
       return *this;
     }
 
@@ -50125,7 +50631,7 @@ namespace cimg_library {
               const unsigned int cmin = std::min(_spectrum,letter._spectrum);
               if (foreground_color)
                 for (unsigned int c = 0; c<cmin; ++c)
-                  if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c]/255;
+                  if (foreground_color[c]!=255) letter.get_shared_channel(c)*=foreground_color[c]/255.0f;
               if (mask) { // Letter has mask
                 if (background_color)
                   for (unsigned int c = 0; c<cmin; ++c)
@@ -52708,7 +53214,13 @@ namespace cimg_library {
             static unsigned int snap_number = 0;
             std::FILE *file;
             do {
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
+#ifdef cimg_use_png
+                            "png",
+#else
+                            "bmp",
+#endif
+                            snap_number++);
               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
             } while (file);
             if (visu0) {
@@ -52724,9 +53236,9 @@ namespace cimg_library {
             do {
 
 #ifdef cimg_use_zlib
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
 #else
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
 #endif
               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
             } while (file);
@@ -53220,7 +53732,7 @@ namespace cimg_library {
       return res;
     }
 
-    // Return a visualizable uchar8 image for display routines.
+    // Return a visualizable 'uchar8' image for display routines.
     CImg<ucharT> _get_select(const CImgDisplay& disp, const int normalization,
                              const int x, const int y, const int z) const {
       if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
@@ -53420,7 +53932,7 @@ namespace cimg_library {
               unsigned int len = (unsigned int)std::strlen(message);
               cimg_forC(*this,c)
                 cimg_snprintf(message._data + len,message._width - len,"%g ",(double)(*this)(x,0,0,c));
-              len = std::strlen(message);
+              len = (unsigned int)std::strlen(message);
               cimg_snprintf(message._data + len,message._width - len,")");
             }
             if (x0>=0 && x1>=0) {
@@ -53483,7 +53995,13 @@ namespace cimg_library {
               CImg<ucharT> &screen = visu?visu:visu0;
               std::FILE *file;
               do {
-                cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
+                cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
+#ifdef cimg_use_png
+                              "png",
+#else
+                              "bmp",
+#endif
+                              snap_number++);
                 if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
               } while (file);
               (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
@@ -53500,9 +54018,9 @@ namespace cimg_library {
               do {
 
 #ifdef cimg_use_zlib
-                cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
+                cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
 #else
-                cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
+                cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
 #endif
                 if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
               } while (file);
@@ -54891,7 +55409,7 @@ namespace cimg_library {
        \param[out] description Description, as stored in the filename.
        \note
        - libtiff support is enabled by defining the precompilation
-        directive \c cimg_use_tif.
+        directive \c cimg_use_tiff.
        - When libtiff is enabled, 2D and 3D (multipage) several
         channel per pixel are supported for
         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
@@ -57074,7 +57592,7 @@ namespace cimg_library {
       else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
 
       if (!is_empty()) cimg_foroff(*this,off) {
-        std::fprintf(cimg::output(),"%g",(double)_data[off]);
+        std::fprintf(cimg::output(),cimg::type<T>::format_s(),cimg::type<T>::format(_data[off]));
         if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
         if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); }
       }
@@ -57867,7 +58385,13 @@ namespace cimg_library {
             static unsigned int snap_number = 0;
             std::FILE *file;
             do {
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
+#ifdef cimg_use_png
+                            "png",
+#else
+                            "bmp",
+#endif
+                            snap_number++);
               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
             } while (file);
             (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
@@ -57879,7 +58403,7 @@ namespace cimg_library {
             static unsigned int snap_number = 0;
             std::FILE *file;
             do {
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.off",snap_number++);
               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
             } while (file);
             (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
@@ -57893,9 +58417,9 @@ namespace cimg_library {
             do {
 
 #ifdef cimg_use_zlib
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
 #else
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
 #endif
               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
             } while (file);
@@ -57911,7 +58435,7 @@ namespace cimg_library {
             static unsigned int snap_number = 0;
             std::FILE *file;
             do {
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.eps",snap_number++);
               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
             } while (file);
             (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp);
@@ -57932,7 +58456,7 @@ namespace cimg_library {
             static unsigned int snap_number = 0;
             std::FILE *file;
             do {
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.svg",snap_number++);
               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
             } while (file);
             (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp);
@@ -59306,7 +59830,7 @@ namespace cimg_library {
        \param use_bigtiff Allow to save big tiff files (>4Gb).
        \note
        - libtiff support is enabled by defining the precompilation
-        directive \c cimg_use_tif.
+        directive \c cimg_use_tiff.
        - When libtiff is enabled, 2D and 3D (multipage) several
         channel per pixel are supported for
         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
@@ -63080,7 +63604,13 @@ namespace cimg_library {
             static unsigned int snap_number = 0;
             std::FILE *file;
             do {
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
+#ifdef cimg_use_png
+                            "png",
+#else
+                            "bmp",
+#endif
+                            snap_number++);
               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
             } while (file);
             if (visu0) {
@@ -63096,9 +63626,9 @@ namespace cimg_library {
             std::FILE *file;
             do {
 #ifdef cimg_use_zlib
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
 #else
-              cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
+              cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
 #endif
               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
             } while (file);
@@ -65112,7 +65642,12 @@ namespace cimg_library {
                                   const char *codec=0, const bool keep_open=false) const {
 #ifndef cimg_use_opencv
       cimg::unused(codec,keep_open);
-      return save_ffmpeg_external(filename,fps);
+      if (keep_open) cimg::warn(_cimglist_instance
+                                "save_video(): Cannot output streamed video, as this requires features from the "
+                                "OpenCV library ('-Dcimg_use_opencv') must be defined).",
+                                cimglist_instance);
+      if (!is_empty()) return save_ffmpeg_external(filename,fps);
+      return *this;
 #else
       try {
         static cv::VideoWriter *writers[32] = {};
diff --git a/examples/CImg_demo.cpp b/examples/CImg_demo.cpp
index 4a03db80e..ad2993276 100644
--- a/examples/CImg_demo.cpp
+++ b/examples/CImg_demo.cpp
@@ -1288,7 +1288,7 @@ void* item_breakout() {
       board.fill(0); visu0 = background;
       cimg_forXY(board,x,y) if (0.2f + cimg::rand(-1,1)>=0) {
         CImg<float> cbrick = CImg<double>::vector(100 + cimg::rand()*155,100 + cimg::rand()*155,100 + cimg::rand()*155).
-          unroll('v').resize(brick.width(),brick.height());
+          unroll('c').resize(brick.width(),brick.height());
         cimg_forC(cbrick,k) (cbrick.get_shared_channel(k).mul(brick))/=255;
         visu0.draw_image(x*32,y*16,cbrick);
         board(x,y) = 1;
diff --git a/examples/image_surface3d.cpp b/examples/image_surface3d.cpp
index 292125975..cba7cef87 100644
--- a/examples/image_surface3d.cpp
+++ b/examples/image_surface3d.cpp
@@ -119,9 +119,9 @@ int main(int argc,char **argv) {
                (rtype==4?"Gouraud-shaded faces":(rtype==5?"Phong-shaded faces":"Isophotes"))))));
     static bool first_time = true;
     if (rtype==6) visu.display_object3d(disp,isopoints,isoprimitives,isocolors,first_time,1,-1,true,
-                                        500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data());
+                                        500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data(),true);
     else visu.display_object3d(disp,points,primitives,colors,first_time,rtype,-1,true,
-                               500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data());
+                               500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data(),true);
     first_time = false;
     switch (disp.key()) {
     case 0: break;
diff --git a/html/header.html b/html/header.html
index b04d1d934..6e382d21a 100644
--- a/html/header.html
+++ b/html/header.html
@@ -23,7 +23,7 @@
     <div class="header">
       <a href="index.html"><img alt="Logo" src="img/logo_header.jpg" class="center_image" style="margin-top:1em;"/></a>
       <h2  style="padding-bottom: 1em">
-        Latest stable version: <b><a href="http://cimg.eu/files/CImg_3.1.6.zip">3.1.6</a></b> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Current pre-release: <b><a href="http://cimg.eu/files/CImg_latest.zip">3.2.0</a></b>
+        Latest stable version: <b><a href="http://cimg.eu/files/CImg_3.2.5.zip">3.2.5</a></b>
       </h2>
 
       <hr/>
@@ -55,9 +55,9 @@
           <li><a href='links.html'><span>
                 <img alt="Links" src='img/menu_links.png' />&nbsp;&nbsp;
                 Links</span></a></li>
-          <li><a target="_blank" href='https://www.amazon.fr/dp/B08WRCZRR3/ref=cm_sw_em_r_mt_dp_Y4VV7GNQSBQDZ4XQ95YR'><span style="background-color:khaki">
-                <img alt="Book_fr" src='img/menu_tutorial.png' />&nbsp;&nbsp;
-                <span style="color: forestgreen">Book (Fr)</span></span></a>
+          <li><a target="_blank" href='https://www.taylorfrancis.com/books/mono/10.1201/9781003323693/digital-image-processing-vincent-barra-christophe-tilmant-david-tschumperle'><span style="background-color:khaki">
+                <img alt="Book" src='img/menu_tutorial.png' />&nbsp;&nbsp;
+                <span style="color: forestgreen">Book</span></span></a></li>
         </ul>
       </div>
       <hr/>
diff --git a/html/header_doxygen.html b/html/header_doxygen.html
index 42a695d03..c15a8fe95 100644
--- a/html/header_doxygen.html
+++ b/html/header_doxygen.html
@@ -26,7 +26,7 @@
     <div class="header">
       <a href="../index.html"><img alt="Logo" src="../img/logo_header.jpg" class="center_image" style="margin-top:1em;"/></a>
       <h2  style="padding-bottom: 1em">
-        Latest stable version: <b><a href="http://cimg.eu/files/CImg_3.1.6.zip">3.1.6</a></b> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Current pre-release: <b><a href="http://cimg.eu/files/CImg_latest.zip">3.2.0</a></b>
+        Latest stable version: <b><a href="http://cimg.eu/files/CImg_3.2.5.zip">3.2.5</a></b>
       </h2>
 
       <hr/>
@@ -58,9 +58,9 @@
           <li><a href='../links.html'><span>
                 <img alt="Links" src='../img/menu_links.png' />&nbsp;&nbsp;
                 Links</span></a></li>
-          <li><a target="_blank" href='https://www.amazon.fr/dp/B08WRCZRR3/ref=cm_sw_em_r_mt_dp_Y4VV7GNQSBQDZ4XQ95YR'><span style="background-color:khaki">
-                <img alt="Book_fr" src='../img/menu_tutorial.png' />&nbsp;&nbsp;
-                <span style="color: forestgreen">Book (Fr)</span></span></a>
+          <li><a target="_blank" href='https://www.taylorfrancis.com/books/mono/10.1201/9781003323693/digital-image-processing-vincent-barra-christophe-tilmant-david-tschumperle'><span style="background-color:khaki">
+                <img alt="Book" src='img/menu_tutorial.png' />&nbsp;&nbsp;
+                <span style="color: forestgreen">Book</span></span></a></li>
         </ul>
       </div>
       <hr/>
diff --git a/html/img/book_cimg_en.jpg b/html/img/book_cimg_en.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..ac3b55ecfa4f47db4a96d7d8d8d28f8aaa29a64b
GIT binary patch
literal 36326
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%0Ai&)qRgNs=s1{q=
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

literal 0
HcmV?d00001

diff --git a/html/index.html b/html/index.html
index 39518763b..422a8a881 100644
--- a/html/index.html
+++ b/html/index.html
@@ -106,15 +106,20 @@
     </div><div class="section_end"></div>
 
     <!-- ************* -->
-    <!--  Book (Fr)    -->
+    <!--  Book (En/Fr) -->
     <!-- ************* -->
-    <div class="section_title"><p>Book (Fr)</div><div class="section_content">
+    <div class="section_title"><p>Book (En/Fr)</div><div class="section_content">
 
       <ul>
-        <li>If you understand French, you may be interested by <a href="https://www.amazon.fr/dp/B08WRCZRR3/ref=cm_sw_em_r_mt_dp_Y4VV7GNQSBQDZ4XQ95YR">the nice book we wrote</a>
-          on how to use the <span class="gmd_cimg"></span> Library to develop image processing algorithms, from scratch.
-          In these 318 pages, we review the important concepts of the library and address a wide variety of applications in image processing
+        <li>We have a comprehensive book, written in English, on how to use the <span class="gmd_cimg"></span> Library
+          to develop various image processing algorithms, from scratch
+          (published by <a href="https://www.taylorfrancis.com/books/mono/10.1201/9781003323693/digital-image-processing-vincent-barra-christophe-tilmant-david-tschumperle">Taylor &amp; Francis Group</a>).
+          In this 308 pages book, we review the important concepts of the library and address a wide variety of applications in image processing
           (<i>Filtering, Mathematical Morphology, Feature Extraction, Segmentation, Multispectral Approaches, 3D Visualization, etc.</i>).<br/><br/>
+          <a target="_blank" href="https://www.taylorfrancis.com/books/mono/10.1201/9781003323693/digital-image-processing-vincent-barra-christophe-tilmant-david-tschumperle"><img class="center_image" src="img/book_cimg_en.jpg" /></a><br/>
+        </li>
+        <li>
+          If you understand French, you may be interested by the <a href="https://www.amazon.fr/dp/B08WRCZRR3/ref=cm_sw_em_r_mt_dp_Y4VV7GNQSBQDZ4XQ95YR">French version of this book</a><br/><br/>
           <a target="_blank" href="https://www.amazon.fr/dp/B08WRCZRR3/ref=cm_sw_em_r_mt_dp_Y4VV7GNQSBQDZ4XQ95YR"><img class="center_image" src="img/book_cimg.jpg" /></a>
         </li>
       </ul>
diff --git a/resources/compile_win_visualcpp.bat b/resources/compile_win_visualcpp.bat
index f553071b4..619901731 100644
--- a/resources/compile_win_visualcpp.bat
+++ b/resources/compile_win_visualcpp.bat
@@ -10,6 +10,6 @@ REM ----------------------------------------------------------------
 CD ..\examples\
 SET CPPFILE=CImg_demo captcha curve_editor2d dtmri_view3d edge_explorer2d fade_images gaussian_fit1d generate_loop_macros hough_transform2d image2ascii image_registration2d image_surface3d jawbreaker mcf_levelsets2d mcf_levelsets3d odykill pde_heatflow2d pde_TschumperleDeriche2d plotter1d radon_transform2d scene3d spherical_function3d tetris tron tutorial wavelet_atrous use_chlpca use_draw_gradient use_nlmeans use_RGBclass use_skeleton
 FOR %%F IN (%CPPFILE%) DO (
-  cl /W4 /wd"4127" /wd"4311" /wd"4312" /wd"4512" /wd"4571" /wd"4640" /wd"4706" /wd"4710" /wd"4800" /wd"4804" /wd"4820" /wd"4996" /Ox /Ob2 /Oi /Ot /c /EHsc /D "_CRT_SECURE_NO_WARNINGS" /I"%SDKPATH%\Include" /I"..\\" %%F.cpp
+  cl /W4 /wd"4127" /wd"4311" /wd"4312" /wd"4512" /wd"4571" /wd"4640" /wd"4706" /wd"4710" /wd"4800" /wd"4804" /wd"4820" /wd"4996" /c /EHsc /D "_CRT_SECURE_NO_WARNINGS" /I"%SDKPATH%\Include" /I"..\\" %%F.cpp
   link /LIBPATH:"%SDKPATH%\Lib" %%F.obj user32.lib gdi32.lib shell32.lib
 )