text:
# Julia wrapper for header: /usr/local/include/cpl_conv.h
# Automatically generated using Clang.jl wrap_c, version 0.0.0

"""
    CPLVerifyConfiguration() -> void
"""
function CPLVerifyConfiguration()
    ccall((:CPLVerifyConfiguration, libgdal), Void, ())
end

"""
    CPLGetConfigOption(const char * pszKey,
                       const char * pszDefault) -> const char *

Get the value of a configuration option.

### Parameters
* **pszKey**: the key of the option to retrieve
* **pszDefault**: a default value if the key does not match existing defined options (may be NULL)

### Returns
the value associated to the key, or the default value if not found
"""
function CPLGetConfigOption(arg1, arg2)
    ccall((:CPLGetConfigOption, libgdal), Cstring, (Cstring, Cstring), arg1, arg2)
end

"""
    CPLGetThreadLocalConfigOption(const char * pszKey,
                                  const char * pszDefault) -> const char *

Same as CPLGetConfigOption() but only with options set with CPLSetThreadLocalConfigOption()
"""
function CPLGetThreadLocalConfigOption(arg1, arg2)
    ccall((:CPLGetThreadLocalConfigOption, libgdal), Cstring, (Cstring, Cstring), arg1, arg2)
end

"""
    CPLSetConfigOption(const char * pszKey,
                       const char * pszValue) -> void

Set a configuration option for GDAL/OGR use.

### Parameters
* **pszKey**: the key of the option
* **pszValue**: the value of the option, or NULL to clear a setting.
"""
function CPLSetConfigOption(arg1, arg2)
    ccall((:CPLSetConfigOption, libgdal), Void, (Cstring, Cstring), arg1, arg2)
end

"""
    CPLSetThreadLocalConfigOption(const char * pszKey,
                                  const char * pszValue) -> void

Set a configuration option for GDAL/OGR use.

### Parameters
* **pszKey**: the key of the option
* **pszValue**: the value of the option, or NULL to clear a setting.
"""
function CPLSetThreadLocalConfigOption(pszKey, pszValue)
    ccall((:CPLSetThreadLocalConfigOption, libgdal), Void, (Cstring, Cstring), pszKey, pszValue)
end

"""
    CPLFreeConfig() -> void
"""
function CPLFreeConfig()
    ccall((:CPLFreeConfig, libgdal), Void, ())
end

"""
    CPLGetConfigOptions(void) -> char **

Return the list of configuration options as KEY=VALUE pairs.

### Returns
a copy of the list, to be freed with CSLDestroy().
"""
function CPLGetConfigOptions()
    ccall((:CPLGetConfigOptions, libgdal), Ptr{Cstring}, ())
end

"""
    CPLSetConfigOptions(const char *const * papszConfigOptions) -> void

Replace the full list of configuration options with the passed list of KEY=VALUE pairs.

### Parameters
* **papszConfigOptions**: the new list (or NULL).
"""
function CPLSetConfigOptions(papszConfigOptions)
    ccall((:CPLSetConfigOptions, libgdal), Void, (Ptr{Cstring},), papszConfigOptions)
end

"""
    CPLGetThreadLocalConfigOptions(void) -> char **

Return the list of thread local configuration options as KEY=VALUE pairs.

### Returns
a copy of the list, to be freed with CSLDestroy().
"""
function CPLGetThreadLocalConfigOptions()
    ccall((:CPLGetThreadLocalConfigOptions, libgdal), Ptr{Cstring}, ())
end

"""
    CPLSetThreadLocalConfigOptions(const char *const * papszConfigOptions) -> void

Replace the full list of thread local configuration options with the passed list of KEY=VALUE pairs.

### Parameters
* **papszConfigOptions**: the new list (or NULL).
"""
function CPLSetThreadLocalConfigOptions(papszConfigOptions)
    ccall((:CPLSetThreadLocalConfigOptions, libgdal), Void, (Ptr{Cstring},), papszConfigOptions)
end

"""
    CPLMalloc(size_t nSize) -> void *

Safe version of malloc().

### Parameters
* **nSize**: size (in bytes) of memory block to allocate.

### Returns
pointer to newly allocated memory, only NULL if nSize is zero.
"""
function CPLMalloc(arg1::Csize_t)
    ccall((:CPLMalloc, libgdal), Ptr{Void}, (Csize_t,), arg1)
end

"""
    CPLCalloc(size_t nCount,
              size_t nSize) -> void *

Safe version of calloc().

### Parameters
* **nCount**: number of objects to allocate.
* **nSize**: size (in bytes) of object to allocate.

### Returns
pointer to newly allocated memory, only NULL if nSize * nCount is NULL.
"""
function CPLCalloc(arg1::Csize_t, arg2::Csize_t)
    ccall((:CPLCalloc, libgdal), Ptr{Void}, (Csize_t, Csize_t), arg1, arg2)
end

"""
    CPLRealloc(void * pData,
               size_t nNewSize) -> void *

Safe version of realloc().

### Parameters
* **pData**: existing memory block which should be copied to the new block.
* **nNewSize**: new size (in bytes) of memory block to allocate.

### Returns
pointer to allocated memory, only NULL if nNewSize is zero.
"""
function CPLRealloc(arg1, arg2::Csize_t)
    ccall((:CPLRealloc, libgdal), Ptr{Void}, (Ptr{Void}, Csize_t), arg1, arg2)
end

"""
    CPLStrdup(const char * pszString) -> char *

Safe version of strdup() function.

### Parameters
* **pszString**: input string to be duplicated. May be NULL.

### Returns
pointer to a newly allocated copy of the string. Free with CPLFree() or VSIFree().
"""
function CPLStrdup(arg1)
    ccall((:CPLStrdup, libgdal), Cstring, (Cstring,), arg1)
end

"""
    CPLStrlwr(char * pszString) -> char *

Convert each characters of the string to lower case.

### Parameters
* **pszString**: input string to be converted.

### Returns
pointer to the same string, pszString.
"""
function CPLStrlwr(arg1)
    ccall((:CPLStrlwr, libgdal), Cstring, (Cstring,), arg1)
end

"""
    CPLFGets(char * pszBuffer,
             int nBufferSize,
             FILE * fp) -> char *

Reads in at most one less than nBufferSize characters from the fp stream and stores them into the buffer pointed to by pszBuffer.

### Parameters
* **pszBuffer**: pointer to the targeting character buffer.
* **nBufferSize**: maximum size of the string to read (not including terminating '\0').
* **fp**: file pointer to read from.

### Returns
pointer to the pszBuffer containing a string read from the file or NULL if the error or end of file was encountered.
"""
function CPLFGets(arg1, arg2::Cint, arg3)
    ccall((:CPLFGets, libgdal), Cstring, (Cstring, Cint, Ptr{FILE}), arg1, arg2, arg3)
end

"""
    CPLReadLine(FILE * fp) -> const char *

Simplified line reading from text file.

### Parameters
* **fp**: file pointer opened with VSIFOpen().

### Returns
pointer to an internal buffer containing a line of text read from the file or NULL if the end of file was encountered.
"""
function CPLReadLine(arg1)
    ccall((:CPLReadLine, libgdal), Cstring, (Ptr{FILE},), arg1)
end

"""
    CPLReadLineL(VSILFILE * fp) -> const char *

Simplified line reading from text file.

### Parameters
* **fp**: file pointer opened with VSIFOpenL().

### Returns
pointer to an internal buffer containing a line of text read from the file or NULL if the end of file was encountered.
"""
function CPLReadLineL(arg1)
    ccall((:CPLReadLineL, libgdal), Cstring, (Ptr{VSILFILE},), arg1)
end

"""
    CPLReadLine2L(VSILFILE * fp,
                  int nMaxCars,
                  const char *const * papszOptions) -> const char *

Simplified line reading from text file.

### Parameters
* **fp**: file pointer opened with VSIFOpenL().
* **nMaxCars**: maximum number of characters allowed, or -1 for no limit.
* **papszOptions**: NULL-terminated array of options. Unused for now.

### Returns
pointer to an internal buffer containing a line of text read from the file or NULL if the end of file was encountered or the maximum number of characters allowed reached.
"""
function CPLReadLine2L(arg1, nMaxCols::Cint, papszOptions)
    ccall((:CPLReadLine2L, libgdal), Cstring, (Ptr{VSILFILE}, Cint, Ptr{Cstring}), arg1, nMaxCols, papszOptions)
end

"""
    CPLAtof(const char *) -> double

Converts ASCII string to floating point number.

### Parameters
* **nptr**: Pointer to string to convert.

### Returns
Converted value, if any.
"""
function CPLAtof(arg1)
    ccall((:CPLAtof, libgdal), Cdouble, (Cstring,), arg1)
end

"""
    CPLAtofDelim(const char *,
                 char) -> double

Converts ASCII string to floating point number.

### Parameters
* **nptr**: Pointer to string to convert.
* **point**: Decimal delimiter.

### Returns
Converted value, if any.
"""
function CPLAtofDelim(arg1, arg2::UInt8)
    ccall((:CPLAtofDelim, libgdal), Cdouble, (Cstring, UInt8), arg1, arg2)
end

"""
    CPLStrtod(const char *,
              char **) -> double

Converts ASCII string to floating point number.

### Parameters
* **nptr**: Pointer to string to convert.
* **endptr**: If is not NULL, a pointer to the character after the last character used in the conversion is stored in the location referenced by endptr.

### Returns
Converted value, if any.
"""
function CPLStrtod(arg1, arg2)
    ccall((:CPLStrtod, libgdal), Cdouble, (Cstring, Ptr{Cstring}), arg1, arg2)
end

"""
    CPLStrtodDelim(const char *,
                   char **,
                   char) -> double

Converts ASCII string to floating point number using specified delimiter.

### Parameters
* **nptr**: Pointer to string to convert.
* **endptr**: If is not NULL, a pointer to the character after the last character used in the conversion is stored in the location referenced by endptr.
* **point**: Decimal delimiter.

### Returns
Converted value, if any.
"""
function CPLStrtodDelim(arg1, arg2, arg3::UInt8)
    ccall((:CPLStrtodDelim, libgdal), Cdouble, (Cstring, Ptr{Cstring}, UInt8), arg1, arg2, arg3)
end

"""
    CPLStrtof(const char *,
              char **) -> float

Converts ASCII string to floating point number.

### Parameters
* **nptr**: Pointer to string to convert.
* **endptr**: If is not NULL, a pointer to the character after the last character used in the conversion is stored in the location referenced by endptr.

### Returns
Converted value, if any.
"""
function CPLStrtof(arg1, arg2)
    ccall((:CPLStrtof, libgdal), Cfloat, (Cstring, Ptr{Cstring}), arg1, arg2)
end

"""
    CPLStrtofDelim(const char *,
                   char **,
                   char) -> float

Converts ASCII string to floating point number using specified delimiter.

### Parameters
* **nptr**: Pointer to string to convert.
* **endptr**: If is not NULL, a pointer to the character after the last character used in the conversion is stored in the location referenced by endptr.
* **point**: Decimal delimiter.

### Returns
Converted value, if any.
"""
function CPLStrtofDelim(arg1, arg2, arg3::UInt8)
    ccall((:CPLStrtofDelim, libgdal), Cfloat, (Cstring, Ptr{Cstring}, UInt8), arg1, arg2, arg3)
end

"""
    CPLAtofM(const char *) -> double

Converts ASCII string to floating point number using any numeric locale.

### Parameters
* **nptr**: The string to convert.

### Returns
Converted value, if any. Zero on failure.
"""
function CPLAtofM(arg1)
    ccall((:CPLAtofM, libgdal), Cdouble, (Cstring,), arg1)
end

"""
    CPLScanString(const char * pszString,
                  int nMaxLength,
                  int bTrimSpaces,
                  int bNormalize) -> char *

Scan up to a maximum number of characters from a given string, allocate a buffer for a new string and fill it with scanned characters.

### Parameters
* **pszString**: String containing characters to be scanned. It may be terminated with a null character.
* **nMaxLength**: The maximum number of character to read. Less characters will be read if a null character is encountered.
* **bTrimSpaces**: If TRUE, trim ending spaces from the input string. Character considered as empty using isspace(3) function.
* **bNormalize**: If TRUE, replace ':' symbol with the '_'. It is needed if resulting string will be used in CPL dictionaries.

### Returns
Pointer to the resulting string buffer. Caller responsible to free this buffer with CPLFree().
"""
function CPLScanString(arg1, arg2::Cint, arg3::Cint, arg4::Cint)
    ccall((:CPLScanString, libgdal), Cstring, (Cstring, Cint, Cint, Cint), arg1, arg2, arg3, arg4)
end

"""
    CPLScanDouble(const char * pszString,
                  int nMaxLength) -> double

Extract double from string.

### Parameters
* **pszString**: String containing characters to be scanned. It may be terminated with a null character.
* **nMaxLength**: The maximum number of character to consider as part of the number. Less characters will be considered if a null character is encountered.

### Returns
Double value, converted from its ASCII form.
"""
function CPLScanDouble(arg1, arg2::Cint)
    ccall((:CPLScanDouble, libgdal), Cdouble, (Cstring, Cint), arg1, arg2)
end

"""
    CPLScanLong(const char * pszString,
                int nMaxLength) -> long

Scan up to a maximum number of characters from a string and convert the result to a long.

### Parameters
* **pszString**: String containing characters to be scanned. It may be terminated with a null character.
* **nMaxLength**: The maximum number of character to consider as part of the number. Less characters will be considered if a null character is encountered.

### Returns
Long value, converted from its ASCII form.
"""
function CPLScanLong(arg1, arg2::Cint)
    ccall((:CPLScanLong, libgdal), Clong, (Cstring, Cint), arg1, arg2)
end

