/*****************************************************************************/
// Copyright 2006-2023 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file in
// accordance with the terms of the Adobe license agreement accompanying it.
/*****************************************************************************/

#ifndef __dng_utils__
#define __dng_utils__

/*****************************************************************************/

#include <cmath>
#include <limits>
#include <utility>

#include "dng_classes.h"
#include "dng_flags.h"
#include "dng_memory.h"
#include "dng_safe_arithmetic.h"
#include "dng_string.h"
#include "dng_types.h"
#include "dng_uncopyable.h"

/*****************************************************************************/

#if qWinOS
#undef min
#undef max
#endif

/*****************************************************************************/

// The unsigned integer overflow is intended here since a wrap around is used to
// calculate the abs() in the branchless version.

DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow")
inline uint32 Abs_int32 (int32 x)
    {

    #if 0

    // Reference version.

    return (uint32) (x < 0 ? -x : x);

    #else

    // Branchless version.

    uint32 mask = (uint32) (x >> 31);

    return (uint32) (((uint32) x + mask) ^ mask);

    #endif

    }

inline int32 Min_int32 (int32 x, int32 y)
    {

    return (x <= y ? x : y);

    }

inline int32 Max_int32 (int32 x, int32 y)
    {

    return (x >= y ? x : y);

    }

inline int32 Pin_int32 (int32 min, int32 x, int32 max)
    {

    return Max_int32 (min, Min_int32 (x, max));

    }

inline int32 Pin_int32_between (int32 a, int32 x, int32 b)
    {

    int32 min, max;
    if (a < b) { min = a; max = b; }
    else { min = b; max = a; }

    return Pin_int32 (min, x, max);

    }

/*****************************************************************************/

inline uint16 Min_uint16 (uint16 x, uint16 y)
    {

    return (x <= y ? x : y);

    }

inline uint16 Max_uint16 (uint16 x, uint16 y)
    {

    return (x >= y ? x : y);

    }

inline int16 Pin_int16 (int32 x)
    {

    x = Pin_int32 (-32768, x, 32767);

    return (int16) x;

    }

/*****************************************************************************/

inline uint32 Min_uint32 (uint32 x, uint32 y)
    {

    return (x <= y ? x : y);

    }

inline uint32 Min_uint32 (uint32 x, uint32 y, uint32 z)
    {

    return Min_uint32 (x, Min_uint32 (y, z));

    }

inline uint32 Max_uint32 (uint32 x, uint32 y)
    {

    return (x >= y ? x : y);

    }

inline uint32 Max_uint32 (uint32 x, uint32 y, uint32 z)
    {

    return Max_uint32 (x, Max_uint32 (y, z));

    }

inline uint32 Pin_uint32 (uint32 min, uint32 x, uint32 max)
    {

    return Max_uint32 (min, Min_uint32 (x, max));

    }

/*****************************************************************************/

inline uint16 Pin_uint16 (int32 x)
    {

    #if 0

    // Reference version.

    x = Pin_int32 (0, x, 0x0FFFF);

    #else

    // Single branch version.

    if (x & ~65535)
        {

        x = ~x >> 31;

        }

    #endif

    return (uint16) x;

    }

/*****************************************************************************/

inline uint32 RoundUp2 (uint32 x)
    {

    return (x + 1) & (uint32) ~1;

    }

inline uint32 RoundUp4 (uint32 x)
    {

    return (x + 3) & (uint32) ~3;

    }

inline uint32 RoundUp8 (uint32 x)
    {

    return (x + 7) & (uint32) ~7;

    }

inline uint32 RoundUp16 (uint32 x)
    {

    return (x + 15) & (uint32) ~15;

    }

inline uint32 RoundUp32 (uint32 x)
    {

    return (x + 31) & (uint32) ~31;

    }

inline uint32 RoundUpSIMD (uint32 x)
    {

    #if qDNGAVXSupport
    return RoundUp32 (x);
    #else
    return RoundUp16 (x);
    #endif

    }

inline uint32 RoundUp4096 (uint32 x)
    {

    return (x + 4095) & (uint32) ~4095;

    }

/******************************************************************************/

inline uint32 RoundDown2 (uint32 x)
    {

    return x & (uint32) ~1;

    }

