metrolopy 0.6.5__py3-none-any.whl → 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
metrolopy/unit.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # module unit
4
4
 
5
- # Copyright (C) 2019 National Research Council Canada
5
+ # Copyright (C) 2025 National Research Council Canada
6
6
  # Author: Harold Parks
7
7
 
8
8
  # This file is part of MetroloPy.
@@ -29,12 +29,20 @@ import numpy as np
29
29
  import weakref
30
30
  import warnings
31
31
  from .exceptions import (UnitLibError,CircularUnitConversionError,
32
- NoUnitConversionFoundError,UnitNotFoundError,
33
- IncompatibleUnitsError,UnitLibNotFoundError,
34
- UnitWarning)
35
- from .printing import PrettyPrinter
32
+ UnitNotFoundError,IncompatibleUnitsError,
33
+ UnitLibNotFoundError,UnitWarning)
34
+ from .printing import PrettyPrinter,MetaPrettyPrinter
36
35
  from .indexed import Indexed
37
36
  from .unitparser import _UnitParser
37
+ from numbers import Rational,Integral,Number
38
+ from abc import ABCMeta
39
+ from fractions import Fraction
40
+ from decimal import Decimal
41
+
42
+ try:
43
+ from mpmath import mp,mpf,rational
44
+ except:
45
+ mp = mpf = rational = None
38
46
 
39
47
  def unit(name,exception=True):
40
48
  """
@@ -106,6 +114,8 @@ class Conversion:
106
114
 
107
115
  def __init__(self,unit,factor=1):
108
116
  self._unit = unit
117
+ if isinstance(factor,Fraction):
118
+ factor = MFraction(factor)
109
119
  self.factor = factor
110
120
 
111
121
  def to(self,g):
@@ -455,24 +465,31 @@ class Unit(PrettyPrinter,Indexed):
455
465
  self._conversion = c
456
466
  if c is not None:
457
467
  c.parent = self
458
-
459
- def _convs(self,d,c,h):
460
- if c is None:
461
- c = self._conversion.copy()
462
- else:
463
- if self in h:
464
- if self is d:
465
- raise CircularUnitConversionError('a circular conversion chain was found starting with unit ' + d.name + ' in library ' + d.library)
466
- else:
467
- raise CircularUnitConversionError('a circular conversion chain was found;\nstarted search with unit ' + d.name + ' in library ' + d.library + ' and the loop started with unit ' + self.name + ' in library ' + self.library)
468
- h.add(self)
469
- d._gconv = c
470
- if self._conversion is None:
471
- return
472
- c = c.chain(self._conversion)
473
468
 
469
+ def _convs(self,h):
470
+ if self in h:
471
+ raise CircularUnitConversionError('a circular conversion chain was found nwith unit ' + self.name)
472
+ h.add(self)
473
+
474
474
  try:
475
- u = self._conversion.unit
475
+ c = self._conversion
476
+ u = c.unit
477
+ if isinstance(u,_CompositeUnit):
478
+ una = u._units
479
+ unb = {}
480
+ for k,v in una:
481
+ if k in unb:
482
+ unb[k] += v
483
+ else:
484
+ unb[k] = v
485
+ unb = {k:v for k,v in unb.items() if v != 0 and k is not one}
486
+ if len(unb) == 0:
487
+ u = one
488
+ else:
489
+ u = _CompositeUnit([(k,v) for k,v in unb.items()])
490
+ u._find_conv()
491
+ c = c.chain(Conversion(u,1))
492
+
476
493
  except UnitNotFoundError:
477
494
  warnings.warn('while searching conversions: unit ' + self.name + ' should have a conversion to ' + self._conversion.unit_name + ' in library ' + str(self._conversion.unit_library) + ' but the second unit was not found',UnitWarning,stacklevel=3)
478
495
  return
@@ -480,7 +497,13 @@ class Unit(PrettyPrinter,Indexed):
480
497
  warnings.warn('while searching conversions: unit ' + self.name + ' should have a conversion to ' + self._conversion.unit_name + ' in library ' + str(self._conversion.unit_library) + ' but the library containing the second unit has not been loaded',UnitWarning,stacklevel=3)
481
498
  return
482
499
 
483
- u._convs(d,c,h)
500
+ if u._conversion is None:
501
+ self._gconv = c
502
+ else:
503
+ r = u._convs(h)
504
+ self._gconv = c.chain(r)
505
+
506
+ return self._gconv
484
507
 
485
508
  @property
486
509
  def linear(self):
@@ -496,18 +519,18 @@ class Unit(PrettyPrinter,Indexed):
496
519
  return self._linear
497
520
 
498
521
  def _convtree(self):
499
- if self._conversion is not None:
500
- self._convs(self,None,set([self]))
522
+ if self.conversion is not None and self._gconv is None:
523
+ self._convs(set())
501
524
 
502
525
  def _convert(self,g,un):
503
- if self._conversion is not None and self._conversion.unit is un:
504
- return self._conversion.to(g)
526
+ if self.conversion is not None and self.conversion.unit is un:
527
+ return self.conversion.to(g)
505
528
 
506
529
  if self._gconv is not None and self._gconv._unit is un:
507
530
  return self._gconv.to(g)
508
531
 
509
- if un._conversion is not None and un._conversion.unit is self:
510
- return un._conversion.frm(g)
532
+ if un.conversion is not None and un.conversion.unit is self:
533
+ return un.conversion.frm(g)
511
534
 
512
535
  if un._gconv is not None and un._gconv._unit is self:
513
536
  return un._gconv.frm(g)
@@ -536,7 +559,7 @@ class Unit(PrettyPrinter,Indexed):
536
559
 
537
560
  unm = unit.short_name
538
561
  if ret is None:
539
- raise NoUnitConversionFoundError('no conversion found from unit ' + self.short_name + ' to unit ' + unm)
562
+ raise IncompatibleUnitsError('no conversion found from unit ' + self.short_name + ' to unit ' + unm)
540
563
  return ret
541
564
 
542
565
  @property
@@ -562,6 +585,20 @@ class Unit(PrettyPrinter,Indexed):
562
585
  """
