# Licensed under a 3-clause BSD style license - see LICENSE.rst

import pytest
import numpy as np
from astropop.testing import (assert_equal, assert_almost_equal,
                              assert_is_instance, assert_is_none,
                              assert_false, assert_true)
from astropop.framedata import FrameData
from astropop.math.physical import QFloat, qfloat, units, \
                                   same_unit, UnitsError, \
                                   equal_within_errors, \
                                   convert_to_qfloat

# pylint: disable=no-member, pointless-statement

# Test units handling --------------------------------------------------------
def test_qfloat_same_unit():
    qf1 = QFloat(1.0, 0.1, 'm')
    qf2 = QFloat(200, 10, 'cm')
    qf3 = QFloat(120, 0.3, 's')
    qf4 = QFloat(2, 0.01, 'min')

    # must be ok, without any convertion
    qf_1, qf_2 = same_unit(qf1, qf1)
    assert_equal(qf_1.nominal, 1.0)
    assert_equal(qf_1.uncertainty, 0.1)
    assert_equal(qf_1.unit, units.m)
    assert_equal(qf_2.nominal, 1.0)
    assert_equal(qf_2.uncertainty, 0.1)
    assert_equal(qf_2.unit, units.m)

    # Doing conversion
    qf_1, qf_2 = same_unit(qf1, qf2)
    assert_equal(qf_1.unit, units.m)
    assert_equal(qf_1.nominal, 1.0)
    assert_equal(qf_1.uncertainty, 0.1)
    assert_equal(qf_2.unit, units.m)
    assert_equal(qf_2.nominal, 2.0)
    assert_equal(qf_2.uncertainty, 0.1)

    # Inverse Conversion
    qf_2, qf_1 = same_unit(qf2, qf1)
    assert_equal(qf_1.unit, units.cm)
    assert_equal(qf_1.nominal, 100)
    assert_equal(qf_1.uncertainty, 10)
    assert_equal(qf_2.unit, units.cm)
    assert_equal(qf_2.nominal, 200)
    assert_equal(qf_2.uncertainty, 10)

    # Raise conversion
    with pytest.raises(UnitsError):
        same_unit(qf3, qf1, test_qfloat_same_unit)

    # non-prefix conversion
    qf_3, qf_4 = same_unit(qf3, qf4)
    assert_equal(qf_3.nominal, 120)
    assert_equal(qf_3.uncertainty, 0.3)
    assert_equal(qf_3.unit, units.s)
    assert_equal(qf_4.nominal, 120)
    assert_equal(qf_4.uncertainty, 0.6)
    assert_equal(qf_4.unit, units.s)

    # Inverse non-prefix conversion
    qf_4, qf_3 = same_unit(qf4, qf3)
    assert_equal(qf_3.nominal, 2)
    assert_equal(qf_3.uncertainty, 0.005)
    assert_equal(qf_3.unit, units.minute)
    assert_equal(qf_4.nominal, 2)
    assert_equal(qf_4.uncertainty, 0.01)
    assert_equal(qf_4.unit, units.minute)


def test_qfloat_same_unit_array():
    # like for single values, everything must run as arrays
    qf1 = QFloat(1.0, 0.1, 'm')
    qf2 = QFloat(np.arange(0, 2000, 100),
                 np.arange(0, 100, 5),
                 'cm')
    qf3 = QFloat([120, 240],
                 [0.3, 0.6],
                 's')
    qf4 = QFloat([2, 10],
                 [0.01, 0.02],
                 'min')

    # Simplre conversion
    qf_1, qf_2 = same_unit(qf1, qf2, test_qfloat_same_unit_array)
    assert_equal(qf_1.nominal, 1.0)
    assert_equal(qf_1.uncertainty, 0.1)
    assert_equal(qf_1.unit, units.m)
    assert_almost_equal(qf_2.nominal, np.arange(0, 20, 1))
    assert_almost_equal(qf_2.std_dev, np.arange(0, 1, 0.05))
    assert_equal(qf_2.unit, units.m)

    # inverse conversion
    qf_2, qf_1 = same_unit(qf2, qf1, test_qfloat_same_unit_array)
    assert_equal(qf_1.nominal, 100)
    assert_equal(qf_1.uncertainty, 10)
    assert_equal(qf_1.unit, units.cm)
    assert_almost_equal(qf_2.nominal, np.arange(0, 2000, 100))
    assert_almost_equal(qf_2.std_dev, np.arange(0, 100, 5))
    assert_equal(qf_2.unit, units.cm)

    # incompatible
    with pytest.raises(UnitsError):
        same_unit(qf3, qf1, test_qfloat_same_unit)

    qf_3, qf_4 = same_unit(qf3, qf4, test_qfloat_same_unit_array)
    assert_almost_equal(qf_3.nominal, [120, 240])
    assert_almost_equal(qf_3.std_dev, [0.3, 0.6])
    assert_equal(qf_3.unit, units.s)
    assert_almost_equal(qf_4.nominal, [120, 600])
    assert_almost_equal(qf_4.std_dev, [0.6, 1.2])
    assert_equal(qf_4.unit, units.s)


def test_qfloat_repr():
    assert_equal(repr(QFloat(1.0, 0.1, 'm')), '<QFloat 1.0+-0.1 m>')
    assert_equal(repr(QFloat(1, 0.1, 'm')), '<QFloat 1.0+-0.1 m>')
    assert_equal(repr(QFloat(1, 0.01, 'm')), '<QFloat 1.00+-0.01 m>')
    assert_equal(repr(QFloat(1.00, 0.01, 'm')), '<QFloat 1.00+-0.01 m>')
    assert_equal(repr(QFloat(1.00, 0.012, 'm')), '<QFloat 1.00+-0.01 m>')
    assert_equal(repr(QFloat(1.00, 0.016, 'm')), '<QFloat 1.00+-0.02 m>')
    assert_equal(repr(QFloat(1.002, 0.016, 'm')), '<QFloat 1.00+-0.02 m>')
    assert_equal(repr(QFloat(1.006, 0.016, 'm')), '<QFloat 1.01+-0.02 m>')
    assert_equal(repr(QFloat(1.000005, 0.01, 'm')), '<QFloat 1.00+-0.01 m>')
    assert_equal(repr(QFloat(9999, 1, 'm')), '<QFloat 9999+-1 m>')
    assert_equal(repr(QFloat(9999, 10, 'm')), '<QFloat 10000+-10 m>')


def test_qfloat_repr_array():
    n = np.array([1, 1, 1, 1.01, 1.001, 1.006])
    s = np.array([0.1, 0.01, 1.0, 0.01, 0.01, 0.01])
    assert_equal(repr(QFloat(n, s, 'm')),
                 '<QFloat\narray([1.0+-0.1, 1.00+-0.01, 1+-1, 1.01+-0.01,'
                 ' 1.00+-0.01, 1.01+-0.01],\n      dtype=object)\nunit=m>')

    qf = QFloat([1, 2, 3, 4, 5], [0.1, 0.2, 0.3, 0.4, 0.5], 'm')
    assert_equal(repr(qf),
                 '<QFloat\narray([1.0+-0.1, 2.0+-0.2, 3.0+-0.3, 4.0+-0.4, 5.0'
                 '+-0.5], dtype=object)\nunit=m>')

    # very large array
    n = np.arange(0, 10000).reshape((100, 100))
    s = n*0.01
    qf = QFloat(n, s, 's')
    assert_equal(repr(qf),
                 '<QFloat\narray([[0+-0.0, 1.00+-0.01, 2.00+-0.02, ..., 97+-1'
                 ', 98+-1, 99+-1],\n       [100+-1, 101+-1, 102+-1, ..., 197+'
                 '-2, 198+-2, 199+-2],\n       [200+-2, 201+-2, 202+-2, ..., '
                 '297+-3, 298+-3, 299+-3],\n       ...,\n       [9700+-100, 9'
                 '700+-100, 9700+-100, ..., 9800+-100, 9800+-100,\n        98'
                 '00+-100],\n       [9800+-100, 9800+-100, 9800+-100, ..., 99'
                 '00+-100, 9900+-100,\n        9900+-100],\n       [9900+-100'
                 ', 9900+-100, 9900+-100, ..., 10000+-100, 10000+-100,\n     '
                 '   10000+-100]], dtype=object)\nunit=s>')


def test_qfloat_properties_getset():
    # access all important properties
    qf = QFloat(5.0, 0.025, 'm')
    assert_equal(qf.uncertainty, 0.025)
    qf.uncertainty = 0.10
    assert_equal(qf.uncertainty, 0.10)
    assert_equal(qf.std_dev, 0.10)
    qf.uncertainty = 0.05
    assert_equal(qf.std_dev, 0.05)

    # setting nominal resets the uncertainty
    assert_equal(qf.nominal, 5.0)
    with pytest.raises(ValueError):
        qf.nominal = None
    assert_equal(qf.nominal, 5.0)
    qf.nominal = 10.0
    assert_equal(qf.nominal, 10.0)
    assert_equal(qf.std_dev, 0.0)
    assert_equal(qf.uncertainty, 0.0)

    # with arrays
    qf = QFloat([1, 2, 3], [0.1, 0.2, 0.3], 'm')
    assert_almost_equal(qf.uncertainty, [0.1, 0.2, 0.3])
    qf.uncertainty = [0.4, 0.5, 0.6]
    assert_almost_equal(qf.uncertainty, [0.4, 0.5, 0.6])
    assert_almost_equal(qf.std_dev, [0.4, 0.5, 0.6])
    qf.std_dev = [0.1, 0.2, 0.3]
    assert_almost_equal(qf.std_dev, [0.1, 0.2, 0.3])

    assert_almost_equal(qf.nominal, [1, 2, 3])
    with pytest.raises(ValueError):
        qf.nominal = None
    assert_almost_equal(qf.nominal, [1, 2, 3])
    qf.nominal = [4, 5, 6]
    assert_almost_equal(qf.nominal, [4, 5, 6])
    assert_almost_equal(qf.std_dev, [0, 0, 0])
    assert_almost_equal(qf.uncertainty, [0, 0, 0])

    # test value readonly property
    assert_equal(qf.value, [4, 5, 6])
    with pytest.raises(AttributeError):
        qf.value = 0