inline uint32 RoundDown4 (uint32 x)
    {

    return x & (uint32) ~3;

    }

inline uint32 RoundDown8 (uint32 x)
    {

    return x & (uint32) ~7;

    }

inline uint32 RoundDown16 (uint32 x)
    {

    return x & (uint32) ~15;

    }

inline uint32 RoundDown32 (uint32 x)
    {

    return x & (uint32) ~31;

    }

inline uint32 RoundDownSIMD (uint32 x)
    {

    #if qDNGAVXSupport
    return RoundDown32 (x);
    #else
    return RoundDown16 (x);
    #endif

    }

/******************************************************************************/

inline bool RoundUpForPixelSize (uint32 x,
                                 uint32 pixelSize,
                                 uint32 *result)
    {

    #if qDNGAVXSupport
    static const uint32 kTargetMultiple = 32;
    #else
    static const uint32 kTargetMultiple = 16;
    #endif

    uint32 multiple;

    switch (pixelSize)
        {

        case 1:
        case 2:
        case 4:
        case 8:
            {
            multiple = kTargetMultiple / pixelSize;
            break;
            }

        default:
            {
            multiple = kTargetMultiple;
            break;
            }

        }

    return RoundUpUint32ToMultiple (x, multiple, result);

    }

/******************************************************************************/

inline uint32 RoundUpForPixelSize (uint32 x,
                                   uint32 pixelSize)
    {

    uint32 result = 0;

    if (!RoundUpForPixelSize (x, pixelSize, &result))
        {
        ThrowOverflow ("RoundUpForPixelSize");
        }

    return result;

    }

/******************************************************************************/

inline int32 RoundUpForPixelSizeAsInt32 (uint32 x,
                                         uint32 pixelSize)
    {

    uint32 result = 0;

    if (!RoundUpForPixelSize (x, pixelSize, &result))
        {
        ThrowOverflow ("RoundUpForPixelSize");
        }

    dng_safe_uint32 safeResult (result);

    return dng_safe_int32 (safeResult).Get ();

    }

/******************************************************************************/

// Type of padding to be performed by ComputeBufferSize.

enum PaddingType
    {

    // Don't perform any padding.

    padNone,

    // Pad each scanline to an integer multiple of SIMD vector width (16 or
    // 32) bytes (in the same way that RoundUpForPixelSize() does).

    padSIMDBytes

    };

// Returns the number of bytes required for an image tile with the given pixel
// type, tile size, number of image planes, and desired padding. Throws a
// dng_exception with dng_error_memory error code if one of the components of
// tileSize is negative or if arithmetic overflow occurs during the
// computation.

uint32 ComputeBufferSize (uint32 pixelType,
                          const dng_point &tileSize,
                          uint32 numPlanes,
                          PaddingType paddingType);

/******************************************************************************/

inline uint64 Abs_int64 (int64 x)
    {

    return (uint64) (x < 0 ? -x : x);

    }

inline int64 Min_int64 (int64 x, int64 y)
    {

    return (x <= y ? x : y);

    }

inline int64 Max_int64 (int64 x, int64 y)
    {

    return (x >= y ? x : y);

    }

inline int64 Pin_int64 (int64 min, int64 x, int64 max)
    {

    return Max_int64 (min, Min_int64 (x, max));

    }

/******************************************************************************/

inline uint64 Min_uint64 (uint64 x, uint64 y)
    {

    return (x <= y ? x : y);

    }

inline uint64 Max_uint64 (uint64 x, uint64 y)
    {

    return (x >= y ? x : y);

    }

inline uint64 Pin_uint64 (uint64 min, uint64 x, uint64 max)
    {

    return Max_uint64 (min, Min_uint64 (x, max));

    }

/*****************************************************************************/

DNG_ALWAYS_INLINE
real32 Abs_real32 (real32 x)
    {

    return (x < 0.0f ? -x : x);

    }

DNG_ALWAYS_INLINE
real32 Min_real32 (real32 x, real32 y)
    {

    return (x < y ? x : y);

    }

DNG_ALWAYS_INLINE
real32 Max_real32 (real32 x, real32 y)
    {

    return (x > y ? x : y);

    }

