'From Squeak2.9alpha of 23 June 2000 [latest update: #2748] on 3 October 2000 at 12:03:58 pm'! "Change Set: Units Date: 4 March 1999, 3 October 2000 Author: Andrew Brault (ajb@tiac.net) For Squeak: Helge Horch (heho@gmx.de) This is a package for manipulating physical unit values. There is not much formal documentation, but most of the classes in this package have comments. The most important methods are those in Unit and UnitValue. You should look through all those. To add your own units, see the classes BaseUnit, DerivedUnit, NamedUnit, and PrefixedUnit, and look through the initialization code. You should duplicate what is done there to add your own units. Remember to send 'Unit initialize' to make your changes take effect. Note that you can send Unit printAbbreviated: (true/false) to control how units print. This package is Copyright 1998, 1999, 2000 by Andrew Brault. All rights reserved. It is licensed under the Squeak license, as displayed at . This software is provided on an 'as-is' basis; use this software at your own risk. No warranty of fitness for any purpose is claimed, implicitly or otherwise. TO THE EXTENT PERMITTED BY LAW, THE AUTHORS SHALL NOT BE LIABLE TO USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. Some runnable examples (select the code, then do 'print it'): 'Create unit values by sending #units: to a number.' 2 units: #inches. 'You can add, divide, multiply, and subtract unit values.' (2 units: #inches) / (3 units: #seconds). 'Adding or subtracting units does appropriate conversions.' (14 units: #feet) + (10 units: #meters) 'You can also explicitly convert values.' (15 units: #miles) / (1 units: #hours) convertTo: (Unit meters / Unit seconds) 'You can use 'derived' units such as the newton.' 3 units: #newtons. 'You can expand such a value into base SI units.' (3 units: #newtons) baseUnits. 'To see kilograms rather than grams, factor with respect to kilograms.' (3 units: #newtons) factor: Unit kilograms. 'Unit values can also be compared (provided they are dimensionally consistent.' (1 units: #inches) < (3 units: #centimeters) "! Object subclass: #SIPrefix instanceVariableNames: 'abbreviation name scalingFactor ' classVariableNames: 'SIPrefixesByAbbreviation SIPrefixesByName ' poolDictionaries: '' category: 'Experimental-Units'! !SIPrefix commentStamp: '' prior: 0! SIPrefix represents a power of 10 attached to a unit. Examples: milli, micro, kilo, etc. ! Object subclass: #Unit instanceVariableNames: '' classVariableNames: 'PrintAbbreviated ' poolDictionaries: '' category: 'Experimental-Units'! !Unit commentStamp: '' prior: 0! Class Unit is the superclass for all other unit classes. Instances represent units which are not attached to numbers; for example 'meters' (a BaseUnit), 'meters per second' (a CompoundUnit), 'joule seconds per liter' (a ComplexUnit), 'degrees Kelvin' (a TemperatureBaseUnit), 'degrees Celsius' (a TemperutareUnit), 'kilograms' (a PrefixedUnit), and 'moles of hydrogen' (a ModifiedUnit). A number of methods on the class side of Unit provide for easy access to built-in units. ! Unit subclass: #CompoundUnit instanceVariableNames: 'units exponents ' classVariableNames: '' poolDictionaries: '' category: 'Experimental-Units'! !CompoundUnit commentStamp: '' prior: 0! Instances of CompoundUnits represent units which are combinations of base units (only). Examples of things that are CompoundUnits: meters per second cubic meters per degree Kelvin grams per mole of calcium Examples of things that are not CompoundUnits: miles per hour cubic meters per degree Farenheit kilograms per mole ! CompoundUnit subclass: #ComplexUnit instanceVariableNames: 'conversionFactor cachedBaseUnits ' classVariableNames: '' poolDictionaries: '' category: 'Experimental-Units'! !ComplexUnit commentStamp: '' prior: 0! This represents a product of one or more different units.! Unit subclass: #ModifiedUnit instanceVariableNames: 'baseUnit modification ' classVariableNames: '' poolDictionaries: '' category: 'Experimental-Units'! !ModifiedUnit commentStamp: '' prior: 0! A base unit with an arbitrary modification that makes it incompatible with anything that does not have the same modification. Works well with domain-specific 'modifications', e.g., 1.6 moles can now become: 1.6 moles of sulfuric acid ("sulfuric acid" might be a String, or a ChemicalCompound, or whatever.) ! Unit subclass: #NamedUnit instanceVariableNames: 'abbreviation name pluralName ' classVariableNames: 'UnitsByAbbreviation UnitsByName UnitsByPluralName ' poolDictionaries: '' category: 'Experimental-Units'! !NamedUnit commentStamp: '' prior: 0! This is a kind of unit with a specific (singular and plural) name, and an abbreviation. e.g., meter, meters, m. ! NamedUnit subclass: #BaseUnit instanceVariableNames: '' classVariableNames: 'SIUnitsByAbbreviation SIUnitsByName SIUnitsByPluralName ' poolDictionaries: '' category: 'Experimental-Units'! !BaseUnit commentStamp: '' prior: 0! This represents one of the SI base units. By default the following "official" units are defined: gram meter second candela (light intensity) mole (pseudo-dimensionless quantity) Two 'extra' base units are included to demonstrate how to extend the system: base pairs (nucleotides on a DNA strand, for automatic analysis) donuts (e.g., 1 homer = 8 donuts/minute) ! NamedUnit subclass: #DerivedUnit instanceVariableNames: 'unitValue ' classVariableNames: '' poolDictionaries: '' category: 'Experimental-Units'! !DerivedUnit commentStamp: '' prior: 0! This is a definition of a unit in terms of a product of powers of other units, plus a scalar value associated with the unit. Example: 1 inch = 2.54 cm Note that the "left side" is always magnitude 1, which corresponds to 1 unit of the DerivedUnit. ! Unit subclass: #PrefixedUnit instanceVariableNames: 'prefix unit ' classVariableNames: '' poolDictionaries: '' category: 'Experimental-Units'! !PrefixedUnit commentStamp: '' prior: 0! This is a unit with an SI prefix attached. See class SIPrefix for details. ! BaseUnit subclass: #TemperatureBaseUnit instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Experimental-Units'! !TemperatureBaseUnit commentStamp: '' prior: 0! Temperature units are different because they require a general linear transformation for basic arithmetic operations. ! DerivedUnit subclass: #TemperatureUnit instanceVariableNames: 'additiveFactor ' classVariableNames: '' poolDictionaries: '' category: 'Experimental-Units'! !TemperatureUnit commentStamp: '' prior: 0! A temperature needs an additional "additive factor" to handle, e.g., Celsius to Fahrenheit. ! Magnitude subclass: #UnitValue instanceVariableNames: 'unit value ' classVariableNames: 'InconsistentUnitsSignal ' poolDictionaries: '' category: 'Experimental-Units'! !UnitValue commentStamp: '' prior: 0! UnitValue is a number with a unit attached. See the methods here for more information. ! !Number methodsFor: 'converting'! units: unitOrSymbol ^UnitValue unit: (UnitValue unitFor: unitOrSymbol) value: self! ! !Float methodsFor: 'arithmetic' stamp: 'hh 10/3/2000 11:46'! / aNumber "Primitive. Answer the result of dividing receiver by aNumber. Fail if the argument is not a Float. Essential. See Object documentation whatIsAPrimitive." aNumber isZero ifTrue: [^(ZeroDivide dividend: self) signal]. ^ aNumber adaptToFloat: self andSend: #/! ! !SIPrefix methodsFor: 'accessing'! abbreviation ^abbreviation! ! !SIPrefix methodsFor: 'accessing'! name ^name! ! !SIPrefix methodsFor: 'accessing'! scalingFactor ^scalingFactor! ! !SIPrefix methodsFor: 'initialization'! abbreviation: myAbbreviation name: myName scalingFactor: myScalingFactor abbreviation _ myAbbreviation. name _ myName. scalingFactor _ myScalingFactor! ! !SIPrefix methodsFor: 'printing'! printOn: stream super printOn: stream. stream nextPutAll: ' ['; nextPutAll: name; nextPut: $]! ! !SIPrefix class methodsFor: 'initialization'! abbreviation: abbreviation name: prefixName scalingFactor: scalingFactor | prefix | prefix _ self new abbreviation: abbreviation name: prefixName scalingFactor: scalingFactor. SIPrefixesByAbbreviation at: abbreviation put: prefix. SIPrefixesByName at: prefixName put: prefix. ^prefix! ! !SIPrefix class methodsFor: 'initialization'! initializeClass "SIPrefix initializeClass." "Do not rename this to #initialize." SIPrefixesByName _ Dictionary new. SIPrefixesByAbbreviation _ Dictionary new. self abbreviation: 'k' name: 'kilo' scalingFactor: (10 raisedTo: 3). self abbreviation: 'M' name: 'mega' scalingFactor: (10 raisedTo: 6). self abbreviation: 'G' name: 'tera' scalingFactor: (10 raisedTo: 9). self abbreviation: 'c' name: 'centi' scalingFactor: (10 raisedTo: -2). self abbreviation: 'm' name: 'milli' scalingFactor: (10 raisedTo: -3). self abbreviation: 'u' name: 'micro' scalingFactor: (10 raisedTo: -6)! ! !SIPrefix class methodsFor: 'accessing'! named: prefixName ^SIPrefixesByName at: prefixName! ! !SIPrefix class methodsFor: 'accessing'! withAbbreviation: abbreviation ^SIPrefixesByAbbreviation at: abbreviation! ! !SIPrefix class methodsFor: 'enumerating'! prefixAbbreviationsDo: block "Evaluate 'block' once with each prefix abbreviation, in no particular order." SIPrefixesByAbbreviation keysDo: block! ! !SIPrefix class methodsFor: 'enumerating'! prefixStringsDo: block "Evaluate 'block' once with each prefix string, in no particular order." SIPrefixesByName keysDo: block! ! !SmallInteger methodsFor: 'arithmetic' stamp: 'hh 10/3/2000 11:47'! / aNumber "Primitive. This primitive (for /) divides the receiver by the argument and returns the result if the division is exact. Fail if the result is not a whole integer. Fail if the argument is 0 or is not a SmallInteger. Optional. No Lookup. See Object documentation whatIsAPrimitive." aNumber isZero ifTrue: [^(ZeroDivide dividend: self) signal]. (aNumber isMemberOf: SmallInteger) ifTrue: [^(Fraction numerator: self denominator: aNumber) reduced] ifFalse: [^super / aNumber]! ! !Unit methodsFor: 'unit arithmetic'! * anotherUnit ^self multipliedBy: anotherUnit! ! !Unit methodsFor: 'unit arithmetic'! / anotherUnit ^self dividedBy: anotherUnit! ! !Unit methodsFor: 'unit arithmetic'! dividedBy: anotherUnit ^self multipliedBy: anotherUnit reciprocal! ! !Unit methodsFor: 'unit arithmetic'! multipliedBy: anotherUnit | unitDictionary units exponents scratch | unitDictionary _ Dictionary new. self unitsAndExponentsDo: [:unit :exponent | (unitDictionary includesKey: unit) ifTrue: [unitDictionary at: unit put: (unitDictionary at: unit) + exponent] ifFalse: [unitDictionary at: unit put: exponent]]. anotherUnit unitsAndExponentsDo: [:unit :exponent | (unitDictionary includesKey: unit) ifTrue: [unitDictionary at: unit put: (unitDictionary at: unit) + exponent] ifFalse: [unitDictionary at: unit put: exponent]]. units _ OrderedCollection new. exponents _ OrderedCollection new. (unitDictionary keys asSortedCollection: CompoundUnit sortBlock) do: [:unit | scratch _ unitDictionary at: unit. scratch isZero ifFalse: [ units add: unit. exponents add: scratch]]. units size = 1 ifTrue: [ (exponents at: 1) = 1 ifTrue: [^units at: 1]]. ^ComplexUnit units: units exponents: exponents! ! !Unit methodsFor: 'unit arithmetic'! per: anotherUnit ^self dividedBy: anotherUnit! ! !Unit methodsFor: 'unit arithmetic'! raisedTo: exponent "Answer the receiver raised to the given exponent." ^ComplexUnit units: (Array with: self) exponents: (Array with: exponent)! ! !Unit methodsFor: 'unit arithmetic'! reciprocal "Answer the inverse of the receiver." ^ComplexUnit units: (Array with: self) exponents: (Array with: -1)! ! !Unit methodsFor: 'unit arithmetic'! squared ^self multipliedBy: self! ! !Unit methodsFor: 'accessing'! abbreviation "Answer an abbreviation for the receiver." | stream | stream _ WriteStream on: String new. self printAbbreviationOn: stream. ^stream contents! ! !Unit methodsFor: 'conversion'! additiveFactor "Only TemperatureUnits have additive factors." ^0! ! !Unit methodsFor: 'conversion'! baseUnits "Answer a Unit object that represents the receiver in reduced form (i.e., with all 'complex' units replaced by SI units)." self subclassResponsibility! ! !Unit methodsFor: 'conversion'! conversionFactor "Answer the factor by which the receiver is larger than the base units from which it is composed." ^self subclassResponsibility! ! !Unit methodsFor: 'conversion'! conversionFactorTo: anotherUnit "Assuming the receiver is consistent with 'anotherUnit', answer a number which represents the amount that a value with the receiver as a unit must be multiplied by to convert to 'anotherUnit' (got it?)" ^self conversionFactor / anotherUnit conversionFactor! ! !Unit methodsFor: 'conversion'! factor: anotherUnit "Factor with respect 'anotherUnit'; answer a new unit equivalent to the receiver." | thisBase argumentBase result | thisBase _ self baseUnits. argumentBase _ anotherUnit baseUnits. result _ thisBase dividedBy: argumentBase. ^anotherUnit multipliedBy: result! ! !Unit methodsFor: 'conversion'! modify: modification "Answer a ModifiedUnit with the receiver and the given modification." self isBaseUnit ifFalse: [self error: 'You can only modify base units.']. ^ModifiedUnit baseUnit: self modification: modification! ! !Unit methodsFor: 'conversion'! prefixedBy: prefixName ^PrefixedUnit prefixName: prefixName unit: self! ! !Unit methodsFor: 'conversion'! uncheckedConvertFrom: unitValue "This is a double-dispatching message used by UnitValue." | factor | factor _ unitValue unit conversionFactorTo: self. ^UnitValue unit: self value: unitValue value * factor! ! !Unit methodsFor: 'conversion'! value: number "Coerce the receiver to be a UnitValue with the given value." ^UnitValue unit: self value: number! ! !Unit methodsFor: 'consistency'! consistentWith: unit "Is the receiver 'consistent' with the argument? Two units must be consistent in order to be added or subtracted." self subclassResponsibility! ! !Unit methodsFor: 'consistency'! consistentWithAnything: anotherUnit ^false! ! !Unit methodsFor: 'consistency'! consistentWithBaseUnit: baseUnit ^self consistentWithAnything: baseUnit! ! !Unit methodsFor: 'consistency'! consistentWithComplexUnit: compositeUnit ^self consistentWithAnything: compositeUnit! ! !Unit methodsFor: 'consistency'! consistentWithCompoundUnit: compositeUnit ^self consistentWithAnything: compositeUnit! ! !Unit methodsFor: 'consistency'! consistentWithModifiedUnit: modifiedUnit ^self consistentWithAnything: modifiedUnit! ! !Unit methodsFor: 'predicates'! isBaseUnit "Answer true if the receiver represents a simple SI base unit with no modifications." ^false! ! !Unit methodsFor: 'predicates'! isNull ^false! ! !Unit methodsFor: 'predicates'! isZeroAsValue: value "If 'value' were the value of a UnitValue with this unit, would it equal zero? Most units are absolute (e.g., length) so this answers true for zero values. Some units are not; for example the Celsius and Fahrenheit temperature scales." ^value isZero! ! !Unit methodsFor: 'printing'! printAbbreviationOn: stream "Print the receiver abbreviated." self subclassResponsibility! ! !Unit methodsFor: 'printing'! printFullNameOn: stream pluralized: pluralized "Print the full name of this unit, pluralized if 'pluralized' is true." self subclassResponsibility! ! !Unit methodsFor: 'printing'! printOn: stream super printOn: stream. stream nextPutAll: ' ['. PrintAbbreviated ifTrue: [self printAbbreviationOn: stream] ifFalse: [self printFullNameOn: stream pluralized: true]. stream nextPut: $]! ! !Unit methodsFor: 'private'! unitPart "For compatibility with UnitValue." ^self! ! !Unit methodsFor: 'enumerating'! unitsAndExponentsDo: block "Evaluate the block once for each unit/exponent pair contained within the receiver. For scalar units, just evaluate the block once with 'self', exponent 1." block value: self value: 1! ! !CompoundUnit methodsFor: 'comparing'! = anotherUnit "This is not the same as #consistentWith:, which checks for isomorphism. This method just checks to see if the receiver and argument have the same units and exponents." anotherUnit class == self class ifFalse: [^false]. ^units = anotherUnit snarfUnits and: [exponents = anotherUnit snarfExponents]! ! !CompoundUnit methodsFor: 'comparing'! hash ^units hash bitXor: exponents hash! ! !CompoundUnit methodsFor: 'conversion'! baseUnits "The receiver already consists entirely of base units." ^self! ! !CompoundUnit methodsFor: 'conversion'! conversionFactor ^1! ! !CompoundUnit methodsFor: 'conversion'! inverse ^self class units: units exponents: (exponents collect: [:each | each negated])! ! !CompoundUnit methodsFor: 'conversion'! prefixedBy: prefixName ^self error: 'You cannot attach prefixes to compound units.'! ! !CompoundUnit methodsFor: 'consistency'! consistentWith: unit ^unit consistentWithCompoundUnit: self! ! !CompoundUnit methodsFor: 'consistency'! consistentWithBaseUnit: baseUnit "Always false, because a CompoundUnit must always have a nontrivial set of units." ^false! ! !CompoundUnit methodsFor: 'consistency'! consistentWithComplexUnit: complexUnit ^self consistentWith: complexUnit baseUnits! ! !CompoundUnit methodsFor: 'consistency'! consistentWithCompoundUnit: compoundUnit "We can check the unit and exponent arrays for equality directly, since the units are assumed to be sorted alphabetically." ^units = compoundUnit snarfUnits and: [exponents = compoundUnit snarfExponents]! ! !CompoundUnit methodsFor: 'consistency'! consistentWithModifiedUnit: modifiedUnit "Same as #consistentWithBaseUnit:." ^false! ! !CompoundUnit methodsFor: 'predicates' stamp: 'hh 10/3/2000 12:02'! includesNegativeExponents ^exponents anySatisfy: [:each | each < 0]! ! !CompoundUnit methodsFor: 'predicates' stamp: 'hh 10/3/2000 12:01'! includesPositiveExponents ^exponents anySatisfy: [:each | each > 0]! ! !CompoundUnit methodsFor: 'predicates'! isNull ^units isEmpty! ! !CompoundUnit methodsFor: 'predicates'! negativeExponentsCount | count | count _ 0. exponents do: [:each | each < 0 ifTrue: [count _ count + 1]]. ^count! ! !CompoundUnit methodsFor: 'predicates'! positiveExponentsCount | count | count _ 0. exponents do: [:each | each > 0 ifTrue: [count _ count + 1]]. ^count! ! !CompoundUnit methodsFor: 'printing'! printAbbreviationOn: stream | first any count | first _ true. any _ false. self unitsAndExponentsDo: [:unit :exponent | exponent > 0 ifTrue: [ any _ true. first ifFalse: [stream nextPut: $*]. first _ false. unit printAbbreviationOn: stream. exponent ~= 1 ifTrue: [stream nextPut: $^; print: exponent]]]. count _ self negativeExponentsCount. count > 0 ifTrue: [ any ifFalse: [stream nextPut: $1]. stream nextPut: $/. count > 1 ifTrue: [stream nextPut: $(]. first _ true. self unitsAndExponentsDo: [:unit :exponent | exponent ~= 1 ifTrue: [ first ifFalse: [stream nextPut: $*]. first _ false. unit printAbbreviationOn: stream. exponent < -1 ifTrue: [stream nextPut: $^; print: exponent negated]]]. count > 1 ifTrue: [stream nextPut: $)]]! ! !CompoundUnit methodsFor: 'printing'! printFullNameOn: stream pluralized: pluralized "Print the full name of this unit, pluralized if 'pluralized' is true." | positive negative | positive _ self includesPositiveExponents. negative _ self includesNegativeExponents. positive ifTrue: [ self printUnitsWhereExponent: [:each | each > 0] on: stream pluralized: pluralized]. (negative and: [positive]) ifTrue: [stream space]. negative ifTrue: [ stream nextPutAll: 'per '. self printUnitsWhereExponent: [:each | each < 0] on: stream pluralized: false]! ! !CompoundUnit methodsFor: 'printing'! printUnitsWhereExponent: block on: stream pluralized: pluralized | power first count index thisPlural | first _ true. count _ (exponents select: block) size. index _ 0. units with: exponents do: [:unit :exponent | (block value: exponent) ifTrue: [ first ifTrue: [first _ false] ifFalse: [stream space]. index _ index + 1. thisPlural _ pluralized and: [index = count]. power _ exponent abs. (power isInteger and: [power <= 3]) ifTrue: [ power = 2 ifTrue: [stream nextPutAll: 'square ']. power = 3 ifTrue: [stream nextPutAll: 'cubic ']]. unit printFullNameOn: stream pluralized: thisPlural. (power > 3 or: [power isInteger not]) ifTrue: [ stream nextPut: $^; print: power]]]! ! !CompoundUnit methodsFor: 'unit arithmetic'! raisedTo: exponent ^self class units: units exponents: (exponents collect: [:each | each * exponent])! ! !CompoundUnit methodsFor: 'unit arithmetic'! reciprocal "Just make a new unit of the same class, with all the exponents negated." ^self class units: units exponents: (exponents collect: [:each | each negated])! ! !CompoundUnit methodsFor: 'private'! snarfExponents ^exponents! ! !CompoundUnit methodsFor: 'private'! snarfUnits ^units! ! !CompoundUnit methodsFor: 'initialization'! units: myUnits exponents: myExponents units _ myUnits. exponents _ myExponents! ! !CompoundUnit methodsFor: 'enumerating'! unitsAndExponentsDo: block units with: exponents do: block! ! !ComplexUnit methodsFor: 'conversion'! baseUnits "Since base units are expensive to compute for ComplexUnits, they are cached here." cachedBaseUnits isNil ifTrue: [cachedBaseUnits _ self calculateBaseUnits]. ^cachedBaseUnits! ! !ComplexUnit methodsFor: 'conversion'! calculateBaseUnits | baseUnits unitDictionary newUnits newExponents scratch | unitDictionary _ IdentityDictionary new. self unitsAndExponentsDo: [:unit :exponent | baseUnits _ unit baseUnits. baseUnits unitsAndExponentsDo: [:subunit :subexponent | (unitDictionary includesKey: subunit) ifFalse: [ unitDictionary at: subunit put: subexponent * exponent] ifTrue: [ unitDictionary at: subunit put: (unitDictionary at: subunit) + (subexponent * exponent)]]]. newUnits _ OrderedCollection new. newExponents _ OrderedCollection new. (unitDictionary keys asSortedCollection: self class sortBlock) do: [:unit | scratch _ unitDictionary at: unit. scratch isZero ifFalse: [ newUnits add: unit. newExponents add: scratch]]. ^CompoundUnit units: newUnits exponents: newExponents! ! !ComplexUnit methodsFor: 'conversion'! conversionFactor ^conversionFactor! ! !ComplexUnit methodsFor: 'conversion'! prefixedBy: prefixName ^self error: 'You cannot attach prefixes to complex units.'! ! !ComplexUnit methodsFor: 'consistency'! consistentWith: unit ^unit consistentWithComplexUnit: self! ! !ComplexUnit methodsFor: 'consistency'! consistentWithBaseUnit: baseUnit "This might be true, since we may be containing non-base units that reduce to the base unit." "For example, 'meters * seconds * Hertz' is consistent with 'meters'." ^self baseUnits consistentWith: baseUnit! ! !ComplexUnit methodsFor: 'consistency'! consistentWithComplexUnit: complexUnit ^self baseUnits consistentWith: complexUnit baseUnits! ! !ComplexUnit methodsFor: 'consistency'! consistentWithCompoundUnit: compoundUnit ^self baseUnits consistentWith: compoundUnit! ! !ComplexUnit methodsFor: 'consistency'! consistentWithModifiedUnit: modifiedUnit "This might be true, since we may be containing non-base units that reduce to the modified unit." "For example, 'moles of hydrogen * seconds * Hertz' is consistent with 'moles of hydrogen'." ^self baseUnits consistentWith: modifiedUnit! ! !ComplexUnit methodsFor: 'initialization'! units: myUnits exponents: myExponents super units: myUnits exponents: myExponents. conversionFactor _ 1. units with: exponents do: [:unit :exponent | conversionFactor _ conversionFactor * (unit conversionFactor raisedTo: exponent)]! ! !ModifiedUnit methodsFor: 'comparing'! = anotherUnit self class = anotherUnit class ifFalse: [^false]. modification = anotherUnit modification ifFalse: [^false]. ^baseUnit = anotherUnit baseUnit! ! !ModifiedUnit methodsFor: 'comparing'! hash ^modification hash bitXor: baseUnit hash! ! !ModifiedUnit methodsFor: 'accessing'! baseUnit ^baseUnit! ! !ModifiedUnit methodsFor: 'accessing'! modification ^modification! ! !ModifiedUnit methodsFor: 'initialization'! baseUnit: myBaseUnit modification: myModification baseUnit _ myBaseUnit. modification _ myModification! ! !ModifiedUnit methodsFor: 'conversion'! baseUnits ^self! ! !ModifiedUnit methodsFor: 'conversion'! conversionFactor ^baseUnit conversionFactor! ! !ModifiedUnit methodsFor: 'consistency'! consistentWith: anotherUnit ^anotherUnit consistentWithModifiedUnit: self! ! !ModifiedUnit methodsFor: 'consistency'! consistentWithBaseUnit: anotherBaseUnit ^false! ! !ModifiedUnit methodsFor: 'consistency'! consistentWithComplexUnit: complexUnit ^self consistentWith: complexUnit baseUnits! ! !ModifiedUnit methodsFor: 'consistency'! consistentWithCompoundUnit: compoundUnit ^false! ! !ModifiedUnit methodsFor: 'consistency'! consistentWithModifiedUnit: modifiedUnit self class == modifiedUnit class ifFalse: [^false]. ^baseUnit = modifiedUnit baseUnit and: [modification = modifiedUnit modification]! ! !ModifiedUnit methodsFor: 'predicates'! isBaseUnit "ModifiedUnits are effectively new base units..." ^true! ! !ModifiedUnit methodsFor: 'printing'! printAbbreviationOn: stream baseUnit printAbbreviationOn: stream. stream nextPut: $(. modification isString ifTrue: [stream nextPutAll: modification] ifFalse: [stream print: modification]. stream nextPut: $)! ! !ModifiedUnit methodsFor: 'printing'! printFullNameOn: stream pluralized: pluralized baseUnit printFullNameOn: stream pluralized: pluralized. stream nextPutAll: ' (of '. "Can't avoid the #isString, since strings print differently than other objects, and it is valid to have non-strings as the modification." modification isString ifTrue: [stream nextPutAll: modification] ifFalse: [stream print: modification]. stream nextPut: $)! ! !NamedUnit methodsFor: 'accessing'! abbreviation ^abbreviation! ! !NamedUnit methodsFor: 'accessing'! name ^name! ! !NamedUnit methodsFor: 'accessing'! pluralName ^pluralName! ! !NamedUnit methodsFor: 'initialization'! abbreviation: myAbbreviation name: myName pluralName: myPluralName abbreviation _ myAbbreviation. name _ myName. pluralName _ myPluralName! ! !NamedUnit methodsFor: 'printing'! printAbbreviationOn: stream stream nextPutAll: abbreviation! ! !NamedUnit methodsFor: 'printing'! printFullNameOn: stream pluralized: pluralized pluralized ifTrue: [stream nextPutAll: pluralName] ifFalse: [stream nextPutAll: name]! ! !BaseUnit methodsFor: 'conversion'! baseUnits "This is already a base unit." ^self! ! !BaseUnit methodsFor: 'conversion'! conversionFactor ^1. "by definition"! ! !BaseUnit methodsFor: 'consistency'! consistentWith: unit ^unit consistentWithBaseUnit: self! ! !BaseUnit methodsFor: 'consistency'! consistentWithBaseUnit: unit ^self == unit! ! !BaseUnit methodsFor: 'consistency'! consistentWithComplexUnit: complexUnit ^self consistentWith: complexUnit baseUnits! ! !BaseUnit methodsFor: 'predicates'! isBaseUnit ^true! ! !DerivedUnit methodsFor: 'conversion'! baseUnits ^unitValue unit baseUnits! ! !DerivedUnit methodsFor: 'conversion'! conversionFactor ^unitValue value * unitValue unit conversionFactor! ! !DerivedUnit methodsFor: 'consistency'! consistentWith: unit "Short-circuit the double dispatching here." ^unitValue unitPart consistentWith: unit! ! !DerivedUnit methodsFor: 'consistency'! consistentWithBaseUnit: baseUnit ^baseUnit consistentWith: self unit! ! !DerivedUnit methodsFor: 'consistency'! consistentWithComplexUnit: complexUnit ^complexUnit consistentWith: self unit! ! !DerivedUnit methodsFor: 'consistency'! consistentWithCompoundUnit: compoundUnit ^compoundUnit consistentWith: self unit! ! !DerivedUnit methodsFor: 'consistency'! consistentWithModifiedUnit: modifiedUnit ^modifiedUnit consistentWith: self unit! ! !DerivedUnit methodsFor: 'accessing'! unit ^unitValue unit! ! !DerivedUnit methodsFor: 'initialization'! value: myUnitValue unitValue _ myUnitValue! ! !PrefixedUnit methodsFor: 'comparing'! = anotherUnit self class == anotherUnit class ifFalse: [^false]. ^prefix = anotherUnit prefix and: [unit = anotherUnit unit]! ! !PrefixedUnit methodsFor: 'comparing'! hash ^prefix hash bitXor: unit hash! ! !PrefixedUnit methodsFor: 'conversion'! baseUnits ^unit baseUnits! ! !PrefixedUnit methodsFor: 'conversion'! conversionFactor ^prefix scalingFactor * unit conversionFactor! ! !PrefixedUnit methodsFor: 'conversion'! prefixedBy: prefixName ^self error: 'This unit already has a prefix.'! ! !PrefixedUnit methodsFor: 'consistency'! consistentWith: anotherUnit "Short-circuit the double dispatching; just compare the actual unit." ^anotherUnit consistentWith: unit! ! !PrefixedUnit methodsFor: 'consistency'! consistentWithAnything: anotherUnit ^unit consistentWith: anotherUnit! ! !PrefixedUnit methodsFor: 'accessing'! prefix ^prefix! ! !PrefixedUnit methodsFor: 'accessing'! unit ^unit! ! !PrefixedUnit methodsFor: 'initialization'! prefix: myPrefix unit: myUnit prefix _ myPrefix. unit _ myUnit! ! !PrefixedUnit methodsFor: 'printing'! printAbbreviationOn: stream stream nextPutAll: prefix abbreviation. unit printAbbreviationOn: stream! ! !PrefixedUnit methodsFor: 'printing'! printFullNameOn: stream pluralized: pluralized stream nextPutAll: prefix name. unit printFullNameOn: stream pluralized: pluralized! ! !TemperatureBaseUnit methodsFor: 'predicates'! isZeroAsValue: value ^false! ! !TemperatureBaseUnit methodsFor: 'conversion'! uncheckedConvertFrom: anotherUnitValue | newValue | newValue := (anotherUnitValue value - anotherUnitValue unitPart additiveFactor) * (anotherUnitValue unit conversionFactorTo: self). ^UnitValue unit: self value: newValue! ! !TemperatureUnit methodsFor: 'accessing'! additiveFactor ^additiveFactor! ! !TemperatureUnit methodsFor: 'initialization'! additiveFactor: myAdditiveFactor additiveFactor _ myAdditiveFactor! ! !TemperatureUnit methodsFor: 'as yet unclassified'! isZeroAsValue: value ! ! !TemperatureUnit methodsFor: 'conversion'! uncheckedConvertFrom: anotherUnitValue | kelvin newValue | ^anotherUnitValue unit isBaseUnit ifTrue: [ newValue := (anotherUnitValue value / (self conversionFactorTo: anotherUnitValue unit)) + additiveFactor. UnitValue unit: self value: newValue] ifFalse: [ kelvin _ anotherUnitValue uncheckedConvertTo: Unit degreesKelvin. kelvin convertTo: self]! ! !Unit class methodsFor: 'area units'! acres ^NamedUnit named: 'acres'! ! !Unit class methodsFor: 'SI base units'! candela ^BaseUnit candela! ! !Unit class methodsFor: 'SI base units'! grams ^BaseUnit grams! ! !Unit class methodsFor: 'SI base units'! meters ^BaseUnit meters! ! !Unit class methodsFor: 'SI base units'! moles ^BaseUnit moles! ! !Unit class methodsFor: 'SI base units'! seconds ^BaseUnit seconds! ! !Unit class methodsFor: 'length units'! centimeters ^self meters prefixedBy: 'centi'! ! !Unit class methodsFor: 'length units'! feet ^NamedUnit named: 'feet'! ! !Unit class methodsFor: 'length units'! inches ^NamedUnit named: 'inches'! ! !Unit class methodsFor: 'length units'! kilometers ^self meters prefixedBy: 'kilo'! ! !Unit class methodsFor: 'length units'! miles ^NamedUnit named: 'miles'! ! !Unit class methodsFor: 'length units'! millimeters ^self meters prefixedBy: 'milli'! ! !Unit class methodsFor: 'length units'! yards ^NamedUnit named: 'yards'! ! !Unit class methodsFor: 'time units'! days ^NamedUnit named: 'days'! ! !Unit class methodsFor: 'time units'! hertz ^NamedUnit named: 'hertz'! ! !Unit class methodsFor: 'time units'! hours ^NamedUnit named: 'hours'! ! !Unit class methodsFor: 'time units'! megahertz ^self hertz prefixedBy: 'mega'! ! !Unit class methodsFor: 'time units'! minutes ^NamedUnit named: 'minutes'! ! !Unit class methodsFor: 'time units'! years ^NamedUnit named: 'years'! ! !Unit class methodsFor: 'temperature units'! degreesCelsius ^NamedUnit named: 'degrees Celsius'! ! !Unit class methodsFor: 'temperature units'! degreesFahrenheit ^NamedUnit named: 'degrees Fahrenheit'! ! !Unit class methodsFor: 'temperature units'! degreesKelvin ^NamedUnit named: 'degrees Kelvin'! ! !Unit class methodsFor: 'miscellaneous units'! gramsPerMole ^Unit grams / Unit moles! ! !Unit class methodsFor: 'initialization'! initialize "Unit initialize." "This is the master 'initialize' method. It calls the #initializeClass methods of the classes in this package *in the appropriate order*. The reason I don't just have the fileout code call #initialize for each class that needs initialization is that this package depends on classes being initialized in a certain order, and the 'topological' fileout order generated by some Smalltalks does not match this order." SIPrefix initializeClass. BaseUnit initializeClass. NamedUnit initializeClass! ! !Unit class methodsFor: 'mass units'! kilograms ^self grams prefixedBy: 'kilo'! ! !Unit class methodsFor: 'generalized lookup'! named: unitName ^self withPluralName: unitName ifAbsent: [self withSingularName: unitName]! ! !Unit class methodsFor: 'generalized lookup'! named: unitName ifAbsent: exceptionBlock ^self withPluralName: unitName ifAbsent: [self withSingularName: unitName ifAbsent: exceptionBlock]! ! !Unit class methodsFor: 'generalized lookup'! withAbbreviation: abbreviation ^self withAbbreviation: abbreviation ifAbsent: [self error: 'There is no unit with the abbreviation ', abbreviation printString, '.']! ! !Unit class methodsFor: 'generalized lookup'! withAbbreviation: unitName ifAbsent: exceptionBlock | unit | unit _ NamedUnit withAbbreviation: unitName ifAbsent: [nil]. unit isNil ifFalse: [^unit]. unit _ BaseUnit withAbbreviation: unitName ifAbsent: exceptionBlock. ^unit! ! !Unit class methodsFor: 'generalized lookup'! withPluralName: unitName ^self withPluralName: unitName ifAbsent: [self error: 'There is no unit named ', unitName printString, '.']! ! !Unit class methodsFor: 'generalized lookup'! withPluralName: unitName ifAbsent: exceptionBlock | unit | unit _ NamedUnit withPluralName: unitName ifAbsent: [nil]. unit isNil ifFalse: [^unit]. unit _ BaseUnit withPluralName: unitName ifAbsent: exceptionBlock. ^unit! ! !Unit class methodsFor: 'generalized lookup'! withSingularName: unitName ^self withSingularName: unitName ifAbsent: [self error: 'There is no unit named ', unitName printString, '.']! ! !Unit class methodsFor: 'generalized lookup'! withSingularName: unitName ifAbsent: exceptionBlock | unit | unit _ NamedUnit withSingularName: unitName ifAbsent: [nil]. unit isNil ifFalse: [^unit]. unit _ BaseUnit withSingularName: unitName ifAbsent: exceptionBlock. ^unit! ! !Unit class methodsFor: 'force units'! newtons ^NamedUnit named: 'newtons'! ! !Unit class methodsFor: 'parameters'! printAbbreviated ^PrintAbbreviated! ! !Unit class methodsFor: 'parameters'! printAbbreviated: boolean "Should we print units in abbreviated format?" PrintAbbreviated _ boolean! ! !CompoundUnit class methodsFor: 'instance creation'! null "A special CompoundUnit used to coerce numbers into the Unit domain." ^self units: #() exponents: #()! ! !CompoundUnit class methodsFor: 'instance creation'! units: units exponents: exponents ^self new units: units exponents: exponents! ! !CompoundUnit class methodsFor: 'sorting'! sortBlock ^[:left :right | left abbreviation < right abbreviation]! ! !ComplexUnit class methodsFor: 'instance creation'! units: units exponents: exponents units detect: [:each | each isBaseUnit not] ifNone: [ "All the units are base units ... we can use a CompoundUnit instead to save space." ^CompoundUnit units: units exponents: exponents]. ^self new units: units exponents: exponents! ! !ModifiedUnit class methodsFor: 'instance creation'! baseUnit: baseUnit modification: modification ^self new baseUnit: baseUnit modification: modification! ! !NamedUnit class methodsFor: 'instance creation'! abbreviation: abbreviation name: unitName pluralName: pluralName "Create and index a new NamedUnit." | unit | unit _ self new abbreviation: abbreviation name: unitName pluralName: pluralName. UnitsByAbbreviation at: abbreviation put: unit. UnitsByName at: unitName put: unit. UnitsByPluralName at: pluralName put: unit. ^unit! ! !NamedUnit class methodsFor: 'instance creation'! abbreviation: abbreviation name: unitName pluralName: pluralName value: unitValue ^DerivedUnit abbreviation: abbreviation name: unitName pluralName: pluralName value: unitValue! ! !NamedUnit class methodsFor: 'instance creation'! abbreviation: abbreviation name: unitName pluralName: pluralName value: unitValue additiveFactor: additiveFactor ^TemperatureUnit abbreviation: abbreviation name: unitName pluralName: pluralName value: unitValue additiveFactor: additiveFactor! ! !NamedUnit class methodsFor: 'initialization'! initializeAreaUnits self abbreviation: 'acre' name: 'acre' pluralName: 'acres' value: (4046.87260987 units: Unit meters squared)! ! !NamedUnit class methodsFor: 'initialization'! initializeClass "NamedUnit initializeClass." "Do not rename this to #initialize." UnitsByAbbreviation _ Dictionary new. UnitsByName _ Dictionary new. UnitsByPluralName _ Dictionary new. self initializeUnits! ! !NamedUnit class methodsFor: 'initialization'! initializeForceUnits self abbreviation: 'N' name: 'newton' pluralName: 'newtons' value: (1 units: (Unit kilograms * Unit meters) / Unit seconds squared)! ! !NamedUnit class methodsFor: 'initialization'! initializeLengthUnits self abbreviation: 'in' name: 'inch' pluralName: 'inches' value: (2.54 units: Unit centimeters). self abbreviation: 'ft' name: 'foot' pluralName: 'feet' value: (12 units: Unit inches). self abbreviation: 'yd' name: 'yard' pluralName: 'yards' value: (3 units: Unit feet). self abbreviation: 'mi' name: 'mile' pluralName: 'miles' value: (5280 units: Unit feet)! ! !NamedUnit class methodsFor: 'initialization'! initializeTemperatureUnits TemperatureBaseUnit abbreviation: 'K' name: 'degree Kelvin' pluralName: 'degrees Kelvin'. self abbreviation: 'F' name: 'degree Fahrenheit' pluralName: 'degrees Fahrenheit' value: (5/9 units: Unit degreesKelvin) additiveFactor: -459.67. self abbreviation: 'C' name: 'degree Celsius' pluralName: 'degrees Celsius' value: (1 units: Unit degreesKelvin) additiveFactor: -273.15! ! !NamedUnit class methodsFor: 'initialization'! initializeTimeUnits self abbreviation: 'min' name: 'minute' pluralName: 'minutes' value: (60 units: Unit seconds). self abbreviation: 'h' name: 'hour' pluralName: 'hours' value: (60 units: Unit minutes). self abbreviation: 'd' name: 'day' pluralName: 'days' value: (24 units: Unit hours). self abbreviation: 'yr' name: 'year' pluralName: 'years' value: (365.242198781 units: Unit days). self abbreviation: 'Hz' name: 'hertz' pluralName: 'hertz' value: 1 / (1 units: Unit seconds)! ! !NamedUnit class methodsFor: 'initialization'! initializeUnits self initializeAreaUnits. self initializeLengthUnits. self initializeForceUnits. self initializeTemperatureUnits. self initializeTimeUnits! ! !NamedUnit class methodsFor: 'accessing'! withAbbreviation: abbreviation ifAbsent: exceptionBlock ^UnitsByAbbreviation at: abbreviation ifAbsent: exceptionBlock! ! !NamedUnit class methodsFor: 'accessing'! withPluralName: pluralName ifAbsent: exceptionBlock ^UnitsByPluralName at: pluralName ifAbsent: exceptionBlock! ! !NamedUnit class methodsFor: 'accessing'! withSingularName: unitName ifAbsent: exceptionBlock ^UnitsByName at: unitName ifAbsent: exceptionBlock! ! !BaseUnit class methodsFor: 'initialization'! addUnit: unitName plural: pluralName abbreviation: abbreviation | unit | unit _ self new abbreviation: abbreviation name: unitName pluralName: pluralName. SIUnitsByName at: unitName put: unit. SIUnitsByPluralName at: pluralName put: unit. SIUnitsByAbbreviation at: abbreviation put: unit. ^unit! ! !BaseUnit class methodsFor: 'initialization'! initializeClass "BaseUnit initializeClass." "Do not rename this to #initialize." PrintAbbreviated _ false. "may as well do it here ..." SIUnitsByName _ Dictionary new. SIUnitsByAbbreviation _ Dictionary new. SIUnitsByPluralName _ Dictionary new. self addUnit: 'gram' plural: 'grams' abbreviation: 'g'; addUnit: 'meter' plural: 'meters' abbreviation: 'm'; addUnit: 'second' plural: 'seconds' abbreviation: 's'; addUnit: 'candela' plural: 'candela' abbreviation: 'c'; addUnit: 'mole' plural: 'moles' abbreviation: 'mol'; addUnit: 'base pair' plural: 'base pairs' abbreviation: 'BP'; "for DNA" addUnit: 'donut' plural: 'donuts' abbreviation: 'donut'! ! !BaseUnit class methodsFor: 'base units'! basePairs ^self withPluralName: 'base pairs'! ! !BaseUnit class methodsFor: 'base units'! candela ^self withPluralName: 'candela'! ! !BaseUnit class methodsFor: 'base units'! donuts ^self withPluralName: 'donuts'! ! !BaseUnit class methodsFor: 'base units'! grams ^self withPluralName: 'grams'! ! !BaseUnit class methodsFor: 'base units'! meters ^self withPluralName: 'meters'! ! !BaseUnit class methodsFor: 'base units'! moles ^self withPluralName: 'moles'! ! !BaseUnit class methodsFor: 'base units'! seconds ^self withPluralName: 'seconds'! ! !BaseUnit class methodsFor: 'accessing'! withAbbreviation: abbreviation ifAbsent: exceptionBlock ^SIUnitsByAbbreviation at: abbreviation ifAbsent: exceptionBlock! ! !BaseUnit class methodsFor: 'accessing'! withPluralName: unitName ifAbsent: exceptionBlock ^SIUnitsByPluralName at: unitName ifAbsent: exceptionBlock! ! !BaseUnit class methodsFor: 'accessing'! withSingularName: unitName ifAbsent: exceptionBlock ^SIUnitsByName at: unitName ifAbsent: exceptionBlock! ! !DerivedUnit class methodsFor: 'instance creation'! abbreviation: abbreviation name: unitName pluralName: pluralName value: unitValue | unit | unit _ super abbreviation: abbreviation name: unitName pluralName: pluralName. unit value: unitValue. ^unit! ! !PrefixedUnit class methodsFor: 'instance creation'! prefix: prefix unit: unit ^self new prefix: prefix unit: unit! ! !PrefixedUnit class methodsFor: 'instance creation'! prefixName: prefixName unit: unit | prefix | prefix _ SIPrefix named: prefixName. ^self prefix: prefix unit: unit! ! !TemperatureUnit class methodsFor: 'instance creation'! abbreviation: abbreviation name: unitName pluralName: pluralName value: unitValue additiveFactor: additiveFactor | unit | unit _ super abbreviation: abbreviation name: unitName pluralName: pluralName value: unitValue. unit additiveFactor: additiveFactor. ^unit! ! !UnitValue methodsFor: 'arithmetic'! % number self shouldNotImplement! ! !UnitValue methodsFor: 'arithmetic'! * number ^number multiplyByUnit: self! ! !UnitValue methodsFor: 'arithmetic'! + number ^number addToUnit: self! ! !UnitValue methodsFor: 'arithmetic'! - number ^number subtractFromUnit: self! ! !UnitValue methodsFor: 'arithmetic'! / number ^number divideIntoUnit: self! ! !UnitValue methodsFor: 'arithmetic'! negated ^self class unit: unit value: value negated! ! !UnitValue methodsFor: 'mathematical'! ** exponent ^self raisedTo: exponent! ! !UnitValue methodsFor: 'mathematical'! raisedTo: exponent ^self class unit: (unit raisedTo: exponent) value: (value raisedTo: exponent)! ! !UnitValue methodsFor: 'mathematical'! sqrt ^self class unit: (unit raisedTo: 1/2) value: value sqrt! ! !UnitValue methodsFor: 'comparing'! < number ^number lessFromUnit: self! ! !UnitValue methodsFor: 'comparing'! = number ^number equalFromUnit: self! ! !UnitValue methodsFor: 'double dispatching'! addToUnit: unitValue | sum | ^(self consistentWith: unitValue) ifTrue: [ sum _ self uncheckedConvertTo: unitValue. sum setValue: unitValue value + sum value. sum reduced] ifFalse: [unitValue inconsistentUnits: self selector: #+]! ! !UnitValue methodsFor: 'double dispatching'! divideIntoUnit: unitValue "Let the VM check for division by zero." ^(self class unit: (unitValue unit dividedBy: unit) value: unitValue value / value) reduced! ! !UnitValue methodsFor: 'double dispatching' stamp: 'hh 3/4/1999 15:08'! equalFromUnit: unitValue "Is 'unitValue' equal in magnitude to the receiver?" | converted | ^(self consistentWith: unitValue) ifTrue: [ "self halt. *** why? ***" converted _ self uncheckedConvertTo: unitValue. unitValue value = converted value] ifFalse: [false]! ! !UnitValue methodsFor: 'double dispatching'! lessFromUnit: unitValue "Does 'unitValue' have a smaller magnitude than us?" | converted | ^(self consistentWith: unitValue) ifTrue: [ converted _ self uncheckedConvertTo: unitValue. unitValue value < converted value] ifFalse: [unitValue inconsistentUnits: self selector: #<]! ! !UnitValue methodsFor: 'double dispatching'! multiplyByUnit: unitValue ^(self class unit: (unit multipliedBy: unitValue unit) value: unitValue value * value) reduced! ! !UnitValue methodsFor: 'double dispatching'! subtractFromUnit: unitValue "Does 'unitValue' have a smaller magnitude than us?" | converted | ^(self consistentWith: unitValue) ifTrue: [ converted _ self uncheckedConvertTo: unitValue. unitValue value < converted value] ifFalse: [unitValue inconsistentUnits: self selector: #<]! ! !UnitValue methodsFor: 'converting'! baseUnits "Answer the receiver reduced to base units only." ^self convertTo: unit baseUnits! ! !UnitValue methodsFor: 'converting'! convertTo: anotherUnit "Convert the receiver to have the same units as 'anotherUnit'. Apply any appropriate scaling factors. Gives an error if the receiver is not consistent with 'anotherUnit'." | u | u _ self class unitFor: anotherUnit. ^(u consistentWith: unit) ifTrue: [u uncheckedConvertFrom: self] ifFalse: [self inconsistentUnits: u selector: #convertTo:]! ! !UnitValue methodsFor: 'converting'! factor: unitValue "Factor with respect to another Unit or UnitValue." | newUnit | newUnit _ unit factor: (self class unitFor: unitValue). ^self convertTo: newUnit! ! !UnitValue methodsFor: 'converting'! reduced "If the receiver's units have 'vanished' (e.g., by dividing 2 seconds by 3 seconds), answer the remaining scalar part of the receiver. Otherwise, answer the receiver unchanged." ^unit isNull ifTrue: [value] ifFalse: [self]! ! !UnitValue methodsFor: 'rounding'! ceiling ^self class unit: unit value: value ceiling! ! !UnitValue methodsFor: 'rounding'! floor ^self class unit: unit value: value floor! ! !UnitValue methodsFor: 'rounding'! roundTo: number ^self class unit: unit value: (value roundTo: number)! ! !UnitValue methodsFor: 'rounding'! roundUpTo: number ^self class unit: unit value: (value roundUpTo: number)! ! !UnitValue methodsFor: 'rounding'! rounded ^self class unit: unit value: value rounded! ! !UnitValue methodsFor: 'rounding'! truncateTo: number ^self class unit: unit value: (value truncateTo: number)! ! !UnitValue methodsFor: 'coercing' stamp: 'hh 3/4/1999 14:40'! adaptToInteger: rcvr andSend: selector "handle coercion for mixed arithmetic uses the method already present for DD" ^(self coerce: rcvr) perform: selector with: self! ! !UnitValue methodsFor: 'coercing' stamp: 'hh 3/4/1999 14:40'! adaptToNumber: rcvr andSend: selector "handle coercion for mixed arithmetic uses the method already present for DD" ^(self coerce: rcvr) perform: selector with: self! ! !UnitValue methodsFor: 'coercing'! coerce: number ^self class unit: CompoundUnit null value: number! ! !UnitValue methodsFor: 'coercing'! generality ^46! ! !UnitValue methodsFor: 'consistency'! consistentWith: anotherUnit ^unit consistentWith: anotherUnit unitPart! ! !UnitValue methodsFor: 'consistency' stamp: 'hh 3/4/1999 14:33'! inconsistentUnits: argument selector: selector self error: 'Inconsistent units for #', selector, ' between ', self printString, ' and ', argument printString! ! !UnitValue methodsFor: 'testing'! isZero ^unit isZeroAsValue: value! ! !UnitValue methodsFor: 'testing'! negative ^value negative! ! !UnitValue methodsFor: 'testing'! sign ^value sign! ! !UnitValue methodsFor: 'printing'! printOn: stream value printOn: stream. Unit printAbbreviated ifTrue: [ stream nextPut: $_. unit printAbbreviationOn: stream] ifFalse: [ stream space. unit printFullNameOn: stream pluralized: value ~= 1]! ! !UnitValue methodsFor: 'private'! setValue: newValue value _ newValue! ! !UnitValue methodsFor: 'private'! uncheckedConvertTo: anotherUnit ^anotherUnit unitPart uncheckedConvertFrom: self! ! !UnitValue methodsFor: 'private'! unitPart "Answer the unit part of the receiver." ^unit! ! !UnitValue methodsFor: 'accessing'! unit ^unit! ! !UnitValue methodsFor: 'accessing'! value "A special interpretation of #value to answer the scalar part of the unit." ^value! ! !UnitValue methodsFor: 'initialization'! unit: myUnit value: myValue unit _ myUnit. value _ myValue! ! !UnitValue class methodsFor: 'instance creation'! one ^1! ! !UnitValue class methodsFor: 'instance creation'! unit: unit value: value ^self basicNew unit: unit value: value! ! !UnitValue class methodsFor: 'instance creation'! zero ^0! ! !UnitValue class methodsFor: 'accessing'! unitFor: object "Some methods in UnitValue (and Number>>#units:) can take either a Unit object, or a symbol representing a unit name, or a UnitValue object. If it is a symbol, it is sent as a message to Unit class to retrieve the actual unit object. If it is a UnitValue, the unitPart is taken." "Since Dolphin has no #isSymbol predicate, we have to compare classes directly here." ^object class == Symbol ifTrue: [Unit perform: object] ifFalse: [object unitPart]! ! Unit initialize! "Postscript: Initialize the Units package" Unit initialize. !