def test_qfloat_properties_reset():
    qf = QFloat(5.0, 0.025, 'm')
    i = id(qf)
    qf.reset(12, 0.2, 's')
    assert_equal(i, id(qf))
    assert_equal(qf, QFloat(12, 0.2, 's'))

    qf.reset([1, 2, 3], [0.1, 0.2, 0.3], 'm')
    assert_equal(i, id(qf))
    assert_equal(qf, QFloat([1, 2, 3], [0.1, 0.2, 0.3], 'm'))


def test_qfloat_creation():
    def _create(*args, **kwargs):
        assert_is_instance(qfloat(*args, **kwargs), QFloat)

    def _raises(*args, **kwargs):
        with pytest.raises(Exception):
            qfloat(*args, **kwargs)

    # Simple scalar
    _create(1.0)
    _create(1.0, 0.1)
    _create(1.0, 0.1, 'm')

    # Arrays
    _create(np.ones(10))
    _create(np.ones(10), np.ones(10))
    _create(np.ones(10), np.ones(10), 'm')

    # Fails
    _raises(np.ones(10), 0.1)
    _raises(10.0, np.ones(10))
    _raises(uncertainty=0.1)
    _raises(unit='m')
    _raises(uncertainty=0.1, unit='m')


def test_qfloat_always_positive_uncertainty():
    qf1 = QFloat(1.0, 0.1, 'm')
    assert_equal(qf1.uncertainty, 0.1)

    qf2 = QFloat(1.0, -0.1, 'm')
    assert_equal(qf2.uncertainty, 0.1)

    qf3 = QFloat(np.ones(10), np.ones(10)*0.1, 'm')
    assert_equal(qf3.uncertainty, np.ones(10)*0.1)

    qf4 = QFloat(np.ones(10), -np.ones(10)*0.1, 'm')
    assert_equal(qf4.uncertainty, np.ones(10)*0.1)


@pytest.mark.parametrize('value,expect', [(QFloat(1.0, 0.1, 'm'),
                                           QFloat(1.0, 0.1, 'm')),
                                          (1, QFloat(1.0, 0, None)),
                                          (np.array([1, 2, 3]),
                                           QFloat([1, 2, 3], unit=None)),
                                          ('string', 'raise'),
                                          (None, 'raise'),
                                          (UnitsError, 'raise'),
                                          (1.0*units.m,
                                           QFloat(1.0, 0.0, 'm')),
                                          (FrameData(1.0, 'm', 'f8', 0.1),
                                           QFloat(1.0, 0.1, 'm'))
                                          ])
def test_qfloat_converttoqfloat(value, expect):
    if expect == 'raise':
        with pytest.raises(Exception):
            convert_to_qfloat(value)
    else:
        conv = convert_to_qfloat(value)
        assert_equal(conv.nominal, expect.nominal)
        assert_equal(conv.uncertainty, expect.uncertainty)
        assert_equal(conv.unit, expect.unit)


def test_qfloat_unit_conversion():
    qf1 = QFloat(1.0, 0.01, 'm')

    # test converting with string
    qf2 = qf1 << 'cm'
    assert_equal(qf2.nominal, 100)
    assert_equal(qf2.uncertainty, 1)
    assert_equal(qf2.unit, units.cm)

    # test converting using instance
    qf3 = qf1 << units.cm
    assert_equal(qf3.nominal, 100)
    assert_equal(qf3.uncertainty, 1)
    assert_equal(qf3.unit, units.cm)

    # But qf1 must stay the same
    assert_equal(qf1.nominal, 1.0)
    assert_equal(qf1.uncertainty, 0.01)
    assert_equal(qf1.unit, units.m)

    with pytest.raises(Exception):
        # None must fail
        qf1 << None

    with pytest.raises(Exception):
        # incompatible must fail
        qf1 << units.s

    # and this must work with arrays
    qf1 = QFloat([1, 2], [0.1, 0.2], unit='m')
    qf2 = qf1 << 'km'
    assert_equal(qf2.nominal, [0.001, 0.002])
    assert_equal(qf2.uncertainty, [0.0001, 0.0002])
    assert_equal(qf2.unit, units.km)


def test_qfloat_unit_conversion_to():
    qf1 = QFloat(1.0, 0.01, 'm')

    # test converting with string
    qf2 = qf1.to('cm')
    assert_equal(qf2.nominal, 100)
    assert_equal(qf2.uncertainty, 1)
    assert_equal(qf2.unit, units.cm)

    # test converting using instance
    qf3 = qf1.to(units.cm)
    assert_equal(qf3.nominal, 100)
    assert_equal(qf3.uncertainty, 1)
    assert_equal(qf3.unit, units.cm)

    # But qf1 must stay the same
    assert_equal(qf1.nominal, 1.0)
    assert_equal(qf1.uncertainty, 0.01)
    assert_equal(qf1.unit, units.m)

    # inline: now qf1 must change
    i = id(qf1)
    qf1 <<= 'cm'
    assert_equal(qf1.nominal, 100)
    assert_equal(qf1.uncertainty, 1)
    assert_equal(qf1.unit, units.cm)
    assert_equal(id(qf1), i)

    with pytest.raises(Exception):
        # None must fail
        qf1.to(None)

    with pytest.raises(Exception):
        # incompatible must fail
        qf1.to(units.s)

    # and this must work with arrays
    qf1 = QFloat([1, 2], [0.1, 0.2], unit='m')
    qf2 = qf1.to('km')
    assert_equal(qf2.nominal, [0.001, 0.002])
    assert_equal(qf2.uncertainty, [0.0001, 0.0002])
    assert_equal(qf2.unit, units.km)


def test_qfloat_getitem():
    # simple array
    qf = QFloat([1, 2, 3, 4, 5],
                [0.1, 0.2, 0.3, 0.4, 0.5],
                's')

    qf1 = qf[0]
    assert_equal(qf1.nominal, 1)
    assert_equal(qf1.uncertainty, 0.1)
    assert_equal(qf1.unit, units.s)

    qf3 = qf[2]
    assert_equal(qf3.nominal, 3)
    assert_equal(qf3.uncertainty, 0.3)
    assert_equal(qf3.unit, units.s)

    qf4 = qf[-1]
    assert_equal(qf4.nominal, 5)
    assert_equal(qf4.uncertainty, 0.5)
    assert_equal(qf4.unit, units.s)

    qf5 = qf[1:4]
    assert_equal(qf5.nominal, [2, 3, 4])
    assert_equal(qf5.uncertainty, [0.2, 0.3, 0.4])
    assert_equal(qf5.unit, units.s)

    with pytest.raises(IndexError):
        qf[10]

    # 2D array
    qf = QFloat(np.arange(1, 17, 1).reshape((4, 4)),
                np.arange(1, 17, 1).reshape((4, 4))*0.01,
                'm')

    qfrow = qf[0]
    assert_equal(qfrow.nominal, [1, 2, 3, 4])
    assert_equal(qfrow.uncertainty, [0.01, 0.02, 0.03, 0.04])
    assert_equal(qfrow.unit, units.m)

    qfcol = qf[:, 1]
    assert_equal(qfcol.nominal, [2, 6, 10, 14])
    assert_equal(qfcol.uncertainty, [0.02, 0.06, 0.1, 0.14])
    assert_equal(qfcol.unit, units.m)

    qf0 = qf[0, 0]
    assert_equal(qf0.nominal, 1)
    assert_equal(qf0.uncertainty, 0.01)
    assert_equal(qf0.unit, units.m)

    qf1 = qf[-1, -1]
    assert_equal(qf1.nominal, 16)
    assert_equal(qf1.uncertainty, 0.16)
    assert_equal(qf1.unit, units.m)

    qfs = qf[2:, 1:3]
    assert_equal(qfs.nominal, [[10, 11], [14, 15]])
    assert_equal(qfs.uncertainty, [[0.10, 0.11], [0.14, 0.15]])
    assert_equal(qfs.unit, units.m)

    with pytest.raises(IndexError):
        qf[10]

    with pytest.raises(IndexError):
        qf[0, 10]

    # Not iterable
    qf = QFloat(10, 0.1, 'm')
    with pytest.raises(TypeError):
        qf[0]


def test_qfloat_setitem():
    # simple array
    qf = QFloat([1, 2, 3, 4, 5],
                [0.1, 0.2, 0.3, 0.4, 0.5],
                's')
    qf[0] = QFloat(10, 0.5, 's')
    qf[-1] = QFloat(-10, unit='min')
    qf[2:4] = QFloat(1, 0.3, 's')
    assert_equal(qf.nominal, [10, 2, 1, 1, -600])
    assert_equal(qf.uncertainty, [0.5, 0.2, 0.3, 0.3, 0])
    assert_equal(qf.unit, units.s)

    with pytest.raises(IndexError):
        qf[5] = QFloat(10, 0.1, 's')
    with pytest.raises(UnitsError):
        qf[0] = 10
    with pytest.raises(UnitsError):
        qf[0] = QFloat(10, 0.1, 'm')

    # 2D array
    qf = QFloat(np.arange(1, 17, 1).reshape((4, 4)),
                np.arange(1, 17, 1).reshape((4, 4))*0.01,
                'm')
    qf[0] = QFloat(np.arange(5, 9, 1), np.arange(0.3, 0.7, 0.1), 'm')
    qf[:, 0] = QFloat(np.arange(1, 5, 1), [0.1, 0.3, 0.9, 0.4], 'm')
    qf[2, 2] = QFloat(1000, 10, 'cm')
    qf[2:, 2:] = QFloat(20, 0.66, 'm')
    assert_equal(qf.nominal, [[1, 6, 7, 8],
                              [2, 6, 7, 8],
                              [3, 10, 20, 20],
                              [4, 14, 20, 20]])
    assert_almost_equal(qf.uncertainty, [[0.1, 0.4, 0.5, 0.6],
                                         [0.3, 0.06, 0.07, 0.08],
                                         [0.9, 0.1, 0.66, 0.66],
                                         [0.4, 0.14, 0.66, 0.66]])
    assert_equal(qf.unit, units.m)

    with pytest.raises(IndexError):
        qf[5, 10] = QFloat(10, 0.1, 'm')
    with pytest.raises(UnitsError):
        qf[0, 0] = 10
    with pytest.raises(UnitsError):
        qf[0, 0] = QFloat(10, 0.1, 's')


