This file is indexed.

/usr/share/pyshared/brian/fundamentalunits.py is in python-brian 1.4.1-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
# ----------------------------------------------------------------------------------
# Copyright ENS, INRIA, CNRS
# Contributors: Romain Brette (brette@di.ens.fr) and Dan Goodman (goodman@di.ens.fr)
# 
# Brian is a computer program whose purpose is to simulate models
# of biological neural networks.
# 
# This software is governed by the CeCILL license under French law and
# abiding by the rules of distribution of free software.  You can  use, 
# modify and/ or redistribute the software under the terms of the CeCILL
# license as circulated by CEA, CNRS and INRIA at the following URL
# "http://www.cecill.info". 
# 
# As a counterpart to the access to the source code and  rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty  and the software's author,  the holder of the
# economic rights,  and the successive licensors  have only  limited
# liability. 
# 
# In this respect, the user's attention is drawn to the risks associated
# with loading,  using,  modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean  that it is complicated to manipulate,  and  that  also
# therefore means  that it is reserved for developers  and  experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or 
# data to be ensured and,  more generally, to use and operate it in the 
# same conditions as regards security. 
# 
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL license and that you accept its terms.
# ----------------------------------------------------------------------------------
# 
"""Defines physical units and quantities

The standard way to use this class is as follows:

V = 3 * volt
I = 2 * amp
R=V/I
print R

will return

1.5 ohm

The following fundamental units are defined:

metre, kilogram, second, amp, kelvin, mole, candle

And these additional basic units:

radian, steradian, hertz, newton, pascal, joule, watt,
coulomb, volt, farad, ohm, siemens, weber, tesla, henry,
celsius, lumen, lux, becquerel, gray, sievert, katal,
gram, gramme

Additionally, it includes all scaled versions of these
units using the standard SI prefixes (see the documentation
for the Unit class for more details), e.g. uamp,
mmetre, etc. It also includes the second and third powers
of each of these units, e.g. mvolt2 = mvolt*mvolt,
metre3 = metre**3, etc.

The module also defines these classes:

-- Dimension
        Stores the physical dimensions (length, mass, etc.)
-- DimensionMismatchError
        Exception raised if you try to add inconsistent units,
        etc.
-- Quantity
        The class of a value with a unit
-- Unit
        The class of the defined units like mvolt, etc.
-- UnitRegistry
        Stores 'known' units for printing

These functions:

-- get_dimensions(x)
        Returns the dimensions of a quantity or number x
-- have_same_dimensions(x,y)
        Tests if x and y have the same dimensions
-- is_dimensionless(x)
        Tests if x is dimensionless
-- display_in_unit(x,u)
        Displays quantity x in unit u
-- register_new_unit(u)
        Add a new unit u to the list of 'known' units for
        printing purposes
-- get_unit(x)
        Returns the fundamental unit of value x if one is known, or
        simply the value 1 with dimensions of x if none is known

And this decorator for function argument checking:

-- check_units(...)

If you want to use shorter named units, import the stdunits
module, which defines things like mV for mvolt, etc. They
are not included by default in the units module because of
the potential for variable name clashes.
"""

__all__ = [
    'Dimension', 'Scale', 'DimensionMismatchError',
    'get_dimensions', 'is_dimensionless', 'have_same_dimensions',
    'display_in_unit', 'Quantity', 'Unit', 'register_new_unit',
    'check_units', 'is_scalar_type', 'get_unit', 'get_unit_fast',
    'scalar_representation', 'quantity_with_dimensions',
    ]

from brian_unit_prefs import bup
from operator import isNumberType, isSequenceType
from itertools import izip
import math, numpy
from utils.approximatecomparisons import *
import types
from functools import *
import sys
# Note that the decorator module below is used to provide signature preserving
# decorators, but it has the unfortunate side effect of messing up the tracebacks
# because it uses eval, so we only use it when we want to generate documentation,
# i.e. if 'sphinx' or 'docutils' or 'epydoc' are loaded.
# TODO: this has stopped working anyway, so it's now removed
#try:
#    import decorator
#    use_decorator = 'sphinx' in sys.modules or 'docutils' in sys.modules or 'epydoc' in sys.modules
#except:
#    use_decorator = False

# SI dimensions (see table at end of file) and various descriptions,
# each description maps to an index i, and the power of each dimension
# is stored in the variable dims[i]
_di = { "Length":0, "length": 0, "metre":0, "metres":0, "metre": 0, "metres":0, "metre":0, "metres":0, "metre": 0, "metres":0, "m": 0, \
       "Mass":1, "mass": 1, "kilogram":1, "kilograms":1, "kilogram": 1, "kilograms":1, "kg": 1, \
       "Time":2, "time": 2, "second":2, "seconds":2, "second": 2, "seconds":2, "s": 2, \
       "Electric Current":3, "Electric Current":3, "electric current": 3, "Current":3, "current":3, "ampere":3, "amperes":3, "ampere": 3, "amperes":3, "A": 3, \
       "Temperature":4, "temperature": 4, "kelvin":4, "kelvins":4, "kelvin": 4, "kelvins":4, "K": 4, \
       "Quantity of Substance":5, "Quantity of substance": 5, "quantity of substance": 5, "Substance":5, "substance":5, "mole":5, "moles":5, "mole": 5, "moles":5, "mol": 5, \
       "Luminosity":6, "luminosity": 6, "candle":6, "candles":6, "candle": 6, "candles":6, "cd": 6 }
_ilabel = ["m", "kg", "s", "A", "K", "mol", "cd"]
# The same labels with the names used for constructing them in Python code
_iclass_label = ["metre", "kilogram", "second", "amp", "kelvin", "mole", "candle"]
# SI unit _prefixes, see table at end of file
_siprefixes = {"y":1e-24, "z":1e-21, "a":1e-18, "f":1e-15, "p":1e-12, "n":1e-9, "u":1e-6, "m":1e-3, "c":1e-2, "d":1e-1, \
            "":1, \
            "da":1e1, "h":1e2, "k":1e3, "M":1e6, "G":1e9, "T":1e12, "P":1e15, "E":1e18, "Z":1e21, "Y":1e24}