563
586
  return [(self,1)]
564
587
 
588
+ @property
589
+ def base(self):
590
+ """read-only
591
+
592
+ Returns the base unit. That is we follow the chain of conversions
593
+ starting with this units `Conversion` instance until we get to a unit
594
+ that has no `Conversion` instance. If this unit has no `Conversion`
595
+ instance `self` is returned.
596
+ """
597
+ if self.conversion is None:
598
+ return self
599
+ self._convtree()
600
+ return self._gconv._unit
601
+
565
602
  @staticmethod
566
603
  def _cancel(a,b=[],asign=1,bsign=1):
567
604
  # Takes .units lists a**asign and b**bsign and attempts to convert
@@ -586,7 +623,7 @@ class Unit(PrettyPrinter,Indexed):
586
623
  b[f] += v
587
624
  converted = True
588
625
  break
589
- except NoUnitConversionFoundError:
626
+ except IncompatibleUnitsError:
590
627
  pass
591
628
  if not converted:
592
629
  b[k] = v
@@ -770,7 +807,7 @@ class Unit(PrettyPrinter,Indexed):
770
807
  if aconv:
771
808
  return (self.convert(a,bunit) % b,bunit)
772
809
  return (a % bunit.convert(b,self),self)
773
- except NoUnitConversionFoundError:
810
+ except IncompatibleUnitsError:
774
811
  raise IncompatibleUnitsError('a quantity with unit ' + self.tostring() + ' may not be added to a quantity with unit ' + bunit.tostring())
775
812
 
776
813
  def _rmod(self,a,bunit,b,aconv):
@@ -784,7 +821,7 @@ class Unit(PrettyPrinter,Indexed):
784
821
  if aconv:
785
822
  return (b % self.convert(a,bunit),bunit)
786
823
  return (bunit.convert(b,self) % a,self)