DNG_ALWAYS_INLINE
real32 Pin_real32 (real32 min, real32 x, real32 max)
    {

    return Max_real32 (min, Min_real32 (x, max));

    }

DNG_ALWAYS_INLINE
real32 Pin_real32 (real32 x)
    {

    return Pin_real32 (0.0f, x, 1.0f);

    }

DNG_ALWAYS_INLINE
real32 Pin_real32_Overrange (real32 min,
                             real32 x,
                             real32 max)
    {

    // Normal numbers in (min,max). No change.

    if (x > min && x < max)
        {
        return x;
        }

    // Map large numbers (including positive infinity) to max.

    else if (x > min)
        {
        return max;
        }

    // Map everything else (including negative infinity and all NaNs) to min.

    return min;

    }

DNG_ALWAYS_INLINE
real32 Pin_Overrange (real32 x)
    {

    // Normal in-range numbers, except for plus and minus zero.

    if (x > 0.0f && x <= 1.0f)
        {
        return x;
        }

    // Large numbers, including positive infinity.

    else if (x > 0.5f)
        {
        return 1.0f;
        }

    // Plus and minus zero, negative numbers, negative infinity, and all NaNs.

    return 0.0f;

    }

DNG_ALWAYS_INLINE
real32 Lerp_real32 (real32 a, real32 b, real32 t)
    {

    return a + t * (b - a);

    }

/*****************************************************************************/

inline real64 Abs_real64 (real64 x)
    {

    return (x < 0.0 ? -x : x);

    }

inline real64 Min_real64 (real64 x, real64 y)
    {

    return (x < y ? x : y);

    }

inline real64 Max_real64 (real64 x, real64 y)
    {

    return (x > y ? x : y);

    }

inline real64 Pin_real64 (real64 min, real64 x, real64 max)
    {

    return Max_real64 (min, Min_real64 (x, max));

    }

inline real64 Pin_real64 (real64 x)
    {

    return Pin_real64 (0.0, x, 1.0);

    }

inline real64 Pin_real64_Overrange (real64 min,
                                    real64 x,
                                    real64 max)
    {

    // Normal numbers in (min,max). No change.

    if (x > min && x < max)
        {
        return x;
        }

    // Map large numbers (including positive infinity) to max.

    else if (x > min)
        {
        return max;
        }

    // Map everything else (including negative infinity and all NaNs) to min.

    return min;

    }

inline real64 Lerp_real64 (real64 a, real64 b, real64 t)
    {

    return a + t * (b - a);

    }

/*****************************************************************************/

inline uint8 Floor_uint8 (real32 x)
    {

    return (uint8) Max_real32 (0.0f, x);

    }

inline uint8 Floor_uint8 (real64 x)
    {

    return (uint8) Max_real64 (0.0, x);

    }

inline uint8 Round_uint8 (real32 x)
    {

    return Floor_uint8 (x + 0.5f);

    }

inline uint8 Round_uint8 (real64 x)
    {

    return Floor_uint8 (x + 0.5);

    }

/*****************************************************************************/

inline int32 Round_int32 (real32 x)
    {

    return (int32) (x > 0.0f ? x + 0.5f : x - 0.5f);

    }

/*****************************************************************************/

inline int32 Round_int32 (real64 x)
    {

    const real64 temp = x > 0.0 ? x + 0.5 : x - 0.5;

    // NaNs will fail this test (because NaNs compare false against
    // everything) and will therefore also take the else branch.

    if (temp > real64 (std::numeric_limits<int32>::min ()) - 1.0 &&
        temp < real64 (std::numeric_limits<int32>::max ()) + 1.0)
        {
        return (int32) temp;
        }

    else
        {
        ThrowProgramError ("Overflow in Round_int32");
        // Dummy return.
        return 0;
        }

    }

/*****************************************************************************/

inline uint32 Floor_uint32 (real64 x)
    {

    const real64 temp = Max_real64 (0.0, x);

    // NaNs will fail this test (because NaNs compare false against
    // everything) and will therefore also take the else branch.

    if (temp < real64 (std::numeric_limits<uint32>::max ()) + 1.0)
        {
        return (uint32) temp;
        }

    else
        {
        ThrowProgramError ("Overflow in Floor_uint32");
        // Dummy return.
        return 0;
        }

    }