"""
    CPLScanULong(const char * pszString,
                 int nMaxLength) -> unsigned long

Scan up to a maximum number of characters from a string and convert the result to a unsigned long.

### Parameters
* **pszString**: String containing characters to be scanned. It may be terminated with a null character.
* **nMaxLength**: The maximum number of character to consider as part of the number. Less characters will be considered if a null character is encountered.

### Returns
Unsigned long value, converted from its ASCII form.
"""
function CPLScanULong(arg1, arg2::Cint)
    ccall((:CPLScanULong, libgdal), Culong, (Cstring, Cint), arg1, arg2)
end

"""
    CPLScanUIntBig(const char * pszString,
                   int nMaxLength) -> GUIntBig

Extract big integer from string.

### Parameters
* **pszString**: String containing characters to be scanned. It may be terminated with a null character.
* **nMaxLength**: The maximum number of character to consider as part of the number. Less characters will be considered if a null character is encountered.

### Returns
GUIntBig value, converted from its ASCII form.
"""
function CPLScanUIntBig(arg1, arg2::Cint)
    ccall((:CPLScanUIntBig, libgdal), GUIntBig, (Cstring, Cint), arg1, arg2)
end

"""
    CPLAtoGIntBig(const char * pszString) -> GIntBig

Convert a string to a 64 bit signed integer.

### Parameters
* **pszString**: String containing 64 bit signed integer.

### Returns
64 bit signed integer.
"""
function CPLAtoGIntBig(pszString)
    ccall((:CPLAtoGIntBig, libgdal), GIntBig, (Cstring,), pszString)
end

"""
    CPLAtoGIntBigEx(const char * pszString,
                    int bWarn,
                    int * pbOverflow) -> GIntBig

Convert a string to a 64 bit signed integer.

### Parameters
* **pszString**: String containing 64 bit signed integer.
* **bWarn**: Issue a warning if an overflow occurs during conversion
* **pbOverflow**: Pointer to an integer to store if an overflow occurred, or NULL

### Returns
64 bit signed integer.
"""
function CPLAtoGIntBigEx(pszString, bWarn::Cint, pbOverflow)
    ccall((:CPLAtoGIntBigEx, libgdal), GIntBig, (Cstring, Cint, Ptr{Cint}), pszString, bWarn, pbOverflow)
end

"""
    CPLScanPointer(const char * pszString,
                   int nMaxLength) -> void *

Extract pointer from string.

### Parameters
* **pszString**: String containing characters to be scanned. It may be terminated with a null character.
* **nMaxLength**: The maximum number of character to consider as part of the number. Less characters will be considered if a null character is encountered.

### Returns
pointer value, converted from its ASCII form.
"""
function CPLScanPointer(arg1, arg2::Cint)
    ccall((:CPLScanPointer, libgdal), Ptr{Void}, (Cstring, Cint), arg1, arg2)
end

"""
    CPLPrintString(char * pszDest,
                   const char * pszSrc,
                   int nMaxLen) -> int

Copy the string pointed to by pszSrc, NOT including the terminating `\0' character, to the array pointed to by pszDest.

### Parameters
* **pszDest**: Pointer to the destination string buffer. Should be large enough to hold the resulting string.
* **pszSrc**: Pointer to the source buffer.
* **nMaxLen**: Maximum length of the resulting string. If string length is greater than nMaxLen, it will be truncated.

### Returns
Number of characters printed.
"""
function CPLPrintString(arg1, arg2, arg3::Cint)
    ccall((:CPLPrintString, libgdal), Cint, (Cstring, Cstring, Cint), arg1, arg2, arg3)
end

"""
    CPLPrintStringFill(char * pszDest,
                       const char * pszSrc,
                       int nMaxLen) -> int

Copy the string pointed to by pszSrc, NOT including the terminating `\0' character, to the array pointed to by pszDest.

### Parameters
* **pszDest**: Pointer to the destination string buffer. Should be large enough to hold the resulting string.
* **pszSrc**: Pointer to the source buffer.
* **nMaxLen**: Maximum length of the resulting string. If string length is greater than nMaxLen, it will be truncated.

### Returns
Number of characters printed.
"""
function CPLPrintStringFill(arg1, arg2, arg3::Cint)
    ccall((:CPLPrintStringFill, libgdal), Cint, (Cstring, Cstring, Cint), arg1, arg2, arg3)
end

"""
    CPLPrintInt32(char * pszBuffer,
                  GInt32 iValue,
                  int nMaxLen) -> int

Print GInt32 value into specified string buffer.

### Parameters
* **pszBuffer**: Pointer to the destination string buffer. Should be large enough to hold the resulting string. Note, that the string will not be NULL-terminated, so user should do this himself, if needed.
* **iValue**: Numerical value to print.
* **nMaxLen**: Maximum length of the resulting string. If string length is greater than nMaxLen, it will be truncated.

### Returns
Number of characters printed.
"""
function CPLPrintInt32(arg1, arg2::GInt32, arg3::Cint)
    ccall((:CPLPrintInt32, libgdal), Cint, (Cstring, GInt32, Cint), arg1, arg2, arg3)
end

"""
    CPLPrintUIntBig(char * pszBuffer,
                    GUIntBig iValue,
                    int nMaxLen) -> int

Print GUIntBig value into specified string buffer.

### Parameters
* **pszBuffer**: Pointer to the destination string buffer. Should be large enough to hold the resulting string. Note, that the string will not be NULL-terminated, so user should do this himself, if needed.
* **iValue**: Numerical value to print.
* **nMaxLen**: Maximum length of the resulting string. If string length is greater than nMaxLen, it will be truncated.

### Returns
Number of characters printed.
"""
function CPLPrintUIntBig(arg1, arg2::GUIntBig, arg3::Cint)
    ccall((:CPLPrintUIntBig, libgdal), Cint, (Cstring, GUIntBig, Cint), arg1, arg2, arg3)
end

"""
    CPLPrintDouble(char * pszBuffer,
                   const char * pszFormat,
                   double dfValue,
                   const char * pszLocale) -> int

Print double value into specified string buffer.

### Parameters
* **pszBuffer**: Pointer to the destination string buffer. Should be large enough to hold the resulting string. Note, that the string will not be NULL-terminated, so user should do this himself, if needed.
* **pszFormat**: Format specifier (for example, "%16.9E").
* **dfValue**: Numerical value to print.
* **pszLocale**: Unused.

### Returns
Number of characters printed.
"""
function CPLPrintDouble(arg1, arg2, arg3::Cdouble, arg4)
    ccall((:CPLPrintDouble, libgdal), Cint, (Cstring, Cstring, Cdouble, Cstring), arg1, arg2, arg3, arg4)
end

"""
    CPLPrintTime(char * pszBuffer,
                 int nMaxLen,
                 const char * pszFormat,
                 const struct tm * poBrokenTime,
                 const char * pszLocale) -> int

Print specified time value accordingly to the format options and specified locale name.

### Parameters
* **pszBuffer**: Pointer to the destination string buffer. Should be large enough to hold the resulting string. Note, that the string will not be NULL-terminated, so user should do this himself, if needed.
* **nMaxLen**: Maximum length of the resulting string. If string length is greater than nMaxLen, it will be truncated.
* **pszFormat**: Controls the output format. Options are the same as for strftime(3) function.
* **poBrokenTime**: Pointer to the broken-down time structure. May be requested with the VSIGMTime() and VSILocalTime() functions.
* **pszLocale**: Pointer to a character string containing locale name ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL we will not manipulate with locale settings and current process locale will be used for printing. Be aware that it may be unsuitable to use current locale for printing time, because all names will be printed in your native language, as well as time format settings also may be adjusted differently from the C/POSIX defaults. To solve these problems this option was introduced.

### Returns
Number of characters printed.
"""
function CPLPrintTime(arg1, arg2::Cint, arg3, arg4, arg5)
    ccall((:CPLPrintTime, libgdal), Cint, (Cstring, Cint, Cstring, Ptr{Void}, Cstring), arg1, arg2, arg3, arg4, arg5)
end

"""
    CPLPrintPointer(char * pszBuffer,
                    void * pValue,
                    int nMaxLen) -> int

Print pointer value into specified string buffer.

### Parameters
* **pszBuffer**: Pointer to the destination string buffer. Should be large enough to hold the resulting string. Note, that the string will not be NULL-terminated, so user should do this himself, if needed.
* **pValue**: Pointer to ASCII encode.
* **nMaxLen**: Maximum length of the resulting string. If string length is greater than nMaxLen, it will be truncated.

### Returns
Number of characters printed.
"""
function CPLPrintPointer(arg1, arg2, arg3::Cint)
    ccall((:CPLPrintPointer, libgdal), Cint, (Cstring, Ptr{Void}, Cint), arg1, arg2, arg3)
end

"""
    CPLGetSymbol(const char * pszLibrary,
                 const char * pszSymbolName) -> void *

Fetch a function pointer from a shared library / DLL.

### Parameters
* **pszLibrary**: the name of the shared library or DLL containing the function. May contain path to file. If not system supplies search paths will be used.
* **pszSymbolName**: the name of the function to fetch a pointer to.

### Returns
A pointer to the function if found, or NULL if the function isn't found, or the shared library can't be loaded.
"""
function CPLGetSymbol(arg1, arg2)
    ccall((:CPLGetSymbol, libgdal), Ptr{Void}, (Cstring, Cstring), arg1, arg2)
end

"""
    CPLGetExecPath(char * pszPathBuf,
                   int nMaxLength) -> int

Fetch path of executable.

### Parameters
* **pszPathBuf**: the buffer into which the path is placed.
* **nMaxLength**: the buffer size, MAX_PATH+1 is suggested.

### Returns
FALSE on failure or TRUE on success.
"""
function CPLGetExecPath(pszPathBuf, nMaxLength::Cint)
    ccall((:CPLGetExecPath, libgdal), Cint, (Cstring, Cint), pszPathBuf, nMaxLength)
end

"""
    CPLGetPath(const char *) -> const char *

Extract directory path portion of filename.

### Parameters
* **pszFilename**: the filename potentially including a path.

### Returns
Path in an internal string which must not be freed. The string may be destroyed by the next CPL filename handling call. The returned will generally not contain a trailing path separator.
"""
function CPLGetPath(arg1)
    ccall((:CPLGetPath, libgdal), Cstring, (Cstring,), arg1)
end

"""
    CPLGetDirname(const char *) -> const char *

Extract directory path portion of filename.

### Parameters
* **pszFilename**: the filename potentially including a path.

### Returns
Path in an internal string which must not be freed. The string may be destroyed by the next CPL filename handling call. The returned will generally not contain a trailing path separator.
"""
function CPLGetDirname(arg1)
    ccall((:CPLGetDirname, libgdal), Cstring, (Cstring,), arg1)
end

"""
    CPLGetFilename(const char *) -> const char *

Extract non-directory portion of filename.

### Parameters
* **pszFullFilename**: the full filename potentially including a path.

### Returns
just the non-directory portion of the path (points back into original string).
"""
function CPLGetFilename(arg1)
    ccall((:CPLGetFilename, libgdal), Cstring, (Cstring,), arg1)
end

"""
    CPLGetBasename(const char *) -> const char *

Extract basename (non-directory, non-extension) portion of filename.

### Parameters
* **pszFullFilename**: the full filename potentially including a path.

### Returns
just the non-directory, non-extension portion of the path in an internal string which must not be freed. The string may be destroyed by the next CPL filename handling call.
"""
function CPLGetBasename(arg1)
    ccall((:CPLGetBasename, libgdal), Cstring, (Cstring,), arg1)
end

"""
    CPLGetExtension(const char *) -> const char *

Extract filename extension from full filename.

### Parameters
* **pszFullFilename**: the full filename potentially including a path.

### Returns
just the extension portion of the path in an internal string which must not be freed. The string may be destroyed by the next CPL filename handling call.
"""
function CPLGetExtension(arg1)
    ccall((:CPLGetExtension, libgdal), Cstring, (Cstring,), arg1)
end

"""
    CPLGetCurrentDir(void) -> char *

Get the current working directory name.

### Returns
a pointer to buffer, containing current working directory path or NULL in case of error. User is responsible to free that buffer after usage with CPLFree() function. If HAVE_GETCWD macro is not defined, the function returns NULL.
"""
function CPLGetCurrentDir()
    ccall((:CPLGetCurrentDir, libgdal), Cstring, ())
end

"""
    CPLFormFilename(const char * pszPath,
                    const char * pszBasename,
                    const char * pszExtension) -> const char *

Build a full file path from a passed path, file basename and extension.

### Parameters
* **pszPath**: directory path to the directory containing the file. This may be relative or absolute, and may have a trailing path separator or not. May be NULL.
* **pszBasename**: file basename. May optionally have path and/or extension. Must NOT be NULL.
* **pszExtension**: file extension, optionally including the period. May be NULL.

### Returns
a fully formed filename in an internal static string. Do not modify or free the returned string. The string may be destroyed by the next CPL call.
"""
function CPLFormFilename(pszPath, pszBasename, pszExtension)
    ccall((:CPLFormFilename, libgdal), Cstring, (Cstring, Cstring, Cstring), pszPath, pszBasename, pszExtension)
end

"""
    CPLFormCIFilename(const char * pszPath,
                      const char * pszBasename,
                      const char * pszExtension) -> const char *

Case insensitive file searching, returning full path.

### Parameters
* **pszPath**: directory path to the directory containing the file. This may be relative or absolute, and may have a trailing path separator or not. May be NULL.
* **pszBasename**: file basename. May optionally have path and/or extension. May not be NULL.
* **pszExtension**: file extension, optionally including the period. May be NULL.

### Returns
a fully formed filename in an internal static string. Do not modify or free the returned string. The string may be destroyed by the next CPL call.
"""
function CPLFormCIFilename(pszPath, pszBasename, pszExtension)
    ccall((:CPLFormCIFilename, libgdal), Cstring, (Cstring, Cstring, Cstring), pszPath, pszBasename, pszExtension)