def test_qfloat_len():
    with pytest.raises(TypeError):
        len(QFloat(1.0, 0.1, 'm'))
    assert_equal(len(QFloat([1], [0.1], 'm')), 1)
    assert_equal(len(QFloat([2, 3], [1, 2], 'm')), 2)

    # same behavior of numpy
    assert_equal(len(QFloat(np.zeros((10, 5)), np.zeros((10, 5)), 'm')), 10)
    assert_equal(len(QFloat(np.zeros((10, 5)), np.zeros((10, 5)), 'm')[0]), 5)

##############################################################################
# Simple math operations -----------------------------------------------------
##############################################################################

# Simple math comparisons -----------------------------------------------------


def test_qfloat_comparison_equality_same_unit():
    # These numbers must not be equal, but equal within errors.
    qf1 = QFloat(1.0, 0.1, 'm')
    qf2 = QFloat(1.05, 0.2, 'm')
    qf3 = QFloat(1.8, 0.1, 'm')
    assert_false(qf1 == qf2)
    assert_true(qf1 != qf2)
    assert_true(qf1 != qf3)
    assert_true(qf2 != qf3)
    assert_true(equal_within_errors(qf1, qf2))
    assert_false(equal_within_errors(qf1, qf3))


def test_qfloat_comparison_equality_convert_units():
    # Units must matter
    qf1 = QFloat(1.0, 0.1, 'm')
    qf3 = QFloat(1.0, 0.1, 'cm')
    qf4 = QFloat(100, 10, 'cm')
    assert_false(qf1 == qf3)
    assert_false(qf1 != qf4)
    assert_true(qf1 != qf3)
    assert_true(qf1 == qf4)
    assert_false(equal_within_errors(qf1, qf3))
    assert_true(equal_within_errors(qf1, qf4))


def test_qfloat_comparison_equality_incompatible_units():
    qf1 = QFloat(1.0, 0.1, 'm')
    qf2 = QFloat(1.0, 0.1, 's')
    assert_false(qf1 == qf2)
    assert_true(qf1 != qf2)
    assert_false(equal_within_errors(qf1, qf2))


def test_qfloat_comparison_inequality_same_unit():
    qf1 = QFloat(1.0, 0.1, 'm')
    qf2 = QFloat(1.0, 0.2, 'm')
    qf3 = QFloat(1.1, 0.1, 'm')
    qf4 = QFloat(1.1, 0.2, 'm')
    qf5 = QFloat(0.9, 0.1, 'm')
    qf6 = QFloat(0.9, 0.5, 'm')

    assert_true(qf1 <= qf2)
    assert_false(qf1 < qf2)
    assert_true(qf1 >= qf2)
    assert_false(qf1 > qf2)

    assert_true(qf1 <= qf3)
    assert_true(qf1 < qf3)
    assert_false(qf1 > qf3)
    assert_false(qf1 >= qf3)

    assert_true(qf1 <= qf4)
    assert_true(qf1 < qf4)
    assert_false(qf1 > qf4)
    assert_false(qf1 >= qf4)

    assert_true(qf1 >= qf5)
    assert_true(qf1 > qf5)
    assert_false(qf1 < qf5)
    assert_false(qf1 <= qf5)

    assert_true(qf1 >= qf6)
    assert_true(qf1 > qf6)
    assert_false(qf1 < qf6)
    assert_false(qf1 <= qf6)


def test_qfloat_comparison_inequality_convert_unit():
    qf1 = QFloat(1.0, 0.1, 'm')
    qf2 = QFloat(200, 0.1, 'cm')
    qf3 = QFloat(0.005, 0.0003, 'km')
    qf4 = QFloat(0.0001, 0.00001, 'km')

    assert_true(qf2 > qf1)
    assert_true(qf2 >= qf1)
    assert_false(qf2 < qf1)
    assert_false(qf2 <= qf1)

    assert_true(qf3 > qf1)
    assert_true(qf3 >= qf1)
    assert_false(qf3 < qf1)
    assert_false(qf3 <= qf1)

    assert_true(qf4 < qf1)
    assert_true(qf4 <= qf1)
    assert_false(qf4 > qf1)
    assert_false(qf4 >= qf1)


def test_qfloat_comparison_inequality_incompatible_units():
    qf1 = QFloat(1.0, 0.1, 'm')
    qf2 = QFloat(1.0, 0.1, 's')

    with pytest.raises(UnitsError):
        qf1 < qf2

    with pytest.raises(UnitsError):
        qf1 <= qf2

    with pytest.raises(UnitsError):
        qf1 > qf2

    with pytest.raises(UnitsError):
        qf1 >= qf2


# ADD --------------------------------------------------------

def test_qfloat_math_add_single():
    qf1 = QFloat(1.0, 0.01, 'm')
    qf2 = QFloat(50, 2, 'cm')
    qf3 = QFloat(10, 0.1, None)
    qf4 = QFloat(60, 0.001, 's')

    res1 = qf1 + qf2
    assert_equal(res1.nominal, 1.5)
    assert_almost_equal(res1.uncertainty, 0.022360679774)
    assert_equal(res1.unit, units.m)

    # same as above, but in cm
    res2 = qf2 + qf1
    assert_equal(res2.nominal, 150)
    assert_almost_equal(res2.uncertainty, 2.2360679774)
    assert_equal(res2.unit, units.cm)

    # Should fail with incompatible units
    with pytest.raises(UnitsError):
        qf1 + qf3

    with pytest.raises(UnitsError):
        qf1 + qf4


def test_qfloat_math_add_array():
    qf1 = QFloat([1, 2, 3, 4],
                 [0.01, 0.02, 0.01, 0.02],
                 'm')
    qf2 = QFloat([150, 250, 50, 550],
                 [0.1, 5, 0.4, 2],
                 'cm')
    qf3 = QFloat(1, 0.01, 'm')
    qf4 = QFloat(10, 0.1, None)
    qf5 = QFloat(60, 0.001, 's')

    res1 = qf1 + qf2
    assert_equal(res1.nominal, [2.5, 4.5, 3.5, 9.5])
    assert_almost_equal(res1.uncertainty, [0.01004987562112089,
                                           0.05385164807134505,
                                           0.010770329614269008,
                                           0.0282842712474619])
    assert_equal(res1.unit, units.m)

    # same as above, but in cm
    res2 = qf2 + qf1
    assert_equal(res2.nominal, [250, 450, 350, 950])
    assert_almost_equal(res2.uncertainty, [1.004987562112089,
                                           5.385164807134505,
                                           1.0770329614269008,
                                           2.82842712474619])
    assert_equal(res2.unit, units.cm)

    # Numpy behavior is to sum arrays with single numbers
    res3 = qf1 + qf3
    assert_equal(res3.nominal, [2.0, 3.0, 4.0, 5.0])
    assert_almost_equal(res3.uncertainty, [0.01414213562373095,
                                           0.022360679774997897,
                                           0.01414213562373095,
                                           0.022360679774997897])
    assert_equal(res3.unit, units.m)

    # So, it should sum numbers with arrays
    res4 = qf3 + qf1
    assert_equal(res4.nominal, [2.0, 3.0, 4.0, 5.0])
    assert_almost_equal(res4.uncertainty, [0.01414213562373095,
                                           0.022360679774997897,
                                           0.01414213562373095,
                                           0.022360679774997897])
    assert_equal(res4.unit, units.m)

    # Should fail with incompatible units
    with pytest.raises(UnitsError):
        qf1 + qf4

    with pytest.raises(UnitsError):
        qf1 + qf5


def test_qfloat_math_add_with_numbers():
    qf1 = QFloat(1, 0.1, 'm')
    qf2 = QFloat(1, 0.1)  # dimensionless
    qf3 = QFloat([2, 3], [0.1, 0.2])
    qf4 = QFloat([2, 3], [0.1, 0.2], 's')

    # Should raise with dimension measurements
    with pytest.raises(UnitsError):
        qf1 + 1
    with pytest.raises(UnitsError):
        1 + qf1

    # but works with dimensionless
    res1 = qf2 + 2
    assert_equal(res1.nominal, 3)
    assert_almost_equal(res1.uncertainty, 0.1)
    assert_equal(res1.unit, units.dimensionless_unscaled)

    # same as above, but inverse
    res2 = 2 + qf2
    assert_equal(res2.nominal, 3)
    assert_almost_equal(res2.uncertainty, 0.1)
    assert_equal(res2.unit, units.dimensionless_unscaled)

    # and with arrays!
    res3 = qf3 + 1
    assert_equal(res3.nominal, [3, 4])
    assert_almost_equal(res3.uncertainty, [0.1, 0.2])
    assert_equal(res3.unit, units.dimensionless_unscaled)

    # and with arrays inverse!
    res4 = 1 + qf3
    assert_equal(res4.nominal, [3, 4])
    assert_almost_equal(res4.uncertainty, [0.1, 0.2])
    assert_equal(res4.unit, units.dimensionless_unscaled)

    # array array
    with pytest.raises(UnitsError):
        qf4 + [1, 2]

    with pytest.raises(UnitsError):
        [1, 2] + qf4

    res5 = qf3 + [1, 2]
    assert_equal(res5.nominal, [3, 5])
    assert_almost_equal(res5.uncertainty, [0.1, 0.2])
    assert_equal(res5.unit, units.dimensionless_unscaled)

    res6 = [1, 2] + qf3
    assert_equal(res6.nominal, [3, 5])
    assert_almost_equal(res6.uncertainty, [0.1, 0.2])
    assert_equal(res6.unit, units.dimensionless_unscaled)


