# -*- coding: utf-8 -*-
"""
Test symbolic unit handling.




"""

# -----------------------------------------------------------------------------
# Copyright (c) 2018, yt Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the LICENSE file, distributed with this software.
# -----------------------------------------------------------------------------


import operator
import pickle

import numpy as np
import pytest
from numpy.testing import (
    assert_allclose,
    assert_almost_equal,
    assert_array_almost_equal_nulp,
    assert_equal,
)
from sympy import Symbol

import unyt.unit_symbols as unit_symbols
from unyt._physical_ratios import (
    m_per_km,
    m_per_mpc,
    m_per_pc,
    mass_sun_kg,
    sec_per_year,
)
from unyt._unit_lookup_table import (
    default_unit_symbol_lut,
    name_alternatives,
    unit_prefixes,
)
from unyt.array import unyt_quantity
from unyt.dimensions import (
    energy,
    length,
    magnetic_field_cgs,
    magnetic_field_mks,
    mass,
    power,
    rate,
    temperature,
    time,
)
from unyt.exceptions import InvalidUnitOperation, UnitConversionError, UnitsNotReducible
from unyt.testing import assert_allclose_units
from unyt.unit_object import Unit, UnitParseError, default_unit_registry
from unyt.unit_registry import UnitRegistry
from unyt.unit_systems import UnitSystem, cgs_unit_system


def test_no_conflicting_symbols():
    """
    Check unit symbol definitions for conflicts.

    """
    full_set = set(default_unit_symbol_lut.keys())

    # go through all possible prefix combos
    for symbol in default_unit_symbol_lut.keys():
        if default_unit_symbol_lut[symbol][4]:
            keys = unit_prefixes.keys()
        else:
            keys = [symbol]
        for prefix in keys:
            new_symbol = "%s%s" % (prefix, symbol)

            # test if we have seen this symbol
            assert new_symbol not in full_set, "Duplicate symbol: %s" % new_symbol

            full_set.add(new_symbol)


def test_dimensionless():
    """
    Create dimensionless unit and check attributes.

    """
    u1 = Unit()

    assert u1.is_dimensionless
    assert u1.expr == 1
    assert u1.base_value == 1
    assert u1.dimensions == 1
    assert u1 != "hello!"
    assert (u1 == "hello") is False

    u2 = Unit("")

    assert u2.is_dimensionless
    assert u2.expr == 1
    assert u2.base_value == 1
    assert u2.dimensions == 1

    assert_equal(u1.latex_repr, "")
    assert_equal(u2.latex_repr, "")


def test_create_from_string():
    """
    Create units with strings and check attributes.

    """

    u1 = Unit("kg * m**2 * s**-2")
    assert u1.dimensions == energy
    assert u1.base_value == 1.0

    # make sure order doesn't matter
    u2 = Unit("m**2 * s**-2 * kg")
    assert u2.dimensions == energy
    assert u2.base_value == 1.0

    # Test rationals
    u3 = Unit("kg**0.5 * m**-0.5 * s**-1")
    assert u3.dimensions == magnetic_field_cgs
    assert u3.base_value == 1.0

    # sqrt functions
    u4 = Unit("sqrt(kg)/sqrt(m)/s")
    assert u4.dimensions == magnetic_field_cgs
    assert u4.base_value == 1.0

    # commutative sqrt function
    u5 = Unit("sqrt(kg/m)/s")
    assert u5.dimensions == magnetic_field_cgs
    assert u5.base_value == 1.0

    # nonzero CGS conversion factor
    u6 = Unit("Msun/pc**3")
    assert u6.dimensions == mass / length**3
    assert_array_almost_equal_nulp(
        np.array([u6.base_value]), np.array([mass_sun_kg / m_per_pc**3])
    )

    with pytest.raises(UnitParseError):
        Unit("m**m")
    with pytest.raises(UnitParseError):
        Unit("m**g")
    with pytest.raises(UnitParseError):
        Unit("m+g")
    with pytest.raises(UnitParseError):
        Unit("m-g")
    with pytest.raises(UnitParseError):
        Unit("hello!")
    with pytest.raises(UnitParseError):
        Unit("True")
    with pytest.raises(UnitParseError):
        Unit("else")
    with pytest.raises(UnitParseError):
        Unit("hello(37)")
    with pytest.raises(UnitParseError):
        Unit("hello(foo=37)")

    cm = Unit("cm")
    data = 1 * cm

    assert Unit(data) == cm
    assert Unit(b"cm") == cm