/*****************************************************************************/

inline uint32 Floor_uint32 (real32 x)
    {

    return Floor_uint32 (static_cast<real64> (x));

    }

/*****************************************************************************/

inline uint32 Round_uint32 (real32 x)
    {

    return Floor_uint32 (x + 0.5f);

    }

/*****************************************************************************/

inline uint32 Round_uint32 (real64 x)
    {

    return Floor_uint32 (x + 0.5);

    }

/******************************************************************************/

inline int64 Round_int64 (real64 x)
    {

    return (int64) (x >= 0.0 ? x + 0.5 : x - 0.5);

    }

/*****************************************************************************/

const int64 kFixed64_One  = (((int64) 1) << 32);
const int64 kFixed64_Half = (((int64) 1) << 31);

/******************************************************************************/

inline int64 Real64ToFixed64 (real64 x)
    {

    return Round_int64 (x * (real64) kFixed64_One);

    }

/******************************************************************************/

inline real64 Fixed64ToReal64 (int64 x)
    {

    return x * (1.0 / (real64) kFixed64_One);

    }

/*****************************************************************************/

inline char ForceUppercase (char c)
    {

    if (c >= 'a' && c <= 'z')
        {

        c -= 'a' - 'A';

        }

    return c;

    }

/*****************************************************************************/

inline uint16 SwapBytes16 (uint16 x)
    {

    return (uint16) ((x << 8) |
                     (x >> 8));

    }

inline uint32 SwapBytes32 (uint32 x)
    {

    return (x << 24) +
           ((x << 8) & 0x00FF0000) +
           ((x >> 8) & 0x0000FF00) +
           (x >> 24);

    }

/*****************************************************************************/

inline bool IsAligned16 (const void *p)
    {

    return (((uintptr) p) & 1) == 0;

    }

inline bool IsAligned32 (const void *p)
    {

    return (((uintptr) p) & 3) == 0;

    }

inline bool IsAligned64 (const void *p)
    {

    return (((uintptr) p) & 7) == 0;

    }

inline bool IsAligned128 (const void *p)
    {

    return (((uintptr) p) & 15) == 0;

    }

/******************************************************************************/

// Converts from RGB values (range 0.0 to 1.0) to HSV values (range 0.0 to
// 6.0 for hue, and 0.0 to 1.0 for saturation and value).
//
// INPUT REQUIREMENTS:
// All input RGB values are expected to be >= 0.
// If any RGB value is < 0 the resulting behavior is undefined and in practice
// can generate out of range S values (even NaN) and negative v values.

inline void DNG_RGBtoHSV (real32 r,
                          real32 g,
                          real32 b,
                          real32 &h,
                          real32 &s,
                          real32 &v)
    {

    v = Max_real32 (r, Max_real32 (g, b));

    real32 gap = v - Min_real32 (r, Min_real32 (g, b));

    if (gap > 0.0f)
        {

        if (r == v)
            {

            h = (g - b) / gap;

            if (h < 0.0f)
                {
                h += 6.0f;
                }

            }

        else if (g == v)
            {
            h = 2.0f + (b - r) / gap;
            }

        else
            {
            h = 4.0f + (r - g) / gap;
            }

        s = gap / v;

        }

    else
        {
        h = 0.0f;
        s = 0.0f;
        }

    }

/******************************************************************************/

// Converts from RGB values (range 0.0 to 1.0) to HSV values (range 0.0 to
// 6.0 for hue, and 0.0 to 1.0 for saturation and value).
//
// This function pins input RGB >= 0 before calling DNG_RGBtoHSV()

inline void DNG_PinnedNonnegativeRGBtoHSV (real32 r,
                                           real32 g,
                                           real32 b,
                                           real32 &h,
                                           real32 &s,
                                           real32 &v)
    {

    // Pin RGB >= 0.

    r = Max_real32 (r, 0.0f);
    g = Max_real32 (g, 0.0f);
    b = Max_real32 (b, 0.0f);

    DNG_RGBtoHSV (r, g, b, h, s, v);

    }

/*****************************************************************************/

// Converts from HSV values (range 0.0 to 6.0 for hue, and 0.0 to 1.0 for
// saturation and value) to RGB values (range 0.0 to 1.0).