class Dimension(object):
    '''Stores the indices of the 7 basic SI unit dimension (length, mass, etc.)
    
    Provides a subset of arithmetic operations appropriate to dimensions:
    multiplication, division and powers, and equality testing.
    
    Methods:
    
    is_dimensionless() returns Boolean value
    
    Notes:
    
    Most users shouldn't use this class directly, but instead write things
    like:
    
    x = 3 * mvolt, etc.
    '''
    __slots__ = ["_dims"]
    #### INITIALISATION ####
    def __init__(self, *args, **keywords):
        """Initialise Dimension object with a vector or keywords
        
        Call as Dimension(list), Dimension(keywords) or Dimension(dim)
        
        list -- a list with the indices of the 7 elements of an SI dimension
        keywords -- a sequence of keyword=value pairs where the keywords are
          the names of the SI dimensions, or the standard unit
        dim -- a dimension object to copy
        
        Examples:
        
        The following are all definitions of the dimensions of force
        
        Dimension(length=1, mass=1, time=-2)
        Dimension(m=1, kg=1, s=-2)
        Dimension([1,1,-2,0,0,0,0])
        
        The 7 units are (in order):
        
        Length, Mass, Time, Electric Current, Temperature,
        Quantity of Substance, Luminosity
        
        and can be referred to either by these names or their SI unit names,
        e.g. length, metre, and m all refer to the same thing here.
        """
        if len(args):
            if isSequenceType(args[0]) and len(args[0]) == 7:
                # initialisation by list
                self._dims = args[0]
            elif isinstance(args[0], Dimension):
                # initialisation by another dimension object
                self._dims = args[0]._dims
        else:
            # initialisation by keywords
            self._dims = [0, 0, 0, 0, 0, 0, 0]
            for k in keywords.keys():
                # _di stores the index of the dimension with name 'k'
                self._dims[_di[k]] = keywords[k]
    #### METHODS ####
    def get_dimension(self, d):
        """Returns the list of dimension indices.
        
        See documentation for __init__.
        """
        return self._dims[_di[d]]

    def set_dimension(self, d, value):
        """Sets the list of dimension indices.
        
        See documentation for __init__.
        """
        self._dims[_di[d]] = value

    def is_dimensionless(self):
        """Tells you whether the object is dimensionless."""
        return sum([x == 0 for x in self._dims]) == 7
    
    #### REPRESENTATION ####    
    def _str_representation(self, python_code=False):
        """String representation in basic SI units, or 1 for dimensionless.
        Use `python_code=False` for display purposes and `True` for valid
        Python code."""
        
        if python_code:
            power_operator = " ** "
        else:
            power_operator = "^"
        
        parts = []
        for i in range(len(self._dims)):
            if self._dims[i]:
                if python_code:
                    s = _iclass_label[i]
                else:
                    s = _ilabel[i]
                if self._dims[i] != 1:
                    s += power_operator + str(self._dims[i])
                parts.append(s)
        if python_code:
            s = " * ".join(parts)
            if not len(s):
                return "%s()" % self.__class__.__name__
        else:
            s = " ".join(parts)
            if not len(s):
                return "1"
        return s.strip()
    
    def __repr__(self):
        return self._str_representation(python_code=True)

    def __str__(self):
        return self._str_representation(python_code=False)
    
    #### ARITHMETIC ####
    # Note that none of the dimension arithmetic objects do sanity checking
    # on their inputs, although most will throw an exception if you pass the
    # wrong sort of input
    def __mul__(self, value):
        return Dimension([x + y for x, y in izip(self._dims, value._dims)])

    def __div__(self, value):
        return Dimension([x - y for x, y in izip(self._dims, value._dims)])

    def __truediv__(self, value):
        return self.__div__(value)

    def __pow__(self, value):
        return Dimension([x * value for x in self._dims])

    def __imul__(self, value):
        self._dims = [x + y for x, y in izip(self._dims, value._dims)]
        return self

    def __idiv__(self, value):
        self._dims = [x - y for x, y in izip(self._dims, value._dims)]
        return self

    def __itruediv__(self, value):
        return self.__idiv__(value)

    def __ipow__(self, value):
        self._dims = [x * value for x in self._dims]
        return self
    #### COMPARISON ####
    def __eq__(self, value):
        #return sum([x==y for x,y in izip(self._dims,value._dims)])==7
        return sum([is_within_absolute_tolerance(x, y) for x, y in izip(self._dims, value._dims)]) == 7

    def __ne__(self, value):
        return not self.__eq__(value)
    #### MAKE DIMENSION PICKABLE ####
    def __getstate__(self):
        return self._dims

    def __setstate__(self, state):
        self._dims = state


class Scale(object):
    """Stores the scale factor for each SI dimension.
    
    Probably would only be very rarely used by a user, but might
    conceivably be useful in certain circumstances.
    
    Methods:
    
    -- Initialisation by list of keywords
    -- scale_factor(dim) gives the overall scaling for a value in
       dimension dim
    -- unit_representation(dim) gives a string representation of
       the unit defined by the Scale object applied to dimension
       dim 
    """
    __slots__ = ["scale"]

    def __init__(self, *args, **keywords):
        """Initialise by list of scales or keywords, see Dimension documentation
        
        e.g. Scale(length="m", time="u") =
             Scale(["m","","u","","","",""])
        corresponds to measuring the unit of length at the milli scale
        and the unit of time at the u scale.
        """
        self.scale = [ "", "", "", "", "", "", "" ]
        for k in keywords:
            self.scale[_di[k]] = keywords[k]

    def scale_factor(self, dim):
        """Returns the scaling factor for dimension dim
        
        For example, if the scale factor of length is milli, and the
        dimensions of dim are length^2 then the scale factor will be
        0.001^2.
        """
        sf = 1
        for s, i in izip(self.scale, dim._dims):
            if i: sf *= _siprefixes[s] ** i
        return sf

    def unit_representation(self, dim):
        """Returns a representation of the dimension dim at this scale
        
        For example, if the scale factor of length is milli, and the
        dimensions of dim are length^2 then this will return mm^2.
        """
        s = ""
        for i in range(7):
            if dim._dims[i]:
                s += self.scale[i] + _ilabel[i]
                if dim._dims[i] != 1:
                    s += "^" + str(dim._dims[i])
                s += " "
        return s.strip()