def test_create_from_expr():
    """
    Create units from sympy Exprs and check attributes.

    """
    pc_mks = m_per_pc
    yr_mks = sec_per_year

    # Symbol expr
    s1 = Symbol("pc", positive=True)
    s2 = Symbol("yr", positive=True)
    # Mul expr
    s3 = s1 * s2
    # Pow expr
    s4 = s1**2 * s2 ** (-1)

    u1 = Unit(s1)
    u2 = Unit(s2)
    u3 = Unit(s3)
    u4 = Unit(s4)

    assert u1.expr == s1
    assert u2.expr == s2
    assert u3.expr == s3
    assert u4.expr == s4

    assert_allclose_units(u1.base_value, pc_mks, 1e-12)
    assert_allclose_units(u2.base_value, yr_mks, 1e-12)
    assert_allclose_units(u3.base_value, pc_mks * yr_mks, 1e-12)
    assert_allclose_units(u4.base_value, pc_mks**2 / yr_mks, 1e-12)

    assert u1.dimensions == length
    assert u2.dimensions == time
    assert u3.dimensions == length * time
    assert u4.dimensions == length**2 / time


def test_create_with_duplicate_dimensions():
    """
    Create units with overlapping dimensions. Ex: km/Mpc.

    """

    u1 = Unit("J * s**-1")
    u2 = Unit("km/s/Mpc")
    km_mks = m_per_km
    Mpc_mks = m_per_mpc

    assert u1.base_value == 1
    assert u1.dimensions == power

    assert_allclose_units(u2.base_value, km_mks / Mpc_mks, 1e-12)
    assert u2.dimensions == rate


def test_create_new_symbol():
    """
    Create unit with unknown symbol.

    """
    u1 = Unit("abc", base_value=42, dimensions=(mass / time))

    assert u1.expr == Symbol("abc", positive=True)
    assert u1.base_value == 42
    assert u1.dimensions == mass / time

    u1 = Unit("abc", base_value=42, dimensions=length**3)

    assert u1.expr == Symbol("abc", positive=True)
    assert u1.base_value == 42
    assert u1.dimensions == length**3

    u1 = Unit("abc", base_value=42, dimensions=length * (mass * length))

    assert u1.expr == Symbol("abc", positive=True)
    assert u1.base_value == 42
    assert u1.dimensions == length**2 * mass

    with pytest.raises(UnitParseError):
        Unit("abc", base_value=42, dimensions=length**length)
    with pytest.raises(UnitParseError):
        Unit("abc", base_value=42, dimensions=length ** (length * length))
    with pytest.raises(UnitParseError):
        Unit("abc", base_value=42, dimensions=length - mass)
    with pytest.raises(UnitParseError):
        Unit("abc", base_value=42, dimensions=length + mass)


def test_create_fail_on_unknown_symbol():
    """
    Fail to create unit with unknown symbol, without base_value and dimensions.

    """
    with pytest.raises(UnitParseError):
        Unit(Symbol("jigawatts"))


def test_create_fail_on_bad_symbol_type():
    """
    Fail to create unit with bad symbol type.

    """
    with pytest.raises(UnitParseError):
        Unit([1])  # something other than Expr and str


def test_create_fail_on_bad_dimensions_type():
    """
    Fail to create unit with bad dimensions type.

    """
    with pytest.raises(UnitParseError):
        Unit("a", base_value=1, dimensions="(mass)")