inline void DNG_HSVtoRGB (real32 h,
                          real32 s,
                          real32 v,
                          real32 &r,
                          real32 &g,
                          real32 &b)
    {

    if (s > 0.0f)
        {

        if (!std::isfinite (h))
            ThrowProgramError ("Unexpected NaN or Inf");

        h = std::fmod (h, 6.0f);

        if (h < 0.0f)
            h += 6.0f;

        int32  i = (int32) h;
        real32 f = h - (real32) i;

        real32 p = v * (1.0f - s);

        #define q   (v * (1.0f - s * f))
        #define t   (v * (1.0f - s * (1.0f - f)))

        // The integer 'i' is normally one of {0,1,2,3,4,5}.
        // However, in rare cases it is possible that i = 6.
        // Namely, if the input h is a negative number with very small
        // magnitude (or if the fmod operation produces such an h value)
        // then "h += 6.0f" can produce a value of EXACTLY 6.0f,
        // in which case 'i' will be 6. This edge case is handled by
        // treating the i=6 case the same as the i=0 case.

        switch (i)
            {
            case 0: r = v; g = t; b = p; break;
            case 1: r = q; g = v; b = p; break;
            case 2: r = p; g = v; b = t; break;
            case 3: r = p; g = q; b = v; break;
            case 4: r = t; g = p; b = v; break;
            case 5: r = v; g = p; b = q; break;
            case 6: r = v; g = t; b = p; break; // see comment above
            }

        #undef q
        #undef t

        }

    else
        {
        r = v;
        g = v;
        b = v;
        }

    }

/******************************************************************************/

// High resolution timer, for code profiling.

real64 TickTimeInSeconds ();

// Lower resolution timer, but more stable.

real64 TickCountInSeconds ();

/******************************************************************************/

void DNGIncrementTimerLevel ();

int32 DNGDecrementTimerLevel ();

/******************************************************************************/

class dng_timer: private dng_uncopyable
    {

    public:

        dng_timer (const char *message);

        ~dng_timer ();

    private:

        const char *fMessage;

        real64 fStartTime;

    };

/*****************************************************************************/

// Returns the maximum squared Euclidean distance from the specified point to the
// specified rectangle rect.

real64 MaxSquaredDistancePointToRect (const dng_point_real64 &point,
                                      const dng_rect_real64 &rect);

/*****************************************************************************/

// Returns the maximum Euclidean distance from the specified point to the specified
// rectangle rect.

real64 MaxDistancePointToRect (const dng_point_real64 &point,
                               const dng_rect_real64 &rect);

/*****************************************************************************/

inline uint32 DNG_HalfToFloat (uint16 halfValue)
    {

    int32 sign     = (halfValue >> 15) & 0x00000001;
    int32 exponent = (halfValue >> 10) & 0x0000001f;
    int32 mantissa =  halfValue        & 0x000003ff;

    if (exponent == 0)
        {

        if (mantissa == 0)
            {

            // Plus or minus zero

            return (uint32) (sign << 31);

            }

        else
            {

            // Denormalized number -- renormalize it

            while (!(mantissa & 0x00000400))
                {
                mantissa <<= 1;
                exponent -=  1;
                }

            exponent += 1;
            mantissa &= ~0x00000400;

            }

        }

    else if (exponent == 31)
        {

        if (mantissa == 0)
            {

            // Positive or negative infinity, convert to maximum (16 bit) values.

            return (uint32) ((sign << 31) | ((0x1eL + 127 - 15) << 23) |  (0x3ffL << 13));

            }

        else
            {

            // Nan -- Just set to zero.

            return 0;

            }

        }

    // Normalized number

    exponent += (127 - 15);
    mantissa <<= 13;

    // Assemble sign, exponent and mantissa.

    return (uint32) ((sign << 31) | (exponent << 23) | mantissa);

    }

/*****************************************************************************/