787
- except NoUnitConversionFoundError:
824
+ except IncompatibleUnitsError:
788
825
  raise IncompatibleUnitsError('a quantity with unit ' + self.tostring() + ' may not be added to a quantity with unit ' + bunit.tostring())
789
826
 
790
827
  def _cpow(self,v):
@@ -801,30 +838,12 @@ class Unit(PrettyPrinter,Indexed):
801
838
  if bunit is not one:
802
839
  try:
803
840
  b = bunit.convert(b,one)
804
- except NoUnitConversionFoundError:
841
+ except IncompatibleUnitsError:
805
842
  raise IncompatibleUnitsError('the exponent is not dimensionless')
806
843
 
807
844
  un,c = self._cpow(b)
808
845
  un = _CompositeUnit(un)
809
846
  return ((a**b)*c,un)
810
- #bb = float(b)
811
- #if bb != b:
812
- #try:
813
- #a = self.convert(a,one)
814
- #except NoUnitConversionFoundError:
815
- #raise IncompatibleUnitsError('a gummy that is not dimensonless may not be raised to a power with an uncertainty')
816
- #return (a**b,one)
817
- #b = bb
818
- #if int(b) == b:
819
- #b = int(b)
820
-
821
- #if b == 0:
822
- #return (a**0,one)
823
-
824
- #un,c = self._cpow(b)
825
- #un = _CompositeUnit(un)
826
-
827
- #return ((a**b)*c,un)
828
847
 
829
848
  def _rpow(self,a,bunit,b,aconv):
830
849
  if isinstance(b,Unit):
@@ -834,28 +853,11 @@ class Unit(PrettyPrinter,Indexed):
834
853
  if self is not one:
835
854
  try:
836
855
  a = self.convert(a,one)
837
- except NoUnitConversionFoundError:
856
+ except IncompatibleUnitsError:
838
857
  raise IncompatibleUnitsError('the exponent is not dimensionless')
839
858
  un,c = bunit._cpow(a)
840
859
  un = _CompositeUnit(un)
841
860
  return ((b**a)*c,un)
842
- #aa = float(a)
843
- #if aa != a:
844
- #try:
845
- #b = self.convert(b,one)
846
- #except NoUnitConversionFoundError:
847
- #raise IncompatibleUnitsError('a gummy that is not dimensonless may not be raised to a power with an uncertainty')
848
- #return (b**a,one)
849
- #a = float(a)
850
- #if int(a) == a:
851
- #va = int(a)
852
-
853
- #if a == 0:
854
- #return (b**0,one)
855
-
856
- #un,c = _CompositeUnit(bunit._cpow(a))
857
-
858
- #return ((b**a)*c,un)
859
861
 
860
862
  def _ufunc(self,func,*args,**kwds):
861
863
  if self.linear:
@@ -919,11 +921,11 @@ class Unit(PrettyPrinter,Indexed):
919
921
  return _CompositeUnit(self.units + vi)
920
922
 
921
923
  def __rtruediv__(self,v):
922
- return Quantity._make(1/v,unit=self)
923
-
924
924
  if v is one or v == 1:
925
925
  return _CompositeUnit([(e[0],-e[1]) for e in self.units])
926
926
 
927
+ return Quantity._make(v,unit=self**-1)
928
+
927
929
  def __pow__(self,v):
928
930
  if v == -1:
929
931
  return _CompositeUnit([(e[0],-e[1]) for e in self.units])
@@ -939,42 +941,6 @@ class Unit(PrettyPrinter,Indexed):
939
941
 
940
942
  return _CompositeUnit([(e[0],v*e[1]) for e in self.units])
941
943
 