def test_create_fail_on_dimensions_content():
    """
    Fail to create unit with bad dimensions expr.

    """
    a = Symbol("a")
    with pytest.raises(UnitParseError):
        Unit("a", base_value=1, dimensions=a)


def test_create_fail_on_base_value_type():
    """
    Fail to create unit with bad base_value type.

    """
    with pytest.raises(UnitParseError):
        Unit("a", base_value="a", dimensions=(mass / time))


def test_string_representation():
    """
    Check unit string representation.

    """
    pc = Unit("pc")
    Myr = Unit("Myr")
    speed = pc / Myr
    dimensionless = Unit()

    assert str(pc) == "pc"
    assert str(Myr) == "Myr"
    assert str(speed) == "pc/Myr"
    assert repr(speed) == "pc/Myr"
    assert str(dimensionless) == "dimensionless"
    assert repr(dimensionless) == "(dimensionless)"


def test_multiplication():
    """
    Multiply two units.

    """
    msun_mks = mass_sun_kg
    pc_mks = m_per_pc

    # Create symbols
    msun_sym = Symbol("Msun", positive=True)
    pc_sym = Symbol("pc", positive=True)
    s_sym = Symbol("s", positive=True)

    # Create units
    u1 = Unit("Msun")
    u2 = Unit("pc")

    # Mul operation
    u3 = u1 * u2

    assert u3.expr == msun_sym * pc_sym
    assert_allclose_units(u3.base_value, msun_mks * pc_mks, 1e-12)
    assert u3.dimensions == mass * length

    # Pow and Mul operations
    u4 = Unit("pc**2")
    u5 = Unit("Msun * s")

    u6 = u4 * u5

    assert u6.expr == pc_sym**2 * msun_sym * s_sym
    assert_allclose_units(u6.base_value, pc_mks**2 * msun_mks, 1e-12)
    assert u6.dimensions == length**2 * mass * time


def test_division():
    """
    Divide two units.

    """
    pc_mks = m_per_pc
    km_mks = m_per_km

    # Create symbols
    pc_sym = Symbol("pc", positive=True)
    km_sym = Symbol("km", positive=True)
    s_sym = Symbol("s", positive=True)

    # Create units
    u1 = Unit("pc")
    u2 = Unit("km * s")

    u3 = u1 / u2

    assert u3.expr == pc_sym / (km_sym * s_sym)
    assert_allclose_units(u3.base_value, pc_mks / km_mks, 1e-12)
    assert u3.dimensions == 1 / time


def test_power():
    """
    Take units to some power.

    """
    from sympy import nsimplify

    from unyt import dimensionless

    pc_mks = m_per_pc
    mK_mks = 1e-3
    u1_dims = mass * length**2 * time**-3 * temperature**4
    u1 = Unit("kg * pc**2 * s**-3 * mK**4")

    u2 = u1**2

    assert u2.dimensions == u1_dims**2
    assert_allclose_units(u2.base_value, (pc_mks**2 * mK_mks**4) ** 2, 1e-12)

    u3 = u1 ** (-1.0 / 3)

    assert u3.dimensions == nsimplify(u1_dims ** (-1.0 / 3))
    assert_allclose_units(
        u3.base_value, (pc_mks**2 * mK_mks**4) ** (-1.0 / 3), 1e-12
    )

    assert u1**0.0 == dimensionless


def test_equality():
    """
    Check unit equality with different symbols, but same dimensions and
    base_value.

    """
    u1 = Unit("km * s**-1")
    u2 = Unit("m * ms**-1")

    assert u1 == u2
    assert u1.copy() == u2