end

"""
    CPLResetExtension(const char *,
                      const char *) -> const char *

Replace the extension with the provided one.

### Parameters
* **pszPath**: the input path, this string is not altered.
* **pszExt**: the new extension to apply to the given path.

### Returns
an altered filename with the new extension. Do not modify or free the returned string. The string may be destroyed by the next CPL call.
"""
function CPLResetExtension(arg1, arg2)
    ccall((:CPLResetExtension, libgdal), Cstring, (Cstring, Cstring), arg1, arg2)
end

"""
    CPLProjectRelativeFilename(const char * pszProjectDir,
                               const char * pszSecondaryFilename) -> const char *

Find a file relative to a project file.

### Parameters
* **pszProjectDir**: the directory relative to which the secondary files path should be interpreted.
* **pszSecondaryFilename**: the filename (potentially with path) that is to be interpreted relative to the project directory.

### Returns
a composed path to the secondary file. The returned string is internal and should not be altered, freed, or depending on past the next CPL call.
"""
function CPLProjectRelativeFilename(pszProjectDir, pszSecondaryFilename)
    ccall((:CPLProjectRelativeFilename, libgdal), Cstring, (Cstring, Cstring), pszProjectDir, pszSecondaryFilename)
end

"""
    CPLIsFilenameRelative(const char * pszFilename) -> int

Is filename relative or absolute?

### Parameters
* **pszFilename**: the filename with path to test.

### Returns
TRUE if the filename is relative or FALSE if it is absolute.
"""
function CPLIsFilenameRelative(pszFilename)
    ccall((:CPLIsFilenameRelative, libgdal), Cint, (Cstring,), pszFilename)
end

"""
    CPLExtractRelativePath(const char *,
                           const char *,
                           int *) -> const char *

Get relative path from directory to target file.

### Parameters
* **pszBaseDir**: the name of the directory relative to which the path should be computed. pszBaseDir may be NULL in which case the original target is returned without relativizing.
* **pszTarget**: the filename to be changed to be relative to pszBaseDir.
* **pbGotRelative**: Pointer to location in which a flag is placed indicating that the returned path is relative to the basename (TRUE) or not (FALSE). This pointer may be NULL if flag is not desired.

### Returns
an adjusted path or the original if it could not be made relative to the pszBaseFile's path.
"""
function CPLExtractRelativePath(arg1, arg2, arg3)
    ccall((:CPLExtractRelativePath, libgdal), Cstring, (Cstring, Cstring, Ptr{Cint}), arg1, arg2, arg3)
end

"""
    CPLCleanTrailingSlash(const char *) -> const char *

Remove trailing forward/backward slash from the path for UNIX/Windows resp.

### Parameters
* **pszPath**: the path to be cleaned up

### Returns
Path in an internal string which must not be freed. The string may be destroyed by the next CPL filename handling call.
"""
function CPLCleanTrailingSlash(arg1)
    ccall((:CPLCleanTrailingSlash, libgdal), Cstring, (Cstring,), arg1)
end

"""
    CPLCorrespondingPaths(const char * pszOldFilename,
                          const char * pszNewFilename,
                          char ** papszFileList) -> char **

Identify corresponding paths.

### Parameters
* **pszOldFilename**: path to old prototype file.
* **pszNewFilename**: path to new prototype file.
* **papszFileList**: list of other files associated with pszOldFilename to rename similarly.

### Returns
a list of files corresponding to papszFileList but renamed to correspond to pszNewFilename.
"""
function CPLCorrespondingPaths(pszOldFilename, pszNewFilename, papszFileList)
    ccall((:CPLCorrespondingPaths, libgdal), Ptr{Cstring}, (Cstring, Cstring, Ptr{Cstring}), pszOldFilename, pszNewFilename, papszFileList)
end

"""
    CPLCheckForFile(char * pszFilename,
                    char ** papszSiblingFiles) -> int

Check for file existence.

### Parameters
* **pszFilename**: name of file to check for - filename case updated in some cases.
* **papszSiblingFiles**: a list of files in the same directory as pszFilename if available, or NULL. This list should have no path components.

### Returns
TRUE if a match is found, or FALSE if not.
"""
function CPLCheckForFile(pszFilename, papszSiblingList)
    ccall((:CPLCheckForFile, libgdal), Cint, (Cstring, Ptr{Cstring}), pszFilename, papszSiblingList)
end

"""
    CPLGenerateTempFilename(const char * pszStem) -> const char *

Generate temporary file name.

### Parameters
* **pszStem**: if non-NULL this will be part of the filename.

### Returns
a filename which is valid till the next CPL call in this thread.
"""
function CPLGenerateTempFilename(pszStem)
    ccall((:CPLGenerateTempFilename, libgdal), Cstring, (Cstring,), pszStem)
end

"""
    CPLExpandTilde(const char * pszFilename) -> const char *

Expands ~/ at start of filename.

### Parameters
* **pszFilename**: filename potentially starting with ~/

### Returns
an expanded filename.
"""
function CPLExpandTilde(pszFilename)
    ccall((:CPLExpandTilde, libgdal), Cstring, (Cstring,), pszFilename)
end

"""
    CPLFindFile(const char * pszClass,
                const char * pszBasename) -> const char *

CPLFindFile.
"""
function CPLFindFile(pszClass, pszBasename)
    ccall((:CPLFindFile, libgdal), Cstring, (Cstring, Cstring), pszClass, pszBasename)
end

"""
    CPLDefaultFindFile(const char * pszClass,
                       const char * pszBasename) -> const char *

CPLDefaultFindFile.
"""
function CPLDefaultFindFile(pszClass, pszBasename)
    ccall((:CPLDefaultFindFile, libgdal), Cstring, (Cstring, Cstring), pszClass, pszBasename)
end

"""
    CPLPushFileFinder(CPLFileFinder pfnFinder) -> void

CPLPushFileFinder.
"""
function CPLPushFileFinder(pfnFinder::CPLFileFinder)
    ccall((:CPLPushFileFinder, libgdal), Void, (CPLFileFinder,), pfnFinder)
end

"""
    CPLPopFileFinder(void) -> CPLFileFinder

CPLPopFileFinder.
"""
function CPLPopFileFinder()
    ccall((:CPLPopFileFinder, libgdal), CPLFileFinder, ())
end

"""
    CPLPushFinderLocation(const char *) -> void

CPLPushFinderLocation.
"""
function CPLPushFinderLocation(arg1)
    ccall((:CPLPushFinderLocation, libgdal), Void, (Cstring,), arg1)
end

"""
    CPLPopFinderLocation(void) -> void

CPLPopFinderLocation.
"""
function CPLPopFinderLocation()
    ccall((:CPLPopFinderLocation, libgdal), Void, ())
end

"""
    CPLFinderClean(void) -> void

CPLFinderClean.
"""
function CPLFinderClean()
    ccall((:CPLFinderClean, libgdal), Void, ())
end

"""
    CPLStat(const char * pszPath,
            VSIStatBuf * psStatBuf) -> int

Same as VSIStat() except it works on "C:" as if it were "C:\".
"""
function CPLStat(arg1, arg2)
    ccall((:CPLStat, libgdal), Cint, (Cstring, Ptr{VSIStatBuf}), arg1, arg2)
end

"""
    CPLOpenShared(const char * pszFilename,
                  const char * pszAccess,
                  int bLargeIn) -> FILE *

Open a shared file handle.

### Parameters
* **pszFilename**: the name of the file to open.
* **pszAccess**: the normal fopen()/VSIFOpen() style access string.
* **bLargeIn**: If TRUE VSIFOpenL() (for large files) will be used instead of VSIFOpen().

### Returns
a file handle or NULL if opening fails.
"""
function CPLOpenShared(arg1, arg2, arg3::Cint)
    ccall((:CPLOpenShared, libgdal), Ptr{FILE}, (Cstring, Cstring, Cint), arg1, arg2, arg3)
end

"""
    CPLCloseShared(FILE * fp) -> void

Close shared file.

### Parameters
* **fp**: file handle from CPLOpenShared() to deaccess.
"""
function CPLCloseShared(arg1)
    ccall((:CPLCloseShared, libgdal), Void, (Ptr{FILE},), arg1)
end

"""
    CPLGetSharedList(int * pnCount) -> CPLSharedFileInfo *

Fetch list of open shared files.

### Parameters
* **pnCount**: place to put the count of entries.

### Returns
the pointer to the first in the array of shared file info structures.
"""
function CPLGetSharedList(arg1)
    ccall((:CPLGetSharedList, libgdal), Ptr{CPLSharedFileInfo}, (Ptr{Cint},), arg1)
end

"""
    CPLDumpSharedList(FILE * fp) -> void

Report open shared files.

### Parameters
* **fp**: File handle to write to.
"""
function CPLDumpSharedList(arg1)
    ccall((:CPLDumpSharedList, libgdal), Void, (Ptr{FILE},), arg1)
end

"""
    CPLCleanupSharedFileMutex() -> void
"""
function CPLCleanupSharedFileMutex()
    ccall((:CPLCleanupSharedFileMutex, libgdal), Void, ())
end

"""
    CPLDMSToDec(const char * is) -> double

CPLDMSToDec.
"""
function CPLDMSToDec(is)
    ccall((:CPLDMSToDec, libgdal), Cdouble, (Cstring,), is)
end

"""
    CPLDecToDMS(double dfAngle,
                const char * pszAxis,
                int nPrecision) -> const char *

Translate a decimal degrees value to a DMS string with hemisphere.
"""
function CPLDecToDMS(dfAngle::Cdouble, pszAxis, nPrecision::Cint)
    ccall((:CPLDecToDMS, libgdal), Cstring, (Cdouble, Cstring, Cint), dfAngle, pszAxis, nPrecision)
end

"""
    CPLPackedDMSToDec(double dfPacked) -> double

Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.

### Parameters
* **dfPacked**: Angle in packed DMS format.

### Returns
Angle in decimal degrees.
"""
function CPLPackedDMSToDec(arg1::Cdouble)
    ccall((:CPLPackedDMSToDec, libgdal), Cdouble, (Cdouble,), arg1)
end

"""
    CPLDecToPackedDMS(double dfDec) -> double

Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).

### Parameters
* **dfDec**: Angle in decimal degrees.

### Returns
Angle in packed DMS format.
"""
function CPLDecToPackedDMS(dfDec::Cdouble)
    ccall((:CPLDecToPackedDMS, libgdal), Cdouble, (Cdouble,), dfDec)
end

"""
    CPLStringToComplex(const char * pszString,
                       double * pdfReal,
                       double * pdfImag) -> void

Fetch the real and imaginary part of a serialized complex number.
"""
function CPLStringToComplex(pszString, pdfReal, pdfImag)
    ccall((:CPLStringToComplex, libgdal), Void, (Cstring, Ptr{Cdouble}, Ptr{Cdouble}), pszString, pdfReal, pdfImag)
end

"""
    CPLUnlinkTree(const char * pszPath) -> int

Recursively unlink a directory.

### Returns
0 on successful completion, -1 if function fails.
"""
function CPLUnlinkTree(arg1)
    ccall((:CPLUnlinkTree, libgdal), Cint, (Cstring,), arg1)
end

"""
    CPLCopyFile(const char * pszNewPath,
                const char * pszOldPath) -> int

Copy a file.
"""
function CPLCopyFile(pszNewPath, pszOldPath)
    ccall((:CPLCopyFile, libgdal), Cint, (Cstring, Cstring), pszNewPath, pszOldPath)
end

"""
    CPLCopyTree(const char * pszNewPath,
                const char * pszOldPath) -> int

Recursively copy a tree.
"""
function CPLCopyTree(pszNewPath, pszOldPath)
    ccall((:CPLCopyTree, libgdal), Cint, (Cstring, Cstring), pszNewPath, pszOldPath)
end

"""
    CPLMoveFile(const char * pszNewPath,
                const char * pszOldPath) -> int

Move a file.
"""
function CPLMoveFile(pszNewPath, pszOldPath)
    ccall((:CPLMoveFile, libgdal), Cint, (Cstring, Cstring), pszNewPath, pszOldPath)
end

"""
    CPLSymlink(const char * pszOldPath,
               const char * pszNewPath,
               char **) -> int

Create a symbolic link.
"""
function CPLSymlink(pszOldPath, pszNewPath, papszOptions)
    ccall((:CPLSymlink, libgdal), Cint, (Cstring, Cstring, Ptr{Cstring}), pszOldPath, pszNewPath, papszOptions)
end

"""
    CPLCreateZip(const char *,
                 char **) -> void *
"""
function CPLCreateZip(pszZipFilename, papszOptions)
    ccall((:CPLCreateZip, libgdal), Ptr{Void}, (Cstring, Ptr{Cstring}), pszZipFilename, papszOptions)
end

"""
    CPLCreateFileInZip(void *,
                       const char *,
                       char **) -> CPLErr
"""
function CPLCreateFileInZip(hZip, pszFilename, papszOptions)
    ccall((:CPLCreateFileInZip, libgdal), CPLErr, (Ptr{Void}, Cstring, Ptr{Cstring}), hZip, pszFilename, papszOptions)
end

"""
    CPLWriteFileInZip(void *,
                      const void *,
                      int) -> CPLErr
"""
function CPLWriteFileInZip(hZip, pBuffer, nBufferSize::Cint)
    ccall((:CPLWriteFileInZip, libgdal), CPLErr, (Ptr{Void}, Ptr{Void}, Cint), hZip, pBuffer, nBufferSize)
end

"""
    CPLCloseFileInZip(void *) -> CPLErr
"""
function CPLCloseFileInZip(hZip)
    ccall((:CPLCloseFileInZip, libgdal), CPLErr, (Ptr{Void},), hZip)
end