942
- # Define comparison operators and a hash function so, for the
943
- # _CompositeUnit subclass we can sort the Unit order and store them as keys
944
- # in a unique OrderedDict.
945
- def __lt__(self, b):
946
- if self.order == b.order:
947
- return self.name < b.name
948
- return self.order < b.order
949
-
950
- def __le__(self, b):
951
- if self.__eq__(b):
952
- return True
953
- if self.order == b.order:
954
- return self.name < b.name
955
- return self.order < b.order
956
-
957
- def __eq__(self, b):
958
- return (self is b)
959
-
960
- def __ge__(self, b):
961
- if self.__eq__(b):
962
- return True
963
- if self.order == b.order:
964
- return self.name > b.name
965
- return self.order > b.order
966
-
967
- def __gt__(self, b):
968
- if self.order == b.order:
969
- return self.name > b.name
970
- return self.order > b.order
971
-
972
- def __ne__(self, b):
973
- return not (self is b)
974
-
975
- def __hash__(self):
976
- return id(self)
977
-
978
944
 
979
945
  class _CompositeUnit(Unit):
980
946
  # Instances of this class represent composite derived unit.
@@ -985,7 +951,7 @@ class _CompositeUnit(Unit):
985
951
 
986
952
  @staticmethod
987
953
  def _unicode_super(e):
988
- if np.modf(e)[0] == 0:
954
+ if int(e) == e:
989
955
  se = str(int(e))
990
956
  so = ''
991
957
  for s in se:
@@ -1012,10 +978,15 @@ class _CompositeUnit(Unit):
1012
978
  if s == '9':
1013
979
  so += '\u2079'
1014
980
  return so
1015
- return '**' + '{:.2f}'.format(e).rstrip('0')
981
+ return '**' + _CompositeUnit._format_sscript(e,parenth=True)
1016
982
 
1017
983
  @staticmethod
1018
- def _format_sscript(e):
984
+ def _format_sscript(e,parenth=False):
985
+ if isinstance(e,Rational) and not isinstance(e,Integral):
986
+ ret = str(e.numerator) + '/' + str(e.denominator)
987
+ if parenth:
988
+ ret = '(' + ret + ')'
989
+ return ret
1019
990
  mf = np.modf(e)
1020
991
  if mf[0] == 0:
1021
992
  return '{:.0f}'.format(e)
@@ -1023,9 +994,7 @@ class _CompositeUnit(Unit):
1023
994
  return '{:.0f}'.format(2*e) + '/2'
1024
995
  if mf[0] == 0.25:
1025
996
  return '{:.0f}'.format(4*e) + '/4'
1026
- if np.modf(3*e)[0] < 0.01:
1027
- return '{:.0f}'.format(3*e) + '/3'
1028
- return '{:.2f}'.format(e).rstrip('0')
997
+ return str(e).rstrip('0.')
1029
998
 
1030
999
  @staticmethod
1031
1000
  def _iaddsym(txt,s,e,fmt,f):
@@ -1057,7 +1026,7 @@ class _CompositeUnit(Unit):
1057
1026
  txt += '*'
1058
1027
  txt += s
1059
1028
  if e != 1:
1060
- txt += '**' + '{:.2f}'.format(e).rstrip('0').rstrip('.')
1029
+ txt += '**' + _CompositeUnit._format_sscript(e,parenth=True)
1061
1030
  else:
1062
1031
  if txt != '':
1063
1032
  txt += ' '
@@ -1071,7 +1040,10 @@ class _CompositeUnit(Unit):
1071
1040
  return _CompositeUnit._iaddsym(txt,u[0].tostring(fmt=fmt),u[1],fmt,f)
1072
1041
 
1073
1042
  def __new__(cls,ul):
1074
- ul = list(ul)
1043
+
1044
+ # ul is a list of tupels in each tuple a (non _CompositeUnit) Unit
1045
+ # instance followed by an exponent.
1046
+ ul = [u for u in ul if u[0] is not one and u[1] != 0]
1075
1047
 
1076
1048
  if len(ul) == 0:
1077
1049
  return one
@@ -1079,7 +1051,7 @@ class _CompositeUnit(Unit):
1079
1051
  if len(ul) == 1 and ul[0][1] == 1:
1080
1052
  return ul[0][0]
1081
1053
 