def test_qfloat_math_add_inline():
    # Sum with another qfloat
    qf = QFloat(1.0, 0.1, 'm')
    i = id(qf)
    qf += QFloat(2.0, 0.1, 'm')
    assert_equal(qf.nominal, 3)
    assert_almost_equal(qf.uncertainty, 0.1414213562373095)
    assert_equal(qf.unit, units.m)
    assert_equal(id(qf), i)

    # raise if incompatible
    with pytest.raises(UnitsError):
        qf += QFloat(3, unit='s')
    assert_equal(id(qf), i)

    with pytest.raises(UnitsError):
        qf += 1
    assert_equal(id(qf), i)

    # Arrays
    qf = QFloat([1, 2, 3, 4],
                [0.01, 0.02, 0.03, 0.04],
                'cm')
    i = id(qf)
    qf += QFloat(1, 0.01, 'cm')
    assert_equal(qf.nominal, [2, 3, 4, 5])
    assert_almost_equal(qf.uncertainty, [0.01414213562373095,
                                         0.022360679774997897,
                                         0.03162277660168379,
                                         0.04123105625617661])
    assert_equal(qf.unit, units.cm)
    assert_equal(id(qf), i)

    with pytest.raises(UnitsError):
        qf += [2, 3, 4, 5]
    assert_equal(id(qf), i)


# SUBTRACT -----------------------------------------------------

def test_qfloat_math_sub_single():
    qf1 = QFloat(1.0, 0.01, 'm')
    qf2 = QFloat(50, 2, 'cm')
    qf3 = QFloat(10, 0.1, None)
    qf4 = QFloat(60, 0.001, 's')

    res1 = qf1 - qf2
    assert_equal(res1.nominal, 0.5)
    assert_almost_equal(res1.uncertainty, 0.022360679774)
    assert_equal(res1.unit, units.m)

    # same as above, but in cm
    res2 = qf2 - qf1
    assert_equal(res2.nominal, -50)
    assert_almost_equal(res2.uncertainty, 2.2360679774)
    assert_equal(res2.unit, units.cm)

    # Should fail with incompatible units
    with pytest.raises(UnitsError):
        qf1 - qf3

    with pytest.raises(UnitsError):
        qf1 - qf4


def test_qfloat_math_sub_array():
    qf1 = QFloat([1, 2, 3, 4],
                 [0.01, 0.02, 0.01, 0.02],
                 'm')
    qf2 = QFloat([50, 40, 30, 550],
                 [0.1, 5, 0.4, 2],
                 'cm')
    qf3 = QFloat(1, 0.01, 'm')
    qf4 = QFloat(10, 0.1, None)
    qf5 = QFloat(60, 0.001, 's')

    res1 = qf1 - qf2
    assert_equal(res1.nominal, [0.5, 1.6, 2.7, -1.5])
    assert_almost_equal(res1.uncertainty, [0.01004987562112089,
                                           0.05385164807134505,
                                           0.010770329614269008,
                                           0.0282842712474619])
    assert_equal(res1.unit, units.m)

    # same as above, but in cm
    res2 = qf2 - qf1
    assert_equal(res2.nominal, [-50, -160, -270, 150])
    assert_almost_equal(res2.uncertainty, [1.004987562112089,
                                           5.385164807134505,
                                           1.0770329614269008,
                                           2.82842712474619])
    assert_equal(res2.unit, units.cm)

    # Numpy behavior is to sum arrays with single numbers
    res3 = qf1 - qf3
    assert_equal(res3.nominal, [0.0, 1.0, 2.0, 3.0])
    assert_almost_equal(res3.uncertainty, [0.01414213562373095,
                                           0.022360679774997897,
                                           0.01414213562373095,
                                           0.022360679774997897])
    assert_equal(res3.unit, units.m)

    # So, it should sum numbers with arrays
    res4 = qf3 - qf1
    assert_equal(res4.nominal, [0.0, -1, -2.0, -3.0])
    assert_almost_equal(res4.uncertainty, [0.01414213562373095,
                                           0.022360679774997897,
                                           0.01414213562373095,
                                           0.022360679774997897])
    assert_equal(res4.unit, units.m)

    # Should fail with incompatible units
    with pytest.raises(UnitsError):
        qf1 - qf4

    with pytest.raises(UnitsError):
        qf1 - qf5


def test_qfloat_math_sub_with_numbers():
    qf1 = QFloat(1, 0.1, 'm')
    qf2 = QFloat(1, 0.1)  # dimensionless
    qf3 = QFloat([2, 3], [0.1, 0.2])
    qf4 = QFloat([2, 3], [0.1, 0.2], 's')

    # Should raise with dimension measurements
    with pytest.raises(UnitsError):
        qf1 - 1
    with pytest.raises(UnitsError):
        1 - qf1

    # but works with dimensionless
    res1 = qf2 - 2
    assert_equal(res1.nominal, -1)
    assert_almost_equal(res1.uncertainty, 0.1)
    assert_equal(res1.unit, units.dimensionless_unscaled)

    # same as above, but inverse
    res2 = 2 - qf2
    assert_equal(res2.nominal, 1)
    assert_almost_equal(res2.uncertainty, 0.1)
    assert_equal(res2.unit, units.dimensionless_unscaled)

    # and with arrays!
    res3 = qf3 - 1
    assert_equal(res3.nominal, [1, 2])
    assert_almost_equal(res3.uncertainty, [0.1, 0.2])
    assert_equal(res3.unit, units.dimensionless_unscaled)

    # and with arrays inverse!
    res4 = 1 - qf3
    assert_equal(res4.nominal, [-1, -2])
    assert_almost_equal(res4.uncertainty, [0.1, 0.2])
    assert_equal(res4.unit, units.dimensionless_unscaled)

    # array array
    with pytest.raises(UnitsError):
        qf4 - [1, 2]

    with pytest.raises(UnitsError):
        [1, 2] - qf4

    res5 = qf3 - [1, 2]
    assert_equal(res5.nominal, [1, 1])
    assert_almost_equal(res5.uncertainty, [0.1, 0.2])
    assert_equal(res5.unit, units.dimensionless_unscaled)

    res6 = [1, 2] - qf3
    assert_equal(res6.nominal, [-1, -1])
    assert_almost_equal(res6.uncertainty, [0.1, 0.2])
    assert_equal(res6.unit, units.dimensionless_unscaled)


def test_qfloat_math_sub_inline():
    # Sum with another qfloat
    qf = QFloat(1.0, 0.01, 'm')
    i = id(qf)
    qf -= QFloat(0.5, 0.01, 'm')
    assert_equal(qf.nominal, 0.5)
    assert_almost_equal(qf.uncertainty, 0.01414213562373095)
    assert_equal(qf.unit, units.m)
    assert_equal(id(qf), i)

    # raise if incompatible
    with pytest.raises(UnitsError):
        qf -= QFloat(3, unit='s')
    assert_equal(id(qf), i)

    with pytest.raises(UnitsError):
        qf += 1
    assert_equal(id(qf), i)

    # Arrays
    qf = QFloat([1, 2, 3, 4],
                [0.01, 0.02, 0.03, 0.04],
                'cm')
    i = id(qf)
    qf -= QFloat(1, 0.01, 'cm')
    assert_equal(qf.nominal, [0, 1, 2, 3])
    assert_almost_equal(qf.uncertainty, [0.01414213562373095,
                                         0.022360679774997897,
                                         0.03162277660168379,
                                         0.04123105625617661])
    assert_equal(qf.unit, units.cm)
    assert_equal(id(qf), i)

    with pytest.raises(UnitsError):
        qf -= [2, 3, 4, 5]
    assert_equal(id(qf), i)


# MULTIPLICATION -------------------------------------------------------------

def test_qfloat_math_mul_single():
    qf1 = QFloat(30, 0.5, 's')
    qf2 = QFloat(10, 0.1, 'm')
    qf3 = QFloat(200, 1, 'cm')
    qf4 = QFloat(5, 0.001, None)

    # different dimensions
    res1 = qf1 * qf2
    assert_equal(res1.nominal, 300)
    assert_equal(res1.uncertainty, 5.830951894845301)
    assert_equal(res1.unit, units.s*units.m)
    # inverse gets just the same
    res2 = qf2 * qf1
    assert_equal(res2.nominal, 300)
    assert_equal(res2.uncertainty, 5.830951894845301)
    assert_equal(res2.unit, units.s*units.m)

    # same dimension. Astropy behavior is not convert the units
    res3 = qf2 * qf3
    assert_equal(res3.nominal, 2000)
    assert_equal(res3.uncertainty, 22.360679774997898)
    assert_equal(res3.unit, units.cm*units.m)
    # inverse the same
    res4 = qf2 * qf3
    assert_equal(res4.nominal, 2000)
    assert_equal(res4.uncertainty, 22.360679774997898)
    assert_equal(res4.unit, units.cm*units.m)

    # None (dimensionless)
    res5 = qf1 * qf4
    assert_equal(res5.nominal, 150)
    assert_equal(res5.uncertainty, 2.5001799935204665)
    assert_equal(res5.unit, units.s)
    # inverse the same
    res5 = qf4 * qf1
    assert_equal(res5.nominal, 150)
    assert_equal(res5.uncertainty, 2.5001799935204665)
    assert_equal(res5.unit, units.s)

    # With numbers
    res6 = qf1 * 2
    assert_equal(res6.nominal, 60)
    assert_equal(res6.uncertainty, 1.0)
    assert_equal(res6.unit, units.s)
    # Inverse same
    res6 = 2 * qf1
    assert_equal(res6.nominal, 60)
    assert_equal(res6.uncertainty, 1.0)
    assert_equal(res6.unit, units.s)

    # With units
    res7 = qf1 * units.m
    assert_equal(res7.nominal, 30)
    assert_equal(res7.uncertainty, 0.5)
    assert_equal(res7.unit, units.m*units.s)
    # And with string!
    res9 = qf1 * 'm'
    assert_equal(res9.nominal, 30)
    assert_equal(res9.uncertainty, 0.5)
    assert_equal(res9.unit, units.m*units.s)
    # inverse
    res_a = 'm' * qf1
    assert_equal(res_a.nominal, 30)
    assert_equal(res_a.uncertainty, 0.5)
    assert_equal(res_a.unit, units.m*units.s)