class DimensionMismatchError(Exception):
    """Exception class for attempted operations with inconsistent dimensions
    
    For example, ``3*mvolt + 2*amp`` raises this exception. The purpose of this
    class is to help catch errors based on incorrect units. The exception will
    print a representation of the dimensions of the two inconsistent objects
    that were operated on. If you want to check for inconsistent units in your
    code, do something like::
    
        try:
            ...
            your code here
            ...
        except DimensionMismatchError, inst:
            ...
            cleanup code here, e.g.
            print "Found dimension mismatch, details:", inst
            ...
    """
    def __init__(self, description, *dims):
        """Raise as DimensionMismatchError(desc,dim1,dim2,...)
        
        desc -- a description of the type of operation being performed, e.g.
                Addition, Multiplication, etc.
        dim -- the dimensions of the objects involved in the operation, any
               number of them is possible
        """
        # Call the base class constructor to make Exception pickable, see:
        # http://bugs.python.org/issue1692335
        Exception.__init__(self, description, *dims)
        self._dims = dims
        self.desc = description

    def __repr__(self):
        return self.__str__()

    def __str__(self):
        s = self.desc + ", dimensions were "
        for d in self._dims:
            s += "(" + str(d) + ") "
        return s

def is_scalar_type(obj):
    """Tells you if the object is a 1d number type
    
    This function is mostly used internally by the module for
    argument type checking. A scalar type can be considered
    a dimensionless quantity (see the documentation for
    Quantity for more information).
    """
    return isNumberType(obj) and not isSequenceType(obj)

def get_dimensions(obj):
    """Returns the dimensions of any object that has them.
    
    Slightly more general than obj.get_dimensions() because it will return
    a new dimensionless Dimension() object if the object is of number type
    but not a Quantity (e.g. a float or int).
    """
    if isNumberType(obj) and not isinstance(obj, Quantity): return Dimension()
    return obj.get_dimensions()


def is_dimensionless(obj):
    """Tests if a scalar value is dimensionless or not, returns a ``bool``.
    
    Note that the syntax may change in later releases of Brian, with tighter
    integration of scalar and array valued quantities.
    """
    return get_dimensions(obj) == Dimension()


def have_same_dimensions(obj1, obj2):
    """Tests if two scalar values have the same dimensions, returns a ``bool``.
    
    Note that the syntax may change in later releases of Brian, with tighter
    integration of scalar and array valued quantities.
    """
    return get_dimensions(obj1) == get_dimensions(obj2)

def display_in_unit(x, u):
    """String representation of the object x in unit u.
    """
    if not have_same_dimensions(x, u):
        raise DimensionMismatchError("Non-matching unit for function display_in_unit", get_dimensions(x), get_dimensions(u))
    s = str(float(x / u)) + " "
    if not is_dimensionless(u):
        if isinstance(u, Unit):
            s += str(u)
        else:
            s += str(u.dim)
    return s.strip()

def quantity_with_dimensions(floatval, dims):
    return Quantity.with_dimensions(floatval, dims)