def test_invalid_operations():
    u1 = Unit("cm")
    u2 = Unit("m")

    with pytest.raises(InvalidUnitOperation):
        u1 + u2
    with pytest.raises(InvalidUnitOperation):
        u1 += u2
    with pytest.raises(InvalidUnitOperation):
        1 + u1
    with pytest.raises(InvalidUnitOperation):
        u1 + 1
    with pytest.raises(InvalidUnitOperation):
        u1 - u2
    with pytest.raises(InvalidUnitOperation):
        u1 -= u2
    with pytest.raises(InvalidUnitOperation):
        1 - u1
    with pytest.raises(InvalidUnitOperation):
        u1 - 1
    with pytest.raises(InvalidUnitOperation):
        u1 *= u2
    with pytest.raises(InvalidUnitOperation):
        u1 * "hello!"
    with pytest.raises(InvalidUnitOperation):
        u1 /= u2
    with pytest.raises(InvalidUnitOperation):
        u1 / "hello!"
    with pytest.raises(InvalidUnitOperation):
        Unit("B") * Unit("V")
    with pytest.raises(InvalidUnitOperation):
        Unit("V") * Unit("B")
    with pytest.raises(InvalidUnitOperation):
        Unit("V") / Unit("Np")
    with pytest.raises(InvalidUnitOperation):
        Unit("dB") / Unit("dB")
    with pytest.raises(InvalidUnitOperation):
        Unit("B") ** 2


def test_base_equivalent():
    """
    Check base equivalent of a unit.

    """
    Msun_mks = mass_sun_kg
    Mpc_mks = m_per_mpc

    u1 = Unit("Msun * Mpc**-3")
    u2 = Unit("kg * m**-3")
    u3 = u1.get_base_equivalent()

    assert u2.expr == u3.expr
    assert u2 == u3

    assert_allclose_units(u1.base_value, Msun_mks / Mpc_mks**3, 1e-12)
    assert u2.base_value == 1
    assert u3.base_value == 1

    mass_density = mass / length**3

    assert u1.dimensions == mass_density
    assert u2.dimensions == mass_density
    assert u3.dimensions == mass_density

    assert_allclose_units(
        u1.get_conversion_factor(u3)[0], Msun_mks / Mpc_mks**3, 1e-12
    )

    with pytest.raises(UnitConversionError):
        u1.get_conversion_factor(Unit("m"))

    with pytest.raises(UnitConversionError):
        u1.get_conversion_factor(Unit("degF"))

    reg = UnitRegistry(unit_system=cgs_unit_system)

    u = Unit("kg", registry=reg)

    assert u.get_base_equivalent() == Unit("g")

    u = Unit("kg")

    assert u.get_base_equivalent() == Unit("kg")

    u = Unit("A")
    assert u.get_base_equivalent(unit_system="mks") == Unit("A")


def test_temperature_offsets():
    u1 = Unit("degC")
    u2 = Unit("degF")

    with pytest.raises(InvalidUnitOperation):
        operator.mul(u1, u2)
    with pytest.raises(InvalidUnitOperation):
        operator.truediv(u1, u2)


def test_latex_repr():
    registry = UnitRegistry()

    # create a fake comoving unit
    registry.add(
        "pccm",
        registry.lut["pc"][0] / (1 + 2),
        length,
        "\\rm{pc}/(1+z)",
        prefixable=True,
    )

    test_unit = Unit("Mpccm", registry=registry)
    assert_almost_equal(test_unit.base_value, m_per_mpc / 3)
    assert_equal(test_unit.latex_repr, r"\rm{Mpc}/(1+z)")

    test_unit = Unit("cm**-3", base_value=1.0, registry=registry)
    assert_equal(test_unit.latex_repr, "\\frac{1}{\\rm{cm}^{3}}")

    test_unit = Unit("m_geom/l_geom**3")
    assert_equal(test_unit.latex_repr, "\\frac{1}{\\rm{M}_\\odot^{2}}")

    test_unit = Unit("1e9*cm")
    assert_equal(test_unit.latex_repr, "1.0 \\times 10^{9}\\ \\rm{cm}")

    test_unit = Unit("1.0*cm")
    assert_equal(test_unit.latex_repr, "\\rm{cm}")