1082
- ul = sorted(ul,key = lambda x: (x[0],-x[1]))
1054
+ ul = sorted(ul,key = lambda x: (x[0].order,-x[1]))
1083
1055
 
1084
1056
  uid = tuple([(id(u[0]),u[1]) for u in ul])
1085
1057
  ret = _CompositeUnit.living_units.get(uid)
@@ -1262,28 +1234,9 @@ class _CompositeUnit(Unit):
1262
1234
  return ret
1263
1235
 
1264
1236
  def _find_conv(self):
1265
- c = None
1266
- cun = {}
1267
1237
  cg = None
1268
1238
  cgun = {}
1269
1239
  for un,e in self._units:
1270
- if un.conversion is None:
1271
- if un in cun:
1272
- cun[un] += e
1273
- else:
1274
- cun[un] = e
1275
- else:
1276
- p = un.conversion.pow(e)
1277
- if c is None:
1278
- c = p
1279
- else:
1280
- c = c.chain(p)
1281
- for t in p.unit.units:
1282
- if t[0] in cun:
1283
- cun[t[0]] += t[1]
1284
- else:
1285
- cun[t[0]] = t[1]
1286
-
1287
1240
  un._convtree()
1288
1241
 
1289
1242
  if un._gconv is None:
@@ -1296,27 +1249,15 @@ class _CompositeUnit(Unit):
1296
1249
  if cg is None:
1297
1250
  cg = p
1298
1251
  else:
1299
- cg = c.chain(p)
1252
+ cg = cg.chain(p)
1300
1253
  for t in p.unit.units:
1301
1254
  if t[0] in cgun:
1302
1255
  cgun[t[0]] += t[1]
1303
1256
  else:
1304
1257
  cgun[t[0]] = t[1]
1305
1258
 
1306
- cun = [(k,v) for k,v in cun.items() if v != 0]
1307
1259
  cgun = [(k,v) for k,v in cgun.items() if v != 0]
1308
1260
 
1309
- cun = _CompositeUnit(cun)
1310
- if cun is not self:
1311
- if c is None:
1312
- c = Conversion(cun,1)
1313
- else:
1314
- c._unit = cun
1315
- c.parent = self
1316
- self._conversion = c
1317
- else:
1318
- self._conversion = None
1319
-
1320
1261
  cgun = _CompositeUnit(cgun)
1321
1262
  if cgun is not self:
1322
1263
  if cg is None:
@@ -1324,9 +1265,9 @@ class _CompositeUnit(Unit):
1324
1265
  else:
1325
1266
  cg._unit = cgun
1326
1267
  cg.parent = self
1327
- self._gconv = cg
1268
+ self._gconv = self._conversion = cg
1328
1269
  else:
1329
- self._gconv = None
1270
+ self._gconv = self._conversion = None
1330
1271
 
1331
1272
  self._have_conversion = True
1332
1273
 
@@ -1368,17 +1309,6 @@ class _CompositeUnit(Unit):
1368
1309
  """
1369
1310
  sha = {a for a in self._aliases if Unit.unit('[' + a + ']',exception=False) is not self}
1370
1311
  return sha
1371
-
1372
- def _convtree(self):
1373
- if not self._have_conversion:
1374
- self._find_conv()
1375
- Unit._convtree(self)
1376
-
1377
- def _convs(self,d,c,h):
1378
- if not self._have_conversion:
1379
- self._find_conv()
1380
- Unit._convs(self,d,c,h)
1381
-
1382
1312
 
1383
1313
  class _One(Unit):
1384
1314
  """
@@ -1423,9 +1353,11 @@ class _One(Unit):
1423
1353
 
1424
1354
  with Unit._builtin():
1425
1355
  one = _One('1','',add_symbol=False)
1426
-
1427
1356
 