class Quantity(numpy.float64):
    """A number with an associated physical dimension.
    
    In most cases, it is not necessary to create a :class:`Quantity` object
    by hand, instead use the constant unit names ``second``, ``kilogram``,
    etc. The details of how :class:`Quantity` objects work is subject to
    change in future releases of Brian, as we plan to reimplement it
    in a more efficient manner, more tightly integrated with numpy. The
    following can be safely used:
    
    * :class:`Quantity`, this name will not change, and the usage
      ``isinstance(x,Quantity)`` should be safe.
    * The standard unit objects, ``second``, ``kilogram``, etc.
      documented in the main documentation will not be subject
      to change (as they are based on SI standardisation).
    * Scalar arithmetic will work with future implementations.
    """

    # This documentation is subject to change.
    """
    This is the main user class for the units module, although
    in most cases it is not necessary to initialise a new
    quantity by hand (see construction below for details).
    
    The Quantity class defines arithmetic operations which
    check for consistency of dimensions and raise the
    DimensionMismatchError exception if they are inconsistent.
    
    The class also defines default and other representations
    of a number for printing purposes.
    
    Typical usage:
    
    I = 3 * amp # I is a Quantity object
    R = 2 * ohm # same for R
    print I*R # displays "6 V"
    print (I*R).in_unit(mvolt) # displays "6000 mV"
    print (I*R)/mvolt # displays "6000"
    x = I + R # raises DimensionMismatchError
    
    See the documentation on the Unit class for more details
    about the available unit names like mvolt, etc.
    
    Casting rules:
    
    The three rules that define the casting operations for
    Quantity object are:
    
    1. Quantity op Quantity = Quantity
        - Performs dimension checking if appropriate
    2. Scalar op Quantity = Quantity 
        - Assumes that the scalar is dimensionless
    3. other op Quantity = other
        - The Quantity object is downcast to a float
    
    Scalar types are 1 dimensional number types, including float, int, etc.
    but not array.
    
    The Quantity class is a derived class of float, so many other operations
    will also downcast to float. For example, sin(x) where x is a quantity
    will return sin(float(x)) without doing any dimension checking.

    Construction details:
    
    x = Quantity(value) returns a dimensionless object, you can then
        set the dimensions via x.set_dimensions(dim)
        
    x = Quantity.with_dimensions(value,dim) returns an object with
        floating point value value, and dimensions dim, see the
        documentation for Quantity.with_dimensions(...) for more.

    Static constructors:
    
    -- with_dimensions(dim)
    -- with_dimensions(keywords...)
    
    Methods:
    
    -- get_dimensions() return Dimension
    -- set_dimensions(dim)
    -- is_dimensionless() return boolean
    -- at_scale(scale) return string
    -- has_same_dimensions(other) return boolean
    -- in_unit(unit) return string
    -- in_best_unit() return string
    """
    __slots__ = ["dim"]
    #### CONSTRUCTION ####
    def __init__(self, value):
        """Initialises as dimensionless
        """
        super(Quantity, self).__init__()
        self.dim = Dimension()
    @staticmethod
    def with_dimensions(value, *args, **keywords):
        """Static method to create a Quantity object with dimensions
        
        Use as Quantity.with_dimensions(value,dim),
               Quantity.with_dimensions(value,dimlist) or
               Quantity.with_dimensions(value,keywords...)
               
        -- value is a float or other scalar type
        -- dim is a dimension object
        -- dimlist, keywords (see the Dimension constructor)
        
        e.g.
        
        x = Quantity.with_dimensions(2,Dimension(length=1))
        x = Quantity.with_dimensions(2,length=1)
        x = 2 * metre
        
        all define the same object.
        """
        x = Quantity(value)
        if len(args) and isinstance(args[0], Dimension):
            x.set_dimensions(args[0])
        else:
            x.set_dimensions(Dimension(*args, **keywords))
        return x
    #### METHODS ####
    def get_dimensions(self):
        """Returns the dimensions of this object
        """
        return self.dim

    def set_dimensions(self, dim):
        """Set the dimensions of this object
        """
        self.dim = dim

    def is_dimensionless(self):
        """Tells you whether this is a dimensionless object
        """
        return self.dim.is_dimensionless()

    def at_scale(self, scale):
        """Returns a string representation at given scale
        """
        return str(float(self) / scale.scale_factor(self.dim)) + " " + scale.unit_representation(self.dim)

    def has_same_dimensions(self, other):
        """Tells you if this object has the same dimensions as another.
        """
        return self.dim == get_dimensions(other)

    def in_unit(self, u, python_code=False):
        """String representation of the object in unit `u`.
        If `python_code` is `True`, this will return valid python code, i.e. a
        string like `5.0 * um ** 2`instead of `5.0 um^2`  
        """
        if not self.has_same_dimensions(u):
            raise DimensionMismatchError("Non-matching unit for method in_unit", self.dim, u.dim)
        s = str(float(self / u)) + " "
        if not u.is_dimensionless():
            if isinstance(u, Unit):
                if python_code:                    
                    s += '* ' + repr(u)
                else:
                    s += str(u)
            else:
                if python_code:
                    s += "* " + repr(u.dim)
                else:
                    s += str(u.dim)
        elif python_code == True:  # A quantity without unit is not recognisable otherwise
            return '%s(%s)' % (self.__class__.__name__, s.strip())
        return s.strip()

    def in_best_unit(self, python_code=False, *regs):
        """String representation of the object in the 'best unit'
        
        If `python_code` is `True`, this will return valid python code, i.e. a
        string like `5.0 * um ** 2`instead of `5.0 um^2`  
        
        For more information, see the documentation for the UnitRegistry
        class. Essentially, this looks at the value of the quantity for
        all 'known' matching units (e.g. mvolt, namp, etc.) and returns
        the one with the most compact representation. Standard units are
        built in, but you can register new units for consideration. 
        """
        u = _get_best_unit(self, *regs)
        return self.in_unit(u, python_code)
    #### METHODS (NUMERICAL) ####
    def sqrt(self):
        return self ** 0.5

    def log(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.log(float(self)), self.dim)
        raise DimensionMismatchError('log', self.dim)

    def exp(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.exp(float(self)), self.dim)
        raise DimensionMismatchError('exp', self.dim)

    def sin(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.sin(float(self)), self.dim)
        raise DimensionMismatchError('sin', self.dim)

    def cos(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.cos(float(self)), self.dim)
        raise DimensionMismatchError('cos', self.dim)

    def tan(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.tan(float(self)), self.dim)
        raise DimensionMismatchError('tan', self.dim)

    def asin(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.asin(float(self)), self.dim)
        raise DimensionMismatchError('asin', self.dim)

    def acos(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.acos(float(self)), self.dim)
        raise DimensionMismatchError('acos', self.dim)

    def atan(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.atan(float(self)), self.dim)
        raise DimensionMismatchError('atan', self.dim)
    arcsin = asin
    arccos = cos
    arctan = tan

    def sinh(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.sinh(float(self)), self.dim)
        raise DimensionMismatchError('sinh', self.dim)

    def cosh(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.cosh(float(self)), self.dim)
        raise DimensionMismatchError('cosh', self.dim)

    def tanh(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(math.tanh(float(self)), self.dim)
        raise DimensionMismatchError('tanh', self.dim)

    def arcsinh(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(numpy.arcsinh(float(self)), self.dim)
        raise DimensionMismatchError('sinh', self.dim)

    def arccosh(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(numpy.arccosh(float(self)), self.dim)
        raise DimensionMismatchError('cosh', self.dim)

    def arctanh(self):
        if self.is_dimensionless():
            return Quantity.with_dimensions(numpy.arctanh(float(self)), self.dim)
        raise DimensionMismatchError('tanh', self.dim)
    #### REPRESENTATION ####
    def __repr__(self):
        return self.in_best_unit(python_code=True)

    def __str__(self):
        #s = super(Quantity,self).__str__()
        #if not self.is_dimensionless(): s += " " + str(self.dim)
        #return s
        return self.in_best_unit()
        #return str(float(self))+'*'+str(get_unit(self))
    #### ARITHMETIC ####
    # Arithmetic operations implement the following set of rules for
    # determining casting:
    # 1. Quantity op Quantity returns Quantity (and performs dimension checking if appropriate)
    # 2. Scalar op Quantity returns Quantity (and performs dimension checking assuming Scalar is dimensionless)
    # 3. other op Quantity returns other (Quantity is downcast to float)
    # Scalar types are those for which is_scalar_type() returns True, including float, int, long, complex but not array
    def __mul__(self, other):
        # This code, like all the other arithmetic code below, implements the casting rules
        # defined above.
        if isinstance(other, Quantity):
            return Quantity.with_dimensions(float(self) * float(other), self.dim * other.dim)
        elif is_scalar_type(other):
            return Quantity.with_dimensions(float(self) * other, self.dim)
        else:
            return NotImplemented
            #return super(Quantity,self).__mul__(other)

    def __rmul__(self, other):
        return self.__mul__(other)

    def __div__(self, other):
        if isinstance(other, Quantity):
            return Quantity.with_dimensions(float(self) / float(other), self.dim / other.dim)
        elif is_scalar_type(other):
            return Quantity.with_dimensions(float(self) / other, self.dim)
        else:
            return NotImplemented
            #return super(Quantity,self).__div__(other)

    def __truediv__(self, other):
        if isinstance(other, Quantity):
            return Quantity.with_dimensions(float(self) / float(other), self.dim / other.dim)
        elif is_scalar_type(other):
            return Quantity.with_dimensions(float(self) / other, self.dim)
        else:
            return NotImplemented
            #return super(Quantity,self).__truediv__(other)

    def __rdiv__(self, other):
        if isinstance(other, Quantity):
            return Quantity.with_dimensions(float(other) / float(self), other.dim / self.dim)
        elif is_scalar_type(other):
            return Quantity.with_dimensions(other / float(self), [-x for x in self.dim._dims])
        else:
            return NotImplemented
            #return super(Quantity,self).__rdiv__(other)

    def __rtruediv__(self, other):
        if isinstance(other, Quantity):
            return Quantity.with_dimensions(float(other) / float(self), other.dim / self.dim)
        elif is_scalar_type(other):
            return Quantity.with_dimensions(other / float(self), [-x for x in self.dim._dims])
        else:
            return NotImplemented
            #return super(Quantity,self).__rtruediv__(other)

    def __mod__(self, other):
        if isinstance(other, Quantity) or is_scalar_type(other):
            dim = get_dimensions(other)
            if dim == self.dim:
                return Quantity.with_dimensions(float(self) % float(other), self.dim)
            else: raise DimensionMismatchError("Addition", self.dim, dim)
        else:
            return NotImplemented
            #return super(Quantity,self).__add__(other)    

    def __add__(self, other):
        if isinstance(other, Quantity) or is_scalar_type(other):
            dim = get_dimensions(other)
            if dim == self.dim:
                return Quantity.with_dimensions(float(self) + float(other), self.dim)
            else: raise DimensionMismatchError("Addition", self.dim, dim)
        else:
            return NotImplemented
            #return super(Quantity,self).__add__(other)    

    def __radd__(self, other):
        return self.__add__(other)

    def __sub__(self, other):
        if isinstance(other, Quantity) or is_scalar_type(other):
            dim = get_dimensions(other)
            if dim == self.dim:
                return Quantity.with_dimensions(float(self) - float(other), self.dim)
            else: raise DimensionMismatchError("Subtraction", self.dim, dim)
        else:
            return NotImplemented
            #return super(Quantity,self).__sub__(other)

    def __rsub__(self, other):
        if isinstance(other, Quantity) or is_scalar_type(other):
            dim = get_dimensions(other)
            if dim == self.dim:
                return Quantity.with_dimensions(float(other) - float(self), self.dim)
            else: raise DimensionMismatchError("Subtraction(R)", self.dim, dim)
        else:
            return NotImplemented
            #return super(Quantity,self).__rsub__(other)

    def __pow__(self, other):
        if isinstance(other, Quantity):
            if other.is_dimensionless():
                # WARNING: because dimension consistency is checked by exact comparison of dimensions,
                # this may lead to unexpected behaviour (e.g. (x**2)**0.5 may not have the same dimensions as x)
                return Quantity.with_dimensions(float(self) ** float(other), self.dim ** float(other))
            else: raise DimensionMismatchError("Power", self.dim, other.dim)
        elif is_scalar_type(other):
            return Quantity.with_dimensions(float(self) ** other, self.dim ** other)
        else:
            return NotImplemented
            #return super(Quantity,self).__pow__(other)

    def __rpow__(self, other):
        if self.is_dimensionless():
            if isinstance(other, Quantity):
                return Quantity.with_dimensions(float(other) ** float(self), other.dim ** float(self))
            elif is_scalar_type(other):
                return Quantity(other ** float(self))
            else:
                return NotImplemented
                #return super(Quantity,self).__pow__(other)
        else:
            raise DimensionMismatchError("Power(R)", self.dim)

    def __neg__(self):
        return Quantity.with_dimensions(-float(self), self.dim)

    def __pos__(self):
        return self

    def __abs__(self):
        return Quantity.with_dimensions(abs(float(self)), self.dim)
    #### COMPARISONS ####
    def __lt__(self, other):
        if isinstance(other, Quantity):
            if self.dim == other.dim:
                return float(self) < float(other)
            else: raise DimensionMismatchError("LessThan", self.dim, other.dim)
        elif is_scalar_type(other):
            if other == 0 or other == 0.: return float(self) < other
            if numpy.isposinf(other): return True
            if numpy.isneginf(other): return False
            if self.is_dimensionless():
                return float(self) < other
            else: raise DimensionMismatchError("LessThan", self.dim, Dimension())
        else:
            return NotImplemented
            #return super(Quantity,self).__lt__(other)

    def __le__(self, other):
        if isinstance(other, Quantity):
            if self.dim == other.dim:
                return float(self) <= float(other)
            else: raise DimensionMismatchError("LessThanOrEquals", self.dim, other.dim)
        elif is_scalar_type(other):
            if other == 0 or other == 0.: return float(self) <= other
            if numpy.isposinf(other): return True
            if numpy.isneginf(other): return False
            if self.is_dimensionless():
                return float(self) <= other
            else: raise DimensionMismatchError("LessThanOrEquals", self.dim, Dimension())
        else:
            return NotImplemented
            #return super(Quantity,self).__le__(other)

    def __gt__(self, other):
        if isinstance(other, Quantity):
            if self.dim == other.dim:
                return float(self) > float(other)
            else: raise DimensionMismatchError("GreaterThan", self.dim, other.dim)
        elif is_scalar_type(other):
            if other == 0 or other == 0.: return float(self) > other
            if numpy.isneginf(other): return True
            if numpy.isposinf(other): return False
            if self.is_dimensionless():
                return float(self) > other
            else: raise DimensionMismatchError("GreaterThan", self.dim, Dimension())
        else:
            return NotImplemented
            #return super(Quantity,self).__gt__(other)

    def __ge__(self, other):
        if isinstance(other, Quantity):
            if self.dim == other.dim:
                return float(self) >= float(other)
            else: raise DimensionMismatchError("GreaterThanOrEquals", self.dim, other.dim)
        elif is_scalar_type(other):
            if other == 0 or other == 0.: return float(self) >= other
            if numpy.isneginf(other): return True
            if numpy.isposinf(other): return False
            if self.is_dimensionless():
                return float(self) >= other
            else: raise DimensionMismatchError("GreaterThanOrEquals", self.dim, Dimension())
        else:
            return NotImplemented
            #return super(Quantity,self).__ge__(other)

    def __eq__(self, other):
        if isinstance(other, Quantity):
            if self.dim == other.dim:
                return float(self) == float(other)
            else: raise DimensionMismatchError("Equals", self.dim, other.dim)
        elif is_scalar_type(other):
            if other == 0 or other == 0. or numpy.isinf(other): return float(self) == other
            if self.dim.is_dimensionless():
                return float(self) == other
            else: raise DimensionMismatchError("Equals", self.dim, Dimension())
        else:
            return NotImplemented
            #return super(Quantity,self).__eq__(other)

    def __ne__(self, other):
        if isinstance(other, Quantity):
            if self.dim == other.dim:
                return float(self) != float(other)
            else: raise DimensionMismatchError("Equals", self.dim, other.dim)
        elif is_scalar_type(other):
            if other == 0 or other == 0. or numpy.isinf(other): return float(self) != other
            if self.dim.is_dimensionless():
                return float(self) != other
            else: raise DimensionMismatchError("NotEquals", self.dim, Dimension())
        else:
            return NotImplemented
            #return super(Quantity,self).__ne__(other)
    #### MAKE QUANTITY PICKABLE ####
    def __reduce__(self):
        return (quantity_with_dimensions, (float(self), self.dim))


class Unit(Quantity):
    '''
    A physical unit
    
    Normally, you do not need to worry about the implementation of
    units. They are derived from the :class:`Quantity` object with
    some additional information (name and string representation).
    You can define new units which will be used when generating
    string representations of quantities simply by doing an
    arithmetical operation with only units, for example::
    
        Nm = newton * metre
    
    Note that operations with units are slower than operations with
    :class:`Quantity` objects, so for efficiency if you do not need the
    extra information that a :class:`Unit` object carries around, write
    ``1*second`` in preference to ``second``.
    '''

    # original documentation
    """A physical unit
    
    Basically, a unit is just a quantity with given dimensions, e.g.
    mvolt = 0.001 with the dimensions of voltage. The units module
    defines a large number of standard units, and you can also define
    your own (see below).
    
    The unit class also keeps track of various things that were used
    to define it so as to generate a nice string representation of it.
    See Representation below.
    
    Typical usage:
    
    x = 3 * mvolt # returns a quantity
    print x.in_unit(uvolt) # returns 3000 uV 
    
    Standard units:
    
    The units class has the following fundamental units:
    
    metre, kilogram, second, amp, kelvin, mole, candle
    
    And these additional basic units:
    
    radian, steradian, hertz, newton, pascal, joule, watt,
    coulomb, volt, farad, ohm, siemens, weber, tesla, henry,
    celsius, lumen, lux, becquerel, gray, sievert, katal
    
    And additionally, it includes all scaled versions of these
    units using the following prefixes

     Factor     Name    Prefix
     -----      ----    ------
     10^24      yotta   Y
     10^21      zetta   Z
     10^18      exa     E
     10^15      peta    P
     10^12      tera    T
     10^9       giga    G
     10^6       mega    M
     10^3       kilo    k
     10^2       hecto   h
     10^1       deka    da
     1           
     10^-1      deci    d
     10^-2      centi   c
     10^-3      milli   m
     10^-6      micro   u (\mu in SI)
     10^-9      nano    n
     10^-12     pico    p
     10^-15     femto   f
     10^-18     atto    a
     10^-21     zepto   z
     10^-24     yocto   y
    
    So for example nohm, ytesla, etc. are all defined.
    
    Defining your own:
    
    It can be useful to define your own units for printing
    purposes. So for example, to define the newton metre, you
    write:
    
    Nm = newton * metre
    
    Writing:
    
    print (1*Nm).in_unit(Nm)
    
    will return "1 Nm" because the Unit class generates a new
    display name of "Nm" from the display names "N" and "m" for
    newtons and metres automatically (see Representation below).
    
    To register this unit for use in the automatic printing
    of the Quantity.in_best_unit() method, see the documentation
    for the UnitRegistry class.
    
    Construction:
    
    The best way to construct a new unit is to use standard units
    already defined and arithmetic operations, e.g. newton*metre.
    See the documentation for __init__ and the static methods create(...)
    and create_scaled_units(...) for more details.
    
    If you don't like the automatically generated display name for
    the unit, use the set_display_name(name) method.
    
    Representation:
    
    A new unit defined by multiplication, division or taking powers
    generates a name for the unit automatically, so that for
    example the name for pfarad/mmetre**2 is "pF/mm^2", etc. If you
    don't like the automatically generated name, use the 
    set_display_name(name) method.
    """
    __slots__ = ["dim", "scale", "scalefactor", "dispname", "name", "iscompound"]
    #### CONSTRUCTION ####
    def __init__(self, value):
        """Initialises a dimensionless unit
        """
        super(Unit, self).__init__(value)
        self.dim = Dimension()
        self.scale = [ "", "", "", "", "", "", "" ]
        self.scalefactor = ""
        self.name = ""
        self.dispname = ""
        self.iscompound = False

    def __new__(typ, *args, **kw):
        obj = super(Unit, typ).__new__(typ, *args, **kw)
        global automatically_register_units
        if automatically_register_units:
            register_new_unit(obj)
        return obj
    @staticmethod
    def create(dim, name="", dispname="", scalefactor="", **keywords):
        """Creates a new named unit
        
        dim -- the dimensions of the unit
        name -- the full name of the unit, e.g. volt
        dispname -- the display name, e.g. V
        scalefactor -- scaling factor, e.g. m for mvolt
        keywords -- the scaling for each SI dimension, e.g. length="m", mass="-1", etc.
        """
        scale = [ "", "", "", "", "", "", "" ]
        for k in keywords:
            scale[_di[k]] = keywords[k]
        v = 1.0
        for s, i in izip(scale, dim._dims):
            if i: v *= _siprefixes[s] ** i
        u = Unit(v * _siprefixes[scalefactor])
        u.dim = dim
        u.scale = scale
        u.scalefactor = scalefactor + ""
        u.name = name + ""
        u.dispname = dispname + ""
        u.iscompound = False
        return u
    @staticmethod
    def create_scaled_unit(baseunit, scalefactor):
        """Create a scaled unit from a base unit
        
        baseunit -- e.g. volt, amp
        scalefactor -- e.g. "m" for mvolt, mamp
        """
        u = Unit(float(baseunit) * _siprefixes[scalefactor])
        u.dim = baseunit.dim
        u.scale = baseunit.scale
        u.scalefactor = scalefactor
        u.name = scalefactor + baseunit.name
        u.dispname = scalefactor + baseunit.dispname
        u.iscompound = False
        return u
    #### METHODS ####
    def set_name(self, name):
        """Sets the name for the unit
        """
        self.name = name

    def set_display_name(self, name):
        """Sets the display name for the unit
        """
        self.dispname = name

    #### REPRESENTATION ####  
    def __repr__(self):
        if self.name == "":
            if self.scalefactor:
                parts = [repr(_siprefixes[self.scalefactor])] 
            else:
                parts = []
            for i in range(7):
                if self.dim._dims[i]:                
                    s = self.scale[i] + _iclass_label[i]
                    if self.dim._dims[i] != 1:
                        s += ' ** ' + str(self.dim._dims[i])
                    parts.append(s)
            s = " * ".join(parts) 
            if not len(s):
                return "%s(1)" % self.__class__.__name__
            return s.strip()
        else:
            return self.name

    def __str__(self):
        if self.dispname == "":
            s = self.scalefactor + " "
            for i in range(7):
                if self.dim._dims[i]:
                    s += self.scale[i] + _ilabel[i]
                    if self.dim._dims[i] != 1:
                        s += "^" + str(self.dim._dims[i])
                    s += " "
            if not len(s):
                return "1"
            return s.strip()
        else:
            return self.dispname

    #### ARITHMETIC ####
    def __mul__(self, other):
        if isinstance(other, Unit):
            u = Unit(float(self) * float(other))
            u.name = self.name + " * " + other.name
            u.dispname = self.dispname + ' ' + other.dispname
            u.dim = self.dim * other.dim
            u.iscompound = True
            return u
        else:
            return super(Unit, self).__mul__(other)

    def __div__(self, other):
        if isinstance(other, Unit):
            u = Unit(float(self) / float(other))            
            if other.iscompound:
                u.dispname = '(' + self.dispname + ')'
                u.name = '(' + self.name + ')'
            else:
                u.dispname = self.dispname
                u.name = self.name
            u.dispname += '/'
            u.name += ' / '
            if other.iscompound:
                u.dispname += '(' + other.dispname + ')'
                u.name += '(' + other.name + ')'
            else:
                u.dispname += other.dispname
                u.name += other.name
            u.dim = self.dim / other.dim
            u.iscompound = True
            return u
        else:
            return super(Unit, self).__div__(other)

    def __pow__(self, other):
        if is_scalar_type(other):
            u = Unit(float(self) ** other)            
            if self.iscompound:
                u.dispname = '(' + self.dispname + ')'
                u.name = '(' + self.name + ')'
            else:
                u.dispname = self.dispname
                u.name = self.name
            u.dispname += '^' + str(other)
            u.name += ' ** ' + repr(other)
            u.dim = self.dim ** other
            return u
        else:
            return super(Unit, self).__mul__(other)

automatically_register_units = True

class UnitRegistry(object):
    """Stores known units for printing in best units
    
    All a user needs to do is to use the register_new_unit(u)
    function.
    
    Default registries:
    
    The units module defines three registries, the standard units,
    user units, and additional units. Finding best units is done
    by first checking standard, then user, then additional. New
    user units are added by using the register_new_unit(u) function.
    
    Standard units includes all the basic non-compound unit names
    built in to the module, including volt, amp, etc. Additional
    units defines some compound units like newton metre (Nm) etc.
    
    Methods:
    
    add(u) - add a new unit
    __getitem__(x) - get the best unit for quantity x
      e.g. UnitRegistry ur; ur[3*mvolt] returns mvolt
    """
    def __init__(self):
        self.objs = []

    def add(self, u):
        """Add a unit to the registry
        """
        self.objs.append(u)

    def __getitem__(self, x):
        """Returns the best unit for quantity x
        
        The algorithm is to consider the value:
        
        m=abs(x/u)
        
        for all matching units u. If there is a unit u with a value of
        m in [1,1000) then we select that unit. Otherwise, we select
        the first matching unit (which will typically be the unscaled
        version).
        """
        matching = filter(lambda o: have_same_dimensions(o, x), self.objs)
        if len(matching) == 0:
            raise KeyError("Unit not found in registry.")
        floatrep = filter(lambda o: 0.1 <= abs(float(x / o)) < 100, matching)
        if len(floatrep):
            return floatrep[0]
        else:
            return matching[0]

def register_new_unit(u):
    """Register a new unit for automatic displaying of quantities
    
    Example usage:
    
    2.0*farad/metre**2 = 2.0 m^-4 kg^-1 s^4 A^2
    register_new_unit(pfarad / mmetre**2)
    2.0*farad/metre**2 = 2000000.0 pF/mm^2
    """
    UserUnitRegister.add(u)

standard_unit_register = UnitRegistry()
additional_unit_register = UnitRegistry()
UserUnitRegister = UnitRegistry()

def all_registered_units(*regs):
    """Returns all registered units in the correct order
    """
    if not len(regs):
        regs = [ standard_unit_register, UserUnitRegister, additional_unit_register]
    for r in regs:
        for u in r.objs:
            yield u

def _get_best_unit(x, *regs):
    """Returns the best unit for quantity x
    
    Checks the registries regs, unless none are provided in which
    case it will check the standard, user and additional unit
    registers in turn.
    """
    if get_dimensions(x) == Dimension():
        return Quantity(1)
    if len(regs):
        for r in regs:
            try:
                return r[x]
            except KeyError:
                pass
        return Quantity.with_dimensions(1, x.dim)
    else:
        return _get_best_unit(x, standard_unit_register, UserUnitRegister, additional_unit_register)

def get_unit(x, *regs):
    '''
    Find the most appropriate consistent unit from the unit registries, or just return a Quantity with the same dimensions and value 1
    '''
    for u in all_registered_units(*regs):
        if is_equal(float(u), 1) and have_same_dimensions(u, x):
            return u
    return Quantity.with_dimensions(1, get_dimensions(x))

def get_unit_fast(x):
    '''
    Return a quantity with value 1 and the same dimensions
    '''
    return Quantity.with_dimensions(1, get_dimensions(x))

#### DECORATORS


def check_units(**au):
    """Decorator to check units of arguments passed to a function
    
    **Sample usage:** ::
    
        @check_units(I=amp,R=ohm,wibble=metre,result=volt)
        def getvoltage(I,R,**k):
            return I*R

    You don't have to check the units of every variable in the function, and
    you can define what the units should be for variables that aren't
    explicitly named in the definition of the function. For example, the code
    above checks that the variable wibble should be a length, so writing::
    
        getvoltage(1*amp,1*ohm,wibble=1)
    
    would fail, but::
    
        getvoltage(1*amp,1*ohm,wibble=1*metre)
    
    would pass.
    String arguments are not checked (e.g. ``getvoltage(wibble='hello')`` would pass).
    
    The special name ``result`` is for the return value of the function.
    
    An error in the input value raises a :exc:`DimensionMismatchError`, and an error
    in the return value raises an ``AssertionError`` (because it is a code
    problem rather than a value problem).
    
    **Notes**
    
    This decorator will destroy the signature of the original function, and
    replace it with the signature ``(*args, **kwds)``. Other decorators will
    do the same thing, and this decorator critically needs to know the signature
    of the function it is acting on, so it is important that it is the first
    decorator to act on a function. It cannot be used in combination with another
    decorator that also needs to know the signature of the function.
    """
    def do_check_units(f):
        @wraps(f)
        def new_f(*args, **kwds):
            newkeyset = kwds.copy()
            arg_names = f.func_code.co_varnames[0:f.func_code.co_argcount]
            for (n, v) in zip(arg_names, args[0:f.func_code.co_argcount]):
                newkeyset[n] = v
            for k in newkeyset.iterkeys():
                # string variables are allowed to pass, the presumption is they
                # name another variable. None is also allowed, useful for 
                # default parameters
                if (k in au.keys() and not isinstance(newkeyset[k], str) and
                                       not newkeyset[k] is None and
                                       not isinstance(newkeyset[k],
                                                      numpy.ndarray)): 
                    if not have_same_dimensions(newkeyset[k], au[k]):
                        raise DimensionMismatchError("Function " + f.__name__ +
                                                     " variable " + k +
                                                     " should have dimensions of " +
                                                     str(au[k]),
                                                     get_dimensions(newkeyset[k]))
            result = f(*args, **kwds)
            if "result" in au:
                assert have_same_dimensions(result, au["result"]), \
                    ("Function " + f.__name__ + " should return a value with unit " +
                     str(au["result"]) + " but has returned " +
                     str(get_dimensions(result)))
            return result
#        new_f.__name__ = f.__name__
#        new_f.__doc__ = f.__doc__
#        new_f.__dict__.update(f.__dict__)
        return new_f
    return do_check_units

## Note: do not normally call this, see note on importing of decorator module at the top of this module
#if use_decorator:
#    old_check_units = check_units
#    def check_units(**au):
#        return lambda f : decorator.new_wrapper(old_check_units(**au)(f), f)
#    check_units.__doc__ = old_check_units.__doc__

def _check_nounits(**au):
    """Don't bother checking units decorator
    """
    def dont_check_units(f):
        return f
    return dont_check_units


def scalar_representation(x):
    if isinstance(x, Unit):
        return x.name
    u = get_unit(x)
    if isinstance(u, Unit):
        return '(' + repr(float(x)) + '*' + u.name + ')'
    if isinstance(x, Quantity):
        return '(Quantity.with_dimensions(' + repr(float(x)) + ',' + repr(x.dim._dims) + '))'
    return repr(x)

# Remove all units
if not bup.use_units:
    check_units = _check_nounits
    def get_dimensions(obj):
        return Dimension()

    def is_dimensionless(obj):
        return True

    def have_same_dimensions(obj1, obj2):
        return True

    def get_unit(x, *regs):
        return 1.

    def scalar_representation(x):
        return '1.0'

###################################################
##### ADDITIONAL INFORMATION

#SI DIMENSIONS
#-------------
#Quantity               Unit      Symbol
#--------               ----      ------
#Length                 metre     m
#Mass                   kilogram  kg
#Time                   second    s
#Electric current       ampere    A
#Temperature            kelvin    K
#Quantity of substance  mole      mol
#Luminosity             candle    cd

# SI UNIT PREFIXES
# ----------------
# Factor     Name    Prefix
# -----      ----    ------
# 10^24      yotta   Y
# 10^21      zetta   Z
# 10^18      exa     E
# 10^15      peta    P
# 10^12      tera    T
# 10^9       giga    G
# 10^6       mega    M
# 10^3       kilo    k
# 10^2       hecto   h
# 10^1       deka    da
# 1           
# 10^-1      deci    d
# 10^-2      centi   c
# 10^-3      milli   m
# 10^-6      micro   u (\mu in SI)
# 10^-9      nano    n
# 10^-12     pico    p
# 10^-15     femto   f
# 10^-18     atto    a
# 10^-21     zepto   z
# 10^-24     yocto   y