def test_latitude_longitude():
    lat = unit_symbols.lat
    lon = unit_symbols.lon
    deg = unit_symbols.deg
    assert_equal(lat.units.base_offset, 90.0)
    assert_equal((deg * 90.0).in_units("lat").value, 0.0)
    assert_equal((deg * 180).in_units("lat").value, -90.0)
    assert_equal((lat * 0.0).in_units("deg"), deg * 90.0)
    assert_equal((lat * -90).in_units("deg"), deg * 180)

    assert_equal(lon.units.base_offset, -180.0)
    assert_equal((deg * 0.0).in_units("lon").value, -180.0)
    assert_equal((deg * 90.0).in_units("lon").value, -90.0)
    assert_equal((deg * 180).in_units("lon").value, 0.0)
    assert_equal((deg * 360).in_units("lon").value, 180.0)

    assert_equal((lon * -180.0).in_units("deg"), deg * 0.0)
    assert_equal((lon * -90.0).in_units("deg"), deg * 90.0)
    assert_equal((lon * 0.0).in_units("deg"), deg * 180.0)
    assert_equal((lon * 180.0).in_units("deg"), deg * 360)


def test_creation_from_ytarray():
    from unyt import electrostatic_unit, elementary_charge_cgs

    u1 = Unit(electrostatic_unit)
    assert_equal(str(u1), "statC")
    assert_equal(u1, Unit("esu"))
    assert_equal(u1, electrostatic_unit.units)

    u2 = Unit(elementary_charge_cgs)
    assert_equal(str(u2), "4.80320467299766e-10*statC")
    assert_equal(u2, Unit("4.80320467299766e-10*statC"))
    assert_equal(u1, elementary_charge_cgs.units)

    assert_allclose((u1 / u2).base_value, electrostatic_unit / elementary_charge_cgs)

    with pytest.raises(UnitParseError):
        Unit([1, 2, 3] * elementary_charge_cgs)


def test_list_same_dimensions():
    from unyt import m

    reg = default_unit_registry
    for equiv in reg.list_same_dimensions(m):
        assert Unit(equiv).dimensions is length


def test_decagram():
    dag = Unit("dag")
    g = Unit("g")
    assert dag.get_conversion_factor(g) == (10.0, None)


def test_pickle():
    cm = Unit("cm")
    assert cm == pickle.loads(pickle.dumps(cm))


def test_preserve_offset():
    from unyt import degF, dimensionless

    new_unit = degF * dimensionless

    assert new_unit is not degF
    assert new_unit == degF
    assert new_unit.base_offset == degF.base_offset

    new_unit = degF / dimensionless

    assert new_unit is not degF
    assert new_unit == degF
    assert new_unit.base_offset == degF.base_offset

    with pytest.raises(InvalidUnitOperation):
        dimensionless / degF


def test_code_unit():
    from unyt import UnitRegistry

    ureg = UnitRegistry()
    ureg.add("code_length", 10.0, length)
    ureg.add("code_magnetic_field", 2.0, magnetic_field_mks)
    u = Unit("code_length", registry=ureg)
    assert u.is_code_unit is True
    assert u.get_base_equivalent() == Unit("m")
    u = Unit("cm")
    assert u.is_code_unit is False

    u = Unit("code_magnetic_field", registry=ureg)
    assert u.get_base_equivalent("mks") == Unit("T")
    with pytest.raises(UnitsNotReducible):
        assert u.get_base_equivalent("cgs")

    # see issue #60
    u = Unit("s/m")
    assert u.get_mks_equivalent() == Unit("s/m")
    assert u.get_mks_equivalent() != Unit("ohm")
    assert u.get_cgs_equivalent() == Unit("s/cm")

    u = Unit("kC")
    assert u.get_cgs_equivalent() == Unit("kesu")
    assert u.get_cgs_equivalent().get_mks_equivalent() == u

    UnitSystem(ureg.unit_system_id, "code_length", "kg", "s", registry=ureg)

    u = Unit("cm", registry=ureg)
    ue = u.get_base_equivalent("code")

    assert str(ue) == "code_length"
    assert ue.base_value == 10
    assert ue.dimensions is length

    class FakeDataset:
        unit_registry = ureg

    ds = FakeDataset()

    UnitSystem(ds, "code_length", "kg", "s", registry=ureg)

    u = Unit("cm", registry=ureg)
    ue = u.get_base_equivalent(ds)

    assert str(ue) == "code_length"
    assert ue.base_value == 10
    assert ue.dimensions is length

    with pytest.raises(UnitParseError):
        Unit("code_length")