def test_qfloat_math_mul_array():
    qf1 = QFloat(np.arange(5), np.arange(5)*0.01, 'm')
    qf2 = QFloat(20, 0.1, 's')
    qf3 = QFloat(np.arange(16).reshape((4, 4)),
                 np.arange(16).reshape((4, 4))*0.0001, 'km')

    res1 = qf1 * qf1
    assert_equal(res1.nominal, [0, 1, 4, 9, 16])
    assert_almost_equal(res1.uncertainty, [0,
                                           0.01414213562373095,
                                           0.0565685424949238,
                                           0.12727922061357855,
                                           0.2262741699796952])
    assert_equal(res1.unit, units.m*units.m)

    res2 = qf1 * qf2
    assert_equal(res2.nominal, [0, 20, 40, 60, 80])
    assert_almost_equal(res2.uncertainty, [0,
                                           0.223606797749979,
                                           0.447213595499958,
                                           0.6708203932499369,
                                           0.894427190999916])
    assert_equal(res2.unit, units.m*units.s)
    # inverse same
    res3 = qf2 * qf1
    assert_equal(res3.nominal, [0, 20, 40, 60, 80])
    assert_almost_equal(res3.uncertainty, [0,
                                           0.223606797749979,
                                           0.447213595499958,
                                           0.6708203932499369,
                                           0.894427190999916])
    assert_equal(res3.unit, units.m*units.s)

    # with numbers
    res4 = qf1 * 10
    assert_equal(res4.nominal, [0, 10, 20, 30, 40])
    assert_equal(res4.uncertainty, [0, 0.1, 0.2, 0.3, 0.4])
    assert_equal(res4.unit, units.m)
    # same inverse
    res5 = 10 * qf1
    assert_equal(res5.nominal, [0, 10, 20, 30, 40])
    assert_equal(res5.uncertainty, [0, 0.1, 0.2, 0.3, 0.4])
    assert_equal(res5.unit, units.m)

    # 2D Array with qfloat
    res6 = qf2 * qf3
    assert_equal(res6.nominal, [[0, 20, 40, 60],
                                [80, 100, 120, 140],
                                [160, 180, 200, 220],
                                [240, 260, 280, 300]])
    assert_almost_equal(res6.uncertainty, [[0, 0.10002,
                                            0.20004, 0.30005999],
                                           [0.40007999, 0.50009999,
                                            0.60011999, 0.70013999],
                                           [0.80015998, 0.90017998,
                                            1.00019998, 1.10021998],
                                           [1.20023998, 1.30025997,
                                            1.40027997, 1.50029997]])
    assert_equal(res6.unit, units.km*units.s)
    # inverse same
    res7 = qf3 * qf2
    assert_equal(res7.nominal, [[0, 20, 40, 60],
                                [80, 100, 120, 140],
                                [160, 180, 200, 220],
                                [240, 260, 280, 300]])
    assert_almost_equal(res7.uncertainty, [[0, 0.10002, 0.20004,
                                            0.30005999],
                                           [0.40007999, 0.50009999,
                                            0.60011999, 0.70013999],
                                           [0.80015998, 0.90017998,
                                            1.00019998, 1.10021998],
                                           [1.20023998, 1.30025997,
                                            1.40027997, 1.50029997]])
    assert_equal(res7.unit, units.km*units.s)

    # 2D array with numbers
    res8 = 3 * qf3
    assert_equal(res8.nominal,
                 np.arange(16).reshape((4, 4))*3)
    assert_almost_equal(res8.uncertainty,
                        np.arange(16).reshape((4, 4))*0.0001*3)
    assert_equal(res8.unit, units.km)
    # same inverse
    res9 = qf3 * 3
    assert_equal(res9.nominal,
                 np.arange(16).reshape((4, 4))*3)
    assert_almost_equal(res9.uncertainty,
                        np.arange(16).reshape((4, 4))*0.0001*3)
    assert_equal(res9.unit, units.km)

    # With units
    res_a = qf1 * units.m
    assert_equal(res_a.nominal, qf1.nominal)
    assert_equal(res_a.uncertainty, qf1.uncertainty)
    assert_equal(res_a.unit, units.m*units.m)
    # And with string!
    res_c = qf1 * 'm'
    assert_equal(res_c.nominal, qf1.nominal)
    assert_equal(res_c.uncertainty, qf1.uncertainty)
    assert_equal(res_c.unit, units.m*units.m)
    # inverse
    res_b = 'm' * qf1
    assert_equal(res_b.nominal, qf1.nominal)
    assert_equal(res_b.uncertainty, qf1.uncertainty)
    assert_equal(res_b.unit, units.m*units.m)


def test_qfloat_math_mul_inline():
    # single number
    qf1 = QFloat(30, 0.5, 's')
    i = id(qf1)
    qf1 *= QFloat(5, 0.01, None)
    assert_equal(qf1.nominal, 150)
    assert_almost_equal(qf1.uncertainty, 2.5179356624028344)
    assert_equal(qf1.unit, units.s)
    assert_equal(i, id(qf1))
    qf1 *= QFloat(2, 0.1, 'm')
    assert_equal(qf1.nominal, 300)
    assert_almost_equal(qf1.uncertainty, 15.822768405054788)
    assert_equal(qf1.unit, units.s*units.m)
    assert_equal(i, id(qf1))
    qf1 *= 10
    assert_equal(qf1.nominal, 3000)
    assert_almost_equal(qf1.uncertainty, 158.22768405054788)
    assert_equal(qf1.unit, units.s*units.m)
    assert_equal(i, id(qf1))
    qf1 *= 'g'
    assert_equal(qf1.nominal, 3000)
    assert_almost_equal(qf1.uncertainty, 158.22768405054788)
    assert_equal(qf1.unit, units.s*units.m*units.g)
    assert_equal(i, id(qf1))
    qf1 *= units.K
    assert_equal(qf1.nominal, 3000)
    assert_almost_equal(qf1.uncertainty, 158.22768405054788)
    assert_equal(qf1.unit, units.s*units.m*units.g*units.K)
    assert_equal(i, id(qf1))

    # array
    qf2 = QFloat([2, 3, 4], [0.1, 0.2, 0.3], 'm')
    i = id(qf2)
    qf2 *= QFloat(5, 0.01, None)
    assert_almost_equal(qf2.nominal, [10, 15, 20])
    assert_almost_equal(qf2.uncertainty, [0.50039984, 1.0004499, 1.50053324])
    assert_equal(qf2.unit, units.m)
    assert_equal(i, id(qf2))
    qf2 *= QFloat(3.5, 0.1, 'm')
    assert_almost_equal(qf2.nominal, [35, 52.5, 70])
    assert_almost_equal(qf2.uncertainty, [2.01677961, 3.80933393, 5.61979537])
    assert_equal(qf2.unit, units.m*units.m)
    assert_equal(i, id(qf2))
    qf2 *= 10
    assert_almost_equal(qf2.nominal, [350, 525, 700])
    assert_almost_equal(qf2.uncertainty, [20.1677961, 38.0933393, 56.1979537])
    assert_equal(qf2.unit, units.m*units.m)
    assert_equal(i, id(qf2))
    qf2 *= 'g'
    assert_almost_equal(qf2.nominal, [350, 525, 700])
    assert_almost_equal(qf2.uncertainty, [20.1677961, 38.0933393, 56.1979537])
    assert_equal(qf2.unit, units.m*units.m*units.g)
    assert_equal(i, id(qf2))
    qf2 *= units.K
    assert_almost_equal(qf2.nominal, [350, 525, 700])
    assert_almost_equal(qf2.uncertainty, [20.1677961, 38.0933393, 56.1979537])
    assert_equal(qf2.unit, units.m*units.m*units.g*units.K)
    assert_equal(i, id(qf2))

# DIVISION -------------------------------------------------------------------


def test_qfloat_math_divmod_single():
    qf1 = QFloat(20, 0.5, 's')
    qf2 = QFloat(100, 0.1, 'm')
    qf3 = QFloat(2030, 1, 'cm')
    qf4 = QFloat(5, 0.01, None)

    res1 = qf2 / qf1
    assert_equal(res1.nominal, 5)
    assert_almost_equal(res1.uncertainty, 0.12509996003196805)
    assert_equal(res1.unit, units.m/units.s)
    res1f = qf2 // qf1
    assert_equal(res1f.nominal, 5)
    assert_almost_equal(res1f.uncertainty, 0.0)
    assert_equal(res1f.unit, units.m/units.s)
    res1m = qf2 % qf1
    assert_equal(res1m.nominal, 0)
    # assert_almost_equal(res1m.uncertainty, np.inf)
    assert_equal(res1m.unit, units.m)
    # inverse inverse
    res2 = qf1 / qf2
    assert_equal(res2.nominal, 0.2)
    assert_almost_equal(res2.uncertainty, 0.005003998401278721)
    assert_equal(res2.unit, units.s/units.m)
    res2f = qf1 // qf2
    assert_equal(res2f.nominal, 0)
    assert_almost_equal(res2f.uncertainty, 0.0)
    assert_equal(res2f.unit, units.s/units.m)
    res2m = qf1 % qf2
    assert_equal(res2m.nominal, 20)
    assert_almost_equal(res2m.uncertainty, 0.5)
    assert_equal(res2m.unit, units.s)

    # same dimensionality
    res3 = qf3 / qf2
    assert_equal(res3.nominal, 20.3)
    assert_almost_equal(res3.uncertainty, 0.022629405648403586)
    assert_equal(res3.unit, units.cm/units.m)
    res3f = qf3 // qf2
    assert_equal(res3f.nominal, 20)
    assert_almost_equal(res3f.uncertainty, 0.0)
    assert_equal(res3f.unit, units.cm/units.m)
    res3m = qf3 % qf2
    assert_equal(res3m.nominal, 30)
    assert_almost_equal(res3m.uncertainty, 2.23606797749979)
    assert_equal(res3m.unit, units.cm)

    # with no dimensionality
    res4 = qf4 / qf2
    assert_equal(res4.nominal, 0.05)
    assert_almost_equal(res4.uncertainty, 0.00011180339887498949)
    assert_equal(res4.unit, units.Unit('1/m'))
    res4f = qf4 // qf2
    assert_equal(res4f.nominal, 0)
    assert_almost_equal(res4f.uncertainty, 0.0)
    assert_equal(res4f.unit, units.Unit('1/m'))
    res4m = qf4 % qf2
    assert_equal(res4m.nominal, 5)
    assert_almost_equal(res4m.uncertainty, 0.01)
    assert_equal(res4m.unit, units.dimensionless_unscaled)

    # with numbers
    res5 = qf1 / 7
    assert_almost_equal(res5.nominal, 2.857142857142857)
    assert_almost_equal(res5.uncertainty, 0.07142857142857142)
    assert_equal(res5.unit, units.s)
    res5f = qf1 // 7
    assert_equal(res5f.nominal, 2)
    assert_almost_equal(res5f.uncertainty, 0.0)
    assert_equal(res5f.unit, units.s)
    res5m = qf1 % 7
    assert_equal(res5m.nominal, 6)
    assert_almost_equal(res5m.uncertainty, 0.5)
    assert_equal(res5m.unit, units.s)
    # and inverse
    res6 = 70 / qf1
    assert_almost_equal(res6.nominal, 3.5)
    assert_almost_equal(res6.uncertainty, 0.0875)
    assert_equal(res6.unit, units.Unit('1/s'))
    res6f = 70 // qf1
    assert_almost_equal(res6f.nominal, 3.0)
    assert_almost_equal(res6f.uncertainty, 0.0)
    assert_equal(res6f.unit, units.Unit('1/s'))
    res6m = 70 % qf1
    assert_almost_equal(res6m.nominal, 10)
    assert_almost_equal(res6m.uncertainty, 1.5)
    assert_equal(res6m.unit, units.dimensionless_unscaled)

    # with units
    res7 = qf1 / units.m
    assert_equal(res7.nominal, qf1.nominal)
    assert_equal(res7.uncertainty, qf1.uncertainty)
    assert_equal(res7.unit, units.s/units.m)
    res7m = qf1 % units.m
    assert_equal(res7m.unit, units.s)
    # string
    res8 = qf1 / 'm'
    assert_equal(res8.nominal, qf1.nominal)
    assert_equal(res8.uncertainty, qf1.uncertainty)
    assert_equal(res8.unit, units.s/units.m)
    res8m = qf1 % 'm'
    assert_equal(res8m.unit, units.s)


