@staticmethod
def types(data, backward=False):
    'Find dimensions and valid types of a nested list-of-lists\n\n        Any given data may be representable by a range of CDF types; infer\n        the CDF types which can represent this data. This breaks down to:\n          1. Proper kind (numerical, string, time)\n          2. Proper range (stores highest and lowest number)\n          3. Sufficient resolution (EPOCH16 required if datetime has\n             microseconds or below.)\n\n        If more than one value satisfies the requirements, types are returned\n        in preferred order:\n          1. Type that matches precision of data first, then\n          2. integer type before float type, then\n          3. Smallest type first, then\n          4. signed type first, then\n          5. specifically-named (CDF_BYTE) vs. generically named (CDF_INT1)\n        So for example, EPOCH_16 is preferred over EPOCH if L{data} specifies\n        below the millisecond level (rule 1), but otherwise EPOCH is preferred\n        (rule 2).\n\n        For floats, four-byte is preferred unless eight-byte is required:\n          1. absolute values between 0 and 3e-39\n          2. absolute values greater than 1.7e38\n        This will switch to an eight-byte double in some cases where four bytes\n        would be sufficient for IEEE 754 encoding, but where DEC formats would\n        require eight.\n\n        @param data: data for which dimensions and CDF types are desired\n        @type data: list (of lists)\n        @param backward: limit to pre-CDF3 types\n        @type backward: bool\n        @return: dimensions of L{data}, in order outside-in;\n                 CDF types which can represent this data;\n                 number of elements required (i.e. length of longest string)\n        @rtype: 3-tuple of lists ([int], [ctypes.c_long], [int])\n        @raise ValueError: if L{data} has irregular dimensions\n        '
    d = numpy.asanyarray(data)
    dims = d.shape
    elements = 1
    types = []
    _Hyperslice.check_well_formed(d)
    if (d.dtype.kind in ('S', 'U')):
        types = [const.CDF_CHAR, const.CDF_UCHAR]
        elements = d.dtype.itemsize
        if (d.dtype.kind == 'U'):
            elements //= 4
    elif (d.size and hasattr(numpy.ma.getdata(d).flat[0], 'microsecond')):
        if (max(((dt.microsecond % 1000) for dt in d.flat)) > 0):
            types = [const.CDF_EPOCH16, const.CDF_EPOCH, const.CDF_TIME_TT2000]
        else:
            types = [const.CDF_EPOCH, const.CDF_EPOCH16, const.CDF_TIME_TT2000]
        if backward:
            del types[types.index(const.CDF_EPOCH16)]
            del types[(- 1)]
        elif (not lib.supports_int8):
            del types[(- 1)]
    elif ((d is data) or isinstance(data, numpy.generic)):
        types = [k for k in lib.numpytypedict if (((lib.numpytypedict[k] == d.dtype) or (lib.numpytypedict[k] == d.dtype.newbyteorder())) and (not (k in lib.timetypes)))]
        if (((not lib.supports_int8) or backward) and (const.CDF_INT8.value in types)):
            del types[types.index(const.CDF_INT8.value)]
        types.sort(key=(lambda x: (x % 50)), reverse=True)
    if (not types):
        if (d.dtype.kind == 'O'):
            trytypes = (numpy.uint64, numpy.int64, numpy.float64)
            for t in trytypes:
                try:
                    newd = d.astype(dtype=t)
                except:
                    continue
                if (newd == d).all():
                    d = newd
                    break
            else:
                raise ValueError('Cannot convert generic objects to CDF type.')
        if (d.dtype.kind in ('i', 'u')):
            minval = numpy.min(d)
            maxval = numpy.max(d)
            if (minval < 0):
                types = [const.CDF_BYTE, const.CDF_INT1, const.CDF_INT2, const.CDF_INT4, const.CDF_INT8, const.CDF_FLOAT, const.CDF_REAL4, const.CDF_DOUBLE, const.CDF_REAL8]
                cutoffs = [(2 ** 7), (2 ** 7), (2 ** 15), (2 ** 31), (2 ** 63), 1.7e+38, 1.7e+38, 8e+307, 8e+307]
            else:
                types = [const.CDF_BYTE, const.CDF_INT1, const.CDF_UINT1, const.CDF_INT2, const.CDF_UINT2, const.CDF_INT4, const.CDF_UINT4, const.CDF_INT8, const.CDF_FLOAT, const.CDF_REAL4, const.CDF_DOUBLE, const.CDF_REAL8]
                cutoffs = [(2 ** 7), (2 ** 7), (2 ** 8), (2 ** 15), (2 ** 16), (2 ** 31), (2 ** 32), (2 ** 63), 1.7e+38, 1.7e+38, 8e+307, 8e+307]
            types = [t for (t, c) in zip(types, cutoffs) if ((c > maxval) and ((minval >= 0) or (minval >= (- c))))]
            if (((not lib.supports_int8) or backward) and (const.CDF_INT8 in types)):
                del types[types.index(const.CDF_INT8)]
        elif (dims is ()):
            if ((d != 0) and ((abs(d) > 1.7e+38) or (abs(d) < 3e-39))):
                types = [const.CDF_DOUBLE, const.CDF_REAL8]
            else:
                types = [const.CDF_FLOAT, const.CDF_REAL4, const.CDF_DOUBLE, const.CDF_REAL8]
        else:
            absolutes = numpy.abs(d[(d != 0)])
            if ((len(absolutes) > 0) and ((numpy.max(absolutes) > 1.7e+38) or (numpy.min(absolutes) < 3e-39))):
                types = [const.CDF_DOUBLE, const.CDF_REAL8]
            else:
                types = [const.CDF_FLOAT, const.CDF_REAL4, const.CDF_DOUBLE, const.CDF_REAL8]
    types = [(t.value if hasattr(t, 'value') else t) for t in types]
    return (dims, types, elements)