"""
    CPLCloseZip(void *) -> CPLErr
"""
function CPLCloseZip(hZip)
    ccall((:CPLCloseZip, libgdal), CPLErr, (Ptr{Void},), hZip)
end

"""
    CPLZLibDeflate(const void *,
                   size_t,
                   int,
                   void *,
                   size_t,
                   size_t * pnOutBytes) -> void *
"""
function CPLZLibDeflate(ptr, nBytes::Csize_t, nLevel::Cint, outptr, nOutAvailableBytes::Csize_t, pnOutBytes)
    ccall((:CPLZLibDeflate, libgdal), Ptr{Void}, (Ptr{Void}, Csize_t, Cint, Ptr{Void}, Csize_t, Ptr{Csize_t}), ptr, nBytes, nLevel, outptr, nOutAvailableBytes, pnOutBytes)
end

"""
    CPLZLibInflate(const void *,
                   size_t,
                   void *,
                   size_t,
                   size_t * pnOutBytes) -> void *
"""
function CPLZLibInflate(ptr, nBytes::Csize_t, outptr, nOutAvailableBytes::Csize_t, pnOutBytes)
    ccall((:CPLZLibInflate, libgdal), Ptr{Void}, (Ptr{Void}, Csize_t, Ptr{Void}, Csize_t, Ptr{Csize_t}), ptr, nBytes, outptr, nOutAvailableBytes, pnOutBytes)
end

"""
    CPLValidateXML(const char * pszXMLFilename,
                   const char * pszXSDFilename,
                   char ** papszOptions) -> int

Validate a XML file against a XML schema.

### Parameters
* **pszXMLFilename**: the filename of the XML file to validate.
* **pszXSDFilename**: the filename of the XSD schema.
* **papszOptions**: unused for now. Set to NULL.

### Returns
TRUE if the XML file validates against the XML schema.
"""
function CPLValidateXML(pszXMLFilename, pszXSDFilename, papszOptions)
    ccall((:CPLValidateXML, libgdal), Cint, (Cstring, Cstring, Ptr{Cstring}), pszXMLFilename, pszXSDFilename, papszOptions)
end

"""
    CPLsetlocale(int category,
                 const char * locale) -> char *

Prevents parallel executions of setlocale().

### Parameters
* **category**: See your compiler's documentation on setlocale.
* **locale**: See your compiler's documentation on setlocale.

### Returns
See your compiler's documentation on setlocale.
"""
function CPLsetlocale(category::Cint, locale)
    ccall((:CPLsetlocale, libgdal), Cstring, (Cint, Cstring), category, locale)
end

"""
    CPLCleanupSetlocaleMutex(void) -> void
"""
function CPLCleanupSetlocaleMutex()
    ccall((:CPLCleanupSetlocaleMutex, libgdal), Void, ())
end


text:
using TimerOutputs
using StatsBase

abstract type MyAbstractCovid end

export Statut_Covid
"""
mutable struct Statut_Covid
#Structure contenant les paramètres épidémiologiques du modèle
"""
mutable struct Statut_covid <: MyAbstractCovid

    # Vecteur contenant l'information sur l'état épidémiologique de chaque individu (0: sain, 1: contaminé, 2: contaminant, 3: guéri)
    contamination::Array{Int32,1}
    # Vecteur contenant l'information du temps d'incubation de chaque individu (exprimé en jour)
    incubation::Array{Int32,1}
    # Vecteur contenant l'information du début de la contamination de chaque individu (exprimé en jour depuis le temps d'incubation)
    deb_cont::Array{Int32,1}
    # Vecteur contenant l'information de la durée de contamination de chaque individu (exprimé en jour)
    duree_cont::Array{Int32,1}
    # Vecteur contenant l'information de la probabilité de transmission de chaque individu
    proba_trans::Array{Float64,1}
    # Vecteur contenant l'information de la présence de symptome (1: oui, 0: asymptomatique)
    symptome::Array{Int32,1}
    # Vecteur contenant l'information sur le nombre de jours de confinement pour chaque individu (si confinement global, tout le monde aura le même nombre)
    jours_conf::Array{Int32,1}
    # Vecteur contenant l'information sur le début de la mesure (si tracage, fin du temps d'incubation et début des symptomes)
    deb_mesure::Array{Int32,1}
    # Vecteur contenant l'information sur le fait de télé-travailler
    tele_travail::Array{Int32,1}
    # Booléen permettant de savoir si la mesure sanitaire est appliquée
    mesure_on::Bool

    function Statut_covid(Individu::Population)

        contamination = zeros(Int32,Individu.nb_pop) ;
        incubation = zeros(Int32,Individu.nb_pop) ;
        deb_cont = zeros(Int32,Individu.nb_pop) ;
        duree_cont = zeros(Int32,Individu.nb_pop) ;
        proba_trans = zeros(Float64,Individu.nb_pop) ;
        symptome = zeros(Int32,Individu.nb_pop) ;
        jours_conf = zeros(Int32,Individu.nb_pop) ;
        deb_mesure = -ones(Int32,Individu.nb_pop) ;
        tele_travail = zeros(Int32,Individu.nb_pop) ;
        mesure_on = false ;

        # On initialise les n_cont premières contaminations. Peut dépendre du statut social !
        if(ParametreCovid.CONT_TO_STATUT)
            Ind = findall(Individu.statut .== ParametreCovid.STAT_CONT_TO) ;
            Inds = StatsBase.randperm(length(Ind))[1:ParamSimu.NB_CONT] ;
            Ind = Ind[Inds] ;
        else
            Ind = StatsBase.randperm(Individu.nb_pop)[1:ParamSimu.NB_CONT] ;
        end

        # On met à jour l'état des premières contaminations (les individus sont déjà contaminants !)
        contamination[Ind] .= 2 ;
        deb_cont[Ind] .= 0 ;

        # Pour chaque individu
        @inbounds for i=1:Individu.nb_pop
            # On calcule les paramètres covid propre à l'individu
            incubation[i], deb_cont[i], duree_cont[i], proba_trans[i] = covid_identite() ;

            # On regarde si la personne est symptomatique/asymptomatique
            if(ParametreCovid.ASYMP_AGE)
                if(rand(Float64)>ParametreCovid.ASYMP_AGE_VALUES[Individu.cat_age[i]])
                    symptome[i] = 1 ;
                end
            else
                if(rand(Float64)>ParametreCovid.TAUX_ASYMP)
                    symptome[i] = 1 ;
                end
            end

            # On regarde si la personne active peut télétravailler suivant les
            if(mesures.INTERVENTION)

                if((mesures.TELETRAVAIL) & (Individu.statut[i] == 1))
                    if(rand(Float64)<Teletravail.TAUX_TELE)
                        tele_travail[i] = 1 ;
                    end
                end
                if((mesures.CONFINEMENT) & (Individu.statut[i] == 1))
                    if(rand(Float64)<Confinement.TAUX_TELE)
                        tele_travail[i] = 1 ;
                    end
                end

            end

        end

        # On initialise le nombre de jours de confinement (global ou individualisé)
        if(mesures.INTERVENTION)
            if(mesures.CONFINEMENT)
                jours_conf .= Confinement.DUREE ;
            end

            #if(mesures.TRACAGE)
            #    jours_conf .= Trace.JOURS_CONF;
            #end

        end

        return new(contamination,incubation,deb_cont,duree_cont,proba_trans,symptome,jours_conf,deb_mesure,tele_travail,mesure_on)

    end

end

abstract type MyAbstractDay end

export DefJournee
"""
Struct DefJournee
#Structure contenant les informations des déplacements des individus (identifiant du lieu, heure d'arrivée et heure de départ)
"""
struct DefJournee <: MyAbstractDay

    # Identifiant du lieu (POI, workingPlace)
    id_lieu::Array{Int32,2}
    # Vecteur des heures d'arrivée dans le lieu
    heure_arr::Array{Float64,2}
    # Vecteur des heures de départ dans le lieu
    heure_dep::Array{Float64,2}

    function DefJournee(nb_pop::Int32,nb_step::Int64)

        id_lieu = zeros(Int32, (nb_pop,nb_step)) ;
        heure_arr = zeros(Float64, (nb_pop,nb_step)) ;
        heure_dep = zeros(Float64, (nb_pop,nb_step)) ;

        return new(id_lieu,heure_arr,heure_dep)

    end

end

"""
Covid_Identite()

    # Function used to define the parameters associated to the Covid for each individual
    # Input:
    # NO INPUT
    # Output:
    # - tps_incub ({Int32}): incubation time (expressed in day)
    # - tps_dt_cont ({Int32}):
    # - proba ({Float64}): probability of contamination (between 0 and 1)

"""
function covid_identite()

    # Définition du temps d'incubation
    tps_incub = maximum([0,floor(Int32,rand(Normal(ParametreCovid.MU_INCUB,ParametreCovid.SIGMA_INCUB)))]) ;
    # Définition du temps de contamination
    duree_cont = maximum([0,floor(Int32,rand(Normal(ParametreCovid.MU_DT_CONT,ParametreCovid.SIGMA_DT_CONT)))]) ;
    # Définition du début de la contamination par rapport à la fin de l'incubation
    tps_deb = maximum([0,floor(Int32,rand(Normal(ParametreCovid.MU_DEB,ParametreCovid.SIGMA_DEB)))]) ;
    # Définition de la probabilité de contamination
    proba = abs(rand(Normal(ParametreCovid.MU_PROB,ParametreCovid.SIGMA_PROB))) ;

    deb_cont = tps_incub + tps_deb;

    return(tps_incub,deb_cont,duree_cont,proba)

end

export contamination_fct!
"""
Fonction Contamination_fct!
#Fonction calculant la contamination au sein du quartier
"""
function contamination_fct!(statcovid::Statut_covid,whereis_local::Array{Int32,1},iday::Int,Jour::DefJournee,tissu_work::Array{Int32,1},statut::Array{Int32,1})

    # Initialisation des vecteurs
    nb_step = size(Jour.id_lieu,2) ;
    Ind_cont = findall((statcovid.contamination .== 2)) ;
    nb_cont = size(Ind_cont,1) ;
    vie_malade = copy(Jour.id_lieu[Ind_cont,:]) ;
    statut_malade = statut[Ind_cont] ;

    # Pour chaque personne contaminée
    @inbounds for i=1:nb_cont
        # Pour chaque période de la journée
        @inbounds for j=1:nb_step
            Ind_pos = findall((Jour.id_lieu[:,j] .== vie_malade[i,j]) .& (statcovid.contamination .== 0) .& (vie_malade[i,j] .!= -1)) ;
            # On regarde s'il y a des personnes saines au même endroit que la personne contaminée
            if size(Ind_pos,1)>0
                # Pour chaque personne saine
                @inbounds for k=1:size(Ind_pos,1)
                    p = contamination_direct(Jour.heure_dep[Ind_cont[i],j],Jour.heure_dep[Ind_pos[k],j],Jour.heure_arr[Ind_cont[i],j],Jour.heure_arr[Ind_pos[k],j],statcovid.proba_trans[Ind_cont[i]],statut_malade[i],j,tissu_work[Ind_pos[k]],tissu_work[Ind_cont[i]]) ;
                    # Si la probabilité de contamination est plus forte que le rand, alors la personne saine devient contaminée
                    if(rand(Float64)<p)
                        statcovid.contamination[Ind_pos[k]] = 1 ;
                        push!(whereis_local,vie_malade[i,j]) ;
                    end
                end
            end
        end
    end

    return()

end

function contamination_fct_v2!(statcovid::Statut_covid,whereis_local::Array{Int32,1},iday::Int,Jour::DefJournee,tissu_work::Array{Int32,1},statut::Array{Int32,1})

    # Initialisation des vecteurs
    nb_step = size(Jour.id_lieu,2) ;
    Ind_cont = findall(statcovid.contamination .== 2) ;
    Ind_pos0 = findall(statcovid.contamination .== 0) ;
    nb_cont = size(Ind_cont,1) ;
    vie_malade = copy(Jour.id_lieu[Ind_cont,:]) ;
    statut_malade = statut[Ind_cont] ;

    # Pour chaque personne contaminée
    @inbounds @simd for i=1:nb_cont
        # Pour chaque période de la journée
        @inbounds for j=1:nb_step
            Ind_pos = findall((Jour.id_lieu[:,j] .== vie_malade[i,j]) .& (statcovid.contamination .== 0) .& (vie_malade[i,j] .!= -1)) ;
            # On regarde s'il y a des personnes saines au même endroit que la personne contaminée
            if size(Ind_pos,1)>0
                # Pour chaque personne saine
                @inbounds for k=1:size(Ind_pos,1)
                    p = contamination_direct(Jour.heure_dep[Ind_cont[i],j],Jour.heure_dep[Ind_pos[k],j],Jour.heure_arr[Ind_cont[i],j],Jour.heure_arr[Ind_pos[k],j],statcovid.proba_trans[Ind_cont[i]],statut_malade[i],j,tissu_work[Ind_pos[k]],tissu_work[Ind_cont[i]]) ;
                    # Si la probabilité de contamination est plus forte que le rand, alors la personne saine devient contaminée
                    if(rand(Float64)<p)
                        statcovid.contamination[Ind_pos[k]] = 1 ;
                        push!(whereis_local,vie_malade[i,j]) ;
                    end
                end
            end
        end
    end

    return()

end

export contamination_direct