inline uint16 DNG_FloatToHalf (uint32 i)
    {

    int32 sign     =  (i >> 16) & 0x00008000;
    int32 exponent = ((i >> 23) & 0x000000ff) - (127 - 15);
    int32 mantissa =   i        & 0x007fffff;

    if (exponent <= 0)
        {

        if (exponent < -10)
            {

            // Zero or underflow to zero.

            return (uint16)sign;

            }

        // E is between -10 and 0.  We convert f to a denormalized half.

        mantissa = (mantissa | 0x00800000) >> (1 - exponent);

        // Round to nearest, round "0.5" up.
        //
        // Rounding may cause the significand to overflow and make
        // our number normalized.  Because of the way a half's bits
        // are laid out, we don't have to treat this case separately;
        // the code below will handle it correctly.

        if (mantissa &  0x00001000)
            mantissa += 0x00002000;

        // Assemble the half from sign, exponent (zero) and mantissa.

        return (uint16)(sign | (mantissa >> 13));

        }

    else if (exponent == 0xff - (127 - 15))
        {

        if (mantissa == 0)
            {

            // F is an infinity; convert f to a half
            // infinity with the same sign as f.

            return (uint16)(sign | 0x7c00);

            }

        else
            {

            // F is a NAN; produce a half NAN that preserves
            // the sign bit and the 10 leftmost bits of the
            // significand of f.

            return (uint16)(sign | 0x7c00 | (mantissa >> 13));

            }

        }

    // E is greater than zero.  F is a normalized float.
    // We try to convert f to a normalized half.

    // Round to nearest, round "0.5" up

    if (mantissa & 0x00001000)
        {

        mantissa += 0x00002000;

        if (mantissa & 0x00800000)
            {
            mantissa =  0;      // overflow in significand,
            exponent += 1;      // adjust exponent
            }

        }

    // Handle exponent overflow

    if (exponent > 30)
        {
        return (uint16)(sign | 0x7c00); // infinity with the same sign as f.
        }

    // Assemble the half from sign, exponent and mantissa.

    return (uint16)(sign | (exponent << 10) | (mantissa >> 13));

    }

/*****************************************************************************/

inline uint32 DNG_FP24ToFloat (const uint8 *input)
    {

    int32 sign     = (input [0] >> 7) & 0x01;
    int32 exponent = (input [0]     ) & 0x7F;
    int32 mantissa = (((int32) input [1]) << 8) | input[2];

    if (exponent == 0)
        {

        if (mantissa == 0)
            {

            // Plus or minus zero

            return (uint32) (sign << 31);

            }

        else
            {

            // Denormalized number -- renormalize it

            while (!(mantissa & 0x00010000))
                {
                mantissa <<= 1;
                exponent -=  1;
                }

            exponent += 1;
            mantissa &= ~0x00010000;

            }

        }

    else if (exponent == 127)
        {

        if (mantissa == 0)
            {

            // Positive or negative infinity, convert to maximum (24 bit) values.

            return (uint32) ((sign << 31) | ((0x7eL + 128 - 64) << 23) |  (0xffffL << 7));

            }

        else
            {

            // Nan -- Just set to zero.

            return 0;

            }

        }

    // Normalized number

    exponent += (128 - 64);
    mantissa <<= 7;

    // Assemble sign, exponent and mantissa.

    return (uint32) ((sign << 31) | (exponent << 23) | mantissa);

    }

/*****************************************************************************/

inline void DNG_FloatToFP24 (uint32 input, uint8 *output)
    {

    int32 exponent = (int32) ((input >> 23) & 0xFF) - 128;
    int32 mantissa = input & 0x007FFFFF;

    if (exponent == 127)    // infinity or NaN
        {

        // Will the NaN alais to infinity?

        if (mantissa != 0x007FFFFF && ((mantissa >> 7) == 0xFFFF))
            {

            mantissa &= 0x003FFFFF;     // knock out msb to make it a NaN

            }

        }

    else if (exponent > 63)     // overflow, map to infinity
        {

        exponent = 63;
        mantissa = 0x007FFFFF;

        }

    else if (exponent <= -64)
        {

        if (exponent >= -79)        // encode as denorm
            {
            mantissa = (mantissa | 0x00800000) >> (-63 - exponent);
            }

        else                        // underflow to zero
            {
            mantissa = 0;
            }

        exponent = -64;

        }

    output [0] = (uint8)(((input >> 24) & 0x80) | (uint32) (exponent + 64));

    output [1] = (mantissa >> 15) & 0x00FF;
    output [2] = (mantissa >>  7) & 0x00FF;

    }

