import math
import unittest

import numpy
import six

import chainer
from chainer.backends import cuda
from chainer import functions
from chainer import gradient_check
from chainer import testing
from chainer.testing import attr
from chainer import utils


@testing.parameterize(*(testing.product({
    # Test dtype
    'dtype': [numpy.float16, numpy.float32, numpy.float64],
    'shape': [(8, 7)],
    'normalize': [True],
    'label_dtype': [numpy.int32],
}) + [
    # Test normalize
    {'dtype': numpy.float32,
     'shape': (8, 7),
     'normalize': False,
     'label_dtype': numpy.int32,
     },
    # Test ignore_all
    {'dtype': numpy.float32,
     'shape': (8, 7),
     'normalize': True,
     'ignore_all': True,
     'label_dtype': numpy.int32,
     },
    # too large shape causes int32 -> float64 issue
    {'dtype': numpy.float32,
     'shape': (65536, 1),
     'normalize': False,
     'label_dtype': numpy.int32,
     },
] + testing.product({
    # Test label_dtype
    'dtype': [numpy.float32],
    'shape': [(8, 7)],
    'normalize': [True],
    'label_dtype': [numpy.int8, numpy.int16, numpy.int32, numpy.int64],
})))
class TestSigmoidCrossEntropy(unittest.TestCase):

    def setUp(self):
        self.x = numpy.random.uniform(-1, 1, self.shape).astype(self.dtype)
        if getattr(self, 'ignore_all', False):
            self.t = -numpy.ones(self.shape).astype(self.label_dtype)
        else:
            self.t = numpy.random.randint(-1, 2,
                                          self.shape).astype(self.label_dtype)
        self.gy = numpy.random.random(self.shape).astype(self.dtype)
        self.ggx = numpy.random.uniform(
            -1, 1, self.shape).astype(self.dtype)

        if self.dtype == numpy.float16:
            self.places = 2
            self.check_backward_options = {
                'dtype': numpy.float64, 'atol': 5e-2, 'rtol': 5e-2}
            self.check_double_backward_options = {
                'dtype': numpy.float64, 'atol': 5e-2, 'rtol': 5e-2}
        else:
            self.places = 5
            self.check_backward_options = {'atol': 5e-3, 'rtol': 5e-3}
            self.check_double_backward_options = {'atol': 5e-3, 'rtol': 5e-3}