"""
ContaminationDirect(depc,deps,arrc,arrs,proba)

    # Function used to compute the contamination
    # Input:
    # - depc:
    # - deps:
    # - arrc:
    # - arrs:
    # - proba:
    # Output:
    # - p

"""
function contamination_direct(depc::Float64,deps::Float64,arrc::Float64,arrs::Float64,proba::Float64,statut_malade::Int32,step_journee::Int64,tissu_pos::Int32,tissu_cont::Int32)

    if(statut_malade != 2)
        stat = JourneeType.Actif[step_journee] ;
    else
        stat = JourneeType.Retraite[step_journee] ;
    end

    duree = min(arrc,arrs) - max(depc,deps) ;

    if((mesures.DISTANCE == true) || (mesures.MASQUE == true))
        d = Distanciation.DISTANCE_SUP ;
    else
        d = 0 ;
    end

    if(stat == 2)
        if(tissu_pos == tissu_cont)
            distance = rand(Uniform(DistanceC.MIN_TISSU,DistanceC.MAX_TISSU)) + d ;
        else
            distance = rand(Uniform(DistanceC.MIN_HTISSU,DistanceC.MAX_HTISSU)) + d ;
        end
    elseif(stat == 1)
        distance = rand(Uniform(DistanceC.MIN_MENAGE,DistanceC.MAX_MENAGE)) + d;
    else
        distance = rand(Uniform(DistanceC.MIN_RESTO,DistanceC.MAX_RESTO)) + d ;
    end

    p = 1 - (1 - proba/distance^2)^duree ;

    if(ParametreCovid.PENALITE)
        p = 0.2*p ;
    end

    return(p) ;

end

export contamination_externe!
"""
Function Contamination_externe!
#Fonction permettant de calculer la contamination ayant lieu à l'extérieur du domaine
#Entrées :
#- statcovid::Statut_covid,
#- incidence::Array{Float64,1}
#- statut::Array{Int32,1}
#- indiv_in_out::Array{Int32,1}
#- whereis_local::Array{Any,1}
#Sorties :

"""
function contamination_externe!(statcovid::Statut_covid,incidence::Array{Float64,1},statut::Array{Int32,1},indiv_in_out::Array{Int32,1})

    # Contamination des gens qui vont travailler et manger hors 5ème
    # => contamination en entreprise et au poi
    # Contamination à l'école
    indiv_out = findall(indiv_in_out .==1) ;
    indiv_in = findall(indiv_in_out .<0) ;
    nbout = length(indiv_out) ;
    nbin = length(indiv_in) ;

    @inbounds for i=1:nbout
        if((statut[indiv_out[i]] .== 2) ||  (statut[indiv_out[i]] .== 3))
            r = rand(Binomial(1,incidence[1])) ;
            if((r==1) .& (statcovid.contamination[indiv_out[i]] .==0))
                statcovid.contamination[indiv_out[i]] = 1 ;
            end
        end

        if(statut[indiv_out[i]] .== 1)
            r = rand(Binomial(1,incidence[3])) ;
            if((r==1) .& (statcovid.contamination[indiv_out[i]] .==0))
                statcovid.contamination[indiv_out[i]] = 1 ;
            end
        end
    end

    # Contamination des gens qui vont travailler et manger dans le 5ème (indiv_in_out = -1)
    # => contamination chez eux

    @inbounds for i = 1:nbin
        r = rand(Binomial(1,incidence[4])) ;
        if((r==1) .& (statcovid.contamination[indiv_in[i]] .==0))
            statcovid.contamination[indiv_in[i]] = 1 ;
        end
    end

    return() ;

end

export statslieu_contamination

function statslieu_contamination(whereis_local::Array{Int32,1},Ville::City,Individu::Population)

    #d = Dict([(i,count(x->x.==i,whereis_local)) for i in unique(whereis_local)]) ;
    #dr = collect(keys(d)) ;
    #dv = collect(values(d)) ;

    nbetud = length(findall((Individu.statut .==2) .& (Individu.indiv_in_out .<=0))) ;
    nbetud = nbetud + length(findall((Individu.statut .==3) .& (Individu.indiv_in_out .<=0))) ;
    nbperspoi = length(findall(Individu.indiv_in_out .<=0)) ;
    nbwork = length(findall((Individu.statut .==1) .& (Individu.indiv_in_out .<=0))) ;
    nbhouse = length(findall(Individu.indiv_in_out .>=0)) ;

    iecole = 0 ;
    ipoi = 0 ;
    iwork = 0 ;
    ihouse = 0 ;

    @inbounds for i=1:length(whereis_local)

        cp = findall(Ville.id_poi .== whereis_local[i]) ;
        if(length(cp)>0)
            ipoi = ipoi + 1 ;
        end

        cw = findall(Individu.id_ind_work .== whereis_local[i]) ;
        if(length(cw)>0)
            if(whereis_local[i] >= 300000)
                iecole = iecole + 1 ;
            else
                iwork = iwork + 1 ;
            end
        end

        ch = findall(Individu.id_house .== whereis_local[i]) ;
        if(length(ch)>0)
            ihouse = ihouse + 1 ;
        end

    end

    res = [iecole/nbetud,ipoi/nbperspoi,iwork/nbwork,ihouse/nbhouse] ;

    return(res)

end

export mise_a_jour!
"""
mise_a_jour!(contamination0,contamination1,p_covid,cpt)

    # Function used to update the contamination at the end of the day
    # Input:
    # - contamination0:
    # - contamination1:
    # - p_covid:
    # - cpt:
    # Output :
    # - NO OUTPUT

"""
function mise_a_jour!(statcovid::Statut_covid,contamination0::Array{Int32,1})

    # Pour les gens contaminés, on met à jour le temps d'incubation
    IndCont = findall((contamination0 .==1)) ;
    if length(IndCont)>=1
        @inbounds for j=1:length(IndCont)
            statcovid.deb_cont[IndCont[j]] -= 1 ;
        end
    end

    # Pour les gens contaminés, on regarde s'ils sont maintenant contaminants
    IndNewContaminant = findall((contamination0 .== 1) .& (statcovid.deb_cont .== 0))
    if length(IndNewContaminant)>=1
        @inbounds for j=1:length(IndNewContaminant)
            statcovid.contamination[IndNewContaminant[j]] = 2 ;
        end
    end

    # Pour les gens contaminants, on met à jour le temps de contamination
    IndOldContaminant = findall((contamination0 .==2))
    if length(IndOldContaminant)>=1
        @inbounds for j=1:length(IndOldContaminant)
            statcovid.duree_cont[IndOldContaminant[j]] -= 1 ;
        end
    end

    # Pour les gens contaminants, ils sont guéris s'ils ne le sont plus
    IndNewGueri = findall((contamination0 .==2) .& (statcovid.duree_cont .== 0))
    if length(IndNewGueri)>=1
        @inbounds for j = 1:length(IndNewGueri)
            statcovid.contamination[IndNewGueri[j]] = 3 ;
        end
    end

    return()

end

export mise_en_place!
"""
Function MiseEnPlace!
#Fonction permettant de mettre en place des mesures sanitaires
#Entrées :
#- statcovid::Statut_covid
#- indicateur::Int32
#Sorties :

"""
function mise_en_place!(statcovid::Statut_covid,indicateur::Int32)

    if(statcovid.mesure_on == false)
        if(mesures.TRACAGE)
            if(indicateur>Trace.SEUIL_INCIDENCE)
                println("Debut du traçage") ;
                statcovid.mesure_on=true ;
            end
        end

        if(mesures.CONFINEMENT)
            if(indicateur>Confinement.SEUIL_INCIDENCE)
                println("Debut du confinement") ;
                statcovid.mesure_on=true ;
            end
        end

    end

end

export miseajour_mesure!
"""
Function miseajour_mesure!

"""
function miseajour_mesure!(statcovid::Statut_covid,contamination0::Array{Int32,1},contamination1::Array{Int32,1})

    # Si on met en place un confinement
    if(mesures.CONFINEMENT)
        statcovid.jours_conf[1] -= 1 ;
        if(statcovid.jours_conf[1] == 0)
            statcovid.mesure_on = false ;
        end
    end

    # Si on met en place un traçage (confinement si positif)
    #if(mesures.TRACAGE)
    #    miseajour_tracage!(statcovid,contamination0,contamination1)
    #end

end

export miseajour_tracage
"""
Function miseajour_tracage!

"""
function miseajour_tracage!(statcovid::Statut_covid,contamination0::Array{Int32,1},contamination1::Array{Int32,1})

     # Les nouveaux contaminés
     Ind = findall((contamination0 .==0) .& (contamination1 .> 0)) ;
     for i=1:length(Ind)
         if(statcovid.symptome[Ind[i]]==1)
             tps = statcovid.incubation[Ind[i]] + rand([1,2,3,4]) ;
             statcovid.deb_mesure[Ind[i]] = tps+1 ;
         end
     end

     # Mise à jour
     Ind = findall(statcovid.deb_mesure .>= 0) ;
     for i=1:length(Ind)
         statcovid.deb_mesure[Ind[i]] -= 1 ;
     end

     # Confinement ?

    if((statcovid.mesure_on) && (mesures.TRACAGE))

        Ind = findall(statcovid.deb_mesure .== 0)
        if(length(Ind)>0)
            for i=1:length(Ind)
                 statcovid.jours_conf[Ind[i]] = Trace.JOURS_CONF + 1 ;
            end
        end

     #
        Ind = findall(statcovid.jours_conf .> 0)
        if(length(Ind)>0)
            for i=1:length(Ind)
                statcovid.jours_conf[Ind[i]] -= 1 ;
            end
        end

    end

end

export deplacement!
"""
Function Deplacement!

Fonction permettant de définir les déplacements des individus sur toute une journée.
Cette journée est différente selon le statut des individus.
Si une mesure sanitaire est mise en place, alors cette journée est impactée.

Entrées :
- Jour : Structure DefJournee à mettre à jour
- Ville : Structure City contenant les informations sur les lieux et bâtiments
- Individu : Structure Population contenant les informations sur les individus
- statcovid : Structure Statut_covid contenant les informations sur la covid

Sortie :
- Jour : Structure Jour contenant les lieux visités par les individus

"""
#function deplacement!(Jour::DefJournee,Ville::City,Individu::Population,statcovid::Statut_covid)

    # S'il n'y a pas de mesures sanitaires, on calcule normalement les déplacements journaliers
#    if(statcovid.mesure_on == false)
#        @inbounds for i = 1:Individu.nb_pop
#            Jour.id_lieu[i,:], Jour.heure_dep[i,:], Jour.heure_arr[i,:] = journee_indiv(i,Ville,Individu) ;
#        end
#    else
        # Si un confinement est mis en place, alors on appelle une autre fonction de déplacement
#        if(mesures.CONFINEMENT)
#            @inbounds for i = 1:Individu.nb_pop
#                Jour.id_lieu[i,:], Jour.heure_dep[i,:], Jour.heure_arr[i,:] = journee_indiv_conf(i,Ville,Individu,statcovid) ;
#            end
        # Si un confinement individualisé est mis en place, alors on appelle une autre fonction de déplacement
#        elseif(mesures.TRACAGE)
#            @inbounds for i = 1:Individu.nb_pop
#                Jour.id_lieu[i,:], Jour.heure_dep[i,:], Jour.heure_arr[i,:] = journee_indiv_trace(i,Ville,Individu,statcovid) ;
#            end
#        end
#    end

#    return() ;

#end

export deplacement_v2!
"""
Function Deplacement_v2!

Fonction permettant de définir les déplacements des individus sur toute une journée.
Cette journée est différente selon le statut des individus.
Si une mesure sanitaire est mise en place, alors cette journée est impactée.

Entrées :
- Jour : Structure DefJournee à mettre à jour
- Ville : Structure City contenant les informations sur les lieux et bâtiments
- Individu : Structure Population contenant les informations sur les individus
- statcovid : Structure Statut_covid contenant les informations sur la covid

Sortie :
- Jour : Structure Jour contenant les lieux visités par les individus

"""
function deplacement_v2!(Jour::DefJournee,Ville::City,Individu::Population,statcovid::Statut_covid)

    @inbounds for i = 1:Individu.nb_pop
        Jour.id_lieu[i,:], Jour.heure_dep[i,:], Jour.heure_arr[i,:] = journee_indiv(i,Ville,Individu,statcovid) ;
    end

    return() ;

end

export journee_indiv
"""
Function journee_indiv
Fonction permettant de définir la journée d'un individu.

Entrées :
- i_indiv : Indice de l'individu
- Ville : Structure City contenant les informations sur les lieux et bâtiments
- Individu : Structure Population contenant les informations sur les individus

"""
function journee_indiv(i_indiv::Int64,Ville::City,Individu::Population,statcovid::Statut_covid)

    step = choix_step(i_indiv,Individu.statut[i_indiv],statcovid) ;

    id_lieu_local = zeros(Int32,length(step)) ;

    # Pour l'ensemble des étapes journalières
    @inbounds for j = 1:length(step)
        if(step[j] == 1) # L'individu est chez lui
            id_lieu_local[j] = Individu.id_house[i_indiv] ; # On récupère son indice du ménage
        elseif(step[j] == 2) # L'individu va au travail
            id_lieu_local[j] = Individu.id_ind_work[i_indiv] ; # On récupère alors l'indice de son entreprise/école
        elseif((step[j] == 3) & (Individu.id_ind_work[i_indiv] >= 0)) # L'individu va manger
            if(Journee.Proba.RESTO[Individu.statut[i_indiv]] > rand()) # Si je sors au resto
                Ind = findall(Ville.id_work .== Individu.id_ind_work[i_indiv]);
                Prob = vec(Ville.proba_resto[Ind,:]) ;
                id_lieu_local[j] = choix_resto(Prob,Ville.id_aliment)  ;
            else # Sinon, je ne sors pas (je vais à la cantine ou je continue à bosser)
                id_lieu_local[j] = Individu.id_ind_work[i_indiv] ;
            end

        elseif((step[j] == 3) & (Individu.id_ind_work[i_indiv]  == -1)) # Si je suis sortie du 5ème
            id_lieu_local[j] = -1 ;

        elseif((step[j] == 4) & (Individu.id_ind_work[i_indiv]  == -1) ) # Je sors faire des courses
            id_lieu_local[j] = - 1 ;

        elseif((step[j] == 4) & (Individu.id_ind_work[i_indiv] >= 0) ) # Je sors faire des courses

            if(Journee.Proba.SORTIECOURSES[Individu.statut[i_indiv]] > rand()) # Si je vais faire des courses
                Ind = findall(Ville.id_work .== Individu.id_ind_work[i_indiv]);
                Prob = vec(Ville.proba_resto[Ind,:]) ;
                id_lieu_local[j] = choix_resto(Prob,Ville.id_aliment)  ;
            else # Sinon je rentre chez moi
                id_lieu_local[j] = Individu.id_house[i_indiv] ;
                step[j] = 1 ;
            end

        elseif((step[j] == 4) & (Individu.id_ind_work[i_indiv]  == -1) ) # Je sors faire des courses
            id_lieu_local[j] = - 1 ;

        elseif((step[j] == 5) & (Individu.id_ind_work[i_indiv] >= 0 )) # Faire un loisir !

            if(Journee.Proba.sortieSoir[Individu.statut[i_indiv]] > rand())
                Ind = findall(Ville.id_work .== Individu.id_ind_work[i_indiv] );
                Prob = vec(Ville.proba_resto[Ind,:]) ;
                id_lieu_local[j] = choix_resto(Prob,Ville.id_aliment)  ;
            else
                id_lieu_local[j] = Individu.id_house[i_indiv] ;
                step[j] = 1 ;
            end

        end
    end

    # Choix des heures de départ et d'arrivée
    depart, arrivee = heure_indiv(Individu.statut[i_indiv]) ;

    return(id_lieu_local,depart,arrivee) ;