def test_bad_equivalence():
    from unyt import cm

    with pytest.raises(KeyError):
        cm.has_equivalent("dne")


def test_em_unit_base_equivalent():
    from unyt import A, cm

    with pytest.raises(UnitsNotReducible):
        (A / cm).get_base_equivalent("cgs")


def test_symbol_lut_length():
    for v in default_unit_symbol_lut.values():
        assert len(v) == 5


def test_simplify():
    import unyt as u

    answers = {
        u.Hz * u.s: "dimensionless",
        u.kg / u.g: "1000",
        u.Hz * u.s * u.km: "km",
        u.kHz * u.s: "1000",
        u.kHz * u.s * u.km: "1000*km",
        u.kHz * u.s**2: "1000*s",
        u.kHz * u.s**2 * u.km: "1000*km*s",
        u.Hz**-1 * u.s: "s/Hz",
        u.Hz**-1 * u.s * u.km: "km*s/Hz",
        u.Hz**1.5 * u.s**1.7: "sqrt(Hz)*s**(7/10)",
        u.Hz**1.5 * u.s**1.7 * u.km: "sqrt(Hz)*km*s**(7/10)",
        u.m**2 / u.cm**2: "10000",
    }

    for unit, answer in answers.items():
        assert str(unit.simplify()) == answer


def test_micro_prefix():
    import unyt as u

    # both versions of unicode mu work correctly
    assert u.um == u.µm
    assert u.um == u.μm

    # parsing both versions works as well
    assert u.ug == u.Unit("µg")
    assert u.ug == u.Unit("μg")


def test_name_alternatives():
    import unyt
    from unyt._unit_lookup_table import (
        default_unit_name_alternatives,
        inv_name_alternatives,
        name_alternatives,
    )

    # concatenated list of all alternative unit names
    allowed_names = sum(name_alternatives.values(), [])

    # ensure the values are all tuples and not e.g. strings
    for val in default_unit_name_alternatives.values():
        assert isinstance(val, tuple)

    # all names are unique
    assert len(set(allowed_names)) == len(allowed_names)
    # each allowed name has a key in the inverse dict
    assert len(inv_name_alternatives.keys()) == len(allowed_names)
    assert set(inv_name_alternatives.keys()) == set(allowed_names)

    for name in allowed_names:
        assert hasattr(unyt, name)
        assert hasattr(unyt.unit_symbols, name)


def test_solar_unit_name_alternatives():
    import unyt
    from unyt import Unit

    # check that m_sun, m_Sun, M_sun, M_Sun, msun, and Msun all work
    for lower_name_prefix in "mrltz":
        base_name = lower_name_prefix + "sun"
        for name_prefix in [lower_name_prefix, lower_name_prefix.upper()]:
            alternative_names = [name_prefix + suf for suf in ["sun", "_sun", "_Sun"]]
            for name in alternative_names:
                assert Unit(name) == Unit(base_name)
                assert hasattr(unyt, name)
                # only solar mass units are in physical constants
                if lower_name_prefix == "m":
                    assert hasattr(unyt.physical_constants, name)


def test_attosecond():
    from unyt import Unit, attosecond, second

    assert Unit("as") == attosecond
    assert str(Unit("as")) == "as"
    assert Unit("as/s") == attosecond / second