1428
- class Quantity(PrettyPrinter):
1357
+ class MetaQuantity(MetaPrettyPrinter,ABCMeta):
1358
+ pass
1359
+
1360
+ class Quantity(PrettyPrinter,Number,metaclass=MetaQuantity):
1429
1361
  """
1430
1362
  Instances of this class represent a quantity with a value and a unit.
1431
1363
  The behavior of Quantity instances under mathematical operations with
@@ -1445,7 +1377,7 @@ class Quantity(PrettyPrinter):
1445
1377
  Parameters
1446
1378
  ----------
1447
1379
 
1448
- value: number like including `ummy`
1380
+ value: numeric (including `ummy`)
1449
1381
  the value of the Quantity
1450
1382
 
1451
1383
  unit: `str` or `Unit`
@@ -1486,6 +1418,10 @@ class Quantity(PrettyPrinter):
1486
1418
  return unit
1487
1419
 
1488
1420
  def __init__(self,value,unit=one):
1421
+ try:
1422
+ hash(value)
1423
+ except TypeError:
1424
+ raise TypeError('value is unhashable')
1489
1425
  self._value = value
1490
1426
  if isinstance(unit,Unit):
1491
1427
  self._unit = unit
@@ -1606,6 +1542,12 @@ class Quantity(PrettyPrinter):
1606
1542
  return c
1607
1543
 
1608
1544
  def tostring(self,fmt=None,**kwds):
1545
+ """
1546
+ tostring(fmt='unicode')
1547
+
1548
+ returns a string representation of the Quantity. fmt may be "unicode",
1549
+ "html","latex" or "ascii". The default is "unicode".
1550
+ """
1609
1551
  unit = self._unit.tostring(fmt=fmt,**kwds)
1610
1552
  unit = self._add_unit_sp(fmt,unit)
1611
1553
  if isinstance(self.value,PrettyPrinter):
@@ -1615,6 +1557,12 @@ class Quantity(PrettyPrinter):
1615
1557
  return value + unit
1616
1558
 
1617
1559
  def copy(self,tofloat=False):
1560
+ """
1561
+ copy(tofloat=False)
1562
+
1563
+ returns a copy of self. If tofloat is True, the self.value will be
1564
+ converted to float. The default is False.
1565
+ """
1618
1566
  if tofloat:
1619
1567
  try:
1620
1568
  return type(self)(self.value.tofloat(),unit=self.unit)
@@ -1627,11 +1575,24 @@ class Quantity(PrettyPrinter):
1627
1575
  return type(self)(self.value,self.unit)
1628
1576
 
1629
1577
  def tofloat(self):
1578
+ """
1579
+ returns a copy of self with value float(self.value) equivalent to
1580
+ copy(tofloat=True)
1581
+ """
1630
1582
  return self.copy(tofloat=True)
1631
1583
 
1632
1584
  def totuple(self):
1585
+ """
1586
+ returns the tuple (self.value,self.unit)
1587
+ """
1633
1588
  return (self.value,self.unit)
1634
1589
 
1590
+ def tobaseunit(self):
1591
+ """
1592
+ Returns a Quantity equal to self converted to unit self.unit.base
1593
+ """
1594
+ return self.convert(self.unit.base)
1595
+
1635
1596
  def splonk(self):
1636
1597
  """
1637
1598
  returns self.value if self.unit is one else returns self
@@ -1720,21 +1681,27 @@ class Quantity(PrettyPrinter):
1720
1681
  if self._unit is not v.unit:
1721
1682
  try:
1722
1683
  v = v.convert(self.unit)
1723
- return f(self.value,v.value)
1724
- except:
1725
- raise IncompatibleUnitsError('Quantities with incompatible units cannot be compared ')
1684
+ except IncompatibleUnitsError:
1685
+ raise IncompatibleUnitsError('Quantities with incompatible units cannot be ordered')
1686
+ return f(self.value,v.value)
1726
1687
  else:
1727
1688
  try:
1728
1689
  s = self.convert(one)
1729
1690
  return f(s.value,v)
1730
- except:
1731
- raise IncompatibleUnitsError('Quantities with incompatible units cannot be compared')
1691
+ except IncompatibleUnitsError:
1692
+ raise IncompatibleUnitsError('Quantities with incompatible units cannot be ordered')
1732
1693
 