end

export choix_step
"""
Function JourneeIndiv_trace

"""
function choix_step(i_indiv::Int64,statut_indiv::Int32,statcovid::Statut_covid)

    if(statcovid.mesure_on == false)

        step = Journee.Semaine.TYPE[:,statut_indiv] ;

    else
        if(mesures.CONFINEMENT)

            if(statut_indiv == 1) #Actif
                if(statcovid.tele_travail[i_indiv] == 1)
                    step = [1,2,3,2,4,1,1];
                else
                    step = [1,1,1,1,4,1,1] ;
                end

            elseif(statut_indiv == 2) # Etudiant
                if(Confinement.FERMETURE_ECOLE)
                    step = [1,1,1,1,1,1,1];
                else
                    step = [1,2,3,2,1,1,1];
                end

            elseif(statut_indiv == 3) # Ecolier
                if(Confinement.FERMETURE_ECOLE)
                    step = [1,1,1,1,1,1,1];
                else
                    step = [1,2,3,2,1,1,1];
                end

            elseif(statut_indiv == 4) # Retraite
                step = [1,1,4,1,1,1,1] ;
            end

        elseif(mesures.TRACAGE)
            step = Journee.Semaine.TYPE[:,statut_indiv] ;
            if(statcovid.jours_conf[i_indiv]>0)
                step = [1,1,1,1,1,1,1];
            end
        end

    end

    return(step)

end

#export journee_indiv_conf
#"""
#Function JourneeIndiv_conf
#Fonction permettant de définir la journée d'un individu lorsqu'un confinement général est mise en place

#Entrées :
#- i_indiv : Indice de l'individu
#- Ville : Structure City contenant les informations sur les lieux et bâtiments
#- Individu : Structure Population contenant les informations sur les individus

#"""
#function journee_indiv_conf(i_indiv::Int64,Ville::City,Individu::Population,statcovid::Statut_covid)

    # Contrairement à la fonction journee_indiv, on va changer les étapes journalières
#    if(Individu.statut[i_indiv] == 1) #Actif
#        if(statcovid.tele_travail[i_indiv] == 1)
#            step = [1,2,3,2,4,1,1];
#        else
#            step = [1,1,1,1,4,1,1] ;
#        end

#    elseif(Individu.statut[i_indiv] == 2) # Etudiant
#        if(Confinement.FERMETURE_ECOLE)
#            step = [1,1,1,1,1,1,1];
#        else
#            step = [1,2,3,2,1,1,1];
 #       end

#    elseif(Individu.statut[i_indiv] == 3) # Ecolier
#        if(Confinement.FERMETURE_ECOLE)
#            step = [1,1,1,1,1,1,1];
#        else
#            step = [1,2,3,2,1,1,1];
#        end

#    elseif(Individu.statut[i_indiv] == 4) # Retraite
#        step = [1,1,4,1,1,1,1] ;
#    end

#    id_lieu_local = zeros(Int32,length(step)) ;

#    @inbounds for j = 1:length(step)
#       if(step[j] == 1) # Chez lui
#            id_lieu_local[j] = Individu.id_house[i_indiv] ;
#        elseif(step[j] == 2) # Il va au travail
#            id_lieu_local[j] = Individu.id_ind_work[i_indiv] ;
#        elseif((step[j] == 3) & (Individu.id_ind_work[i_indiv] >= 0)) # Je vais manger

#            if(Journee.Proba.resto[Individu.statut[i_indiv]] > rand()) # Si je sors au resto
#                Ind = findall(Ville.id_work .== Individu.id_ind_work[i_indiv]);
#                Prob = vec(Ville.proba_resto[Ind,:]) ;
#                id_lieu_local[j] = choix_resto(Prob,Ville.id_aliment)  ;
#            else # Sinon, je ne sors pas (je vais à la cantine ou je continue à bosser)
#                id_lieu_local[j] = Individu.id_ind_work[i_indiv] ;
#            end

#        elseif((step[j] == 3) & (Individu.id_ind_work[i_indiv]  == -1)) # Si je suis sortie du 5ème
#            id_lieu_local[j] = -1 ;

#        elseif((step[j] == 4) & (Individu.id_ind_work[i_indiv]  == -1) ) # Je sors faire des courses
#            id_lieu_local[j] = - 1 ;

#        elseif((step[j] == 4) & (Individu.id_ind_work[i_indiv] >= 0) ) # Je sors faire des courses
#            if(Journee.Proba.sortieCourses[Individu.statut[i_indiv]] > rand())
#                Ind = findall(Ville.id_work .== Individu.id_ind_work[i_indiv]);
#                Prob = vec(Ville.proba_resto[Ind,:]) ;
#                id_lieu_local[j] = choix_resto(Prob,Ville.id_aliment)  ;
#            else
#                id_lieu_local[j] = Individu.id_house[i_indiv] ;
#                step[j] = 1 ;
#            end

#        elseif((step[j] == 4) & (Individu.id_ind_work[i_indiv]  == -1) ) # Je sors faire des courses
#            id_lieu_local[j] = - 1 ;

#        elseif((step[j] == 5) & (Individu.id_ind_work[i_indiv] >= 0 )) # Faire un loisir !

 #           if(Journee.Proba.sortieSoir[Individu.statut[i_indiv]] > rand())
 #               Ind = findall(Ville.id_work .== Individu.id_ind_work[i_indiv] );
 #               Prob = vec(Ville.proba_resto[Ind,:]) ;
 #               id_lieu_local[j] = choix_resto(Prob,Ville.id_aliment)  ;
 #           else
 #               id_lieu_local[j] = Individu.id_house[i_indiv] ;
 #               step[j] = 1 ;
 #           end
#
 #       end
  #  end

  #  depart, arrivee = heure_indiv(Individu.statut[i_indiv]) ;

  #  return(id_lieu_local,depart,arrivee) ;

#end

#export journee_indiv_trace
#"""
#Function JourneeIndiv_trace

#"""
#function journee_indiv_trace(i_indiv::Int64,Ville::City,Individu::Population,statcovid::Statut_covid)

#    step = Journee.Semaine.Type[:,Individu.statut[i_indiv]] ;

#    if(statcovid.jours_conf[i_indiv]>0)
#        step = [1,1,1,1,1,1,1]
#    end

#    id_lieu_local = zeros(Int32,length(step)) ;

#    @inbounds for j = 1:length(step)
#        if(step[j] == 1) # Chez lui
#            id_lieu_local[j] = Individu.id_house[i_indiv] ;
#        elseif(step[j] == 2) # Il va au travail
#            id_lieu_local[j] = Individu.id_ind_work[i_indiv] ;
#        elseif((step[j] == 3) & (Individu.id_ind_work[i_indiv] >= 0)) # Je vais manger

#            if(Journee.Proba.resto[Individu.statut[i_indiv]] > rand()) # Si je sors au resto
#                Ind = findall(Ville.id_work .== Individu.id_ind_work[i_indiv]);
#                Prob = vec(Ville.proba_resto[Ind,:]) ;
#                id_lieu_local[j] = choix_resto(Prob,Ville.id_aliment)  ;
#            else # Sinon, je ne sors pas (je vais à la cantine ou je continue à bosser)
#                id_lieu_local[j] = Individu.id_ind_work[i_indiv] ;
#            end

#        elseif((step[j] == 3) & (Individu.id_ind_work[i_indiv]  == -1)) # Si je suis sortie du 5ème
#            id_lieu_local[j] = -1 ;

#        elseif((step[j] == 4) & (Individu.id_ind_work[i_indiv]  == -1) ) # Je sors faire des courses
#            id_lieu_local[j] = - 1 ;

#        elseif((step[j] == 4) & (Individu.id_ind_work[i_indiv] >= 0) ) # Je sors faire des courses
#            if(Journee.Proba.sortieCourses[Individu.statut[i_indiv]] > rand())
#                Ind = findall(Ville.id_work .== Individu.id_ind_work[i_indiv]);
#                Prob = vec(Ville.proba_resto[Ind,:]) ;
#                id_lieu_local[j] = choix_resto(Prob,Ville.id_aliment)  ;
#            else
#                id_lieu_local[j] = Individu.id_house[i_indiv] ;
#                step[j] = 1 ;
#            end

#        elseif((step[j] == 4) & (Individu.id_ind_work[i_indiv]  == -1) ) # Je sors faire des courses
#            id_lieu_local[j] = - 1 ;

#        elseif((step[j] == 5) & (Individu.id_ind_work[i_indiv] >= 0 )) # Faire un loisir !

#            if(Journee.Proba.sortieSoir[Individu.statut[i_indiv]] > rand())
#                Ind = findall(Ville.id_work .== Individu.id_ind_work[i_indiv] );
#                Prob = vec(Ville.proba_resto[Ind,:]) ;
#                id_lieu_local[j] = choix_resto(Prob,Ville.id_aliment)  ;
#            else
#                id_lieu_local[j] = Individu.id_house[i_indiv] ;
#                step[j] = 1 ;
#            end

#        end
#    end

#    depart, arrivee = heure_indiv(Individu.statut[i_indiv]) ;

#    return(id_lieu_local,depart,arrivee) ;

#end

ext: jl


text:
using ModelingToolkit
using DifferentialEquations
using Einsum

# initial state
nAge=9
nHS=8
nIso=4
nI=4
nTest=4

# yeah
include("utils.jl")
param=(nAge=nAge, nHS=nHS, nIso=nIso, nTest=nTest)

include("diseaseProg.jl")

ssize=(nAge, nHS, nIso, nTest)

using PyCall
using Test

PyCall.pyimport_conda("pandas", "pandas")
PyCall.pyimport_conda("numpy", "numpy")
PyCall.pyimport_conda("scipy", "scipy")
PyCall.pyimport_conda("dask", "dask")
PyCall.pyimport_conda("cloudpickle", "cloudpickle")
PyCall.pyimport_conda("distributed", "distributed")
PyCall.pyimport_conda("xlrd", "xlrd")

py"""
import os
exec(open(os.path.join(os.getcwd(), "coexist_python", "model_COVID_testing-ODEoutput.py")).read())
"""

function dydt_Complete(t, stateTensor_flattened)
	stateTensor = reshape(stateTensor_flattened, (nTest, nIso, nHS, nAge))
	dydt = zeros(size(stateTensor)...)
	trTensor_complete = zeros((nTest, nIso, nHS, nTest, nIso, nHS, nAge))
	trTensor_diseaseProgression = trFunc_diseaseProgression(;param...)
	for k in 1:4
		_s = size(trTensor_diseaseProgression[:,k,:,:])
		_y = reshape(trTensor_diseaseProgression[:,k,:,:], (_s[1:end-2]..., 1, _s[end-1:end]...))
		_t = trTensor_complete[:,k,:,:,k,:,:]
		@einsum _temp[m,l,j,i] := _t[l,m,l,j,i]
		_temp .+= _y
		@einsum _t[l,m,l,j,i] = _temp[m,l,j,i]
		trTensor_complete[:,k,:,:,k,:,:] = _t
	end

	# @einsum _mod[l,k,j,i] := trTensor_complete[l,k,j,l,k,j,i]
	_mod = einsum("ijkljkl->ijkl", trTensor_complete)
	_mod .-= einsum("...jkl->...", trTensor_complete)
	# @einsum trTensor_complete[l,k,j,l,k,j,i] = _temp[l,k,j,i]
	trTensor_complete = _einsum11(trTensor_complete, _mod)

	dydt = einsum("ijkl,ijklmnp->imnp", stateTensor, trTensor_complete)
	return reshape(dydt, prod(size(dydt)))
end

state = 50*ones(9*8*4*4)
r = dydt_Complete(0, state)

@testset "lol" begin
	@test r == py"out"
end