def test_qfloat_math_divmod_array():
    qf1 = QFloat(np.arange(1, 5)*2, np.arange(1, 5)*0.01, 'm')
    qf2 = QFloat(np.arange(1, 5), np.arange(1, 5)*0.01, 's')
    qf3 = QFloat(2, 0.1, 'min')
    qf4 = QFloat(np.arange(1, 17).reshape((4, 4)),
                 np.arange(1, 17).reshape((4, 4))*0.01, 'km')
    qf5 = QFloat(np.arange(1, 17).reshape((4, 4))*4.5,
                 np.arange(1, 17).reshape((4, 4))*0.01, 'h')

    res1 = qf1 / qf2
    assert_equal(res1.nominal, np.ones(4)*2)
    assert_almost_equal(res1.uncertainty, np.ones(4)*0.022360679774997897)
    assert_equal(res1.unit, units.m/units.s)
    res1f = qf1 // qf2
    assert_equal(res1f.nominal, np.ones(4)*2)
    assert_equal(res1f.uncertainty, np.ones(4)*0.0)
    assert_equal(res1f.unit, units.m/units.s)
    res1m = qf1 % qf2
    assert_equal(res1m.nominal, np.ones(4)*0)
    # not continuous
    # assert_equal(res1m.uncertainty, np.ones(4)*0.0)
    assert_equal(res1m.unit, units.m)
    # inverse
    res2 = qf2 / qf1
    assert_equal(res2.nominal, np.ones(4)*0.5)
    assert_almost_equal(res2.uncertainty, np.ones(4)*0.005590169943749474)
    assert_equal(res2.unit, units.s/units.m)
    res2f = qf2 // qf1
    assert_equal(res2f.nominal, np.ones(4)*0)
    assert_equal(res2f.uncertainty, np.ones(4)*0.0)
    assert_equal(res2f.unit, units.s/units.m)
    res2m = qf2 % qf1
    assert_equal(res2m.nominal, [1, 2, 3, 4])
    assert_equal(res2m.uncertainty, [0.01, 0.02, 0.03, 0.04])
    assert_equal(res2m.unit, units.s)

    # 2D arrays
    res3 = qf5 / qf4
    assert_equal(res3.nominal, np.ones(16).reshape((4, 4))*4.5)
    assert_almost_equal(res3.uncertainty,
                        np.ones(16).reshape((4, 4))*0.046097722286464436)
    assert_equal(res3.unit, units.h/units.km)
    res4 = qf5 // qf4
    assert_equal(res4.nominal, np.ones(16).reshape((4, 4))*4.0)
    assert_almost_equal(res4.uncertainty,
                        np.ones(16).reshape((4, 4))*0.0)
    assert_equal(res4.unit, units.h/units.km)
    res4 = qf5 % qf4
    assert_equal(res4.nominal, np.arange(1, 17).reshape((4, 4))*0.5)
    assert_almost_equal(res4.uncertainty,
                        np.arange(1, 17).reshape((4, 4))*0.04123105625617)
    assert_equal(res4.unit, units.h)

    # Array and single
    res5 = qf2 / qf3
    assert_equal(res5.nominal, np.arange(1, 5)*0.5)
    assert_almost_equal(res5.uncertainty,
                        np.arange(1, 5)*0.025495097567963927)
    assert_equal(res5.unit, units.s/units.min)
    res5f = qf2 // qf3
    assert_equal(res5f.nominal, [0, 1, 1, 2])
    assert_almost_equal(res5f.uncertainty, np.zeros(4))
    assert_equal(res5f.unit, units.s/units.min)
    res5m = qf2 % qf3
    assert_equal(res5m.nominal, [1, 0, 1, 0])
    assert_almost_equal(res5m.uncertainty, [0.01, np.inf, 0.1044031, np.inf])
    assert_equal(res5m.unit, units.s)
    # inverse
    res6 = qf3 / qf2
    assert_almost_equal(res6.nominal, [2, 1, 2/3, 0.5])
    assert_almost_equal(res6.uncertainty, [0.10198039027185571,
                                           0.050990195135927854,
                                           0.033993463423951896,
                                           0.025495097567963927])
    assert_equal(res6.unit, units.min/units.s)
    res6f = qf3 // qf2
    assert_equal(res6f.nominal, [2, 1, 0, 0])
    assert_almost_equal(res6f.uncertainty, np.zeros(4))
    assert_equal(res6f.unit, units.min/units.s)
    res6m = qf3 % qf2
    assert_equal(res6m.nominal, [0, 0, 2, 2])
    assert_equal(res6m.unit, units.min)

    # with units
    res7 = qf2 / units.m
    assert_almost_equal(res7.nominal, qf2.nominal)
    assert_almost_equal(res7.uncertainty, qf2.uncertainty)
    assert_equal(res7.unit, units.s/units.m)
    res7m = qf2 % units.m
    assert_equal(res7m.unit, units.s)
    # string
    res8 = qf2 / 'm'
    assert_almost_equal(res8.nominal, qf2.nominal)
    assert_almost_equal(res8.uncertainty, qf2.uncertainty)
    assert_equal(res8.unit, units.s/units.m)
    res8m = qf2 % 'm'
    assert_equal(res8m.unit, units.s)

    # with numbers
    res9 = qf1 / 4
    assert_almost_equal(res9.nominal, np.arange(1, 5)*0.5)
    assert_almost_equal(res9.uncertainty, np.arange(1, 5)*0.0025)
    assert_equal(res9.unit, units.m)
    res9f = qf1 // 4
    assert_almost_equal(res9f.nominal, [0, 1, 1, 2])
    assert_almost_equal(res9f.uncertainty, [0, 0, 0, 0])
    assert_equal(res9f.unit, units.m)
    res9m = qf1 % 4
    assert_almost_equal(res9m.nominal, [2, 0, 2, 0])
    assert_almost_equal(res9m.uncertainty, [0.01, np.nan, 0.03, np.nan])
    assert_equal(res9m.unit, units.m)
    res_a = 8 / qf1
    assert_almost_equal(res_a.nominal, [4, 2, 4/3, 1])
    assert_almost_equal(res_a.uncertainty, [0.02, 0.01, 6/900,
                                            0.005])
    assert_equal(res_a.unit, units.Unit('1/m'))
    res_af = 8 // qf1
    assert_almost_equal(res_af.nominal, [4, 2, 1, 1])
    assert_almost_equal(res_af.uncertainty, [0.0, 0.0, 0.0, 0.0])
    assert_equal(res_af.unit, units.Unit('1/m'))
    res_am = 8 % qf1
    assert_almost_equal(res_am.nominal, [0, 0, 2, 0])
    assert_almost_equal(res_am.std_dev, [np.inf, np.inf, 0.03, np.inf])
    assert_equal(res_am.unit, units.dimensionless_unscaled)