Find dimensions and valid types of a nested list-of-lists

Any given data may be representable by a range of CDF types; infer
the CDF types which can represent this data. This breaks down to:
  1. Proper kind (numerical, string, time)
  2. Proper range (stores highest and lowest number)
  3. Sufficient resolution (EPOCH16 required if datetime has
     microseconds or below.)

If more than one value satisfies the requirements, types are returned
in preferred order:
  1. Type that matches precision of data first, then
  2. integer type before float type, then
  3. Smallest type first, then
  4. signed type first, then
  5. specifically-named (CDF_BYTE) vs. generically named (CDF_INT1)
So for example, EPOCH_16 is preferred over EPOCH if L{data} specifies
below the millisecond level (rule 1), but otherwise EPOCH is preferred
(rule 2).

For floats, four-byte is preferred unless eight-byte is required:
  1. absolute values between 0 and 3e-39
  2. absolute values greater than 1.7e38
This will switch to an eight-byte double in some cases where four bytes
would be sufficient for IEEE 754 encoding, but where DEC formats would
require eight.

@param data: data for which dimensions and CDF types are desired
@type data: list (of lists)
@param backward: limit to pre-CDF3 types
@type backward: bool
@return: dimensions of L{data}, in order outside-in;
         CDF types which can represent this data;
         number of elements required (i.e. length of longest string)