text:
@testset "serialization.jl" begin
    @testset "saving and loading" begin
        # Save model.
        file = joinpath(mktempdir(), "gdemo_default.jls")
        serialize(file, gdemo_default)

        # Sample from deserialized model.
        gdemo_default_copy = deserialize(file)
        samples = [gdemo_default_copy() for _ in 1:1_000]
        samples_s = first.(samples)
        samples_m = last.(samples)

        @test mean(samples_s) ≈ 3 atol = 0.1
        @test mean(samples_m) ≈ 0 atol = 0.1
    end
    @testset "pmap" begin
        # Add worker processes.
        pids = addprocs()
        @info "serialization test: using $(nworkers()) processes"

        # Load packages on all processes.
        @everywhere begin
            using DynamicPPL
            using Distributions
        end

        # Define model on all proceses.
        @everywhere @model function model()
            return m ~ Normal(0, 1)
        end

        # Generate `Model` objects on all processes.
        models = pmap(_ -> model(), 1:100)
        @test models isa Vector{<:Model}
        @test length(models) == 100

        # Sample from model on all processes.
        n = 1_000
        samples1 = pmap(_ -> model()(), 1:n)
        m = model()
        samples2 = pmap(_ -> m(), 1:n)

        for samples in (samples1, samples2)
            @test samples isa Vector{Float64}
            @test length(samples) == n
            @test mean(samples) ≈ 0 atol = 0.15
            @test std(samples) ≈ 1 atol = 0.1
        end

        # Remove processes
        rmprocs(pids...)
    end
end


text:
@testset "Solver $(solver)" for solver in [RAFF.lmlovo, RAFF.gnlslovo]

    model = (x, θ) -> θ[1] * x[1] + θ[2]
    gmodel! = (g, x, θ) -> begin
        g[1] = x[1]
        g[2] = 1.0
    end

    @testset "Simple cases ($(np), $p)" for (np, p) in [(10, 9), (100, 90)]

        data = Array{Float64, 2}(undef, np, 2)
        n = 2
        θsol = [-p, 0.5 * np]

        for (i, x) in enumerate(range(-10, stop=10, length=np))
            data[i, 1] = x
            data[i, 2] = model(x, θsol)
        end

        θ = [1.0, 1.0]

        data[1:np - p, 2] .+= 100

        r = solver(model, gmodel!, θ, data, n, p)

        @test(r.outliers == [1:np - p;])
        @test(r.solution ≈ θsol, atol=1.0e-4)
        @test(r.status == 1)
        @test(r.p == p)
        @test(r.nf >= r.iter)
        @test(r.nj >= r.iter)

        θ = [1.0, 1.0]

        r = solver(model, θ, data, n, p)

        @test(r.outliers == [1:np - p;])
        @test(r.solution ≈ θsol, atol=1.0e-4)
        @test(r.status == 1)
        @test(r.p == p)
        @test(r.nf >= r.iter)
        @test(r.nj >= r.iter)

    end

    @testset "Wrong arguments" begin

        np = 10
        p  = 9

        data = Array{Float64, 2}(undef, np, 2)
        n = 2
        θsol = [-p, 0.5 * np]

        for (i, x) in enumerate(range(-10, stop=10, length=np))
            data[i, 1] = x
            data[i, 2] = model(x, θsol)
        end

        θ = [1.0, 1.0]

        data[1:np - p, 2] .+= 100

        r = solver(model, gmodel!, θ, data, n, p)

        @test_throws(AssertionError, solver(model, θ, data, 0, 1))
        @test_throws(AssertionError, solver(model, θ, data, 2, -1))

    end

    # Test to check Issue #1

    @testset "Error in printing" begin

        m(x, θ) = θ[1] * x[1]^2 + θ[2]

        A = [ -2.0  5.00;
              -1.5  3.25;
              -1.0  2.00;
              -0.5  1.25;
              0.0  1.00;
              0.5  1.25;
              1.0  2.00;
              1.5  3.25;
              2.0  5.00 ]

        θ = [0.0, 0.0]

        # Changes log just for this test
        rout = with_logger(Logging.NullLogger()) do

            lmlovo(m, θ, A, 2, 4)

        end

        @test rout.status == 1
        @test rout.p == 4

    end

end


text:
import MPI
import Test: @test

MPI.Init()

macro test_exists_and_rm(fn)
    return quote
        @test ispath($(esc(fn)))
        rm($(esc(fn)))
    end
end

# unit tests
include(joinpath("unit", "helper", "test_ratios.jl"))

include(joinpath("unit", "core", "test_Args.jl"))
include(joinpath("unit", "core", "test_Config.jl"))
include(joinpath("unit", "core", "test_Ctrl.jl"))
include(joinpath("unit", "core", "test_DataStream.jl"))
include(joinpath("unit", "core", "test_DataGen.jl"))
include(joinpath("unit", "core", "test_IO.jl"))

include(joinpath("unit", "hello", "test_hello.jl"))

# functional tests
include(joinpath("functional", "test_main.jl"))

# MPI.Finalize() can be called only once per run. We do it here for all
# "in-process" tests. The cmdline tests are done outside the MPI context since
# they launch other MPI tasks using system calls
MPI.Finalize()

include(joinpath("functional", "test_cmdline.jl"))


text:
# nhk_data.jl
#######################

# Load packages.
using Plots, CSV, Dates, DataFrames

# Download data from the MHLW web site.
download("https://www3.nhk.or.jp/n-data/opendata/coronavirus/nhk_news_covid19_prefectures_daily_data.csv","./csv/nhk_prefectures.csv");
download("https://www3.nhk.or.jp/n-data/opendata/coronavirus/nhk_news_covid19_domestic_daily_data.csv","./csv/nhk_japan.csv");

# Shape up the data.
Acsv=DataFrame(CSV.File("./csv/nhk_prefectures.csv", header=false, delim=','));
Jcsv=DataFrame(CSV.File("./csv/nhk_japan.csv", header=false, delim=','));
(pa,qa)=size(Acsv);
A0=parse.(Int64,Acsv[2:pa,2]);
A1=parse.(Float64,Acsv[2:pa,4]);
A2=parse.(Float64,Acsv[2:pa,5]);
A3=parse.(Float64,Acsv[2:pa,7]);
(pj,qj)=size(Jcsv);
J1=parse.(Float64,Jcsv[2:pj,2]);
J2=parse.(Float64,Jcsv[2:pj,3]);
J3=parse.(Float64,Jcsv[2:pj,5]);

# Plot the data
# d0: the initial date
# df: the final day
d0=Date(2020,1,16)
D=length(J1);
df=d0+Day(D-1);
l0=string(d0);
l1=string(d0+Day(Int(floor((D-1)/4))));
l2=string(d0+Day(Int(floor((D-1)/2))));
l3=string(d0+Day(Int(floor(3*(D-1)/4))));
l4=string(df);

dd0=Date(2021,7,1);
DD=D-532;
ll0=string(dd0);
ll1=string(dd0+Day(Int(floor((DD-1)/2))));
ll2=string(dd0+Day(Int(floor(DD-1))));

# Japan (126M)
PJPN=125.36;
TJPN=J2/PJPN;
NJPN=zeros(D)
for j=1:7
    NJPN[j]=TJPN[j]/j;
end
for j=8:D
    NJPN[j]=(TJPN[j]-TJPN[j-7])/7;
end
DJPN=J3/PJPN;
NDJPN=zeros(D);
for j=1:7
    NDJPN[j]=(NDJPN[j]-NDJPN[1])/j;
end
for j=8:D
    NDJPN[j]=(DJPN[j]-DJPN[j-7])/7;
end

# Okinawa (1.46M): code 47
ROWOKNW=findall(x->x==47,A0);
POKNW=1.458870;
TOKNW=A2[ROWOKNW]/POKNW;
NOKNW=zeros(D)
for j=1:7
    NOKNW[j]=TOKNW[j]/j;
end
for j=8:D
    NOKNW[j]=(TOKNW[j]-TOKNW[j-7])/7;
end
DOKNW=A3[ROWOKNW]/POKNW;
NDOKNW=zeros(D);
for j=1:7
    NDOKNW[j]=(NDOKNW[j]-NDOKNW[1])/j;
end
for j=8:D
    NDOKNW[j]=(DOKNW[j]-DOKNW[j-7])/7;
end

# Tokyo (14M): code 13,
PTKY=14.049146;
ROWTKY=findall(x->x==13,A0);
TTKY=A2[ROWTKY]/PTKY;
NTKY=zeros(D)
for j=1:7
    NTKY[j]=TTKY[j]/j
end
for j=8:D
    NTKY[j]=(TTKY[j]-TTKY[j-7])/7
end
DTKY=A3[ROWTKY]/PTKY;
NDTKY=zeros(D);
for j=1:7
    NDTKY[j]=(NDTKY[j]-NDTKY[1])/j;
end
for j=8:D
    NDTKY[j]=(DTKY[j]-DTKY[j-7])/7;
end

# Osaka (8.81M): code 27
POSK=8.798545;
ROWOSK=findall(x->x==27,A0);
NOSK=A1[ROWOSK]/POSK;
TOSK=A2[ROWOSK]/POSK;
DOSK=A3[ROWOSK]/POSK;
NDOSK=zeros(D);
for j=1:7
    NDOSK[j]=(NDOSK[j]-NDOSK[1])/j;
end
for j=8:D
    NDOSK[j]=(DOSK[j]-DOSK[j-7])/7;
end

# Hyogo (5.43M): code 28,
PHYG=5.446455;
rowhyg=findall(x->x==28,A0);
NHYG=A1[rowhyg]/PHYG;
THYG=A2[rowhyg]/PHYG;
DHYG=A3[rowhyg]/PHYG;
NDHYG=zeros(D);
for j=1:7
    NDHYG[j]=(NDHYG[j]-NDHYG[1])/j;
end
for j=8:D
    NDHYG[j]=(DHYG[j]-DHYG[j-7])/7;
end

# Hokkaido (5.27M): code 1,
PHKD=5.207185;
ROWHKD=findall(x->x==1,A0);
NHKD=A1[ROWHKD]/PHKD;
THKD=A2[ROWHKD]/PHKD;
DHKD=A3[ROWHKD]/PHKD;
NDHKD=zeros(D);
for j=1:7
    NDHKD[j]=(NDHKD[j]-NDHKD[1])/j;
end
for j=8:D
    NDHKD[j]=(DHKD[j]-DHKD[j-7])/7;
end

p1=plot([TJPN TTKY TOKNW],
    grid=false,
    linewidth=2,
    legendfont=font(10),
    label=["Japan" "Tokyo" "Okinawa"],
    title="COVID-19: total cases per 1M \n data sourced by NHK (Japanese Public TV)",
    right_margin=Plots.Measures.Length(:mm, 10.0),
    xticks = ([1 floor(D/4)  floor(D/2) floor(3*D/4) D;], [l0 l1 l2 l3 l4]),
    xlabel="date",
    yaxis="cases/1M",
    palette = :seaborn_bright,
    legend = :topleft)
savefig("./nhk/nhk_cases.png")

p2=plot([DJPN DTKY DOKNW DOSK DHYG DHKD],
    grid=false,
    linewidth=2,
    legendfont=font(10),
    title="COVID-19: deaths per 1M \n data sourced by NHK (Japanese Public TV)",
    right_margin=Plots.Measures.Length(:mm, 10.0),
    xticks = ([1 floor(D/4)  floor(D/2) floor(3*D/4) D;], [l0 l1 l2 l3 l4]),
    xlabel="date",
    yaxis="deaths/1M",
    label=["Japan" "Tokyo" "Okinawa" "Osaka" "Hyogo" "Hokkaido"],
    palette = :seaborn_bright,
    legend = :topleft)
savefig("./nhk/nhk_deaths.png")

p3=plot([NJPN NTKY NOKNW],
    grid=false,
    linewidth=2,
    title="COVID-19: 7-day average of new cases per 1M \n data sourced by NHK (Japanese Public TV)",
    right_margin=Plots.Measures.Length(:mm, 10.0),
    xticks = ([1 floor(D/4)  floor(D/2) floor(3*D/4) D;], [l0 l1 l2 l3 l4]),
    xlabel="date",
    yaxis="cases/1M",
    legendfont=font(10),
    label=["Japan" "Tokyo" "Osaka" "Okinawa" "Hyogo" "Hokkaido"],
    palette = :seaborn_bright,
    legend = :topleft)
savefig("./nhk/nhk_new_cases.png")

p4=plot([NDJPN NDTKY NDOKNW NDOSK NDHYG NDHKD],
    grid=false,
    linewidth=2,
    title="COVID-19: 7-day average deaths per 1M \n data sourced by NHK (Japanese Public TV)",
    right_margin=Plots.Measures.Length(:mm, 10.0),
    xticks = ([1 floor(D/4)  floor(D/2) floor(3*D/4) D;], [l0 l1 l2 l3 l4]),
    xlabel="date",
    yaxis="deaths/1M",
    legendfont=font(10),
    label=["Japan" "Tokyo" "Okinawa" "Osaka" "Hyogo" "Hokkaido"],
    palette = :seaborn_bright,
    legend = :topleft)
savefig("./nhk/nhk_recent_deaths.png")

plot(p1, p2, p3, p4,
     layout=(2,2),
     size=(1260,840),
     left_margin=Plots.Measures.Length(:mm, 5.0),
     right_margin=Plots.Measures.Length(:mm, 15.0),
     top_margin=Plots.Measures.Length(:mm, 5.0),
     bottom_margin=Plots.Measures.Length(:mm, 5.0))
savefig("./nhk/nhk.png")


text:
using Test

@timed_testset "generated_weights/" begin
    include("generated_weights/mfc+mfc+softmax.jl")
    include("generated_weights/conv+fc+softmax.jl")
end