/******************************************************************************/

// The following code was from PSDivide.h in Photoshop.

// High order 32-bits of an unsigned 32 by 32 multiply.

#ifndef MULUH

#if defined(_X86_) && defined(_MSC_VER)

inline uint32 Muluh86 (uint32 x, uint32 y)
    {
    uint32 result;
    __asm
        {
        MOV     EAX, x
        MUL     y
        MOV     result, EDX
        }
    return (result);
    }

#define MULUH   Muluh86

#else

#define MULUH(x,y)  ((uint32) (((x) * (uint64) (y)) >> 32))

#endif

#endif

// High order 32-bits of an signed 32 by 32 multiply.

#ifndef MULSH

#if defined(_X86_) && defined(_MSC_VER)

inline int32 Mulsh86 (int32 x, int32 y)
    {
    int32 result;
    __asm
        {
        MOV     EAX, x
        IMUL    y
        MOV     result, EDX
        }
    return (result);
    }

#define MULSH   Mulsh86

#else

#define MULSH(x,y)  ((int32) (((x) * (int64) (y)) >> 32))

#endif

#endif

/******************************************************************************/

// Random number generator (identical to Apple's) for portable use.

// This implements the "minimal standard random number generator"
// as proposed by Park and Miller in CACM October, 1988.
// It has a period of 2147483647 (0x7fffffff)

// This is the ACM standard 30 bit generator:
// x' = (x * 16807) mod 2^31-1

DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow")
inline uint32 DNG_Random (uint32 seed)
    {

    // high = seed / 127773

    uint32 temp = MULUH (0x069C16BD, seed);
    uint32 high = (temp + ((seed - temp) >> 1)) >> 16;

    // low = seed % 127773

    uint32 low = seed - high * 127773;

    // seed = (seed * 16807) % 2147483647

    seed = 16807 * low - 2836 * high;

    if (seed & 0x80000000)
        seed += 2147483647;

    return seed;

    }

/*****************************************************************************/

class dng_dither: private dng_uncopyable
    {

    public:

        static const uint32 kRNGBits = 7;

        static const uint32 kRNGSize = 1 << kRNGBits;

        static const uint32 kRNGMask = kRNGSize - 1;

        static const uint32 kRNGSize2D = kRNGSize * kRNGSize;

    private:

        dng_memory_data fNoiseBuffer;

    private:

        dng_dither ();

    public:

        static const dng_dither & Get ();

    public:

        const uint16 *NoiseBuffer16 () const
            {
            return fNoiseBuffer.Buffer_uint16 ();
            }

    };

/*****************************************************************************/

void HistogramArea (dng_host &host,
                    const dng_image &image,
                    const dng_rect &area,
                    uint32 *hist,
                    uint32 histLimit,
                    uint32 plane = 0);

/*****************************************************************************/

void LimitFloatBitDepth (dng_host &host,
                         const dng_image &srcImage,
                         dng_image &dstImage,
                         uint32 bitDepth,
                         real32 scale = 1.0f);

/*****************************************************************************/

#if qMacOS

/*****************************************************************************/

template<typename T>
class CFReleaseHelper
    {

    private:

        T fRef;

    public:

        CFReleaseHelper (T ref)
            :   fRef (ref)
            {
            }

        ~CFReleaseHelper ()
            {
            if (fRef)
                {
                CFRelease (fRef);
                }
            }

        T Get () const
            {
            return fRef;
            }

    };

/*****************************************************************************/

#endif  // qMacOS

/*****************************************************************************/

// x is assumed to be in [0,1].
// Result will also be in [0,1].
//
// Applies smooth cubic function f(x) such that first and second derivatives
// at endpoints are both zero, i.e., f'(x) = 0 and f''(x) = 0 for x = 0 and x
// = 1.

static inline real64 SmoothStep (real64 x)
    {

    return x * x * (3.0 - 2.0 * x);

    }

/*****************************************************************************/

uint32 MinBackwardVersionForCompression (uint32 compression);

/*****************************************************************************/