def test_qfloat_math_truediv_inline():
    qf1 = QFloat(30, 0.5, 's')
    qf2 = QFloat(12000, 20, 'm')
    qf3 = QFloat([6000, 3000, 150], [15, 10, 0.5], 'kg')
    qf4 = QFloat([10, 20, 0.1], [0.1, 0.4, 0.01])

    # qf/qf
    i = id(qf2)
    qf2 /= qf1
    assert_equal(qf2.nominal, 400)
    assert_almost_equal(qf2.uncertainty, 6.699917080747261)
    assert_equal(qf2.unit, units.m/units.s)
    assert_equal(i, id(qf2))
    # number
    qf2 /= 2
    assert_equal(qf2.nominal, 200)
    assert_almost_equal(qf2.uncertainty, 3.3499585403736303)
    assert_equal(qf2.unit, units.m/units.s)
    assert_equal(i, id(qf2))
    # unit
    qf2 /= units.s
    assert_equal(qf2.nominal, 200)
    assert_almost_equal(qf2.uncertainty, 3.3499585403736303)
    assert_equal(qf2.unit, units.m/(units.s*units.s))
    assert_equal(i, id(qf2))
    # string
    qf2 /= 'kg'
    assert_equal(qf2.nominal, 200)
    assert_almost_equal(qf2.uncertainty, 3.3499585403736303)
    assert_equal(qf2.unit, units.m/(units.s*units.s*units.kg))
    assert_equal(i, id(qf2))
    # quantity
    qf2 /= 4/units.s
    assert_equal(qf2.nominal, 50)
    assert_almost_equal(qf2.uncertainty, 0.8374896350934076)
    assert_equal(qf2.unit, units.m/(units.s*units.kg))
    assert_equal(i, id(qf2))

    # array / qfloat
    i = id(qf3)
    qf3 /= qf1
    assert_almost_equal(qf3.nominal, [200, 100, 5])
    assert_almost_equal(qf3.uncertainty, [3.370624736026114,
                                          1.699673171197595,
                                          0.08498365855987974])
    assert_equal(qf3.unit, units.kg/units.s)
    assert_equal(i, id(qf3))
    # array / array
    qf3 /= qf4
    assert_almost_equal(qf3.nominal, [20, 5, 50])
    assert_almost_equal(qf3.uncertainty, [0.39193253387682825,
                                          0.13123346456686352,
                                          5.071708018234312])
    assert_equal(qf3.unit, units.kg/units.s)
    assert_equal(i, id(qf3))
    # array / number
    qf3 /= 5
    assert_almost_equal(qf3.nominal, [4, 1, 10])
    assert_almost_equal(qf3.uncertainty, [0.07838650677536566,
                                          0.026246692913372706,
                                          1.0143416036468624])
    assert_equal(qf3.unit, units.kg/units.s)
    assert_equal(i, id(qf3))
    # array  / unit
    qf3 /= units.m
    assert_almost_equal(qf3.nominal, [4, 1, 10])
    assert_almost_equal(qf3.uncertainty, [0.07838650677536566,
                                          0.026246692913372706,
                                          1.0143416036468624])
    assert_equal(qf3.unit, units.kg/(units.s*units.m))
    assert_equal(i, id(qf3))
    # array / string
    qf3 /= 's'
    assert_almost_equal(qf3.nominal, [4, 1, 10])
    assert_almost_equal(qf3.uncertainty, [0.07838650677536566,
                                          0.026246692913372706,
                                          1.0143416036468624])
    assert_equal(qf3.unit, units.kg/(units.s*units.s*units.m))
    assert_equal(i, id(qf3))
    # array / quantity
    qf3 /= 1/units.kg
    assert_almost_equal(qf3.nominal, [4, 1, 10])
    assert_almost_equal(qf3.uncertainty, [0.07838650677536566,
                                          0.026246692913372706,
                                          1.0143416036468624])
    assert_equal(qf3.unit, units.kg*units.kg/(units.s*units.s*units.m))
    assert_equal(i, id(qf3))


def test_qfloat_math_floordiv_inline():
    qf1 = QFloat(30, 0.5, 's')
    qf2 = QFloat(12010, 20, 'm')
    qf3 = QFloat([6040, 3012, 153], [15, 10, 0.5], 'kg')
    qf4 = QFloat([10, 20, 0.1], [0.1, 0.4, 0.01])

    # qf/qf
    i = id(qf2)
    qf2 //= qf1
    assert_equal(qf2.nominal, 400)
    assert_almost_equal(qf2.uncertainty, 0)
    assert_equal(qf2.unit, units.m/units.s)
    assert_equal(i, id(qf2))
    # number
    qf2 //= 7
    assert_equal(qf2.nominal, 57)
    assert_almost_equal(qf2.uncertainty, 0)
    assert_equal(qf2.unit, units.m/units.s)
    assert_equal(i, id(qf2))
    # unit
    qf2 //= units.s
    assert_equal(qf2.nominal, 57)
    assert_almost_equal(qf2.uncertainty, 0)
    assert_equal(qf2.unit, units.m/(units.s*units.s))
    assert_equal(i, id(qf2))
    # string
    qf2 //= 'kg'
    assert_equal(qf2.nominal, 57)
    assert_almost_equal(qf2.uncertainty, 0)
    assert_equal(qf2.unit, units.m/(units.s*units.s*units.kg))
    assert_equal(i, id(qf2))
    # quantity
    qf2 //= 4/units.s
    assert_equal(qf2.nominal, 14)
    assert_almost_equal(qf2.uncertainty, 0.0)
    assert_equal(qf2.unit, units.m/(units.s*units.kg))
    assert_equal(i, id(qf2))

    # array / qfloat
    i = id(qf3)
    qf3 //= qf1
    assert_almost_equal(qf3.nominal, [201, 100, 5])
    assert_almost_equal(qf3.uncertainty, [0, 0, 0])
    assert_equal(qf3.unit, units.kg/units.s)
    assert_equal(i, id(qf3))
    # array / array
    qf3 //= qf4
    assert_almost_equal(qf3.nominal, [20, 5, 49])
    assert_almost_equal(qf3.uncertainty, [0, 0, 0])
    assert_equal(qf3.unit, units.kg/units.s)
    assert_equal(i, id(qf3))
    # array / number
    qf3 //= 5
    assert_almost_equal(qf3.nominal, [4, 1, 9])
    assert_almost_equal(qf3.uncertainty, [0, 0, 0])
    assert_equal(qf3.unit, units.kg/units.s)
    assert_equal(i, id(qf3))
    # array  / unit
    qf3 //= units.m
    assert_almost_equal(qf3.nominal, [4, 1, 9])
    assert_almost_equal(qf3.uncertainty, [0, 0, 0])
    assert_equal(qf3.unit, units.kg/(units.s*units.m))
    assert_equal(i, id(qf3))
    # array / string
    qf3 //= 's'
    assert_almost_equal(qf3.nominal, [4, 1, 9])
    assert_almost_equal(qf3.uncertainty, [0, 0, 0])
    assert_equal(qf3.unit, units.kg/(units.s*units.s*units.m))
    assert_equal(i, id(qf3))
    # array / quantity
    qf3 //= 1/units.kg
    assert_almost_equal(qf3.nominal, [4, 1, 9])
    assert_almost_equal(qf3.uncertainty, [0, 0, 0])
    assert_equal(qf3.unit, units.kg*units.kg/(units.s*units.s*units.m))
    assert_equal(i, id(qf3))


def test_qfloat_math_mod_inline():
    qf1 = QFloat(30, 0.5, 's')
    qf2 = QFloat(12010, 20, 'm')
    qf3 = QFloat([6040, 3012, 153], [15, 10, 0.5], 'kg')
    qf4 = QFloat([7, 5, 2], [0.1, 0.4, 0.01])

    # crazy uncertainties

    # qf/qf
    i = id(qf2)
    qf2 %= qf1
    assert_equal(qf2.nominal, 10)
    assert_equal(qf2.unit, units.m)
    assert_equal(i, id(qf2))
    # number
    qf2 %= 7
    assert_equal(qf2.nominal, 3)
    assert_equal(qf2.unit, units.m)
    assert_equal(i, id(qf2))
    # quantity
    qf2 %= 2/units.s
    assert_equal(qf2.nominal, 1)
    assert_equal(qf2.unit, units.m)
    assert_equal(i, id(qf2))

    # array / qfloat
    i = id(qf3)
    qf3 %= qf1
    assert_almost_equal(qf3.nominal, [10, 12, 3])
    assert_equal(qf3.unit, units.kg)
    assert_equal(i, id(qf3))
    # array / array
    qf3 %= qf4
    assert_almost_equal(qf3.nominal, [3, 2, 1])
    assert_equal(qf3.unit, units.kg)
    assert_equal(i, id(qf3))
    # array / number
    qf3 %= 2
    assert_almost_equal(qf3.nominal, [1, 0, 1])
    assert_equal(qf3.unit, units.kg)
    assert_equal(i, id(qf3))
    # array / quantity
    qf3 %= 1/units.kg
    assert_almost_equal(qf3.nominal, [0, 0, 0])
    assert_equal(qf3.unit, units.kg)
    assert_equal(i, id(qf3))


# POWER ----------------------------------------------------------------------


def test_qfloat_math_power_single():
    qf1 = QFloat(2.0, 0.1, 'm')
    qf2 = QFloat(1.5, 0.2, 'cm')
    qf3 = QFloat(0.5, 0.01)
    qf4 = QFloat(4.0, 0.01)

    with pytest.raises(ValueError):
        qf1 ** qf2

    res1 = qf1 ** qf3
    assert_almost_equal(res1.nominal, np.sqrt(2))
    assert_almost_equal(res1.uncertainty, 0.03668910741328604)
    assert_equal(res1.unit, units.m**0.5)

    res2 = qf1 ** qf4
    assert_almost_equal(res2.nominal, 2**4)
    assert_almost_equal(res2.uncertainty, 3.201921235314246)
    assert_equal(res2.unit, units.m*units.m*units.m*units.m)

    res3 = qf1 ** 2
    assert_almost_equal(res3.nominal, 4)
    assert_almost_equal(res3.uncertainty, 0.4)
    assert_equal(res3.unit, units.m*units.m)

    res4 = qf1 ** 0.5
    assert_almost_equal(res4.nominal, np.sqrt(2))
    assert_almost_equal(res4.uncertainty, 0.03535533905932738)
    assert_equal(res4.unit, units.m**0.5)

    # only dimensionless quantity
    with pytest.raises(ValueError):
        qf1 ** (1*units.m)
    res5 = qf1 ** (1*units.dimensionless_unscaled)
    assert_almost_equal(res5.nominal, 2)
    assert_almost_equal(res5.uncertainty, 0.1)
    assert_equal(res5.unit, units.m)

    # string and unit must fail, except for dimensionless
    with pytest.raises(ValueError):
        qf1 ** 's'
    with pytest.raises(ValueError):
        qf1 ** units.s
    res6 = qf1 ** ''
    assert_almost_equal(res6.nominal, 2)
    assert_almost_equal(res6.uncertainty, 0.1)
    assert_equal(res6.unit, units.m)
    res7 = qf1 ** units.dimensionless_unscaled
    assert_almost_equal(res7.nominal, 2)
    assert_almost_equal(res7.uncertainty, 0.1)
    assert_equal(res7.unit, units.m)

    # only dimensionless should power
    with pytest.raises(ValueError):
        1 ** qf1
    res8 = 2 ** qf4
    assert_almost_equal(res8.nominal, 16)
    assert_almost_equal(res8.uncertainty, 0.11090354888959125)
    assert_equal(res8.unit, units.dimensionless_unscaled)