def test_micro():
    from unyt import Unit

    assert str(Unit("um")) == "μm"
    assert str(Unit("us")) == "μs"


def test_show_all_units_doc_table_ops():
    for name in set(name_alternatives.keys()):
        u = Unit(name)
        (1 * u).in_mks()
        try:
            (1 * u).in_cgs()
        except UnitsNotReducible:
            pass


def test_hPa_mbar():
    assert Unit("hPa").dimensions == Unit("bar").dimensions
    assert (5 * Unit("hPa") == 5 * Unit("mbar")).all()
    assert (5 * Unit("hPa") != 1 * Unit("bar")).all()


def test_percent():
    a = 300 * Unit("percent")
    b = 3.0 * Unit("dimensionless")
    c = 300.0 * Unit("%")
    d = 300.0 * Unit("V*%/V")

    assert a == b
    assert str(a) == "300 %"
    assert repr(a) == "unyt_quantity(300, '%')"

    assert a == c
    assert c == d


def test_equal_has_same_hash():
    a = Unit("m")
    b = Unit("m")
    c = Unit("m*s/s")

    assert a == b
    assert b == c
    assert hash(a) == hash(b)
    assert hash(b) == hash(c)


def test_bel_neper():
    assert Unit("B").dimensions == Unit("Np").dimensions
    a = 1 * Unit("B") / (np.log(10) / 2)
    assert_allclose_units(a.to("Np"), 1 * Unit("Np"))
    a = 2 * Unit("B")
    b = 20 * Unit("decibel")
    assert (a == b).all()
    c = 2 * Unit("Np")
    d = 20 * Unit("decineper")
    assert (c == d).all()
    assert Unit("dB") ** 1 == Unit("dB")


def test_henry():
    assert (Unit("H") / Unit("Ω")).dimensions == time


def test_degC():
    assert Unit("degree_celsius") == Unit("degC")
    assert Unit("degree_Celsius") == Unit("degC")
    assert Unit("Celsius") == Unit("degC")
    assert Unit("°C") == Unit("degC")
    a = 1 * Unit("degC")
    assert str(a) == "1 °C"


def test_degC_with_SIprefixes():
    assert_allclose_units(1 * Unit("mdegC"), 0.001 * Unit("degC"))
    assert_allclose_units(1 * Unit("degC"), 1000 * Unit("mdegC"))
    assert_allclose_units(73 * Unit("degF"), 22777.779 * Unit("mdegC"))
    assert_allclose_units(22777.779 * Unit("mdegC"), 73 * Unit("degF"))
    assert_allclose_units(22777.779 * Unit("mdegC"), 532.67 * Unit("R"))
    assert_allclose_units(1 * Unit("mK"), -273149.0 * Unit("mdegC"))
    assert_allclose_units(1 * Unit("mdegC"), 273151.0 * Unit("mK"))


def test_delta_degC():
    a = 1 * Unit("delta_degC")
    assert str(a) == "1 Δ°C"


def test_degF():
    assert Unit("degree_fahrenheit") == Unit("degF")
    assert Unit("degree_Fahrenheit") == Unit("degF")
    assert Unit("Fahrenheit") == Unit("degF")
    assert Unit("°F") == Unit("degF")
    a = 1 * Unit("degF")
    assert str(a) == "1 °F"


def test_delta_degF():
    a = 1 * Unit("delta_degF")
    assert str(a) == "1 Δ°F"


def test_mixed_registry_operations():

    reg = UnitRegistry(unit_system="cgs")
    reg.add("fake_length", 0.001, length)
    a = unyt_quantity(1, units="fake_length", registry=reg)
    b = unyt_quantity(1, "cm")

    assert_almost_equal(a + b, b + a)
    assert_almost_equal(a - b, -(b - a))
    assert_almost_equal(a * b, b * a)
    assert_almost_equal(b / a, b / a.in_units("km"))
    assert_almost_equal(a / b, a / b.in_units("km"))