text:
"""

`ungroup(arr, ...)`

Ungroup array elements in an array into scalar elements along some direction.

##### Arguments

* `arr` : an array.
* `...` can be `axis`, `indices`, or `ref_field` : either an axis index along which to ungroup, a range tuple coordinates of the form `(i_1,...,i_k,:,i_{k+1},...,i_n)` for some integer i's, or the selected elements of `arr` after applying those types of tuples.

##### Return

An ungrouped array of the same type as `arr`. If `arr` is `LabeledArray`, the axis along the ungrouping direction will become a new field (a generic field name is provided if it was not a `DictArray` axis.

##### Examples

```julia
julia> t = larr(a=Any[[1,2,3],[4,5]], b=[:x,:x], c=Any[[11,12,13],[14,15]], axis1=[:X,:Y])
2 LabeledArray

  |a       b c
--+---------------------
X |[1,2,3] x [11,12,13]
Y |[4,5]   x [14,15]

julia> ungroup(t, 1)
5 LabeledArray

  |x1 a b c
--+----------
1 |X  1 x 11
2 |X  2 x 12
3 |X  3 x 13
4 |Y  4 x 14
5 |Y  5 x 15

julia> ungroup(t, (:,1))
5 LabeledArray

  |x1 a b c
--+----------
1 |X  1 x 11
2 |X  2 x 12
3 |X  3 x 13
4 |Y  4 x 14
5 |Y  5 x 15

julia> m = nalift(reshape(Any[[1,2],[3,4],[5,6,7],[8,9,10]],2,2))
2x2 MultidimensionalTables.AbstractArrayWrapper{Nullable{Array{Int64,1}},2,Array{Nullable{Array{Int64,1}},2}}:
 Nullable([1,2])  Nullable([5,6,7])
 Nullable([3,4])  Nullable([8,9,10])

julia> ungroup(m, 2)
2x5 MultidimensionalTables.AbstractArrayWrapper{Nullable{Int64},2,Array{Nullable{Int64},2}}:
 Nullable(1)  Nullable(2)  Nullable(5)  Nullable(6)  Nullable(7)
 Nullable(3)  Nullable(4)  Nullable(8)  Nullable(9)  Nullable(10)
```

"""
function ungroup end

ungroup(arr::AbstractArray, ref_field::AbstractArray) = begin
  (cumsum, offsets) = create_ungroup_offsets(ref_field)
  # the total length of the final ungrouped array will be cumsum-1.
  ungroup(arr, ref_field, cumsum, offsets)
end
ungroup(arr::AbstractArray, indices::Tuple) = ungroup(arr, arr[indices...])
ungroup(arr::AbstractArray, axis::Integer) =
  ungroup(arr, ntuple(d->d==axis ? Colon() :
                          d>axis ? 1 :
                          (1:1), ndims(arr)))

create_ungroup_offsets(ref_field::AbstractArray) = begin
  lenref = length(ref_field)
  offsets = Array(Int, lenref + 1)
  cumsum = 1
  for i in 1:lenref
    @inbounds offsets[i] = cumsum
    @inbounds cumsum += length(ref_field[i].value)
  end
  offsets[lenref+1] = cumsum
  (cumsum, offsets)
end

@generated ungroup{N,M}(arr::AbstractArray{TypeVar(:T),N}, ref_field::AbstractArray{TypeVar(:U),M}, cumsum::Int, offsets::Vector{Int}) = begin
  isym = symbol("i_", M)
  quote
    if length(arr) == 0
      error("not yet determined: what to do when ungrouping an empty array?")
    end
    sizearr = size(arr)
    resultsize = ntuple(ndims(arr)) do d
      if d == $M
        cumsum-1
      else
        sizearr[d]
      end
    end
    elemtype = peel_nullabletype(eltype(arr))
    if elemtype <: AbstractArray
      result = similar(arr, eltype(elemtype), resultsize)
      @nloops $N i arr begin
        elem = (@nref $N arr i).value
        m_coord = offsets[$isym]
        next_m_coord = offsets[$isym + 1]
        m_coord_len = next_m_coord-m_coord
        if m_coord_len != length(elem)
          error("ungroup error: lengths do not match.")
        end
        for j in 1:m_coord_len
          @nref($N, result, k->k==$M ? m_coord+j-1 : i_k) = elem[j]
        end
      end
      wrap_array(nalift(result))
    elseif isa(arr[1].value, AbstractArray)
      # this is when the types are mixed.
      result = similar(arr, Any, resultsize)
      @nloops $N i arr begin
        elem = (@nref $N arr i).value
        m_coord = offsets[$isym]
        next_m_coord = offsets[$isym + 1]
        m_coord_len = next_m_coord-m_coord
        if m_coord_len != length(elem)
          error("ungroup error: lengths do not match.")
        end
        for j in 1:m_coord_len
          @nref($N, result, k->k==$M ? m_coord+j-1 : i_k) = elem[j]
        end
      end
      simplify_array(nalift(result))
    else
      # this is not an element of an array type.
      result = similar(arr, resultsize)
      @nloops $N i arr begin
        elem = (@nref $N arr i).value
        m_coord = offsets[$isym]
        next_m_coord = offsets[$isym + 1]
        for j in 1:next_m_coord-m_coord
          @nref($N, result, k->k==$M ? m_coord+j-1 : i_k) = elem
        end
      end
      wrap_array(result)
    end
  end
end

ungroup(arr::DictArray, ref_field::AbstractArray) =
  DictArray(LDict(arr.data.keys, map(v->ungroup(v, ref_field), arr.data.values)))
ungroup(arr::DictArray, indices::Tuple) = begin
  if length(arr) == 0
    ungroup(arr, [])
  end
  for v in arr.data.values
    if typeof(v[1].value) <: AbstractArray
      return ungroup(arr, v[indices...])
    end
  end
  error("ungroup a DictArray: cannot find an array component whose elements are also arrays.")
end

ungroup(arr::LabeledArray, ref_field::AbstractArray) = begin
  newdata0 = ungroup(arr.data, ref_field)
  axis_index = ndims(ref_field)
  theaxis = arr.axes[axis_index]
  newdata = if isa(theaxis, DefaultAxis)
    newdata0
  else
    ungrouped_axis = if isa(theaxis, DictArray)
      DictArray(LDict(theaxis.data.keys, map(elem->ungroup(BroadcastAxis(elem, arr, axis_index), ref_field), theaxis.data.values)))
    else
      DictArray(create_additional_fieldname(arr) => ungroup(BroadcastAxis(arr.axes[axis_index], arr, axis_index), ref_field))
    end
    newdata = DictArray(LDict([ungrouped_axis.data.keys;newdata0.data.keys],
                              [ungrouped_axis.data.values;newdata0.data.values]))
  end
  newaxes = ntuple(ndims(arr)) do d
    if d == axis_index
      DefaultAxis(size(newdata)[d])
    else
      arr.axes[d]
    end
  end
  LabeledArray(newdata, newaxes)
end

ungroup(arr::LabeledArray, indices::Tuple) = begin
  if length(arr) == 0
    ungroup(arr, [])
  end
  for v in arr.data.data.values
    if typeof(v[1].value) <: AbstractArray
      return ungroup(arr, v[indices...])
    end
  end
  error("ungroup a LabeledArray: cannot find an array component whose elements are also arrays.")
end


text:
using AQFED, Test
using StatsBase
using AQFED.Black
using AQFED.Collocation
import Polynomials:coeffs
using AQFED.Math
#using Plots

@testset "FilterConvex" begin
    strikes = Float64.([20, 25, 50, 55, 75, 100, 120, 125, 140, 150, 160, 175, 180, 195, 200, 210, 230, 240, 250, 255, 260, 270, 275, 280, 285, 290, 300, 310, 315, 320, 325, 330, 335, 340, 350, 360, 370, 380, 390, 400, 410, 420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 550, 580, 590, 600, 650, 670, 680, 690, 700])
    vols =[1.21744983334323, 1.1529735541872308, 1.0013512993166844, 1.0087013871410198, 0.9055919576135943, 0.8196499269009432, 0.779704840770866, 0.753927847741657, 0.7255349986293694, 0.7036962946028743, 0.6870907597202961, 0.6631489459500445, 0.6542809839143336, 0.6310048431894977, 0.6231979513191437, 0.6154526014300009, 0.5866214834144697, 0.5783751483731193, 0.5625036590124762, 0.5625539176150428, 0.5572331684618123, 0.5485212417739607, 0.5456131657256524, 0.540060895711996,0.5384776792271245, 0.5325298112839504, 0.5222410647552144, 0.5202396738775005, 0.5168414254536685, 0.5127405490209541, 0.5100440087558921, 0.50711984442627, 0.5042896073005682, 0.5013959697030379, 0.4961897259221575, 0.4914478237113829, 0.48571052433313705, 0.4820982302575811, 0.4776551485043659, 0.4682253137830999, 0.46912624306506934, 0.4652049749994563, 0.4621036693145566, 0.45969798571592985, 0.4561356005182957, 0.45418189139835186, 0.4515451651258398,0.44541885580442636, 0.4452833907060621, 0.44303755690672525, 0.43939212779385645, 0.4413175310749832, 0.4336322023390991, 0.4297053821023934, 0.4284357423754355, 0.4241077476619805, 0.4222672729031064, 0.4203436892852212, 0.4193419518701644, 0.41934732346075626, 0.41758929420417745]
    forward = 356.73063159822254
    tte = 1.5917808219178082
    refPricesf = [337.9478782712897, 333.2264008151271, 310.74145637852655, 306.2719712239312, 288.3940506055495, 266.219801810884, 249.06397096223955, 244.7750182500784, 232.02358238485036, 223.57092985061377, 215.48527697171835, 203.47284496330764, 199.46870596050405, 187.64870164975872, 183.87579062925917, 176.5074646794032, 161.77083277969126, 154.64429335245714, 147.51776392522302, 144.31801826019864, 141.1182775951742, 134.7188062651254, 131.6082360685426, 128.49767087195968, 125.40239375195168, 122.30712163194369, 116.11658739192775, 110.79059339934288, 108.12760140305048, 105.46461440675806, 102.80163241046563, 100.15226884869381, 97.60223566948574, 95.07799161470712, 90.23581650058495, 85.6198931055724, 81.10708395817021, 76.78697167463605, 72.46686939110188, 68.14677710756771, 64.77986823082749, 61.41296935408727, 58.04608047734704, 54.95662415902851, 51.90375987138524, 49.029996842756624, 46.15624381412802, 43.2825007854994, 41.01535154006894, 38.748212294638485, 36.481083049208024, 34.612533924958775, 29.006916552211, 24.412399354175687, 22.88090362149725, 21.349417888818817, 16.324515763803383, 14.545066178175531, 13.74560498753116, 12.997715379078853, 12.249835770626547]
    w1 = ones(length(strikes))
    prices, weights = Collocation.weightedPrices(true, strikes, vols, w1, forward, 1.0, tte)
    strikes, pricesf = Collocation.filterConvexPrices(strikes, prices, w1, forward,tol=1e-6)
    for (k, p, pf,rpf) = zip(strikes, prices, pricesf,refPricesf)
        println(k, " ", p," ", pf, " ",pf-p)
        @test isapprox(rpf, pf, atol = 1e-5)
    end

    isoc,m = Collocation.makeIsotonicCollocation(strikes, pricesf, weights, tte, forward, 1.0,deg=5)
    sol = Collocation.Polynomial(isoc)
    println("Solution ", sol, " ",coeffs(sol), " ",Collocation.stats(sol), " measure ",m)
    refCoeffs = [348.59726724651006, 221.54300193865407, -24.275731591637737, -12.821670197699545, 10.80303198111674, 2.940110896855002]
    for (ciRef, ci) = zip(refCoeffs, coeffs(sol))
        @test isapprox(ciRef, ci, atol=1e-2)
    end
end

@testset "BadRoots" begin
    strikes = Float64.([20, 25, 50, 55, 75, 100, 120, 125, 140, 150, 160, 175, 180, 195, 200, 210, 230, 240, 250, 255, 260, 270, 275, 280, 285, 290, 300, 310, 315, 320, 325, 330, 335, 340, 350, 360, 370, 380, 390, 400, 410, 420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 550, 580, 590, 600, 650, 670, 680, 690, 700]);
    vols =[1.21744983334323, 1.1529735541872308, 1.0013512993166844, 1.0087013871410198, 0.9055919576135943, 0.8196499269009432, 0.779704840770866, 0.753927847741657, 0.7255349986293694, 0.7036962946028743, 0.6870907597202961, 0.6631489459500445, 0.6542809839143336, 0.6310048431894977, 0.6231979513191437, 0.6154526014300009, 0.5866214834144697, 0.5783751483731193, 0.5625036590124762, 0.5625539176150428, 0.5572331684618123, 0.5485212417739607, 0.5456131657256524, 0.540060895711996,0.5384776792271245, 0.5325298112839504, 0.5222410647552144, 0.5202396738775005, 0.5168414254536685, 0.5127405490209541, 0.5100440087558921, 0.50711984442627, 0.5042896073005682, 0.5013959697030379, 0.4961897259221575, 0.4914478237113829, 0.48571052433313705, 0.4820982302575811, 0.4776551485043659, 0.4682253137830999, 0.46912624306506934, 0.4652049749994563, 0.4621036693145566, 0.45969798571592985, 0.4561356005182957, 0.45418189139835186, 0.4515451651258398,0.44541885580442636, 0.4452833907060621, 0.44303755690672525, 0.43939212779385645, 0.4413175310749832, 0.4336322023390991, 0.4297053821023934, 0.4284357423754355, 0.4241077476619805, 0.4222672729031064, 0.4203436892852212, 0.4193419518701644, 0.41934732346075626, 0.41758929420417745];
    forward = 356.73063159822254
    tte = 1.5917808219178082
    w1 = ones(length(strikes))
    prices, weights = Collocation.weightedPrices(true, strikes, vols, w1, forward, 1.0, tte)
    isoc,m = Collocation.makeIsotonicCollocation(strikes, prices, weights, tte, forward, 1.0,deg=11,degGuess=1)
    sol = Collocation.Polynomial(isoc)
    k = collect(800:0.1:1000);
    #Halley/SuperHalley does not behave well between 900 and 950
    #LithBoonkkampIJzerman does not bethave well between 825 and 830 (requires ~ 64*3 iterations)
    ivk = @. Black.impliedVolatility(true, Collocation.priceEuropean(sol, true, k,forward,1.0), forward, k, tte, 1.0);
    for iv in ivk
        @test iv > 0 && iv < 10.0
    end
end