@rtype: 3-tuple of lists ([int], [ctypes.c_long], [int])
@raise ValueError: if L{data} has irregular dimensions
@staticmethod
def types(data, backward=False):
    'Find dimensions and valid types of a nested list-of-lists\n\n        Any given data may be representable by a range of CDF types; infer\n        the CDF types which can represent this data. This breaks down to:\n          1. Proper kind (numerical, string, time)\n          2. Proper range (stores highest and lowest number)\n          3. Sufficient resolution (EPOCH16 required if datetime has\n             microseconds or below.)\n\n        If more than one value satisfies the requirements, types are returned\n        in preferred order:\n          1. Type that matches precision of data first, then\n          2. integer type before float type, then\n          3. Smallest type first, then\n          4. signed type first, then\n          5. specifically-named (CDF_BYTE) vs. generically named (CDF_INT1)\n        So for example, EPOCH_16 is preferred over EPOCH if L{data} specifies\n        below the millisecond level (rule 1), but otherwise EPOCH is preferred\n        (rule 2).\n\n        For floats, four-byte is preferred unless eight-byte is required:\n          1. absolute values between 0 and 3e-39\n          2. absolute values greater than 1.7e38\n        This will switch to an eight-byte double in some cases where four bytes\n        would be sufficient for IEEE 754 encoding, but where DEC formats would\n        require eight.\n\n        @param data: data for which dimensions and CDF types are desired\n        @type data: list (of lists)\n        @param backward: limit to pre-CDF3 types\n        @type backward: bool\n        @return: dimensions of L{data}, in order outside-in;\n                 CDF types which can represent this data;\n                 number of elements required (i.e. length of longest string)\n        @rtype: 3-tuple of lists ([int], [ctypes.c_long], [int])\n        @raise ValueError: if L{data} has irregular dimensions\n        '
    d = numpy.asanyarray(data)
    dims = d.shape
    elements = 1
    types = []
    _Hyperslice.check_well_formed(d)
    if (d.dtype.kind in ('S', 'U')):
        types = [const.CDF_CHAR, const.CDF_UCHAR]
        elements = d.dtype.itemsize
        if (d.dtype.kind == 'U'):
            elements //= 4
    elif (d.size and hasattr(numpy.ma.getdata(d).flat[0], 'microsecond')):
        if (max(((dt.microsecond % 1000) for dt in d.flat)) > 0):
            types = [const.CDF_EPOCH16, const.CDF_EPOCH, const.CDF_TIME_TT2000]
        else:
            types = [const.CDF_EPOCH, const.CDF_EPOCH16, const.CDF_TIME_TT2000]
        if backward:
            del types[types.index(const.CDF_EPOCH16)]
            del types[(- 1)]
        elif (not lib.supports_int8):
            del types[(- 1)]
    elif ((d is data) or isinstance(data, numpy.generic)):
        types = [k for k in lib.numpytypedict if (((lib.numpytypedict[k] == d.dtype) or (lib.numpytypedict[k] == d.dtype.newbyteorder())) and (not (k in lib.timetypes)))]
        if (((not lib.supports_int8) or backward) and (const.CDF_INT8.value in types)):
            del types[types.index(const.CDF_INT8.value)]
        types.sort(key=(lambda x: (x % 50)), reverse=True)
    if (not types):
        if (d.dtype.kind == 'O'):
            trytypes = (numpy.uint64, numpy.int64, numpy.float64)
            for t in trytypes:
                try:
                    newd = d.astype(dtype=t)
                except:
                    continue
                if (newd == d).all():
                    d = newd
                    break
            else:
                raise ValueError('Cannot convert generic objects to CDF type.')
        if (d.dtype.kind in ('i', 'u')):
            minval = numpy.min(d)
            maxval = numpy.max(d)
            if (minval < 0):
                types = [const.CDF_BYTE, const.CDF_INT1, const.CDF_INT2, const.CDF_INT4, const.CDF_INT8, const.CDF_FLOAT, const.CDF_REAL4, const.CDF_DOUBLE, const.CDF_REAL8]
                cutoffs = [(2 ** 7), (2 ** 7), (2 ** 15), (2 ** 31), (2 ** 63), 1.7e+38, 1.7e+38, 8e+307, 8e+307]
            else:
                types = [const.CDF_BYTE, const.CDF_INT1, const.CDF_UINT1, const.CDF_INT2, const.CDF_UINT2, const.CDF_INT4, const.CDF_UINT4, const.CDF_INT8, const.CDF_FLOAT, const.CDF_REAL4, const.CDF_DOUBLE, const.CDF_REAL8]
                cutoffs = [(2 ** 7), (2 ** 7), (2 ** 8), (2 ** 15), (2 ** 16), (2 ** 31), (2 ** 32), (2 ** 63), 1.7e+38, 1.7e+38, 8e+307, 8e+307]
            types = [t for (t, c) in zip(types, cutoffs) if ((c > maxval) and ((minval >= 0) or (minval >= (- c))))]
            if (((not lib.supports_int8) or backward) and (const.CDF_INT8 in types)):
                del types[types.index(const.CDF_INT8)]
        elif (dims is ()):
            if ((d != 0) and ((abs(d) > 1.7e+38) or (abs(d) < 3e-39))):
                types = [const.CDF_DOUBLE, const.CDF_REAL8]
            else:
                types = [const.CDF_FLOAT, const.CDF_REAL4, const.CDF_DOUBLE, const.CDF_REAL8]
        else:
            absolutes = numpy.abs(d[(d != 0)])
            if ((len(absolutes) > 0) and ((numpy.max(absolutes) > 1.7e+38) or (numpy.min(absolutes) < 3e-39))):
                types = [const.CDF_DOUBLE, const.CDF_REAL8]
            else:
                types = [const.CDF_FLOAT, const.CDF_REAL4, const.CDF_DOUBLE, const.CDF_REAL8]
    types = [(t.value if hasattr(t, 'value') else t) for t in types]
    return (dims, types, elements)