/*
 * This file defines a compile time constant casting table for use in
 * a few situations:
 * 1. As a fast-path in can-cast (untested how much it helps).
 * 2. To define the actual cast safety stored on the CastingImpl/ArrayMethod
 * 3. For scalar math, since it also needs cast safety information.
 *
 * It is useful to have this constant to allow writing compile time generic
 * code based on cast safety in the scalar math code.
 */

#ifndef NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_
#define NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_

#include "numpy/ndarraytypes.h"


/* The from type fits into to (it has a smaller or equal number of bits) */
#define FITS(FROM, TO) (NPY_SIZEOF_##FROM <= NPY_SIZEOF_##TO)
/* Unsigned "from" fits a signed integer if it is truly smaller */
#define UFITS(FROM, TO) (NPY_SIZEOF_##FROM < NPY_SIZEOF_##TO)
/* Integer "from" only fits a float if it is truly smaller or double... */
#define IFITS(FROM, TO) (  \
    NPY_SIZEOF_##FROM < NPY_SIZEOF_##TO || (  \
            NPY_SIZEOF_##FROM == NPY_SIZEOF_##TO  \
            && NPY_SIZEOF_##FROM >= NPY_SIZEOF_DOUBLE))

/*
 * NOTE: The Order is bool, integers (signed, unsigned) tuples, float, cfloat,
 *       then 6 fixed ones (object, string, unicode, void, datetime, timedelta),
 *       and finally half.
 *       Note that in the future we may only need the numeric casts here, but
 *       currently it fills in the others as well.
 */
#define CASTS_SAFELY_FROM_UINT(FROM)  \
    {0,  \
     UFITS(FROM, BYTE), FITS(FROM, BYTE), UFITS(FROM, SHORT), FITS(FROM, SHORT),  \
     UFITS(FROM, INT), FITS(FROM, INT), UFITS(FROM, LONG), FITS(FROM, LONG),  \
     UFITS(FROM, LONGLONG), FITS(FROM, LONGLONG),  \
     IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE),  \
     IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE),  \
     1, 1, 1, 1, 0, NPY_SIZEOF_##FROM < NPY_SIZEOF_TIMEDELTA, IFITS(FROM, HALF)}

#define CASTS_SAFELY_FROM_INT(FROM)  \
    {0,  \
     FITS(FROM, BYTE), 0, FITS(FROM, SHORT), 0,  \
     FITS(FROM, INT), 0, FITS(FROM, LONG), 0,  \
     FITS(FROM, LONGLONG), 0,  \
     IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE),  \
     IFITS(FROM, FLOAT), IFITS(FROM, DOUBLE), IFITS(FROM, LONGDOUBLE),  \
     1, 1, 1, 1, 0, NPY_SIZEOF_##FROM <= NPY_SIZEOF_TIMEDELTA, IFITS(FROM, HALF)}

/* Floats are similar to ints, but cap at double */
#define CASTS_SAFELY_FROM_FLOAT(FROM)  \
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  \
     FITS(FROM, FLOAT), FITS(FROM, DOUBLE), FITS(FROM, LONGDOUBLE),  \
     FITS(FROM, FLOAT), FITS(FROM, DOUBLE), FITS(FROM, LONGDOUBLE),  \
     1, 1, 1, 1, 0, 0, FITS(FROM, HALF)}

#define CASTS_SAFELY_FROM_CFLOAT(FROM)  \
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  \
     0, 0, 0,  \
     FITS(FROM, FLOAT), FITS(FROM, DOUBLE), FITS(FROM, LONGDOUBLE),  \
     1, 1, 1, 1, 0, 0, 0}

static const npy_bool _npy_can_cast_safely_table[NPY_NTYPES_LEGACY][NPY_NTYPES_LEGACY] = {
        /* Bool safely casts to anything except datetime (has no zero) */
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 0, 1, 1},
        /* Integers in pairs of signed, unsigned */
        CASTS_SAFELY_FROM_INT(BYTE), CASTS_SAFELY_FROM_UINT(BYTE),
        CASTS_SAFELY_FROM_INT(SHORT), CASTS_SAFELY_FROM_UINT(SHORT),
        CASTS_SAFELY_FROM_INT(INT), CASTS_SAFELY_FROM_UINT(INT),
        CASTS_SAFELY_FROM_INT(LONG), CASTS_SAFELY_FROM_UINT(LONG),
        CASTS_SAFELY_FROM_INT(LONGLONG), CASTS_SAFELY_FROM_UINT(LONGLONG),
        /* Floats and complex */
        CASTS_SAFELY_FROM_FLOAT(FLOAT),
        CASTS_SAFELY_FROM_FLOAT(DOUBLE),
        CASTS_SAFELY_FROM_FLOAT(LONGDOUBLE),
        CASTS_SAFELY_FROM_CFLOAT(FLOAT),
        CASTS_SAFELY_FROM_CFLOAT(DOUBLE),
        CASTS_SAFELY_FROM_CFLOAT(LONGDOUBLE),
        /*
         * Following the main numeric types are:
         * object, string, unicode, void, datetime, timedelta (and half)
         */
        /* object casts safely only to itself */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 0, 0, 0, 0, 0, 0},
        /* String casts safely to object, unicode and void */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 1, 1, 1, 0, 0, 0},
        /* Unicode casts safely to object and void */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 0, 1, 1, 0, 0, 0},
        /* Void cast safely to object */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 0, 0, 1, 0, 0, 0},
        /* datetime cast safely to object, string, unicode, void */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 1, 1, 1, 1, 0, 0},
        /* timedelta cast safely to object, string, unicode, void */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* bool + ints */
         0, 0, 0, 0, 0, 0,  /* floats (without half) */
         1, 1, 1, 1, 0, 1, 0},
        /* half */
        CASTS_SAFELY_FROM_FLOAT(HALF),
};

#undef FITS
#undef UFITS
#undef IFITS
#undef CASTS_SAFELY_TO_UINT
#undef CASTS_SAFELY_TO_INT
#undef CASTS_SAFELY_TO_FLOAT
#undef CASTS_SAFELY_TO_CFLOAT

#endif  /* NUMPY_CORE_SRC_MULTIARRAY_CAN_CAST_TABLE_H_ */