1733
1694
  def __eq__(self, v):
1734
- return self._cmp(v,lambda x,y: x == y)
1695
+ try:
1696
+ return self._cmp(v,lambda x,y: x == y)
1697
+ except IncompatibleUnitsError:
1698
+ return False
1735
1699
 
1736
1700
  def __ne__(self, v):
1737
- return self._cmp(v,lambda x,y: x != y)
1701
+ try:
1702
+ return self._cmp(v,lambda x,y: x != y)
1703
+ except IncompatibleUnitsError:
1704
+ return True
1738
1705
 
1739
1706
  def __lt__(self, v):
1740
1707
  return self._cmp(v,lambda x,y: x < y)
@@ -1767,7 +1734,7 @@ class Quantity(PrettyPrinter):
1767
1734
  if self.splonk_func_ret:
1768
1735
  return func(*x,**kwds)
1769
1736
  return make(func(*x,**kwds))
1770
- except NoUnitConversionFoundError:
1737
+ except IncompatibleUnitsError:
1771
1738
  raise NotImplementedError()
1772
1739
 
1773
1740
  def __array_ufunc__(self,ufunc,method,*args,**kwds):
@@ -1780,22 +1747,55 @@ class Quantity(PrettyPrinter):
1780
1747
  def __array_function__(self,func,method,*args,**kwds):
1781
1748
  return self._ufunc(func,*args,**kwds)
1782
1749
 
1783
- #def __float__(self):
1784
- #return float(self.value)
1785
-
1786
- #def __int__(self):
1787
- #return int(self.value)
1750
+ def __float__(self):
1751
+ s = self.convert(1)
1752
+ return float(s.value)
1788
1753
 
1789
- #def __complex__(self):
1790
- #return complex(self.value)
1754
+ def __complex__(self):
1755
+ s = self.convert(1)
1756
+ return complex(s.value)
1757
+
1758
+ def __bool__(self):
1759
+ return self != 0
1791
1760
 
1792
1761
  @property
1793
1762
  def real(self):
1794
- return self.copy()
1795
-
1763
+ if self.unit.linear:
1764
+ s = self
1765
+ else:
1766
+ s = self.tobaseunit()
1767
+
1768
+ try:
1769
+ v = s.value.real
1770
+ except:
1771
+ v = float(s.value)
1772
+ return type(self)(v,unit=s.unit)
1773
+
1796
1774
  @property
1797
1775
  def imag(self):
1798
- return type(self)(0,unit=self.unit)
1776
+ if self.unit.linear:
1777
+ s = self
1778
+ else:
1779
+ s = self.tobaseunit()
1780
+
1781
+ try:
1782
+ v = s.value.imag
1783
+ except:
1784
+ v = 0
1785
+ return type(self)(v,unit=s.unit)
1786
+
1787
+ def __hash__(self):
1788
+ r = self.tobaseunit().totuple()
1789
+ if r[1] is one:
1790
+ return hash(r[0])
1791
+ return hash(r)
1792
+
1793
+ def conjugate(self):
1794
+ try:
1795
+ v = self.value.conjugate()
1796
+ except:
1797
+ v = complex(float(self.value))
1798
+ return type(self)(v,unit=self.unit)
1799
1799
 
1800
1800
 
1801
1801
  class QuantityArray(Quantity):
@@ -1849,5 +1849,110 @@ class QuantityArray(Quantity):
1849
1849
 
1850
1850
 
1851
1851
  Quantity._arraytype = QuantityArray
1852
+
1852
1853
 