class dng_line_real32
    {

    public:

        real32 fX;
        real32 fY;
        real32 fSlope;

    public:

        dng_line_real32 (real32 x0,
                         real32 y0,
                         real32 x1,
                         real32 y1)

            :   fX (x0)
            ,   fY (y0)

            ,   fSlope ((x0 == x1) ? 0.0f : ((y0 - y1) / (x0 - x1)))

            {

            }

        DNG_ALWAYS_INLINE real32 Evaluate (real32 x) const
            {

            return fY + fSlope * (x - fX);

            }

    };

/*****************************************************************************/

// Implements ImageSequenceInfo tag introduced in DNG 1.7.

class dng_image_sequence_info
    {

    public:

        dng_string fSequenceID;

        dng_string fSequenceType;

        dng_string fFrameInfo;

        uint32 fIndex = 0;

        uint32 fCount = 0;

        uint8 fIsFinal = 2;      // unknown

    public:

        bool IsValid () const
            {
            return (fSequenceID  .Length () >= 8 &&
                    fSequenceType.Length () >= 1);
            }

        uint32 TagCount () const
            {

            uint32 sum = SafeUint32Add (fSequenceID  .Length (),
                                        fSequenceType.Length ());

            sum = SafeUint32Add (fFrameInfo.Length (), sum);

            sum = SafeUint32Add (sum,
                                 1 +         // null-term for sequence ID
                                 1 +         // null-term for sequence type
                                 1 +         // null-term for frame info
                                 4 +         // index
                                 4 +         // count
                                 1);         // final

            return sum;

            }

        tiff_tag * MakeTag (dng_memory_allocator &allocator) const;

        bool operator== (const dng_image_sequence_info &src) const
            {
            return (fSequenceID   == src.fSequenceID   &&
                    fSequenceType == src.fSequenceType &&
                    fFrameInfo    == src.fFrameInfo    &&
                    fIndex        == src.fIndex        &&
                    fCount        == src.fCount        &&
                    fIsFinal      == src.fIsFinal);
            }

    };

/*****************************************************************************/

// Implements ImageStats tag introduced in DNG 1.7.

class dng_image_stats
    {

    public:

        static constexpr uint32 kTag_WeightedAverage   = 1;
        static constexpr uint32 kTag_WeightedSamples   = 2;
        static constexpr uint32 kTag_Weights           = 3;
        static constexpr uint32 kTag_ColorAverage      = 4;
        static constexpr uint32 kTag_ColorSamples      = 5;

        static constexpr size_t kMaxSamples = 1024;

    public:

        struct weighted_sample
            {

            real32 fFrac  = 0.0f;
            real32 fValue = 0.0f;

            bool operator== (const weighted_sample &src) const
                {
                return (fFrac  == src.fFrac &&
                        fValue == src.fValue);
                }

            };

        struct color_sample
            {

            real32 fFrac = 0.0f;

            std::vector<real32> fValues;

            bool operator== (const color_sample &src) const
                {
                return (fFrac   == src.fFrac &&
                        fValues == src.fValues);
                }

            };

    public:

        // Empty vectors mean invalid/missing.

        std::vector<real32> fWeightedAverage;

        std::vector<weighted_sample> fWeightedSamples;

        std::vector<real32> fWeights;

        std::vector<real32> fColorAverage;

        std::vector<color_sample> fColorSamples;

    public:

        bool IsValidForPlaneCount (uint32 planeCount) const;

        uint32 TagCount () const;

        tiff_tag * MakeTag (dng_memory_allocator &allocator) const;

        bool operator== (const dng_image_stats &src) const;

        void Parse (dng_stream &stream);

        #if qDNGValidate
        void Dump () const;
        #endif

    };

/*****************************************************************************/

DNG_ALWAYS_INLINE real32 EncodeOverrange (real32 x)
    {

    x = Max_real32 (x, 0.0f);

    return x * (256.0f + x) / (256.0f * (1.0f + x));

    }

/*****************************************************************************/

DNG_ALWAYS_INLINE real32 DecodeOverrange (real32 x)
    {

    x = Max_real32 (x, 0.0f);

    return 16.0f * ((8.0f * x) - 8.0f + sqrtf (64.0f * x * x - 127.0f * x + 64.0f));

    }

/*****************************************************************************/

#endif  // __dng_utils__

/*****************************************************************************/