def test_qfloat_math_power_array():
    qf1 = QFloat([2, 3, 4], [0.1, 0.2, 0.3], 'm')
    qf2 = QFloat(1.5, 0.2, 'cm')
    qf3 = QFloat(0.5, 0.01)
    qf4 = QFloat([1, 2, 3], [0.1, 0.2, 0.3])

    with pytest.raises(ValueError):
        qf1 ** qf2

    res1 = qf1 ** qf3
    assert_almost_equal(res1.nominal, np.sqrt([2, 3, 4]))
    assert_almost_equal(res1.uncertainty, [0.03668910741328604,
                                           0.06078995000472617,
                                           0.07996077052073174])
    assert_equal(res1.unit, units.m**0.5)

    with pytest.raises(ValueError):
        qf1 ** qf4

    res3 = qf1 ** 2
    assert_almost_equal(res3.nominal, [4, 9, 16])
    assert_almost_equal(res3.uncertainty, [0.4, 1.2, 2.4])
    assert_equal(res3.unit, units.m*units.m)

    res4 = qf1 ** 0.5
    assert_almost_equal(res4.nominal, np.sqrt([2, 3, 4]))
    assert_almost_equal(res4.uncertainty, [0.03535533905932738,
                                           0.057735026918962574,
                                           0.075])
    assert_equal(res4.unit, units.m**0.5)

    # only dimensionless quantity
    with pytest.raises(ValueError):
        qf1 ** (1*units.m)
    res5 = qf1 ** (1*units.dimensionless_unscaled)
    assert_almost_equal(res5.nominal, [2, 3, 4])
    assert_almost_equal(res5.uncertainty, [0.1, 0.2, 0.3])
    assert_equal(res5.unit, units.m)

    # string and unit must fail, except for dimensionless
    with pytest.raises(ValueError):
        qf1 ** 's'
    with pytest.raises(ValueError):
        qf1 ** units.s
    res6 = qf1 ** ''
    assert_almost_equal(res5.nominal, [2, 3, 4])
    assert_almost_equal(res5.uncertainty, [0.1, 0.2, 0.3])
    assert_equal(res6.unit, units.m)
    res7 = qf1 ** units.dimensionless_unscaled
    assert_almost_equal(res5.nominal, [2, 3, 4])
    assert_almost_equal(res5.uncertainty, [0.1, 0.2, 0.3])
    assert_equal(res7.unit, units.m)

    # only dimensionless size-1 array should work
    with pytest.raises(ValueError):
        1 ** qf1
    with pytest.raises(ValueError):
        1 ** qf2
    with pytest.raises(ValueError):
        1 ** qf4


def test_qfloat_math_power_inline():
    qf1 = QFloat(2.0, 0.1, 'm')
    qf2 = QFloat(1.5, 0.2, 'cm')
    qf3 = QFloat(0.5, 0.01)
    qf4 = QFloat(4.0, 0.01)
    qf5 = QFloat([2, 3, 4], [0.1, 0.2, 0.3], 'm')
    qf6 = QFloat([1, 2, 3], [0.1, 0.2, 0.3])

    i = id(qf1)
    with pytest.raises(ValueError):
        qf1 **= qf2
    with pytest.raises(ValueError):
        qf1 **= qf5
    with pytest.raises(ValueError):
        qf1 **= qf6
    with pytest.raises(ValueError):
        qf1 **= 'm'
    with pytest.raises(ValueError):
        qf1 **= units.s
    assert_equal(i, id(qf1))
    qf1 **= qf3
    assert_almost_equal(qf1.nominal, np.sqrt(2))
    assert_almost_equal(qf1.uncertainty, 0.03668910741328604)
    assert_equal(qf1.unit, units.m**0.5)
    assert_equal(i, id(qf1))
    qf1 **= qf4
    assert_almost_equal(qf1.nominal, 4)
    assert_almost_equal(qf1.uncertainty, 0.4153212953387695)
    assert_equal(qf1.unit, units.m**2)
    assert_equal(i, id(qf1))
    qf1 **= 3
    assert_almost_equal(qf1.nominal, 64)
    assert_almost_equal(qf1.uncertainty, 19.935422176260943)
    assert_equal(qf1.unit, units.m**6)
    assert_equal(i, id(qf1))
    qf1 **= ''
    assert_almost_equal(qf1.nominal, 64)
    assert_almost_equal(qf1.uncertainty, 19.935422176260943)
    assert_equal(qf1.unit, units.m**6)
    assert_equal(i, id(qf1))
    qf1 **= units.dimensionless_unscaled
    assert_almost_equal(qf1.nominal, 64)
    assert_almost_equal(qf1.uncertainty, 19.935422176260943)
    assert_equal(qf1.unit, units.m**6)
    assert_equal(i, id(qf1))

    i = id(qf5)
    with pytest.raises(ValueError):
        qf5 **= qf1
    with pytest.raises(ValueError):
        qf5 **= qf2
    with pytest.raises(ValueError):
        qf5 **= qf6
    with pytest.raises(ValueError):
        qf5 **= 'm'
    with pytest.raises(ValueError):
        qf5 **= units.s
    assert_equal(i, id(qf5))
    qf5 **= qf3
    assert_almost_equal(qf5.nominal, np.sqrt([2, 3, 4]))
    assert_almost_equal(qf5.uncertainty, [0.03668910741328604,
                                          0.06078995000472617,
                                          0.07996077052073174])
    assert_equal(qf5.unit, units.m**0.5)
    assert_equal(i, id(qf5))
    qf5 **= qf4
    assert_almost_equal(qf5.nominal, np.square([2, 3, 4]))
    assert_almost_equal(qf5.uncertainty, [0.4153212953387695,
                                          1.2644622006872943,
                                          2.5611469725808176])
    assert_equal(qf5.unit, units.m**2)
    assert_equal(i, id(qf5))
    qf5 **= 3
    assert_almost_equal(qf5.nominal, [64, 729, 4096])
    assert_almost_equal(qf5.uncertainty, [19.935422176260943,
                                          307.2643147670124,
                                          1966.960874942068])
    assert_equal(qf5.unit, units.m**6)
    assert_equal(i, id(qf5))
    qf5 **= ''
    assert_almost_equal(qf5.nominal, [64, 729, 4096])
    assert_almost_equal(qf5.uncertainty, [19.935422176260943,
                                          307.2643147670124,
                                          1966.960874942068])
    assert_equal(qf5.unit, units.m**6)
    assert_equal(i, id(qf5))
    qf5 **= units.dimensionless_unscaled
    assert_almost_equal(qf5.nominal, [64, 729, 4096])
    assert_almost_equal(qf5.uncertainty, [19.935422176260943,
                                          307.2643147670124,
                                          1966.960874942068])
    assert_equal(qf5.unit, units.m**6)
    assert_equal(i, id(qf5))


# POS and NEG ----------------------------------------------------------------


def test_qfloat_math_neg():
    qf1 = QFloat(1.0, 0.1, 'm')
    qfn1 = -qf1
    assert_equal(qfn1.nominal, -1.0)
    assert_equal(qfn1.uncertainty, 0.1)
    assert_equal(qfn1.unit, units.m)

    qf2 = QFloat(-5, 0.1, 'm')
    qfn2 = -qf2
    assert_equal(qfn2.nominal, 5.0)
    assert_equal(qfn2.uncertainty, 0.1)
    assert_equal(qfn2.unit, units.m)

    qf3 = QFloat([-2, 3, -5, 10],
                 [0.1, 0.4, 0.3, -0.1],
                 'm')
    qfn3 = -qf3
    assert_equal(qfn3.nominal, [2, -3, 5, -10])
    assert_equal(qfn3.uncertainty, [0.1, 0.4, 0.3, 0.1])
    assert_equal(qfn3.unit, units.m)


def test_qfloat_math_pos():
    qf1 = QFloat(1.0, 0.1, 'm')
    qfn1 = +qf1
    assert_equal(qfn1.nominal, 1.0)
    assert_equal(qfn1.uncertainty, 0.1)
    assert_equal(qfn1.unit, units.m)

    qf2 = QFloat(-5, 0.1, 'm')
    qfn2 = +qf2
    assert_equal(qfn2.nominal, -5.0)
    assert_equal(qfn2.uncertainty, 0.1)
    assert_equal(qfn2.unit, units.m)

    qf3 = QFloat([-2, 3, -5, 10],
                 [0.1, 0.4, 0.3, -0.1],
                 'm')
    qfn3 = +qf3
    assert_equal(qfn3.nominal, [-2, 3, -5, 10])
    assert_equal(qfn3.uncertainty, [0.1, 0.4, 0.3, 0.1])
    assert_equal(qfn3.unit, units.m)


def test_qfloat_math_abs():
    qf1 = QFloat(1.0, 0.1, 'm')
    qfn1 = abs(qf1)
    assert_equal(qfn1.nominal, 1.0)
    assert_equal(qfn1.uncertainty, 0.1)
    assert_equal(qfn1.unit, units.m)

    qf2 = QFloat(-5, 0.1, 'm')
    qfn2 = abs(qf2)
    assert_equal(qfn2.nominal, 5.0)
    assert_equal(qfn2.uncertainty, 0.1)
    assert_equal(qfn2.unit, units.m)

    qf3 = QFloat([-2, 3, -5, 10],
                 [0.1, 0.4, 0.3, -0.1],
                 'm')
    qfn3 = abs(qf3)
    assert_equal(qfn3.nominal, [2, 3, 5, 10])
    assert_equal(qfn3.uncertainty, [0.1, 0.4, 0.3, 0.1])
    assert_equal(qfn3.unit, units.m)


def test_qfloat_math_float():
    qf1 = QFloat(1.5, 0.4, 'm')
    qf2 = QFloat([1.4, 2.5, 3.6], [0.2, 0.3, 0.4], 's')

    assert_equal(float(qf1), 1.5)
    with pytest.raises(TypeError):
        float(qf2)


def test_qfloat_math_int():
    qf1 = QFloat(1.5, 0.4, 'm')
    qf2 = QFloat([1.4, 2.5, 3.6], [0.2, 0.3, 0.4], 's')

    assert_equal(int(qf1), 1)
    with pytest.raises(TypeError):
        int(qf2)