1854
+ class MFraction(Fraction):
1855
+ """
1856
+ A fraction.Fraction sub-class that works with Decimal and mpmath.mpf objects
1857
+ """
1858
+
1859
+ @classmethod
1860
+ def fromnum(cls,x):
1861
+ return MFraction(Fraction(x))
1862
+
1863
+ def __new__(cls, *args, **kwargs):
1864
+ if len(args) == 1 and not (isinstance(args[0],str)
1865
+ or isinstance(args[0],Fraction)):
1866
+ return args[0]
1867
+ ret = super(MFraction, cls).__new__(cls, *args, **kwargs)
1868
+ if ret.denominator == 1:
1869
+ return ret.numerator
1870
+ return ret
1871
+
1872
+ def _mpmath_(self,p,r):
1873
+ return rational.mpq(self.numerator,self.denominator)
1874
+
1875
+ def todecimal(self):
1876
+ return Decimal(self.numerator)/Decimal(self.denominator)
1877
+
1878
+ def __add__(self,v):
1879
+ if isinstance(v,Decimal):
1880
+ return self.todecimal().__add__(v)
1881
+ return MFraction(super().__add__(v))
1882
+
1883
+ def __radd__(self,v):
1884
+ if isinstance(v,Decimal):
1885
+ return self.todecimal().__radd__(v)
1886
+ return MFraction(super().__radd__(v))
1887
+
1888
+ def __sub__(self,v):
1889
+ if isinstance(v,Decimal):
1890
+ return self.todecimal().__sub__(v)
1891
+ return MFraction(super().__sub__(v))
1892
+
1893
+ def __rsub__(self,v):
1894
+ if isinstance(v,Decimal):
1895
+ return self.todecimal().__rsub__(v)
1896
+ return MFraction(super().__rsub__(v))
1897
+
1898
+ def __mul__(self,v):
1899
+ if isinstance(v,Decimal):
1900
+ return self.todecimal().__mul__(v)
1901
+ return MFraction(super().__mul__(v))
1902
+
1903
+ def __rmul__(self,v):
1904
+ if isinstance(v,Decimal):
1905
+ return self.todecimal().__rmul__(v)
1906
+ return MFraction(super().__rmul__(v))
1907
+
1908
+ def __truediv__(self,v):
1909
+ if isinstance(v,Decimal):
1910
+ return self.todecimal().__truediv__(v)
1911
+ return MFraction(super().__truediv__(v))
1912
+
1913
+ def __rtruediv__(self,v):
1914
+ if isinstance(v,Decimal):
1915
+ return self.todecimal().__rtruediv__(v)
1916
+ return MFraction(super().__rtruediv__(v))
1917
+
1918
+ def __pow__(self,v):
1919
+ if isinstance(v,Decimal):
1920
+ return self.todecimal().__pow__(v)
1921
+ return MFraction(super().__pow__(v))
1922
+
1923
+ def __rpow__(self,v):
1924
+ if isinstance(v,Decimal):
1925
+ return self.todecimal().__rpow__(v)
1926
+ if isinstance(v,Fraction):
1927
+ return MFraction(v).__pow__(self)
1928
+ return MFraction(super().__rpow__(v))
1929
+
1930
+ def __floordiv__(self,v):
1931
+ if isinstance(v,Decimal):
1932
+ return self.todecimal().__floordiv__(v)
1933
+ return MFraction(super().__floordiv__(v))
1934
+
1935
+ def __rfloordiv__(self,v):
1936
+ if isinstance(v,Decimal):
1937
+ return self.todecimal().__rfloordiv__(v)
1938
+ return MFraction(super().__rfloordiv__(v))
1939
+
1940
+ def __mod__(self,v):
1941
+ if isinstance(v,Decimal):
1942
+ return self.todecimal().__mod__(v)
1943
+ return MFraction(super().__mod__(v))
1944
+
1945
+ def __rmod__(self,v):
1946
+ if isinstance(v,Decimal):
1947
+ return self.todecimal().__rmod__(v)
1948
+ return MFraction(super().__rmod__(v))
1949
+
1950
+ def __abs__(self):
1951
+ return MFraction(super().__abs__())
1952
+
1953
+ def __neg__(self):
1954
+ return MFraction(super().__neg__())
1955
+
1956
+ def __pos__(self):
1957
+ return MFraction(super().__pos__())
1853
1958