UnitConverter.st
changeset 4514 360a740542a7
parent 4092 61384ffc7271
child 4515 34fbe1d0b79f
equal deleted inserted replaced
4513:2c986c93c992 4514:360a740542a7
     1 "{ Encoding: utf8 }"
       
     2 
       
     3 "
     1 "
     4  COPYRIGHT (c) 1996 eXept Software AG
     2  COPYRIGHT (c) 1996 eXept Software AG
     5               All Rights Reserved
     3               All Rights Reserved
     6 
     4 
     7  This software is furnished under a license and may be used
     5  This software is furnished under a license and may be used
   384     self addConversion:(4/3)  from:#teaspoon     to:#fldr.
   382     self addConversion:(4/3)  from:#teaspoon     to:#fldr.
   385 
   383 
   386 
   384 
   387     "/ ---------------- temperature ---------------- 
   385     "/ ---------------- temperature ---------------- 
   388 
   386 
   389     self addConversion:[:d | d * 1.8 + 32] from:#celsius    to:#fahrenheit.
   387     self addConversion:[:d | d * 1.8 + 32]   from:#celsius    to:#fahrenheit.
   390     self addConversion:[:f | (f - 32) / 1.8] from:#fahrenheit to:#celsius.
   388     self addConversion:[:f | (f - 32) / 1.8] from:#fahrenheit to:#celsius.
       
   389     self addConversion:[:d | d + 273.15]     from:#celsius    to:#kelvin.
       
   390     self addConversion:[:k | k - 273.15]     from:#kelvin     to:#celsius.
   391 
   391 
   392     "/ ---------------- nature ---------------- 
   392     "/ ---------------- nature ---------------- 
   393     Constants at:#planckLength   put:#(1.616e-35   #'m').
   393     Constants at:#planckLength   put:#(1.616e-35   #'m').
   394     Constants at:#planckMass     put:#(2.176-8     #'Kg').
   394     Constants at:#planckMass     put:#(2.176-8     #'Kg').
   395     Constants at:#planckTime     put:#(5.391e-44   #'s').
   395     Constants at:#planckTime     put:#(5.391e-44   #'s').
   397     "
   397     "
   398      Conversions := nil.
   398      Conversions := nil.
   399      UnitConverter initializeConversions
   399      UnitConverter initializeConversions
   400     "
   400     "
   401 
   401 
   402     "Created: / 22.7.1997 / 13:56:40 / cg"
   402     "Created: / 22-07-1997 / 13:56:40 / cg"
   403     "Modified: / 10.10.2001 / 14:39:04 / cg"
   403     "Modified: / 18-09-2017 / 08:38:14 / cg"
   404 !
   404 !
   405 
   405 
   406 initializePrintValues
   406 initializePrintValues
   407     "/ inch to (roughly) a typesetter point
   407     "/ inch to (roughly) a typesetter point
   408     self addConversion:(1/72)      from:#point to:#inch.
   408     self addConversion:(1/72)      from:#point to:#inch.
   575 convert:howMany from:sourceUnit to:destUnit
   575 convert:howMany from:sourceUnit to:destUnit
   576     "given a value in sourceUnit (symbolic), try to convert it
   576     "given a value in sourceUnit (symbolic), try to convert it
   577      to destUnit (symbolic); return nil, if the conversion is
   577      to destUnit (symbolic); return nil, if the conversion is
   578      unknown."
   578      unknown."
   579 
   579 
       
   580     ^ self convert:howMany from:sourceUnit to:destUnit pairsAlreadyTried:(OrderedCollection new)
       
   581 
       
   582     "direct - how many meters are there in two inches:
       
   583      UnitConverter convert:2 from:#inch to:#meter   
       
   584      
       
   585      reverse - how many inches are there in one meter
       
   586      UnitConverter convert:1 from:#meter to:#inch   
       
   587 
       
   588      with alias:
       
   589      UnitConverter convert:1 from:#inch to:#m    
       
   590      UnitConverter convert:1 from:#inch to:#mm          
       
   591      UnitConverter convert:1 from:#inch to:#millimeter   
       
   592      UnitConverter convert:1 from:#inch to:#nanometer 
       
   593      UnitConverter convert:1 from:#mm to:#km  
       
   594      UnitConverter convert:1 from:#km to:#foot   
       
   595 
       
   596      indirect:
       
   597      UnitConverter convert:1 from:#mm   to:#twip  
       
   598      UnitConverter convert:1 from:#inch to:#twip  
       
   599      UnitConverter convert:1 from:'letterH' to:#point  
       
   600      UnitConverter convert:1 from:'letterlH' to:#point  
       
   601 
       
   602      UnitConverter convert:5 from:#barrel to:#liter  
       
   603      UnitConverter convert:10 from:#kilogram to:#carat  
       
   604 
       
   605      UnitConverter convert:1 from:#liter to:#floz  
       
   606      UnitConverter convert:1 from:#floz to:#liter  
       
   607      UnitConverter convert:1 from:#oz to:#gram  
       
   608      UnitConverter convert:1 from:#ounce to:#gram  
       
   609 
       
   610      UnitConverter convert:37 from:#celsius to:#fahrenheit  -> 98.6    
       
   611      UnitConverter convert:37 from:#celsius to:#kelvin      -> 310.15
       
   612      UnitConverter convert:0 from:#celsius to:#kelvin       -> 273.15  
       
   613      UnitConverter convert:0 from:#kelvin to:#celsius       -> -273.15 
       
   614      UnitConverter convert:0 from:#kelvin to:#fahrenheit    -> -459.67    
       
   615      UnitConverter convert:0 from:#fahrenheit to:#celsius   -> -17.7777777777778    
       
   616      UnitConverter convert:0 from:#fahrenheit to:#kelvin    -> 255.372222222222   
       
   617 
       
   618     "
       
   619 
       
   620     "Created: / 31-05-1996 / 16:23:38 / cg"
       
   621     "Modified (comment): / 18-09-2017 / 09:02:09 / cg"
       
   622 !
       
   623 
       
   624 convert:howMany from:sourceUnit to:destUnit pairsAlreadyTried:pairsAlready
       
   625     "given a value in sourceUnit (symbolic), try to convert it
       
   626      to destUnit (symbolic); return nil, if the conversion is
       
   627      unknown."
       
   628 
   580     |u conversions alias rslt sU dU const val unit 
   629     |u conversions alias rslt sU dU const val unit 
   581      i suNumerator suDenominator duNumerator duDenominator uN uD
   630      i suNumerator suDenominator duNumerator duDenominator uN uD
   582      sF1 sF2 dF1 dF2
   631      sF1 sF2 dF1 dF2
   583      s d|
   632      s d|
   584 
   633 
   590 
   639 
   591     "/ somehow, recursion must end ...
   640     "/ somehow, recursion must end ...
   592     sourceUnit == destUnit ifTrue:[
   641     sourceUnit == destUnit ifTrue:[
   593         ^ howMany
   642         ^ howMany
   594     ].
   643     ].
   595 
   644     thisContext isReallyRecursive ifTrue:[
       
   645         ^ self noConversionFrom:sourceUnit to:destUnit.
       
   646     ].
       
   647     (pairsAlready includes:(sourceUnit->destUnit)) ifTrue:[
       
   648         ^ self noConversionFrom:sourceUnit to:destUnit.
       
   649     ].
       
   650     pairsAlready add:(sourceUnit->destUnit).
       
   651     
   596     sourceUnit isSymbol ifFalse:[
   652     sourceUnit isSymbol ifFalse:[
   597         s := sourceUnit withoutSeparators.
   653         s := sourceUnit withoutSeparators.
   598         sU := s asSymbolIfInterned.
   654         sU := s asSymbolIfInterned.
   599         sU notNil ifTrue:[
   655         sU notNil ifTrue:[
   600             ^ self convert:howMany from:sU to:destUnit
   656             ^ self convert:howMany from:sU to:destUnit pairsAlreadyTried:pairsAlready
   601         ].
   657         ].
   602         (s startsWith:$() ifTrue:[
   658         (s startsWith:$() ifTrue:[
   603             (s endsWith:$)) ifTrue:[
   659             (s endsWith:$)) ifTrue:[
   604                 s := s copyFrom:2 to:(s size - 1).
   660                 s := s copyFrom:2 to:(s size - 1).
   605                 ^ self convert:howMany from:s to:destUnit
   661                 ^ self convert:howMany from:s to:destUnit pairsAlreadyTried:pairsAlready
   606             ]
   662             ]
   607         ].
   663         ].
   608     ].
   664     ].
   609     destUnit isSymbol ifFalse:[
   665     destUnit isSymbol ifFalse:[
   610         d := destUnit withoutSeparators.
   666         d := destUnit withoutSeparators.
   611         dU := d asSymbolIfInterned.
   667         dU := d asSymbolIfInterned.
   612         dU notNil ifTrue:[
   668         dU notNil ifTrue:[
   613             ^ self convert:howMany from:sourceUnit to:dU
   669             ^ self convert:howMany from:sourceUnit to:dU pairsAlreadyTried:pairsAlready
   614         ].
   670         ].
   615         (d startsWith:$() ifTrue:[
   671         (d startsWith:$() ifTrue:[
   616             (d endsWith:$)) ifTrue:[
   672             (d endsWith:$)) ifTrue:[
   617                 d := d copyFrom:2 to:(d size - 1).
   673                 d := d copyFrom:2 to:(d size - 1).
   618                 ^ self convert:howMany from:sourceUnit to:d
   674                 ^ self convert:howMany from:sourceUnit to:d pairsAlreadyTried:pairsAlready
   619             ]
   675             ]
   620         ].
   676         ].
   621     ].
   677     ].
   622 
   678 
   623     "/ first, get rid of scalers ...
   679     "/ first, get rid of scalers ...
   624 
   680 
   625     u := self unscaled:sourceUnit.
   681     u := self unscaled:sourceUnit.
   626     u notNil ifTrue:[
   682     u notNil ifTrue:[
   627         ^ self convert:(howMany*(u value)) from:(u key) to:destUnit
   683         ^ self convert:(howMany*(u value)) from:(u key) to:destUnit pairsAlreadyTried:pairsAlready
   628     ].
   684     ].
   629 
   685 
   630     u := self unscaled:destUnit.
   686     u := self unscaled:destUnit.
   631     u notNil ifTrue:[
   687     u notNil ifTrue:[
   632         ^ self convert:(howMany/(u value)) from:sourceUnit to:(u key)
   688         ^ self convert:(howMany/(u value)) from:sourceUnit to:(u key) pairsAlreadyTried:pairsAlready
   633     ].
   689     ].
   634 
   690 
   635     "/ and of aliases ...
   691     "/ and of aliases ...
   636 
   692 
   637     alias := Aliases at:sourceUnit ifAbsent:nil.
   693     alias := Aliases at:sourceUnit ifAbsent:nil.
   638     alias notNil ifTrue:[
   694     alias notNil ifTrue:[
   639         ^ self convert:howMany from:alias to:destUnit
   695         ^ self convert:howMany from:alias to:destUnit pairsAlreadyTried:pairsAlready
   640     ].
   696     ].
   641     alias := Aliases at:destUnit ifAbsent:nil.
   697     alias := Aliases at:destUnit ifAbsent:nil.
   642     alias notNil ifTrue:[
   698     alias notNil ifTrue:[
   643         ^ self convert:howMany from:sourceUnit to:alias
   699         ^ self convert:howMany from:sourceUnit to:alias pairsAlreadyTried:pairsAlready
   644     ].
   700     ].
   645 
   701 
   646     "/ any constants ?
   702     "/ any constants ?
   647 
   703 
   648     (Constants includesKey:sourceUnit) ifTrue:[
   704     (Constants includesKey:sourceUnit) ifTrue:[
   649         const := Constants at:sourceUnit.
   705         const := Constants at:sourceUnit.
   650         val := const at:1.
   706         val := const at:1.
   651         unit := const at:2.
   707         unit := const at:2.
   652         ^ self convert:(howMany*val) from:unit to:destUnit
   708         ^ self convert:(howMany*val) from:unit to:destUnit pairsAlreadyTried:pairsAlready
   653     ].
   709     ].
   654 
   710 
   655     "/ compounds (^ , / or *) are very naively parsed
   711     "/ compounds (^ , / or *) are very naively parsed
   656     "/ need a full expression parser (tree) for full power.
   712     "/ need a full expression parser (tree) for full power.
   657     "/ I leave that as an excercise to you ...
   713     "/ I leave that as an excercise to you ...
   661 
   717 
   662     ((sourceUnit endsWith:'^2') and:[destUnit endsWith:'^2']) ifTrue:[
   718     ((sourceUnit endsWith:'^2') and:[destUnit endsWith:'^2']) ifTrue:[
   663         sU := (sourceUnit copyButLast:2) asSymbolIfInterned.
   719         sU := (sourceUnit copyButLast:2) asSymbolIfInterned.
   664         dU := (destUnit copyButLast:2) asSymbolIfInterned.
   720         dU := (destUnit copyButLast:2) asSymbolIfInterned.
   665         (sU notNil and:[dU notNil]) ifTrue:[
   721         (sU notNil and:[dU notNil]) ifTrue:[
   666             ^ (self convert:(howMany sqrt) from:sU to:dU) squared
   722             ^ (self convert:(howMany sqrt) from:sU to:dU pairsAlreadyTried:pairsAlready) squared 
   667         ].
   723         ].
   668         ^ self noConversionFrom:sourceUnit to:destUnit.
   724         ^ self noConversionFrom:sourceUnit to:destUnit.
   669     ].
   725     ].
   670     ((sourceUnit endsWith:'^3') and:[destUnit endsWith:'^3']) ifTrue:[
   726     ((sourceUnit endsWith:'^3') and:[destUnit endsWith:'^3']) ifTrue:[
   671         sU := (sourceUnit copyButLast:2) asSymbolIfInterned.
   727         sU := (sourceUnit copyButLast:2) asSymbolIfInterned.
   672         dU := (destUnit copyButLast:2) asSymbolIfInterned.
   728         dU := (destUnit copyButLast:2) asSymbolIfInterned.
   673         (sU notNil and:[dU notNil]) ifTrue:[
   729         (sU notNil and:[dU notNil]) ifTrue:[
   674             ^ (self convert:(howMany raisedTo:(1/3)) from:sU to:dU) raisedTo:3
   730             ^ (self convert:(howMany raisedTo:(1/3)) from:sU to:dU pairsAlreadyTried:pairsAlready) raisedTo:3
   675         ].
   731         ].
   676         ^ self noConversionFrom:sourceUnit to:destUnit.
   732         ^ self noConversionFrom:sourceUnit to:destUnit.
   677     ].
   733     ].
   678 
   734 
   679     "/ working with fractions ?
   735     "/ working with fractions ?
   685         suDenominator := sourceUnit copyFrom:(i + 1).
   741         suDenominator := sourceUnit copyFrom:(i + 1).
   686         i := destUnit indexOf:$/.
   742         i := destUnit indexOf:$/.
   687         duNumerator := destUnit copyTo:(i - 1).
   743         duNumerator := destUnit copyTo:(i - 1).
   688         duDenominator := destUnit copyFrom:(i + 1).
   744         duDenominator := destUnit copyFrom:(i + 1).
   689 
   745 
   690         uN := self convert:howMany from:suNumerator to:duNumerator.
   746         uN := self convert:howMany from:suNumerator to:duNumerator pairsAlreadyTried:pairsAlready.
   691         uN notNil ifTrue:[
   747         uN notNil ifTrue:[
   692             uD := self convert:1 from:suDenominator to:duDenominator.
   748             uD := self convert:1 from:suDenominator to:duDenominator pairsAlreadyTried:pairsAlready.
   693             uD notNil ifTrue:[
   749             uD notNil ifTrue:[
   694                 ^ uN / uD
   750                 ^ uN / uD
   695             ]
   751             ]
   696         ].
   752         ].
   697     ].
   753     ].
   704         sF2 := sourceUnit copyFrom:(i + 1).
   760         sF2 := sourceUnit copyFrom:(i + 1).
   705         i := destUnit indexOf:$*.
   761         i := destUnit indexOf:$*.
   706         dF1 := destUnit copyTo:(i - 1).
   762         dF1 := destUnit copyTo:(i - 1).
   707         dF2 := destUnit copyFrom:(i + 1).
   763         dF2 := destUnit copyFrom:(i + 1).
   708 
   764 
   709         u := self convert:howMany from:sF1 to:dF1.
   765         u := self convert:howMany from:sF1 to:dF1 pairsAlreadyTried:pairsAlready.
   710         u notNil ifTrue:[
   766         u notNil ifTrue:[
   711             u := self convert:u from:sF2 to:dF2.
   767             u := self convert:u from:sF2 to:dF2 pairsAlreadyTried:pairsAlready.
   712             u notNil ifTrue:[
   768             u notNil ifTrue:[
   713                 ^ u
   769                 ^ u
   714             ]
   770             ]
   715         ].
   771         ].
   716     ].
   772     ].
   721 
   777 
   722     rslt := self convertDirect:howMany from:sourceUnit to:destUnit.
   778     rslt := self convertDirect:howMany from:sourceUnit to:destUnit.
   723     rslt notNil ifTrue:[^ rslt].
   779     rslt notNil ifTrue:[^ rslt].
   724 
   780 
   725     "/ try inverse conversion ...
   781     "/ try inverse conversion ...
   726 
   782     "/ but here, only linear factors (numeric conversions) are allowed!!
   727     rslt := self convertDirect:1 from:destUnit to:sourceUnit.
   783     [
   728     rslt notNil ifTrue:[ ^ howMany / rslt].
   784         |conversions revFactor|
   729 
   785         
       
   786         conversions := Conversions at:destUnit ifAbsent:nil.
       
   787         conversions notNil ifTrue:[
       
   788             revFactor := conversions at:sourceUnit ifAbsent:nil.
       
   789             revFactor isNumber ifTrue:[
       
   790                 ^ howMany / revFactor
       
   791             ].
       
   792         ].
       
   793     ] value.    
       
   794 
       
   795     
   730     "/ here's the deep recursion ...
   796     "/ here's the deep recursion ...
   731 
   797 
   732 
       
   733     "/ try indirect conversion from source
   798     "/ try indirect conversion from source
   734 
       
   735     conversions := Conversions at:sourceUnit ifAbsent:nil.
   799     conversions := Conversions at:sourceUnit ifAbsent:nil.
   736     conversions notNil ifTrue:[
   800     conversions notNil ifTrue:[
   737         conversions keysAndValuesDo:[:intermediateUnit :factor1 |
   801         conversions keysAndValuesDo:[:intermediateUnit :factor1 |
   738             |factor2|
   802             |factor2 intermediateValue result|
   739 
   803 
   740             factor2 := self convert:factor1 from:intermediateUnit to:destUnit.
   804             factor1 isBlock ifTrue:[
   741             factor2 notNil ifTrue:[^ factor2 * howMany].
   805                 intermediateValue := factor1 value:howMany.
       
   806                 result := self convert:intermediateValue from:intermediateUnit to:destUnit pairsAlreadyTried:pairsAlready.
       
   807                 result notNil ifTrue:[ ^ result ].
       
   808             ] ifFalse:[    
       
   809                 factor2 := self convert:factor1 from:intermediateUnit to:destUnit pairsAlreadyTried:pairsAlready.
       
   810                 factor2 notNil ifTrue:[^ factor2 * howMany].
       
   811             ].
   742         ].
   812         ].
   743     ].
   813     ].
   744 
   814 
   745     "/ try indirect conversion from dest
   815     "/ try indirect conversion from dest
   746 
   816 
   747     conversions := Conversions at:destUnit ifAbsent:nil.
   817     conversions := Conversions at:destUnit ifAbsent:nil.
   748     conversions notNil ifTrue:[
   818     conversions notNil ifTrue:[
   749         conversions keysAndValuesDo:[:intermediateUnit :factor1 |
   819         conversions keysAndValuesDo:[:intermediateUnit :factor1 |
   750             |factor2|
   820             |factor2|
   751 
   821 
   752             factor2 := self convert:(factor1) from:intermediateUnit to:sourceUnit.
   822             factor1 isBlock ifFalse:[    
   753             factor2 notNil ifTrue:[^ howMany / factor2].
   823                 factor2 := self convert:(factor1) from:intermediateUnit to:sourceUnit pairsAlreadyTried:pairsAlready.
       
   824                 factor2 notNil ifTrue:[^ howMany / factor2].
       
   825             ].
   754         ].
   826         ].
   755     ].
   827     ].
   756 
   828 
   757     "/ if working with a product, try each component
   829     "/ if working with a product, try each component
   758 
   830 
   771                 "/ ok; want a/b -> x
   843                 "/ ok; want a/b -> x
   772                 "/ found a/c -> any
   844                 "/ found a/c -> any
   773                 "/ what about c->b ?
   845                 "/ what about c->b ?
   774 
   846 
   775                 iUnit := aUnit copyFrom:pref size + 1.
   847                 iUnit := aUnit copyFrom:pref size + 1.
   776                 factor2 := self convert:1 from:sF2 to:iUnit.
   848                 factor2 := self convert:1 from:sF2 to:iUnit pairsAlreadyTried:pairsAlready.
   777                 factor2 notNil ifTrue:[
   849                 factor2 notNil ifTrue:[
   778                     "/ good ...
   850                     "/ good ...
   779                     rslt := self convert:(factor2 * howMany)
   851                     rslt := self convert:(factor2 * howMany) from:aUnit to:destUnit pairsAlreadyTried:pairsAlready.
   780                                     from:aUnit to:destUnit.
       
   781                     rslt notNil ifTrue:[^ rslt].
   852                     rslt notNil ifTrue:[^ rslt].
   782                 ]
   853                 ]
   783             ]
   854             ]
   784         ].
   855         ].
   785     ].
   856     ].
   809 
   880 
   810      UnitConverter convert:5 from:#barrel to:#liter  
   881      UnitConverter convert:5 from:#barrel to:#liter  
   811      UnitConverter convert:10 from:#kilogram to:#carat  
   882      UnitConverter convert:10 from:#kilogram to:#carat  
   812 
   883 
   813      UnitConverter convert:1 from:#liter to:#floz  
   884      UnitConverter convert:1 from:#liter to:#floz  
   814     "
   885      UnitConverter convert:0 from:#fahrenheit to:#kelvin  
   815 
   886      UnitConverter convert:0 from:#fahrenheit to:#celsius  
   816     "Created: 31.5.1996 / 16:23:38 / cg"
   887      UnitConverter convert:0 from:#celsius to:#kelvin  
   817     "Modified: 6.8.1997 / 18:10:22 / cg"
   888      UnitConverter convert:0 from:#kelvin to:#celsius  
       
   889     "
       
   890 
       
   891     "Created: / 17-09-2017 / 10:11:40 / cg"
       
   892     "Modified: / 18-09-2017 / 08:39:15 / cg"
   818 !
   893 !
   819 
   894 
   820 fileSizeBinaryStringFor:nBytes
   895 fileSizeBinaryStringFor:nBytes
   821     "return a useful string for a size-in-bytes in programmer's notation; 
   896     "return a useful string for a size-in-bytes in programmer's notation; 
   822      Scales by Ki (*1024), Mi (*1024*1024) or Gi (*1024*1024*1024) or even Ti
   897      Scales by Ki (*1024), Mi (*1024*1024) or Gi (*1024*1024*1024) or even Ti
  1324     "Created: 29.3.1997 / 17:56:03 / cg"
  1399     "Created: 29.3.1997 / 17:56:03 / cg"
  1325 ! !
  1400 ! !
  1326 
  1401 
  1327 !UnitConverter class methodsFor:'private'!
  1402 !UnitConverter class methodsFor:'private'!
  1328 
  1403 
  1329 addConversion:factor from:sourceMetric to:destMetric
  1404 addConversion:factorOrBlock from:sourceMetric to:destMetric
  1330     "add a conversion"
  1405     "add a conversion"
  1331 
  1406 
  1332     |conversion|
  1407     |conversion|
  1333 
  1408 
  1334     conversion := Conversions at:sourceMetric ifAbsent:nil.
  1409     conversion := Conversions at:sourceMetric ifAbsent:nil.
  1335     conversion isNil ifTrue:[
  1410     conversion isNil ifTrue:[
  1336 	conversion := IdentityDictionary new.
  1411 	conversion := IdentityDictionary new.
  1337 	Conversions at:sourceMetric put:conversion
  1412 	Conversions at:sourceMetric put:conversion
  1338     ].
  1413     ].
  1339     conversion at:destMetric put:factor.
  1414     conversion at:destMetric put:factorOrBlock.
  1340     ^ conversion
  1415     ^ conversion
  1341 
  1416 
  1342     "Created: 31.5.1996 / 13:51:25 / cg"
  1417     "Created: 31.5.1996 / 13:51:25 / cg"
  1343     "Modified: 29.3.1997 / 17:19:31 / cg"
  1418     "Modified: 29.3.1997 / 17:19:31 / cg"
  1344 !
  1419 !