├── .project ├── src ├── .properties ├── Units-Core │ ├── package.st │ ├── String.extension.st │ ├── TemperatureBaseUnit.class.st │ ├── Point.extension.st │ ├── MessageDelegate.class.st │ ├── TemperatureUnit.class.st │ ├── DerivedUnit.class.st │ ├── PrefixedUnit.class.st │ ├── ModifiedUnit.class.st │ ├── ComplexUnit.class.st │ ├── SIPrefix.class.st │ ├── BaseUnit.class.st │ ├── CompoundUnit.class.st │ ├── NamedUnit.class.st │ ├── Number.extension.st │ ├── UnitValue.class.st │ └── Unit.class.st ├── Units-Tests │ ├── package.st │ └── UnitsTest.class.st ├── BaselineOfUnits │ ├── package.st │ └── BaselineOfUnits.class.st └── ConfigurationOfUnits │ ├── package.st │ └── ConfigurationOfUnits.class.st ├── .smalltalk.ston ├── .travis.yml ├── workflows └── build.yml ├── .github └── workflows │ └── build.yml ├── LICENSE └── README.md /.project: -------------------------------------------------------------------------------- 1 | { 2 | 'srcDirectory' : 'src' 3 | } -------------------------------------------------------------------------------- /src/.properties: -------------------------------------------------------------------------------- 1 | { 2 | #format : #tonel 3 | } -------------------------------------------------------------------------------- /src/Units-Core/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Units-Core' } 2 | -------------------------------------------------------------------------------- /src/Units-Tests/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Units-Tests' } 2 | -------------------------------------------------------------------------------- /src/BaselineOfUnits/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #BaselineOfUnits } 2 | -------------------------------------------------------------------------------- /src/ConfigurationOfUnits/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #ConfigurationOfUnits } 2 | -------------------------------------------------------------------------------- /.smalltalk.ston: -------------------------------------------------------------------------------- 1 | SmalltalkCISpec { 2 | #loading : [ 3 | SCIMetacelloLoadSpec { 4 | #baseline : 'Units', 5 | #directory : 'src' 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: smalltalk 2 | sudo: false 3 | 4 | os: 5 | - linux 6 | - osx 7 | 8 | smalltalk: 9 | - Pharo64-9.0 10 | - Pharo64-8.0 11 | - Pharo64-7.0 12 | 13 | matrix: 14 | fast_finish: true 15 | -------------------------------------------------------------------------------- /src/Units-Core/String.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #String } 2 | 3 | { #category : #'*Units-Core' } 4 | String >> asUnit [ 5 | "Answer the receiver as a unit." 6 | 7 | ^UnitValue unitFor: self asSymbol 8 | ] 9 | 10 | { #category : #'*Units-Core' } 11 | String >> asUnitValue [ 12 | "Answer the receiver as a unit value." 13 | 14 | |n p i| 15 | n := self asNumber. 16 | i := self findString: (p := n printString). 17 | i = 0 ifTrue: [^nil]. 18 | ^n units: (self allButFirst: p size) withBlanksTrimmed asUnit 19 | 20 | ] 21 | -------------------------------------------------------------------------------- /src/BaselineOfUnits/BaselineOfUnits.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #BaselineOfUnits, 3 | #superclass : #BaselineOf, 4 | #category : #BaselineOfUnits 5 | } 6 | 7 | { #category : #baselines } 8 | BaselineOfUnits >> baseline: spec [ 9 | 10 | 11 | spec for: #'common' do: [ 12 | spec 13 | package: 'Units-Core'; 14 | package: 'Units-Tests' with: [ spec requires: 'Units-Core' ]. 15 | 16 | spec 17 | group: 'default' with: #('core' 'test'); 18 | group: 'core' with: #('Units-Core'); 19 | group: 'test' with: #('Units-Tests')]. 20 | 21 | 22 | 23 | ] 24 | -------------------------------------------------------------------------------- /src/Units-Core/TemperatureBaseUnit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Temperature units are different because they require 3 | a general linear transformation for basic arithmetic operations. 4 | 5 | " 6 | Class { 7 | #name : #TemperatureBaseUnit, 8 | #superclass : #BaseUnit, 9 | #category : #'Units-Core' 10 | } 11 | 12 | { #category : #predicates } 13 | TemperatureBaseUnit >> isZeroAsValue: value [ 14 | ^false 15 | ] 16 | 17 | { #category : #conversion } 18 | TemperatureBaseUnit >> uncheckedConvertFrom: anotherUnitValue [ 19 | | newValue | 20 | newValue := 21 | (anotherUnitValue value - anotherUnitValue unitPart additiveFactor) * 22 | (anotherUnitValue unit conversionFactorTo: self). 23 | ^UnitValue unit: self value: newValue 24 | ] 25 | -------------------------------------------------------------------------------- /src/Units-Core/Point.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Point } 2 | 3 | { #category : #'*Units-Core' } 4 | Point >> as [ 5 | "Answer a delegate on the receiver #as: to handle the next message." 6 | 7 | ^MessageDelegate on: [:m | self as: m selector] 8 | ] 9 | 10 | { #category : #'*Units-Core' } 11 | Point >> as: anotherUnit [ 12 | "Convert the receiver to have the same units as 'anotherUnit'. Apply any appropriate 13 | scaling factors. Gives an error if the receiver's x and y are not consistent with 'anotherUnit'." 14 | 15 | ^(self x as: anotherUnit) @ (self y as: anotherUnit) 16 | ] 17 | 18 | { #category : #'*Units-Core' } 19 | Point >> value [ 20 | "Answer a new point with the x and y being the respective 21 | values of the receiver's ordinates." 22 | 23 | ^self x value @ self y value 24 | ] 25 | -------------------------------------------------------------------------------- /workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 5 | 6 | # Controls when the action will run. Triggers the workflow on push or pull request 7 | # events but only for the development branch 8 | on: 9 | pull_request: 10 | types: [assigned, opened, synchronize, reopened] 11 | 12 | push: 13 | branches: 14 | - development 15 | 16 | jobs: 17 | build: 18 | strategy: 19 | matrix: 20 | pharoversion: [ Pharo64-11, Pharo64-10 ] 21 | name: ${{ matrix.pharoversion }} 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: hpi-swa/setup-smalltalkCI@v1 26 | id: smalltalkci 27 | with: 28 | smalltalk-version: ${{ matrix.pharoversion }} 29 | - run: smalltalkci -s ${{ steps.smalltalkci.outputs.smalltalk-version }} 30 | shell: bash 31 | timeout-minutes: 15 32 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 5 | 6 | # Controls when the action will run. Triggers the workflow on push or pull request 7 | # events but only for the development branch 8 | on: 9 | pull_request: 10 | types: [assigned, opened, synchronize, reopened] 11 | 12 | push: 13 | branches: 14 | - main 15 | 16 | jobs: 17 | build: 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | pharoversion: [ Pharo64-alpha, Pharo64-12, Pharo64-11 ] 22 | name: ${{ matrix.pharoversion }} 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: hpi-swa/setup-smalltalkCI@v1 27 | id: smalltalkci 28 | with: 29 | smalltalk-version: ${{ matrix.pharoversion }} 30 | - run: smalltalkci -s ${{ steps.smalltalkci.outputs.smalltalk-version }} 31 | shell: bash 32 | timeout-minutes: 15 33 | -------------------------------------------------------------------------------- /src/Units-Core/MessageDelegate.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An object that passes messages on to a private delegate 3 | " 4 | Class { 5 | #name : #MessageDelegate, 6 | #superclass : #ProtoObject, 7 | #instVars : [ 8 | 'privateDelegate' 9 | ], 10 | #category : #'Units-Core' 11 | } 12 | 13 | { #category : #'instance creation' } 14 | MessageDelegate class >> on: anObject [ 15 | "Answer a new instance of the receiver with the given delegate." 16 | 17 | ^self new privateDelegate: anObject 18 | ] 19 | 20 | { #category : #'system primitives' } 21 | MessageDelegate >> doesNotUnderstand: aMessage [ 22 | "Pass on to the delegate." 23 | 24 | ^self privateDelegate value: aMessage 25 | ] 26 | 27 | { #category : #accessing } 28 | MessageDelegate >> privateDelegate [ 29 | "Answer the value of privateDelegate" 30 | 31 | ^ privateDelegate 32 | ] 33 | 34 | { #category : #accessing } 35 | MessageDelegate >> privateDelegate: anObject [ 36 | "Set the value of privateDelegate" 37 | 38 | privateDelegate := anObject 39 | ] 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ZWEIDENKER GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Units-Core/TemperatureUnit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A temperature needs an additional ""additive factor"" to handle, 3 | e.g., Celsius to Fahrenheit. 4 | 5 | " 6 | Class { 7 | #name : #TemperatureUnit, 8 | #superclass : #DerivedUnit, 9 | #instVars : [ 10 | 'additiveFactor' 11 | ], 12 | #category : #'Units-Core' 13 | } 14 | 15 | { #category : #'instance creation' } 16 | TemperatureUnit class >> abbreviation: abbreviation 17 | name: unitName 18 | pluralName: pluralName 19 | value: unitValue 20 | additiveFactor: additiveFactor [ 21 | | unit | 22 | unit := super 23 | abbreviation: abbreviation 24 | name: unitName 25 | pluralName: pluralName 26 | value: unitValue. 27 | unit additiveFactor: additiveFactor. 28 | ^unit 29 | ] 30 | 31 | { #category : #accessing } 32 | TemperatureUnit >> additiveFactor [ 33 | ^additiveFactor 34 | ] 35 | 36 | { #category : #initialization } 37 | TemperatureUnit >> additiveFactor: myAdditiveFactor [ 38 | additiveFactor := myAdditiveFactor 39 | ] 40 | 41 | { #category : #predicates } 42 | TemperatureUnit >> isZeroAsValue: value [ 43 | 44 | ] 45 | 46 | { #category : #conversion } 47 | TemperatureUnit >> uncheckedConvertFrom: anotherUnitValue [ 48 | | kelvin newValue | 49 | ^anotherUnitValue unit isBaseUnit 50 | ifTrue: [ 51 | newValue := 52 | (anotherUnitValue value / 53 | (self conversionFactorTo: anotherUnitValue unit)) + additiveFactor. 54 | UnitValue 55 | unit: self 56 | value: newValue] 57 | ifFalse: [ 58 | kelvin := anotherUnitValue uncheckedConvertTo: Unit kelvin. 59 | kelvin convertTo: self] 60 | ] 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI matrix](https://github.com//ZweiDenker/Units/actions/workflows/build.yml/badge.svg)](https://github.com//ZweiDenker/Units/actions/workflows/build.yml) 2 | 3 | 4 | # Units 5 | 6 | A simple package for Units management in Pharo. 7 | 8 | ## Installation 9 | 10 | To install Units on your Pharo image you can just execute the following script: 11 | 12 | ```Smalltalk 13 | Metacello new 14 | githubUser: 'zweidenker' project: 'Units' commitish: 'master' path: 'src'; 15 | baseline: 'Units'; 16 | load 17 | ``` 18 | 19 | To add Units to your baseline just add this: 20 | 21 | ```Smalltalk 22 | spec 23 | baseline: 'Units' 24 | with: [ spec repository: 'github://zweidenker/Units:master/src' ] 25 | ``` 26 | 27 | Note that you can replace the #master by another branch or a tag. 28 | 29 | ## Examples 30 | 31 | ### Create units 32 | 33 | ```Smalltalk 34 | 35 | 15 units: #gram. "15 grams" 36 | 37 | 15 units: #newton. "15 newtons" 38 | 39 | 15g. "15 grams" 40 | 41 | (15 units: #mile) / (1 units: #second). "15 miles per second" 42 | 43 | 15mi/1 s. "15 miles per second" 44 | 45 | 3m * 3m. "9 square metres" 46 | 47 | ``` 48 | 49 | ### Compare units 50 | 51 | ```Smalltalk 52 | 53 | 1cm > 2cm. "false" 54 | 55 | 1cm >= 1cm. "true" 56 | 57 | 15 cm = 150 mm. "true" 58 | 59 | 15cm > 1500mm. "false" 60 | 61 | ``` 62 | 63 | ### Operations on units 64 | 65 | ```Smalltalk 66 | 67 | 1g + 1g. "2 grams" 68 | 69 | 1kg + 1g. "(1001/1000) kilograms" 70 | 71 | 1g + 1kg. "1001 grams" 72 | 73 | 14foot + 15m. "63.212598425196845 feet" 74 | 75 | 10g - 1g. "9 grams" 76 | 77 | 9 * 1g. "9 grams" 78 | 79 | 9g * 3. "27 grams" 80 | 81 | 100g / 2. "50 grams" 82 | 83 | ``` 84 | 85 | ### Convertions 86 | 87 | ```Smalltalk 88 | 89 | 15mi/1h convertTo: (Unit m / Unit s). "6.7056 metres per second" 90 | 91 | 3kg factor: Unit g. "3000 grams" 92 | 93 | (3 units: #newton) factor: Unit g. "3000 gram metres per square second" 94 | 95 | ``` 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/Units-Core/DerivedUnit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This is a definition of a unit in terms of a product of powers of 3 | other units, plus a scalar value associated with the unit. 4 | 5 | Example: 1 inch = 2.54 cm 6 | Note that the ""left side"" is always magnitude 1, which corresponds to 1 unit of the DerivedUnit. 7 | 8 | " 9 | Class { 10 | #name : #DerivedUnit, 11 | #superclass : #NamedUnit, 12 | #instVars : [ 13 | 'unitValue' 14 | ], 15 | #category : #'Units-Core' 16 | } 17 | 18 | { #category : #'instance creation' } 19 | DerivedUnit class >> abbreviation: abbreviation name: unitName pluralName: pluralName value: unitValue [ 20 | | unit | 21 | unit := super 22 | abbreviation: abbreviation 23 | name: unitName 24 | pluralName: pluralName. 25 | unit value: unitValue. 26 | ^unit 27 | ] 28 | 29 | { #category : #conversion } 30 | DerivedUnit >> baseUnits [ 31 | ^unitValue unit baseUnits 32 | ] 33 | 34 | { #category : #consistency } 35 | DerivedUnit >> consistentWith: unit [ 36 | "Short-circuit the double dispatching here." 37 | ^unitValue unitPart consistentWith: unit 38 | ] 39 | 40 | { #category : #consistency } 41 | DerivedUnit >> consistentWithBaseUnit: baseUnit [ 42 | ^baseUnit consistentWith: self unit 43 | ] 44 | 45 | { #category : #consistency } 46 | DerivedUnit >> consistentWithComplexUnit: complexUnit [ 47 | ^complexUnit consistentWith: self unit 48 | ] 49 | 50 | { #category : #consistency } 51 | DerivedUnit >> consistentWithCompoundUnit: compoundUnit [ 52 | ^compoundUnit consistentWith: self unit 53 | ] 54 | 55 | { #category : #consistency } 56 | DerivedUnit >> consistentWithModifiedUnit: modifiedUnit [ 57 | ^modifiedUnit consistentWith: self unit 58 | ] 59 | 60 | { #category : #conversion } 61 | DerivedUnit >> conversionFactor [ 62 | ^unitValue value * unitValue unit conversionFactor 63 | ] 64 | 65 | { #category : #accessing } 66 | DerivedUnit >> unit [ 67 | ^unitValue unit 68 | ] 69 | 70 | { #category : #initialization } 71 | DerivedUnit >> value: myUnitValue [ 72 | unitValue := myUnitValue 73 | ] 74 | -------------------------------------------------------------------------------- /src/Units-Core/PrefixedUnit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This is a unit with an SI prefix attached. 3 | See class SIPrefix for details. 4 | 5 | " 6 | Class { 7 | #name : #PrefixedUnit, 8 | #superclass : #Unit, 9 | #instVars : [ 10 | 'prefix', 11 | 'unit' 12 | ], 13 | #category : #'Units-Core' 14 | } 15 | 16 | { #category : #'instance creation' } 17 | PrefixedUnit class >> prefix: prefix unit: unit [ 18 | ^self new prefix: prefix unit: unit 19 | ] 20 | 21 | { #category : #'instance creation' } 22 | PrefixedUnit class >> prefixName: prefixName unit: unit [ 23 | | prefix | 24 | prefix := SIPrefix named: prefixName. 25 | ^self prefix: prefix unit: unit 26 | ] 27 | 28 | { #category : #comparing } 29 | PrefixedUnit >> = anotherUnit [ 30 | self class == anotherUnit class ifFalse: [^false]. 31 | ^prefix = anotherUnit prefix and: [unit = anotherUnit unit] 32 | ] 33 | 34 | { #category : #conversion } 35 | PrefixedUnit >> baseUnits [ 36 | ^unit baseUnits 37 | ] 38 | 39 | { #category : #consistency } 40 | PrefixedUnit >> consistentWith: anotherUnit [ 41 | "Short-circuit the double dispatching; just compare the actual unit." 42 | ^anotherUnit consistentWith: unit 43 | ] 44 | 45 | { #category : #consistency } 46 | PrefixedUnit >> consistentWithAnything: anotherUnit [ 47 | ^unit consistentWith: anotherUnit 48 | ] 49 | 50 | { #category : #conversion } 51 | PrefixedUnit >> conversionFactor [ 52 | ^prefix scalingFactor * unit conversionFactor 53 | ] 54 | 55 | { #category : #comparing } 56 | PrefixedUnit >> hash [ 57 | ^prefix hash bitXor: unit hash 58 | ] 59 | 60 | { #category : #accessing } 61 | PrefixedUnit >> prefix [ 62 | ^prefix 63 | ] 64 | 65 | { #category : #initialization } 66 | PrefixedUnit >> prefix: myPrefix unit: myUnit [ 67 | prefix := myPrefix. 68 | unit := myUnit 69 | ] 70 | 71 | { #category : #conversion } 72 | PrefixedUnit >> prefixedBy: prefixName [ 73 | ^self error: 'This unit already has a prefix.' 74 | ] 75 | 76 | { #category : #printing } 77 | PrefixedUnit >> printAbbreviationOn: stream [ 78 | stream nextPutAll: prefix abbreviation. 79 | unit printAbbreviationOn: stream 80 | ] 81 | 82 | { #category : #printing } 83 | PrefixedUnit >> printFullNameOn: stream pluralized: pluralized [ 84 | stream nextPutAll: prefix name. 85 | unit printFullNameOn: stream pluralized: pluralized 86 | ] 87 | 88 | { #category : #accessing } 89 | PrefixedUnit >> unit [ 90 | ^unit 91 | ] 92 | -------------------------------------------------------------------------------- /src/Units-Tests/UnitsTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #UnitsTest, 3 | #superclass : #TestCase, 4 | #category : #'Units-Tests' 5 | } 6 | 7 | { #category : #testing } 8 | UnitsTest >> testAdd [ 9 | "Adding or subtracting units does appropriate conversions." 10 | | u | 11 | u := (14 units: #foot) + (10 units: #metre). 12 | self assert: (u printString = '46.80839895013123 feet'). 13 | 14 | ] 15 | 16 | { #category : #testing } 17 | UnitsTest >> testCompare [ 18 | "Unit values can also be compared (provided they are dimensionally consistent." 19 | self assert: ((1 units: #inch) < (3 units: #centimetre)). 20 | 21 | ] 22 | 23 | { #category : #testing } 24 | UnitsTest >> testConvert [ 25 | "You can also explicitly convert values." 26 | | u | 27 | u := (15 units: #mile) / (1 units: #hour) 28 | convertTo: (Unit metre / Unit second). 29 | self assert: (u printString = '6.7056 metres per second'). 30 | 31 | ] 32 | 33 | { #category : #testing } 34 | UnitsTest >> testCreate [ 35 | "Create unit values by sending #units: to a number." 36 | | u | 37 | u := 2 units: #inch. 38 | self assert: (u printString = '2 inches'). 39 | 40 | ] 41 | 42 | { #category : #testing } 43 | UnitsTest >> testCreateAdditionalUnit [ 44 | "Creating additional units is easy:" 45 | | microfortnight | 46 | microfortnight := PrefixedUnit 47 | prefixName: 'micro' 48 | unit: (DerivedUnit 49 | abbreviation: 'FN' 50 | name: 'fortnight' 51 | pluralName: 'fortnights' 52 | value: (14 units: #days)). 53 | 54 | self assert: ((1 units: microfortnight) baseUnits asString = '(756/625) seconds'). 55 | ] 56 | 57 | { #category : #testing } 58 | UnitsTest >> testDivide [ 59 | "You can divide unit values." 60 | | u | 61 | u := (2 units: #inch) / (3 units: #second). 62 | self assert: (u printString = '(2/3) inches per second'). 63 | 64 | ] 65 | 66 | { #category : #testing } 67 | UnitsTest >> testExpand [ 68 | "You can expand 'derived' units into base SI units." 69 | | u | 70 | u := (3 units: #newton) baseUnits. 71 | self assert: (u printString = '3 kilogram metres per square second'). 72 | 73 | "To see grams rather than kilograms, factor with respect to grams." 74 | u := (3 units: #newton) factor: Unit gram. 75 | self assert: (u printString = '3000 gram metres per square second'). 76 | 77 | ] 78 | 79 | { #category : #testing } 80 | UnitsTest >> testNewton [ 81 | "You can use 'derived' units such as the newton." 82 | | u | 83 | u := 3 units: #newton. 84 | self assert: (u printString = '3 newtons'). 85 | 86 | ] 87 | 88 | { #category : #testing } 89 | UnitsTest >> testReduce [ 90 | self assert: 5 km / 5000 m equals: 1 91 | ] 92 | 93 | { #category : #testing } 94 | UnitsTest >> testSqrt [ 95 | "if we take the root of a squared unit, we need to make sure to reduce the 96 | unit and not use a ComplexUnit" 97 | self assert: (2 units: Unit km squared) sqrt unit class equals: PrefixedUnit 98 | ] 99 | -------------------------------------------------------------------------------- /src/Units-Core/ModifiedUnit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A base unit with an arbitrary modification that makes it incompatible 3 | with anything that does not have the same modification. Works well with domain-specific 4 | 'modifications', e.g., 5 | 1.6 moles 6 | can now become: 7 | 1.6 moles of sulfuric acid 8 | 9 | (""sulfuric acid"" might be a String, or a ChemicalCompound, or whatever.) 10 | 11 | " 12 | Class { 13 | #name : #ModifiedUnit, 14 | #superclass : #Unit, 15 | #instVars : [ 16 | 'baseUnit', 17 | 'modification' 18 | ], 19 | #category : #'Units-Core' 20 | } 21 | 22 | { #category : #'instance creation' } 23 | ModifiedUnit class >> baseUnit: baseUnit modification: modification [ 24 | ^self new 25 | baseUnit: baseUnit 26 | modification: modification 27 | ] 28 | 29 | { #category : #comparing } 30 | ModifiedUnit >> = anotherUnit [ 31 | self class = anotherUnit class ifFalse: [^false]. 32 | modification = anotherUnit modification ifFalse: [^false]. 33 | ^baseUnit = anotherUnit baseUnit 34 | ] 35 | 36 | { #category : #accessing } 37 | ModifiedUnit >> baseUnit [ 38 | ^baseUnit 39 | ] 40 | 41 | { #category : #initialization } 42 | ModifiedUnit >> baseUnit: myBaseUnit modification: myModification [ 43 | baseUnit := myBaseUnit. 44 | modification := myModification 45 | ] 46 | 47 | { #category : #conversion } 48 | ModifiedUnit >> baseUnits [ 49 | ^self 50 | ] 51 | 52 | { #category : #consistency } 53 | ModifiedUnit >> consistentWith: anotherUnit [ 54 | ^anotherUnit consistentWithModifiedUnit: self 55 | ] 56 | 57 | { #category : #consistency } 58 | ModifiedUnit >> consistentWithBaseUnit: anotherBaseUnit [ 59 | ^false 60 | ] 61 | 62 | { #category : #consistency } 63 | ModifiedUnit >> consistentWithComplexUnit: complexUnit [ 64 | ^self consistentWith: complexUnit baseUnits 65 | ] 66 | 67 | { #category : #consistency } 68 | ModifiedUnit >> consistentWithCompoundUnit: compoundUnit [ 69 | ^false 70 | ] 71 | 72 | { #category : #consistency } 73 | ModifiedUnit >> consistentWithModifiedUnit: modifiedUnit [ 74 | self class == modifiedUnit class ifFalse: [^false]. 75 | ^baseUnit = modifiedUnit baseUnit 76 | and: [modification = modifiedUnit modification] 77 | ] 78 | 79 | { #category : #conversion } 80 | ModifiedUnit >> conversionFactor [ 81 | ^baseUnit conversionFactor 82 | ] 83 | 84 | { #category : #comparing } 85 | ModifiedUnit >> hash [ 86 | ^modification hash bitXor: baseUnit hash 87 | ] 88 | 89 | { #category : #predicates } 90 | ModifiedUnit >> isBaseUnit [ 91 | "ModifiedUnits are effectively new base units..." 92 | ^true 93 | ] 94 | 95 | { #category : #accessing } 96 | ModifiedUnit >> modification [ 97 | ^modification 98 | ] 99 | 100 | { #category : #printing } 101 | ModifiedUnit >> printAbbreviationOn: stream [ 102 | baseUnit printAbbreviationOn: stream. 103 | stream nextPut: $(. 104 | modification isString 105 | ifTrue: [stream nextPutAll: modification] 106 | ifFalse: [stream print: modification]. 107 | stream nextPut: $) 108 | ] 109 | 110 | { #category : #printing } 111 | ModifiedUnit >> printFullNameOn: stream pluralized: pluralized [ 112 | baseUnit printFullNameOn: stream pluralized: pluralized. 113 | stream nextPutAll: ' (of '. 114 | "Can't avoid the #isString, since strings print differently than other objects, 115 | and it is valid to have non-strings as the modification." 116 | modification isString 117 | ifTrue: [stream nextPutAll: modification] 118 | ifFalse: [stream print: modification]. 119 | stream nextPut: $) 120 | ] 121 | -------------------------------------------------------------------------------- /src/Units-Core/ComplexUnit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This represents a product of one or more different units. 3 | " 4 | Class { 5 | #name : #ComplexUnit, 6 | #superclass : #CompoundUnit, 7 | #instVars : [ 8 | 'conversionFactor', 9 | 'cachedBaseUnits' 10 | ], 11 | #category : #'Units-Core' 12 | } 13 | 14 | { #category : #'instance creation' } 15 | ComplexUnit class >> units: units exponents: exponents [ 16 | "if we have just one exponent of size one, the unit itself is enough" 17 | (exponents size = 1 and: [exponents first = 1]) ifTrue: [ ^units first ]. 18 | units 19 | detect: [:each | each isBaseUnit not] 20 | ifNone: [ 21 | "All the units are base units ... we can use a CompoundUnit instead to save space." 22 | ^CompoundUnit 23 | units: units 24 | exponents: exponents]. 25 | ^self new 26 | units: units 27 | exponents: exponents 28 | ] 29 | 30 | { #category : #conversion } 31 | ComplexUnit >> baseUnits [ 32 | "Since base units are expensive to compute for ComplexUnits, they are cached here." 33 | cachedBaseUnits isNil 34 | ifTrue: [cachedBaseUnits := self calculateBaseUnits]. 35 | ^cachedBaseUnits 36 | ] 37 | 38 | { #category : #conversion } 39 | ComplexUnit >> calculateBaseUnits [ 40 | | baseUnits unitDictionary newUnits newExponents scratch | 41 | unitDictionary := IdentityDictionary new. 42 | self unitsAndExponentsDo: [:unit :exponent | 43 | baseUnits := unit baseUnits. 44 | baseUnits unitsAndExponentsDo: [:subunit :subexponent | 45 | (unitDictionary includesKey: subunit) 46 | ifFalse: [ 47 | unitDictionary 48 | at: subunit 49 | put: subexponent * exponent] 50 | ifTrue: [ 51 | unitDictionary 52 | at: subunit 53 | put: (unitDictionary at: subunit) + (subexponent * exponent)]]]. 54 | newUnits := OrderedCollection new. 55 | newExponents := OrderedCollection new. 56 | (unitDictionary keys asSortedCollection: self class sortBlock) do: [:unit | 57 | scratch := unitDictionary at: unit. 58 | scratch isZero ifFalse: [ 59 | newUnits add: unit. 60 | newExponents add: scratch]]. 61 | ^CompoundUnit 62 | units: newUnits 63 | exponents: newExponents 64 | ] 65 | 66 | { #category : #consistency } 67 | ComplexUnit >> consistentWith: unit [ 68 | ^unit consistentWithComplexUnit: self 69 | ] 70 | 71 | { #category : #consistency } 72 | ComplexUnit >> consistentWithBaseUnit: baseUnit [ 73 | "This might be true, since we may be containing non-base units that reduce 74 | to the base unit." 75 | "For example, 'meters * seconds * Hertz' is consistent with 'meters'." 76 | ^self baseUnits consistentWith: baseUnit 77 | ] 78 | 79 | { #category : #consistency } 80 | ComplexUnit >> consistentWithComplexUnit: complexUnit [ 81 | ^self baseUnits consistentWith: complexUnit baseUnits 82 | ] 83 | 84 | { #category : #consistency } 85 | ComplexUnit >> consistentWithCompoundUnit: compoundUnit [ 86 | ^self baseUnits consistentWith: compoundUnit 87 | ] 88 | 89 | { #category : #consistency } 90 | ComplexUnit >> consistentWithModifiedUnit: modifiedUnit [ 91 | "This might be true, since we may be containing non-base units that reduce to the 92 | modified unit." 93 | "For example, 'moles of hydrogen * seconds * Hertz' is consistent with 'moles of 94 | hydrogen'." 95 | ^self baseUnits consistentWith: modifiedUnit 96 | ] 97 | 98 | { #category : #conversion } 99 | ComplexUnit >> conversionFactor [ 100 | ^conversionFactor 101 | ] 102 | 103 | { #category : #testing } 104 | ComplexUnit >> isComplexUnit [ 105 | ^ true 106 | ] 107 | 108 | { #category : #conversion } 109 | ComplexUnit >> prefixedBy: prefixName [ 110 | ^self error: 'You cannot attach prefixes to complex units.' 111 | ] 112 | 113 | { #category : #initialization } 114 | ComplexUnit >> units: myUnits exponents: myExponents [ 115 | super units: myUnits exponents: myExponents. 116 | conversionFactor := 1. 117 | units with: exponents do: [:unit :exponent | 118 | conversionFactor := conversionFactor * 119 | (unit conversionFactor raisedTo: exponent)] 120 | ] 121 | -------------------------------------------------------------------------------- /src/Units-Core/SIPrefix.class.st: -------------------------------------------------------------------------------- 1 | " 2 | SIPrefix represents a power of 10 attached to a unit. 3 | Examples: milli, micro, kilo, etc. 4 | 5 | " 6 | Class { 7 | #name : #SIPrefix, 8 | #superclass : #Object, 9 | #instVars : [ 10 | 'abbreviation', 11 | 'name', 12 | 'scalingFactor' 13 | ], 14 | #classVars : [ 15 | 'SIPrefixesByAbbreviation', 16 | 'SIPrefixesByName' 17 | ], 18 | #category : #'Units-Core' 19 | } 20 | 21 | { #category : #initialization } 22 | SIPrefix class >> abbreviation: abbreviation name: prefixName scalingFactor: scalingFactor [ 23 | | prefix | 24 | prefix := self new 25 | abbreviation: abbreviation 26 | name: prefixName 27 | scalingFactor: scalingFactor. 28 | SIPrefixesByAbbreviation at: abbreviation put: prefix. 29 | SIPrefixesByName at: prefixName put: prefix. 30 | ^prefix 31 | ] 32 | 33 | { #category : #initialization } 34 | SIPrefix class >> initializeClass [ 35 | "SIPrefix initializeClass." 36 | "Do not rename this to #initialize." 37 | SIPrefixesByName := Dictionary new. 38 | SIPrefixesByAbbreviation := Dictionary new. 39 | self abbreviation: 'y' name: 'yocto' scalingFactor: (10 raisedTo: -24). 40 | self abbreviation: 'z' name: 'zepto' scalingFactor: (10 raisedTo: -21). 41 | self abbreviation: 'a' name: 'atto' scalingFactor: (10 raisedTo: -18). 42 | self abbreviation: 'f' name: 'femto' scalingFactor: (10 raisedTo: -15). 43 | self abbreviation: 'p' name: 'pico' scalingFactor: (10 raisedTo: -12). 44 | self abbreviation: 'n' name: 'nano' scalingFactor: (10 raisedTo: -9). 45 | self abbreviation: 'u' name: 'micro' scalingFactor: (10 raisedTo: -6). 46 | self abbreviation: 'm' name: 'milli' scalingFactor: (10 raisedTo: -3). 47 | self abbreviation: 'c' name: 'centi' scalingFactor: (10 raisedTo: -2). 48 | self abbreviation: 'd' name: 'deci' scalingFactor: (10 raisedTo: -1). 49 | self abbreviation: 'da' name: 'deka' scalingFactor: (10 raisedTo: 1). 50 | self abbreviation: 'h' name: 'hecto' scalingFactor: (10 raisedTo: 2). 51 | self abbreviation: 'k' name: 'kilo' scalingFactor: (10 raisedTo: 3). 52 | self abbreviation: 'M' name: 'mega' scalingFactor: (10 raisedTo: 6). 53 | self abbreviation: 'G' name: 'giga' scalingFactor: (10 raisedTo: 9). 54 | self abbreviation: 'T' name: 'tera' scalingFactor: (10 raisedTo: 12). 55 | self abbreviation: 'P' name: 'peta' scalingFactor: (10 raisedTo: 15). 56 | self abbreviation: 'E' name: 'exa' scalingFactor: (10 raisedTo: 18). 57 | self abbreviation: 'Z' name: 'zetta' scalingFactor: (10 raisedTo: 21). 58 | self abbreviation: 'Y' name: 'yotta' scalingFactor: (10 raisedTo: 24) 59 | ] 60 | 61 | { #category : #accessing } 62 | SIPrefix class >> named: prefixName [ 63 | ^SIPrefixesByName at: prefixName 64 | ] 65 | 66 | { #category : #enumerating } 67 | SIPrefix class >> prefixAbbreviationsDo: block [ 68 | "Evaluate 'block' once with each prefix abbreviation, in no particular order." 69 | SIPrefixesByAbbreviation keysDo: block 70 | ] 71 | 72 | { #category : #enumerating } 73 | SIPrefix class >> prefixStringsDo: block [ 74 | "Evaluate 'block' once with each prefix string, in no particular order." 75 | SIPrefixesByName keysDo: block 76 | ] 77 | 78 | { #category : #accessing } 79 | SIPrefix class >> withAbbreviation: abbreviation [ 80 | ^SIPrefixesByAbbreviation at: abbreviation 81 | ] 82 | 83 | { #category : #accessing } 84 | SIPrefix >> abbreviation [ 85 | ^abbreviation 86 | ] 87 | 88 | { #category : #initialization } 89 | SIPrefix >> abbreviation: myAbbreviation name: myName scalingFactor: myScalingFactor [ 90 | abbreviation := myAbbreviation. 91 | name := myName. 92 | scalingFactor := myScalingFactor 93 | ] 94 | 95 | { #category : #accessing } 96 | SIPrefix >> name [ 97 | ^name 98 | ] 99 | 100 | { #category : #printing } 101 | SIPrefix >> printOn: stream [ 102 | super printOn: stream. 103 | stream 104 | nextPutAll: ' ['; 105 | nextPutAll: name; 106 | nextPut: $] 107 | ] 108 | 109 | { #category : #accessing } 110 | SIPrefix >> scalingFactor [ 111 | ^scalingFactor 112 | ] 113 | 114 | { #category : #printing } 115 | SIPrefix >> storeOn: aStream [ 116 | "Store the code to lookup the receiver instead of for a new instance." 117 | 118 | aStream 119 | nextPutAll: '(SIPrefix named: '''; 120 | nextPutAll: name; 121 | nextPutAll: ''')' 122 | ] 123 | -------------------------------------------------------------------------------- /src/Units-Core/BaseUnit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This represents one of the SI base units. 3 | 4 | By default the following ""official"" units are defined: 5 | gram 6 | meter 7 | second 8 | candela (light intensity) 9 | mole (pseudo-dimensionless quantity) 10 | 11 | Two 'extra' base units are included to demonstrate how to extend the system: 12 | base pairs (nucleotides on a DNA strand, for automatic analysis) 13 | donuts (e.g., 1 homer = 8 donuts/minute) 14 | 15 | " 16 | Class { 17 | #name : #BaseUnit, 18 | #superclass : #NamedUnit, 19 | #classVars : [ 20 | 'SIUnitsByAbbreviation', 21 | 'SIUnitsByName', 22 | 'SIUnitsByPluralName' 23 | ], 24 | #category : #'Units-Core' 25 | } 26 | 27 | { #category : #initialization } 28 | BaseUnit class >> addUnit: unitName plural: pluralName abbreviation: abbreviation [ 29 | | unit | 30 | unit := self new 31 | abbreviation: abbreviation 32 | name: unitName 33 | pluralName: pluralName. 34 | SIUnitsByName at: unitName put: unit. 35 | SIUnitsByPluralName at: pluralName put: unit. 36 | SIUnitsByAbbreviation at: abbreviation put: unit. 37 | ^unit 38 | ] 39 | 40 | { #category : #'base units' } 41 | BaseUnit class >> ampere [ 42 | 43 | ^self withSingularName: 'ampere' 44 | ] 45 | 46 | { #category : #'base units' } 47 | BaseUnit class >> amperes [ 48 | 49 | ^self ampere 50 | ] 51 | 52 | { #category : #example } 53 | BaseUnit class >> basePair [ 54 | 55 | ^self withPluralName: 'base pair' 56 | ] 57 | 58 | { #category : #example } 59 | BaseUnit class >> basePairs [ 60 | 61 | ^ self withPluralName: 'base pairs' 62 | ] 63 | 64 | { #category : #'base units' } 65 | BaseUnit class >> candela [ 66 | 67 | ^self withSingularName: 'candela' 68 | ] 69 | 70 | { #category : #example } 71 | BaseUnit class >> donut [ 72 | 73 | ^ self withSingularName: 'donut' 74 | ] 75 | 76 | { #category : #example } 77 | BaseUnit class >> donuts [ 78 | 79 | ^ self donut 80 | ] 81 | 82 | { #category : #initialization } 83 | BaseUnit class >> initializeClass [ 84 | "BaseUnit initializeClass." 85 | "Do not rename this to #initialize." 86 | PrintAbbreviated := false. "may as well do it here ..." 87 | SIUnitsByName := Dictionary new. 88 | SIUnitsByAbbreviation := Dictionary new. 89 | SIUnitsByPluralName := Dictionary new. 90 | self 91 | addUnit: 'kilogram' plural: 'kilograms' abbreviation: 'kg'; 92 | addUnit: 'metre' plural: 'metres' abbreviation: 'm'; 93 | addUnit: 'second' plural: 'seconds' abbreviation: 's'; 94 | addUnit: 'candela' plural: 'candela' abbreviation: 'cd'; 95 | addUnit: 'mole' plural: 'moles' abbreviation: 'mol'; 96 | addUnit: 'ampere' plural: 'amperes' abbreviation: 'A'; 97 | "Kelvin is defined in subclass" 98 | addUnit: 'radian' plural: 'radians' abbreviation: 'rad'; 99 | addUnit: 'base pair' plural: 'base pairs' abbreviation: 'BP'; "for DNA" 100 | addUnit: 'donut' plural: 'donuts' abbreviation: 'donut'; 101 | addUnit: 'volt' plural: 'volts' abbreviation: 'V' 102 | ] 103 | 104 | { #category : #'base units' } 105 | BaseUnit class >> kelvin [ 106 | 107 | ^self withSingularName: 'kelvin' 108 | ] 109 | 110 | { #category : #'base units' } 111 | BaseUnit class >> kilogram [ 112 | 113 | ^self withSingularName: 'kilogram' 114 | ] 115 | 116 | { #category : #'base units' } 117 | BaseUnit class >> kilograms [ 118 | 119 | ^ self kilogram 120 | ] 121 | 122 | { #category : #'base units' } 123 | BaseUnit class >> meter [ 124 | 125 | ^self metre 126 | ] 127 | 128 | { #category : #'base units' } 129 | BaseUnit class >> meters [ 130 | 131 | ^self metre 132 | ] 133 | 134 | { #category : #'base units' } 135 | BaseUnit class >> metre [ 136 | 137 | ^self withSingularName: 'metre' 138 | ] 139 | 140 | { #category : #'base units' } 141 | BaseUnit class >> metres [ 142 | 143 | ^ self metre 144 | ] 145 | 146 | { #category : #'base units' } 147 | BaseUnit class >> mole [ 148 | 149 | ^self withSingularName: 'mole' 150 | ] 151 | 152 | { #category : #'base units' } 153 | BaseUnit class >> moles [ 154 | 155 | ^self mole 156 | ] 157 | 158 | { #category : #'base units' } 159 | BaseUnit class >> second [ 160 | 161 | ^self withSingularName: 'second' 162 | ] 163 | 164 | { #category : #'base units' } 165 | BaseUnit class >> seconds [ 166 | 167 | ^ self second 168 | ] 169 | 170 | { #category : #'base units' } 171 | BaseUnit class >> volt [ 172 | 173 | ^self withSingularName: 'volt' 174 | ] 175 | 176 | { #category : #'base units' } 177 | BaseUnit class >> volts [ 178 | 179 | ^self volt 180 | ] 181 | 182 | { #category : #accessing } 183 | BaseUnit class >> withAbbreviation: abbreviation ifAbsent: exceptionBlock [ 184 | ^SIUnitsByAbbreviation 185 | at: abbreviation 186 | ifAbsent: exceptionBlock 187 | ] 188 | 189 | { #category : #accessing } 190 | BaseUnit class >> withPluralName: unitName ifAbsent: exceptionBlock [ 191 | ^SIUnitsByPluralName 192 | at: unitName 193 | ifAbsent: exceptionBlock 194 | ] 195 | 196 | { #category : #accessing } 197 | BaseUnit class >> withSingularName: unitName ifAbsent: exceptionBlock [ 198 | ^SIUnitsByName 199 | at: unitName 200 | ifAbsent: exceptionBlock 201 | ] 202 | 203 | { #category : #conversion } 204 | BaseUnit >> baseUnits [ 205 | "This is already a base unit." 206 | ^self 207 | ] 208 | 209 | { #category : #consistency } 210 | BaseUnit >> consistentWith: unit [ 211 | ^unit consistentWithBaseUnit: self 212 | ] 213 | 214 | { #category : #consistency } 215 | BaseUnit >> consistentWithBaseUnit: unit [ 216 | ^self == unit 217 | ] 218 | 219 | { #category : #consistency } 220 | BaseUnit >> consistentWithComplexUnit: complexUnit [ 221 | ^self consistentWith: complexUnit baseUnits 222 | ] 223 | 224 | { #category : #conversion } 225 | BaseUnit >> conversionFactor [ 226 | ^1. "by definition" 227 | ] 228 | 229 | { #category : #predicates } 230 | BaseUnit >> isBaseUnit [ 231 | ^true 232 | ] 233 | 234 | { #category : #printing } 235 | BaseUnit >> storeOn: aStream [ 236 | "Store the code to lookup the receiver instead of for a new instance." 237 | 238 | aStream 239 | nextPutAll: '(BaseUnit withPluralName: '''; 240 | nextPutAll: self pluralName; 241 | nextPutAll: ''')' 242 | ] 243 | -------------------------------------------------------------------------------- /src/Units-Core/CompoundUnit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Instances of CompoundUnits represent units which are combinations 3 | of base units (only). 4 | 5 | Examples of things that are CompoundUnits: 6 | meters per second 7 | cubic meters per degree Kelvin 8 | grams per mole of calcium 9 | 10 | Examples of things that are not CompoundUnits: 11 | 12 | miles per hour 13 | cubic meters per degree Farenheit 14 | kilograms per mole 15 | 16 | " 17 | Class { 18 | #name : #CompoundUnit, 19 | #superclass : #Unit, 20 | #instVars : [ 21 | 'units', 22 | 'exponents' 23 | ], 24 | #category : #'Units-Core' 25 | } 26 | 27 | { #category : #'instance creation' } 28 | CompoundUnit class >> null [ 29 | "A special CompoundUnit used to coerce numbers into the Unit domain." 30 | ^self 31 | units: #() 32 | exponents: #() 33 | ] 34 | 35 | { #category : #sorting } 36 | CompoundUnit class >> sortBlock [ 37 | ^[:left :right | left abbreviation < right abbreviation] 38 | ] 39 | 40 | { #category : #'instance creation' } 41 | CompoundUnit class >> units: units exponents: exponents [ 42 | ^self new 43 | units: units 44 | exponents: exponents 45 | ] 46 | 47 | { #category : #comparing } 48 | CompoundUnit >> = anotherUnit [ 49 | "This is not the same as #consistentWith:, which checks for isomorphism. This method 50 | just checks to see if the receiver and argument have the same units and exponents." 51 | anotherUnit class == self class ifFalse: [^false]. 52 | ^units = anotherUnit snarfUnits 53 | and: [exponents = anotherUnit snarfExponents] 54 | ] 55 | 56 | { #category : #conversion } 57 | CompoundUnit >> baseUnits [ 58 | "The receiver already consists entirely of base units." 59 | ^self 60 | ] 61 | 62 | { #category : #consistency } 63 | CompoundUnit >> consistentWith: unit [ 64 | ^unit consistentWithCompoundUnit: self 65 | ] 66 | 67 | { #category : #consistency } 68 | CompoundUnit >> consistentWithBaseUnit: baseUnit [ 69 | "Always false, because a CompoundUnit must always have a nontrivial set of units." 70 | ^false 71 | ] 72 | 73 | { #category : #consistency } 74 | CompoundUnit >> consistentWithComplexUnit: complexUnit [ 75 | ^self consistentWith: complexUnit baseUnits 76 | ] 77 | 78 | { #category : #consistency } 79 | CompoundUnit >> consistentWithCompoundUnit: compoundUnit [ 80 | "We can check the unit and exponent arrays for equality directly, since the units are assumed to be sorted alphabetically." 81 | ^units = compoundUnit snarfUnits 82 | and: [exponents = compoundUnit snarfExponents] 83 | ] 84 | 85 | { #category : #consistency } 86 | CompoundUnit >> consistentWithModifiedUnit: modifiedUnit [ 87 | "Same as #consistentWithBaseUnit:." 88 | ^false 89 | ] 90 | 91 | { #category : #conversion } 92 | CompoundUnit >> conversionFactor [ 93 | ^1 94 | ] 95 | 96 | { #category : #comparing } 97 | CompoundUnit >> hash [ 98 | ^units hash bitXor: exponents hash 99 | ] 100 | 101 | { #category : #predicates } 102 | CompoundUnit >> includesNegativeExponents [ 103 | ^exponents anySatisfy: [:each | each < 0] 104 | ] 105 | 106 | { #category : #predicates } 107 | CompoundUnit >> includesPositiveExponents [ 108 | ^exponents anySatisfy: [:each | each > 0] 109 | ] 110 | 111 | { #category : #conversion } 112 | CompoundUnit >> inverse [ 113 | ^self class 114 | units: units 115 | exponents: (exponents collect: [:each | each negated]) 116 | ] 117 | 118 | { #category : #predicates } 119 | CompoundUnit >> isNull [ 120 | ^units isEmpty 121 | ] 122 | 123 | { #category : #predicates } 124 | CompoundUnit >> negativeExponentsCount [ 125 | | count | 126 | count := 0. 127 | exponents do: [:each | each < 0 ifTrue: [count := count + 1]]. 128 | ^count 129 | ] 130 | 131 | { #category : #predicates } 132 | CompoundUnit >> positiveExponentsCount [ 133 | | count | 134 | count := 0. 135 | exponents do: [:each | each > 0 ifTrue: [count := count + 1]]. 136 | ^count 137 | ] 138 | 139 | { #category : #conversion } 140 | CompoundUnit >> prefixedBy: prefixName [ 141 | ^self error: 'You cannot attach prefixes to compound units.' 142 | ] 143 | 144 | { #category : #printing } 145 | CompoundUnit >> printAbbreviationOn: stream [ 146 | | first any count | 147 | first := true. 148 | any := false. 149 | self unitsAndExponentsDo: [:unit :exponent | 150 | exponent > 0 ifTrue: [ 151 | any := true. 152 | first ifFalse: [stream nextPut: $*]. 153 | first := false. 154 | unit printAbbreviationOn: stream. 155 | exponent ~= 1 156 | ifTrue: [stream nextPut: $^; print: exponent]]]. 157 | count := self negativeExponentsCount. 158 | count > 0 ifTrue: [ 159 | any ifFalse: [stream nextPut: $1]. 160 | stream nextPut: $/. 161 | count > 1 ifTrue: [stream nextPut: $(]. 162 | first := true. 163 | self unitsAndExponentsDo: [:unit :exponent | 164 | exponent ~= 1 ifTrue: [ 165 | first ifFalse: [stream nextPut: $*]. 166 | first := false. 167 | unit printAbbreviationOn: stream. 168 | exponent < -1 ifTrue: [stream nextPut: $^; print: exponent negated]]]. 169 | count > 1 ifTrue: [stream nextPut: $)]] 170 | ] 171 | 172 | { #category : #printing } 173 | CompoundUnit >> printFullNameOn: stream pluralized: pluralized [ 174 | "Print the full name of this unit, pluralized if 'pluralized' is true." 175 | | positive negative | 176 | positive := self includesPositiveExponents. 177 | negative := self includesNegativeExponents. 178 | positive ifTrue: [ 179 | self 180 | printUnitsWhereExponent: [:each | each > 0] 181 | on: stream 182 | pluralized: pluralized]. 183 | (negative and: [positive]) ifTrue: [stream space]. 184 | negative ifTrue: [ 185 | stream nextPutAll: 'per '. 186 | self 187 | printUnitsWhereExponent: [:each | each < 0] 188 | on: stream 189 | pluralized: false] 190 | ] 191 | 192 | { #category : #printing } 193 | CompoundUnit >> printUnitsWhereExponent: block on: stream pluralized: pluralized [ 194 | | power first count index thisPlural | 195 | first := true. 196 | count := (exponents select: block) size. 197 | index := 0. 198 | units with: exponents do: [:unit :exponent | 199 | (block value: exponent) ifTrue: [ 200 | first ifTrue: [first := false] ifFalse: [stream space]. 201 | index := index + 1. 202 | thisPlural := pluralized and: [index = count]. 203 | power := exponent abs. 204 | (power isInteger and: [power <= 3]) ifTrue: [ 205 | power = 2 ifTrue: [stream nextPutAll: 'square ']. 206 | power = 3 ifTrue: [stream nextPutAll: 'cubic ']]. 207 | unit printFullNameOn: stream pluralized: thisPlural. 208 | (power > 3 or: [power isInteger not]) ifTrue: [ 209 | stream 210 | nextPut: $^; 211 | print: power]]] 212 | ] 213 | 214 | { #category : #'unit arithmetic' } 215 | CompoundUnit >> raisedTo: exponent [ 216 | ^self class 217 | units: units 218 | exponents: (exponents collect: [:each | each * exponent]) 219 | ] 220 | 221 | { #category : #'unit arithmetic' } 222 | CompoundUnit >> reciprocal [ 223 | "Just make a new unit of the same class, with all the exponents negated." 224 | ^self class 225 | units: units 226 | exponents: (exponents collect: [:each | each negated]) 227 | ] 228 | 229 | { #category : #private } 230 | CompoundUnit >> snarfExponents [ 231 | ^exponents 232 | ] 233 | 234 | { #category : #private } 235 | CompoundUnit >> snarfUnits [ 236 | ^units 237 | ] 238 | 239 | { #category : #initialization } 240 | CompoundUnit >> units: myUnits exponents: myExponents [ 241 | units := myUnits. 242 | exponents := myExponents 243 | ] 244 | 245 | { #category : #enumerating } 246 | CompoundUnit >> unitsAndExponentsDo: block [ 247 | units with: exponents do: block 248 | ] 249 | -------------------------------------------------------------------------------- /src/Units-Core/NamedUnit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This is a kind of unit with a specific (singular and plural) name, and an abbreviation. e.g., meter, meters, m. 3 | 4 | " 5 | Class { 6 | #name : #NamedUnit, 7 | #superclass : #Unit, 8 | #instVars : [ 9 | 'abbreviation', 10 | 'name', 11 | 'pluralName' 12 | ], 13 | #classVars : [ 14 | 'UnitsByAbbreviation', 15 | 'UnitsByName', 16 | 'UnitsByPluralName' 17 | ], 18 | #category : #'Units-Core' 19 | } 20 | 21 | { #category : #'instance creation' } 22 | NamedUnit class >> abbreviation: abbreviation name: unitName pluralName: pluralName [ 23 | "Create and index a new NamedUnit." 24 | | unit | 25 | unit := self new 26 | abbreviation: abbreviation 27 | name: unitName 28 | pluralName: pluralName. 29 | UnitsByAbbreviation at: abbreviation put: unit. 30 | UnitsByName at: unitName put: unit. 31 | UnitsByPluralName at: pluralName put: unit. 32 | ^unit 33 | ] 34 | 35 | { #category : #'instance creation' } 36 | NamedUnit class >> abbreviation: abbreviation name: unitName pluralName: pluralName value: unitValue [ 37 | ^DerivedUnit 38 | abbreviation: abbreviation 39 | name: unitName 40 | pluralName: pluralName 41 | value: unitValue 42 | ] 43 | 44 | { #category : #'instance creation' } 45 | NamedUnit class >> abbreviation: abbreviation 46 | name: unitName 47 | pluralName: pluralName 48 | value: unitValue 49 | additiveFactor: additiveFactor [ 50 | ^TemperatureUnit 51 | abbreviation: abbreviation 52 | name: unitName 53 | pluralName: pluralName 54 | value: unitValue 55 | additiveFactor: additiveFactor 56 | ] 57 | 58 | { #category : #initialization } 59 | NamedUnit class >> initializeAreaUnits [ 60 | self 61 | abbreviation: 'acre' 62 | name: 'acre' 63 | pluralName: 'acres' 64 | value: (4046.87260987 units: Unit metre squared). 65 | self 66 | abbreviation: 'a' 67 | name: 'are' 68 | pluralName: 'ares' 69 | value: (100 units: Unit metre squared) 70 | ] 71 | 72 | { #category : #initialization } 73 | NamedUnit class >> initializeClass [ 74 | "NamedUnit initializeClass." 75 | "Do not rename this to #initialize." 76 | UnitsByAbbreviation := Dictionary new. 77 | UnitsByName := Dictionary new. 78 | UnitsByPluralName := Dictionary new. 79 | self initializeUnits 80 | ] 81 | 82 | { #category : #initialization } 83 | NamedUnit class >> initializeForceUnits [ 84 | self 85 | abbreviation: 'N' 86 | name: 'newton' 87 | pluralName: 'newtons' 88 | value: (1 units: (Unit kilogram * Unit metre) / Unit second squared) 89 | ] 90 | 91 | { #category : #initialization } 92 | NamedUnit class >> initializeLengthUnits [ 93 | self 94 | abbreviation: 'in' 95 | name: 'inch' 96 | pluralName: 'inches' 97 | value: (2.54 units: Unit centimetre). 98 | self 99 | abbreviation: 'ft' 100 | name: 'foot' 101 | pluralName: 'feet' 102 | value: (12 units: Unit inch). 103 | self 104 | abbreviation: 'yd' 105 | name: 'yard' 106 | pluralName: 'yards' 107 | value: (3 units: Unit foot). 108 | self 109 | abbreviation: 'mi' 110 | name: 'mile' 111 | pluralName: 'miles' 112 | value: (5280 units: Unit foot). 113 | self 114 | abbreviation: 'nm' 115 | name: 'nauticalmile' 116 | pluralName: 'nauticalmiles' 117 | value: (1852 units: Unit metre). 118 | self 119 | abbreviation: 'pt' 120 | name: 'point' 121 | pluralName: 'points' 122 | value: (1/72 units: Unit inch). 123 | self 124 | abbreviation: 'pc' 125 | name: 'pica' 126 | pluralName: 'picas' 127 | value: (12 units: Unit point). 128 | self 129 | abbreviation: 'px' 130 | name: 'pixel' 131 | pluralName: 'pixels' 132 | value: (0.2635872 units: Unit millimetre). 133 | self 134 | abbreviation: 'L' 135 | name: 'litre' 136 | pluralName: 'litres' 137 | value: (1/1000 units: (Unit metre raisedTo: 3)). 138 | self 139 | abbreviation: 'ua' 140 | name: 'astronomical unit' 141 | pluralName: 'astronomical units' 142 | value: ((1.49598 raisedTo: 11) units: Unit metre). 143 | self 144 | abbreviation: 'angstrom' 145 | name: 'angstrom' 146 | pluralName:'angstroms' 147 | value: (1e-10 units: Unit metre) 148 | ] 149 | 150 | { #category : #initialization } 151 | NamedUnit class >> initializeMassUnits [ 152 | "Initialize the mass units." 153 | 154 | self 155 | abbreviation: 't' 156 | name: 'tonne' 157 | pluralName: 'tonnes' 158 | value: (1000 units: Unit kilogram). 159 | self 160 | abbreviation: 'lb' 161 | name: 'pound' 162 | pluralName: 'pounds' 163 | value: (0.45359237 units: Unit kilogram). 164 | self 165 | abbreviation: 'oz' 166 | name: 'ounce' 167 | pluralName: 'ounces' 168 | value: (1/16 units: Unit pound). 169 | self 170 | abbreviation: 'st' 171 | name: 'stone' 172 | pluralName: 'stone' 173 | value: (14 units: Unit pound). 174 | self 175 | abbreviation: 'g' 176 | name: 'gram' 177 | pluralName: 'grams' 178 | value: ((1/1000) units: BaseUnit kilogram). 179 | ] 180 | 181 | { #category : #initialization } 182 | NamedUnit class >> initializeOtherUnits [ 183 | 184 | self 185 | abbreviation: 'C' 186 | name: 'coulomb' 187 | pluralName: 'coulombs' 188 | value: (1 units: ((Unit second) * (Unit ampere))). 189 | self 190 | abbreviation: 'F' 191 | name: 'farad' 192 | pluralName: 'farads' 193 | value: (1 units: ((Unit coulomb) / (Unit ampere))). 194 | self 195 | abbreviation: 'W' 196 | name: 'watt' 197 | pluralName: 'watts' 198 | value: (1 units: ((Unit ampere) * (Unit volt))). 199 | 200 | DerivedUnit 201 | abbreviation: 'deg' 202 | name: 'arcdegree' 203 | pluralName: 'arcdegrees' 204 | value: (1 degreesToRadians units: Unit radian). 205 | ] 206 | 207 | { #category : #initialization } 208 | NamedUnit class >> initializeTemperatureUnits [ 209 | TemperatureBaseUnit 210 | abbreviation: 'K' 211 | name: 'kelvin' 212 | pluralName: 'kelvins'. 213 | self 214 | abbreviation: '°F' 215 | name: 'degree Fahrenheit' 216 | pluralName: 'degrees Fahrenheit' 217 | value: (5/9 units: Unit kelvin) 218 | additiveFactor: -459.67. 219 | self 220 | abbreviation: '°C' 221 | name: 'degree Celsius' 222 | pluralName: 'degrees Celsius' 223 | value: (1 units: Unit kelvin) 224 | additiveFactor: -273.15 225 | ] 226 | 227 | { #category : #initialization } 228 | NamedUnit class >> initializeTimeUnits [ 229 | self 230 | abbreviation: 'min' 231 | name: 'minute' 232 | pluralName: 'minutes' 233 | value: (60 units: Unit second). 234 | self 235 | abbreviation: 'h' 236 | name: 'hour' 237 | pluralName: 'hours' 238 | value: (60 units: Unit minute). 239 | self 240 | abbreviation: 'd' 241 | name: 'day' 242 | pluralName: 'days' 243 | value: (24 units: Unit hour). 244 | self 245 | abbreviation: 'yr' 246 | name: 'year' 247 | pluralName: 'years' 248 | value: (365.25 units: Unit day). "use Julian year" 249 | self 250 | abbreviation: 'Hz' 251 | name: 'hertz' 252 | pluralName: 'hertz' 253 | value: 1 / (1 units: Unit second) 254 | ] 255 | 256 | { #category : #initialization } 257 | NamedUnit class >> initializeUnits [ 258 | 259 | self 260 | initializeAreaUnits; 261 | initializeLengthUnits; 262 | initializeMassUnits; 263 | initializeForceUnits; 264 | initializeTemperatureUnits; 265 | initializeTimeUnits; 266 | initializeOtherUnits 267 | ] 268 | 269 | { #category : #accessing } 270 | NamedUnit class >> withAbbreviation: abbreviation ifAbsent: exceptionBlock [ 271 | ^UnitsByAbbreviation 272 | at: abbreviation 273 | ifAbsent: exceptionBlock 274 | ] 275 | 276 | { #category : #accessing } 277 | NamedUnit class >> withPluralName: pluralName ifAbsent: exceptionBlock [ 278 | ^UnitsByPluralName 279 | at: pluralName 280 | ifAbsent: exceptionBlock 281 | ] 282 | 283 | { #category : #accessing } 284 | NamedUnit class >> withSingularName: unitName ifAbsent: exceptionBlock [ 285 | ^UnitsByName 286 | at: unitName 287 | ifAbsent: exceptionBlock 288 | ] 289 | 290 | { #category : #accessing } 291 | NamedUnit >> abbreviation [ 292 | ^abbreviation 293 | ] 294 | 295 | { #category : #initialization } 296 | NamedUnit >> abbreviation: myAbbreviation name: myName pluralName: myPluralName [ 297 | abbreviation := myAbbreviation. 298 | name := myName. 299 | pluralName := myPluralName 300 | ] 301 | 302 | { #category : #accessing } 303 | NamedUnit >> name [ 304 | ^name 305 | ] 306 | 307 | { #category : #accessing } 308 | NamedUnit >> pluralName [ 309 | ^pluralName 310 | ] 311 | 312 | { #category : #printing } 313 | NamedUnit >> printAbbreviationOn: stream [ 314 | stream nextPutAll: abbreviation 315 | ] 316 | 317 | { #category : #printing } 318 | NamedUnit >> printFullNameOn: stream pluralized: pluralized [ 319 | pluralized 320 | ifTrue: [stream nextPutAll: pluralName] 321 | ifFalse: [stream nextPutAll: name] 322 | ] 323 | 324 | { #category : #printing } 325 | NamedUnit >> storeOn: aStream [ 326 | "Store the code to lookup the receiver instead of for a new instance." 327 | 328 | aStream 329 | nextPutAll: '(NamedUnit withPluralName: '''; 330 | nextPutAll: self pluralName; 331 | nextPutAll: ''')' 332 | ] 333 | -------------------------------------------------------------------------------- /src/ConfigurationOfUnits/ConfigurationOfUnits.class.st: -------------------------------------------------------------------------------- 1 | " 2 | (ConfigurationOfUnits project version: #stable) load 3 | " 4 | Class { 5 | #name : #ConfigurationOfUnits, 6 | #superclass : #Object, 7 | #instVars : [ 8 | 'project' 9 | ], 10 | #classVars : [ 11 | 'LastVersionLoad' 12 | ], 13 | #category : #ConfigurationOfUnits 14 | } 15 | 16 | { #category : #'development support' } 17 | ConfigurationOfUnits class >> DevelopmentSupport [ 18 | 19 | "See the methods in the 'development support' category on the class-side of MetacelloBaseConfiguration. Decide what development support methods you would like to use and copy them the the class-side of your configuration." 20 | 21 | 22 | ] 23 | 24 | { #category : #private } 25 | ConfigurationOfUnits class >> baseConfigurationClassIfAbsent: aBlock [ 26 | 27 | ^Smalltalk 28 | at: #MetacelloBaseConfiguration 29 | ifAbsent: [ 30 | self ensureMetacelloBaseConfiguration. 31 | Smalltalk at: #MetacelloBaseConfiguration ifAbsent: aBlock ]. 32 | 33 | ] 34 | 35 | { #category : #catalog } 36 | ConfigurationOfUnits class >> catalogChangeLog [ 37 | 38 | ^ ' 39 | - 2013-12-28 Version 1.1 - split core from tests. 40 | 41 | [ [ [ 42 | (ConfigurationOfUnit project version: ''1.1'') load. 43 | ] ] ] 44 | 45 | - 2013-08-31 Version 1.0 - first configuration. 46 | 47 | [ [ [ 48 | (ConfigurationOfUnit project version: ''1.0'') load. 49 | ] ] ] 50 | ' 51 | ] 52 | 53 | { #category : #catalog } 54 | ConfigurationOfUnits class >> catalogDescription [ 55 | ^ 'Units is a simple and powerful library to manage different units.' 56 | ] 57 | 58 | { #category : #catalog } 59 | ConfigurationOfUnits class >> catalogKeyClassesAndExample [ 60 | 61 | ^ 'The key classes are 62 | 63 | - ==Unit== 64 | Class Unit is the superclass for all other unit classes. 65 | Instances represent units which are attached to numbers; for example 66 | meters (a BaseUnit), meters per second (a CompoundUnit), 67 | joule seconds per liter (a ComplexUnit), degrees Kelvin (a TemperatureBaseUnit), 68 | degrees Celsius (a TemperutareUnit), kilohertz (a PrefixedUnit), 69 | and moles of hydrogen (a ModifiedUnit). 70 | 71 | ==UnitValue== which represents a number with a unit. 72 | ' 73 | ] 74 | 75 | { #category : #catalog } 76 | ConfigurationOfUnits class >> catalogKeywords [ 77 | ^ #(Units Comparison Meter Points) 78 | ] 79 | 80 | { #category : #private } 81 | ConfigurationOfUnits class >> ensureMetacello [ 82 | 83 | (self baseConfigurationClassIfAbsent: []) ensureMetacello 84 | ] 85 | 86 | { #category : #private } 87 | ConfigurationOfUnits class >> ensureMetacelloBaseConfiguration [ 88 | 89 | Smalltalk 90 | at: #MetacelloBaseConfiguration 91 | ifAbsent: [ 92 | | repository version | 93 | repository := MCHttpRepository location: 'http://seaside.gemstone.com/ss/metacello' user: '' password: ''. 94 | repository 95 | versionReaderForFileNamed: 'Metacello-Base-DaleHenrichs.2.mcz' 96 | do: [ :reader | 97 | version := reader version. 98 | version load. 99 | version workingCopy repositoryGroup addRepository: repository ] ] 100 | ] 101 | 102 | { #category : #'metacello tool support' } 103 | ConfigurationOfUnits class >> isMetacelloConfig [ 104 | "Answer true and the Metacello tools will operate on you" 105 | 106 | ^true 107 | ] 108 | 109 | { #category : #'metacello tool support' } 110 | ConfigurationOfUnits class >> lastMetacelloVersionLoad [ 111 | "Answer the last version loaded and the list of packages loaded for that version." 112 | 113 | LastVersionLoad == nil ifTrue: [ LastVersionLoad := nil -> 'default' ]. 114 | ^LastVersionLoad 115 | ] 116 | 117 | { #category : #loading } 118 | ConfigurationOfUnits class >> load [ 119 | "Load the #stable version defined for this platform. The #stable version is the version that is recommended to be used on this platform." 120 | 121 | "self load" 122 | 123 | 124 | ^(self project version: #stable) load 125 | ] 126 | 127 | { #category : #loading } 128 | ConfigurationOfUnits class >> loadBleedingEdge [ 129 | "Load the latest versions of the mcz files defined for this project. It is not likely that the #bleedingEdge has been tested." 130 | 131 | "self loadBleedingEdge" 132 | 133 | 134 | ^(self project version: #bleedingEdge) load 135 | ] 136 | 137 | { #category : #loading } 138 | ConfigurationOfUnits class >> loadDevelopment [ 139 | "Load the #development version defined for this platform. The #development version will change over time and is not expected to be stable." 140 | 141 | "self loadDevelopment" 142 | 143 | 144 | ^(self project version: #development) load 145 | ] 146 | 147 | { #category : #'metacello tool support' } 148 | ConfigurationOfUnits class >> metacelloVersion: versionString loads: anArrayOrString [ 149 | "Stash the last version loaded and the list of packages loaded for that version. The list 150 | of packages will be used by the tools when doing 'Load Package Version'" 151 | 152 | LastVersionLoad := versionString -> anArrayOrString 153 | ] 154 | 155 | { #category : #accessing } 156 | ConfigurationOfUnits class >> project [ 157 | 158 | ^self new project 159 | ] 160 | 161 | { #category : #'unloading Metacello' } 162 | ConfigurationOfUnits class >> unloadMetacello [ 163 | 164 | Smalltalk at: #ConfigurationOfMetacello ifPresent: [:cls | cls unloadMetacello ] 165 | ] 166 | 167 | { #category : #'development support' } 168 | ConfigurationOfUnits class >> validate [ 169 | "Check the configuration for Errors, Critical Warnings, and Warnings (see class comment for MetacelloMCVersionValidator for more information). 170 | Errors identify specification issues that will result in unexpected behaviour when you load the configuration. 171 | Critical Warnings identify specification issues that may result in unexpected behavior when you load the configuration. 172 | Warnings identify specification issues that are technically correct, but are worth take a look at." 173 | 174 | "self validate" 175 | 176 | 177 | self ensureMetacello. 178 | ^ ((Smalltalk at: #MetacelloToolBox) validateConfiguration: self debug: #() recurse: false) explore 179 | ] 180 | 181 | { #category : #baselines } 182 | ConfigurationOfUnits >> baseline10: spec [ 183 | 184 | 185 | spec for: #'pharo' do: [ 186 | spec blessing: #'baseline'. 187 | spec repository: 'http://smalltalkhub.com/mc/MarcusDenker/Units/main'. 188 | spec package: 'Units']. 189 | 190 | 191 | 192 | ] 193 | 194 | { #category : #baselines } 195 | ConfigurationOfUnits >> baseline11: spec [ 196 | 197 | 198 | spec for: #'common' do: [ 199 | spec blessing: #'baseline'. 200 | spec repository: 'http://smalltalkhub.com/mc/MarcusDenker/Units/main'. 201 | 202 | spec 203 | package: 'Units-Core'; 204 | package: 'Units-Tests' with: [ spec requires: 'Units-Core' ]. 205 | 206 | spec 207 | group: 'default' with: #('core' 'test'); 208 | group: 'core' with: #('Units-Core'); 209 | group: 'test' with: #('Units-Tests')]. 210 | 211 | 212 | 213 | ] 214 | 215 | { #category : #accessing } 216 | ConfigurationOfUnits >> customProjectAttributes [ 217 | "Edit to return a collection of any custom attributes e.g. for conditional loading: Array with: #'Condition1' with: #'Condition2. 218 | For more information see: http://code.google.com/p/metacello/wiki/CustomProjectAttrributes" 219 | 220 | ^ #(). 221 | ] 222 | 223 | { #category : #'symbolic versions' } 224 | ConfigurationOfUnits >> development: spec [ 225 | 226 | 227 | spec for: #'common' version: '1.1-baseline'. 228 | 229 | ] 230 | 231 | { #category : #accessing } 232 | ConfigurationOfUnits >> project [ 233 | 234 | ^ project ifNil: [ | constructor | 235 | "Bootstrap Metacello if it is not already loaded" 236 | (self class baseConfigurationClassIfAbsent: []) ensureMetacello. 237 | "Construct Metacello project" 238 | project := MetacelloMCProject new projectAttributes: self customProjectAttributes. 239 | constructor := (Smalltalk at: #MetacelloVersionConstructor) on: self project: project. 240 | project loadType: #linear. "change to #atomic if desired" 241 | project ] 242 | ] 243 | 244 | { #category : #'symbolic versions' } 245 | ConfigurationOfUnits >> stable: spec [ 246 | 247 | 248 | spec for: #'common' version: '1.2'. 249 | 250 | ] 251 | 252 | { #category : #versions } 253 | ConfigurationOfUnits >> version10: spec [ 254 | 255 | 256 | spec for: #'common' do: [ 257 | spec blessing: #'release'. 258 | spec description: 'First release'. 259 | spec author: 'tbn'. 260 | spec timestamp: '6/14/2013 08:58'. 261 | spec package: 'Units' with: 'Units-espin.36'. 262 | ]. 263 | 264 | 265 | ] 266 | 267 | { #category : #versions } 268 | ConfigurationOfUnits >> version11: spec [ 269 | 270 | 271 | spec for: #'common' do: [ 272 | spec blessing: #'release'. 273 | spec description: 'Cleanup and split core from tests'. 274 | spec author: 'tbn'. 275 | spec timestamp: '12/28/2013 08:58'. 276 | spec package: 'Units-Core' with: 'Units-Core-TorstenBergmann.2'. 277 | spec package: 'Units-Tests' with: 'Units-Tests-TorstenBergmann.1' 278 | ]. 279 | 280 | 281 | ] 282 | 283 | { #category : #versions } 284 | ConfigurationOfUnits >> version12: spec [ 285 | 286 | 287 | spec for: #'common' do: [ 288 | spec blessing: #'development'. 289 | spec description: 'Current development'. 290 | spec author: 'tbn'. 291 | spec timestamp: '12/28/2013 08:58'. 292 | spec package: 'Units-Core' with: 'Units-Core-GuillaumeLarcheveque.5'. 293 | spec package: 'Units-Tests' with: 'Units-Tests-GuillaumeLarcheveque.2' 294 | ]. 295 | 296 | 297 | ] 298 | -------------------------------------------------------------------------------- /src/Units-Core/Number.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Number } 2 | 3 | { #category : #'*Units-Core' } 4 | Number >> A [ 5 | 6 | ^self units: #ampere 7 | ] 8 | 9 | { #category : #'*Units-Core' } 10 | Number >> C [ 11 | 12 | ^self units: #coulomb 13 | ] 14 | 15 | { #category : #'*Units-Core' } 16 | Number >> F [ 17 | 18 | ^self units: #farad 19 | ] 20 | 21 | { #category : #'*Units-Core' } 22 | Number >> GHz [ 23 | 24 | ^self units: #gigahertz 25 | ] 26 | 27 | { #category : #'*Units-Core' } 28 | Number >> GV [ 29 | 30 | ^ self units: #gigavolt 31 | ] 32 | 33 | { #category : #'*Units-Core' } 34 | Number >> Hz [ 35 | 36 | ^self units: #hertz 37 | ] 38 | 39 | { #category : #'*Units-Core' } 40 | Number >> K [ 41 | 42 | ^self units: #kelvin 43 | ] 44 | 45 | { #category : #'*Units-Core' } 46 | Number >> L [ 47 | 48 | ^ self units: #litre 49 | ] 50 | 51 | { #category : #'*Units-Core' } 52 | Number >> MHz [ 53 | 54 | ^self units: #megahertz 55 | ] 56 | 57 | { #category : #'*Units-Core' } 58 | Number >> MV [ 59 | 60 | ^ self units: #megavolt 61 | ] 62 | 63 | { #category : #'*Units-Core' } 64 | Number >> Mt [ 65 | 66 | ^self units: #megatonne 67 | ] 68 | 69 | { #category : #'*Units-Core' } 70 | Number >> V [ 71 | 72 | ^self units: #volt 73 | ] 74 | 75 | { #category : #'*Units-Core' } 76 | Number >> W [ 77 | 78 | ^self units: #watt 79 | ] 80 | 81 | { #category : #'*Units-Core' } 82 | Number >> a [ 83 | 84 | ^ self units: #are 85 | ] 86 | 87 | { #category : #'*Units-Core' } 88 | Number >> addToUnit: unitValue [ 89 | 90 | ^unitValue class 91 | unit: unitValue unit 92 | value: unitValue value + self 93 | ] 94 | 95 | { #category : #'*Units-Core' } 96 | Number >> angstrom [ 97 | 98 | ^self units: #angstrom 99 | ] 100 | 101 | { #category : #'*Units-Core' } 102 | Number >> angstroms [ 103 | 104 | ^self angstrom 105 | ] 106 | 107 | { #category : #'*Units-Core' } 108 | Number >> arcdegree [ 109 | 110 | ^self units: #arcdegree 111 | ] 112 | 113 | { #category : #'*Units-Core' } 114 | Number >> baseUnits [ 115 | 116 | ^ self 117 | ] 118 | 119 | { #category : #'*Units-Core' } 120 | Number >> candela [ 121 | 122 | ^self units: #candela 123 | ] 124 | 125 | { #category : #'*Units-Core' } 126 | Number >> cd [ 127 | 128 | ^self candela 129 | ] 130 | 131 | { #category : #'*Units-Core' } 132 | Number >> celsius [ 133 | 134 | ^self degreeCelsius 135 | ] 136 | 137 | { #category : #'*Units-Core' } 138 | Number >> centimeter [ 139 | 140 | ^self cm 141 | ] 142 | 143 | { #category : #'*Units-Core' } 144 | Number >> centimeters [ 145 | 146 | ^self cm 147 | ] 148 | 149 | { #category : #'*Units-Core' } 150 | Number >> cm [ 151 | 152 | ^self units: #centimeter 153 | ] 154 | 155 | { #category : #'*Units-Core' } 156 | Number >> degreeCelsius [ 157 | 158 | ^self units: #degreeCelsius 159 | ] 160 | 161 | { #category : #'*Units-Core' } 162 | Number >> degreeFahrenheit [ 163 | 164 | ^self units: #degreeFahrenheit 165 | ] 166 | 167 | { #category : #'*Units-Core' } 168 | Number >> degreeKelvin [ 169 | 170 | ^self kelvin 171 | ] 172 | 173 | { #category : #'*Units-Core' } 174 | Number >> divideIntoUnit: unitValue [ 175 | 176 | ^unitValue class 177 | unit: unitValue unit 178 | value: unitValue value / self 179 | ] 180 | 181 | { #category : #'*Units-Core' } 182 | Number >> equalFromUnit: unitValue [ 183 | "Is 'unitValue' equal in magnitude to the receiver?" 184 | 185 | ^unitValue value = self 186 | ] 187 | 188 | { #category : #'*Units-Core' } 189 | Number >> fahrenheit [ 190 | 191 | ^self degreeFahrenheit 192 | ] 193 | 194 | { #category : #'*Units-Core' } 195 | Number >> feet [ 196 | 197 | ^self foot 198 | ] 199 | 200 | { #category : #'*Units-Core' } 201 | Number >> foot [ 202 | 203 | ^self units: #foot 204 | ] 205 | 206 | { #category : #'*Units-Core' } 207 | Number >> ft [ 208 | 209 | ^self foot 210 | ] 211 | 212 | { #category : #'*Units-Core' } 213 | Number >> g [ 214 | 215 | ^self units: #gram 216 | ] 217 | 218 | { #category : #'*Units-Core' } 219 | Number >> gigahertz [ 220 | 221 | ^self units: #gigahertz 222 | ] 223 | 224 | { #category : #'*Units-Core' } 225 | Number >> grams [ 226 | 227 | ^self g 228 | ] 229 | 230 | { #category : #'*Units-Core' } 231 | Number >> h [ 232 | 233 | ^ self units: #hour 234 | ] 235 | 236 | { #category : #'*Units-Core' } 237 | Number >> ha [ 238 | 239 | ^ self units: #hectare 240 | ] 241 | 242 | { #category : #'*Units-Core' } 243 | Number >> hertz [ 244 | 245 | ^self units: #hertz 246 | ] 247 | 248 | { #category : #'*Units-Core' } 249 | Number >> in [ 250 | 251 | ^self units: #inch 252 | ] 253 | 254 | { #category : #'*Units-Core' } 255 | Number >> inches [ 256 | 257 | ^self in 258 | ] 259 | 260 | { #category : #'*Units-Core' } 261 | Number >> kHz [ 262 | 263 | ^self units: #kilohertz 264 | ] 265 | 266 | { #category : #'*Units-Core' } 267 | Number >> kV [ 268 | ^self units: #kilovolt 269 | ] 270 | 271 | { #category : #'*Units-Core' } 272 | Number >> kW [ 273 | 274 | ^self units: #kilowatt 275 | ] 276 | 277 | { #category : #'*Units-Core' } 278 | Number >> kWh [ 279 | 280 | ^ (self units: #kilowatt) * (1 units: #hour) 281 | ] 282 | 283 | { #category : #'*Units-Core' } 284 | Number >> kelvin [ 285 | 286 | ^self units: #kelvin 287 | ] 288 | 289 | { #category : #'*Units-Core' } 290 | Number >> kg [ 291 | 292 | ^self units: #kilogram 293 | ] 294 | 295 | { #category : #'*Units-Core' } 296 | Number >> kilograms [ 297 | 298 | ^self kg 299 | ] 300 | 301 | { #category : #'*Units-Core' } 302 | Number >> kilohertz [ 303 | 304 | ^self units: #kilohertz 305 | ] 306 | 307 | { #category : #'*Units-Core' } 308 | Number >> kilometer [ 309 | 310 | ^self kilometre 311 | ] 312 | 313 | { #category : #'*Units-Core' } 314 | Number >> kilometers [ 315 | 316 | ^self kilometre 317 | ] 318 | 319 | { #category : #'*Units-Core' } 320 | Number >> kilometre [ 321 | 322 | ^self units: #kilometre 323 | ] 324 | 325 | { #category : #'*Units-Core' } 326 | Number >> km [ 327 | 328 | ^self kilometre 329 | ] 330 | 331 | { #category : #'*Units-Core' } 332 | Number >> lb [ 333 | 334 | ^self pound 335 | ] 336 | 337 | { #category : #'*Units-Core' } 338 | Number >> lessFromUnit: unitValue [ 339 | "Does 'unitValue' have a smaller magnitude than us?" 340 | 341 | ^unitValue value < self 342 | ] 343 | 344 | { #category : #'*Units-Core' } 345 | Number >> liter [ 346 | 347 | ^self litre 348 | ] 349 | 350 | { #category : #'*Units-Core' } 351 | Number >> liters [ 352 | 353 | ^self litre 354 | ] 355 | 356 | { #category : #'*Units-Core' } 357 | Number >> litre [ 358 | 359 | ^self units: #litre 360 | ] 361 | 362 | { #category : #'*Units-Core' } 363 | Number >> m [ 364 | 365 | ^self metre 366 | ] 367 | 368 | { #category : #'*Units-Core' } 369 | Number >> mA [ 370 | 371 | ^self units: #milliamp 372 | ] 373 | 374 | { #category : #'*Units-Core' } 375 | Number >> mV [ 376 | 377 | ^self units: #millivolt 378 | ] 379 | 380 | { #category : #'*Units-Core' } 381 | Number >> megahertz [ 382 | 383 | ^self units: #megahertz 384 | ] 385 | 386 | { #category : #'*Units-Core' } 387 | Number >> megatonne [ 388 | 389 | ^self units: #megatonne 390 | ] 391 | 392 | { #category : #'*Units-Core' } 393 | Number >> meter [ 394 | 395 | ^self metre 396 | ] 397 | 398 | { #category : #'*Units-Core' } 399 | Number >> meters [ 400 | 401 | ^self metre 402 | ] 403 | 404 | { #category : #'*Units-Core' } 405 | Number >> metre [ 406 | 407 | ^self units: #metre 408 | ] 409 | 410 | { #category : #'*Units-Core' } 411 | Number >> mg [ 412 | 413 | ^self milligram 414 | ] 415 | 416 | { #category : #'*Units-Core' } 417 | Number >> mi [ 418 | 419 | ^ self units: #mile 420 | ] 421 | 422 | { #category : #'*Units-Core' } 423 | Number >> milligram [ 424 | 425 | ^ self units: #milligram 426 | ] 427 | 428 | { #category : #'*Units-Core' } 429 | Number >> millimeter [ 430 | 431 | ^self millimetre 432 | ] 433 | 434 | { #category : #'*Units-Core' } 435 | Number >> millimeters [ 436 | 437 | ^self millimetre 438 | ] 439 | 440 | { #category : #'*Units-Core' } 441 | Number >> millimetre [ 442 | 443 | ^ self units: #millimetre 444 | ] 445 | 446 | { #category : #'*Units-Core' } 447 | Number >> mm [ 448 | 449 | ^self millimetre 450 | ] 451 | 452 | { #category : #'*Units-Core' } 453 | Number >> multiplyByUnit: unitValue [ 454 | 455 | ^unitValue class 456 | unit: unitValue unit 457 | value: unitValue value * self 458 | ] 459 | 460 | { #category : #'*Units-Core' } 461 | Number >> nV [ 462 | ^self units: #nanovolt 463 | ] 464 | 465 | { #category : #'*Units-Core' } 466 | Number >> ounce [ 467 | 468 | ^ self units: #ounce 469 | ] 470 | 471 | { #category : #'*Units-Core' } 472 | Number >> oz [ 473 | 474 | ^self ounce 475 | ] 476 | 477 | { #category : #'*Units-Core' } 478 | Number >> pV [ 479 | ^self units: #picovolt 480 | ] 481 | 482 | { #category : #'*Units-Core' } 483 | Number >> pc [ 484 | 485 | ^self pica 486 | ] 487 | 488 | { #category : #'*Units-Core' } 489 | Number >> pica [ 490 | 491 | ^ self units: #pica 492 | ] 493 | 494 | { #category : #'*Units-Core' } 495 | Number >> pixel [ 496 | 497 | ^ self units: #pixel 498 | ] 499 | 500 | { #category : #'*Units-Core' } 501 | Number >> pixels [ 502 | 503 | ^ self pixel 504 | ] 505 | 506 | { #category : #'*Units-Core' } 507 | Number >> point [ 508 | 509 | ^ self units: #point 510 | ] 511 | 512 | { #category : #'*Units-Core' } 513 | Number >> pound [ 514 | 515 | ^self units: #pound 516 | ] 517 | 518 | { #category : #'*Units-Core' } 519 | Number >> pt [ 520 | 521 | ^self point 522 | ] 523 | 524 | { #category : #'*Units-Core' } 525 | Number >> px [ 526 | 527 | ^self pixel 528 | ] 529 | 530 | { #category : #'*Units-Core' } 531 | Number >> rad [ 532 | 533 | ^self radian 534 | ] 535 | 536 | { #category : #'*Units-Core' } 537 | Number >> radian [ 538 | 539 | ^self units: #rad 540 | ] 541 | 542 | { #category : #'*Units-Core' } 543 | Number >> radians [ 544 | 545 | ^self rad 546 | ] 547 | 548 | { #category : #'*Units-Core' } 549 | Number >> s [ 550 | 551 | ^ self units: #second 552 | ] 553 | 554 | { #category : #'*Units-Core' } 555 | Number >> st [ 556 | 557 | ^ self stone 558 | ] 559 | 560 | { #category : #'*Units-Core' } 561 | Number >> stone [ 562 | 563 | ^ self units: #stone 564 | ] 565 | 566 | { #category : #'*Units-Core' } 567 | Number >> stones [ 568 | 569 | ^ self st 570 | ] 571 | 572 | { #category : #'*Units-Core' } 573 | Number >> subtractFromUnit: unitValue [ 574 | 575 | ^unitValue class 576 | unit: unitValue unit 577 | value: unitValue value - self 578 | ] 579 | 580 | { #category : #'*Units-Core' } 581 | Number >> t [ 582 | 583 | ^ self tonne 584 | ] 585 | 586 | { #category : #'*Units-Core' } 587 | Number >> tonne [ 588 | 589 | ^ self units: #tonne 590 | ] 591 | 592 | { #category : #'*Units-Core' } 593 | Number >> tonnes [ 594 | 595 | ^ self t 596 | ] 597 | 598 | { #category : #'*Units-Core' } 599 | Number >> ua [ 600 | 601 | ^self units: #ua 602 | ] 603 | 604 | { #category : #'*Units-Core' } 605 | Number >> units: unitOrSymbol [ 606 | ^UnitValue 607 | unit: (UnitValue unitFor: unitOrSymbol) 608 | value: self 609 | ] 610 | 611 | { #category : #'*Units-Core' } 612 | Number >> yard [ 613 | 614 | ^ self units: #yard 615 | ] 616 | 617 | { #category : #'*Units-Core' } 618 | Number >> yards [ 619 | 620 | ^ self yard 621 | ] 622 | 623 | { #category : #'*Units-Core' } 624 | Number >> yd [ 625 | 626 | ^self yard 627 | ] 628 | 629 | { #category : #'*Units-Core' } 630 | Number >> µg [ 631 | 632 | ^ self units: #microgram 633 | ] 634 | -------------------------------------------------------------------------------- /src/Units-Core/UnitValue.class.st: -------------------------------------------------------------------------------- 1 | " 2 | UnitValue is a number with a unit attached. 3 | See the methods here for more information. 4 | 5 | " 6 | Class { 7 | #name : #UnitValue, 8 | #superclass : #Magnitude, 9 | #instVars : [ 10 | 'unit', 11 | 'value' 12 | ], 13 | #category : #'Units-Core' 14 | } 15 | 16 | { #category : #'instance creation' } 17 | UnitValue class >> one [ 18 | ^1 19 | ] 20 | 21 | { #category : #'instance creation' } 22 | UnitValue class >> unit: unit value: value [ 23 | ^self basicNew unit: unit value: value 24 | ] 25 | 26 | { #category : #accessing } 27 | UnitValue class >> unitFor: object [ 28 | "Some methods in UnitValue (and Number>>#units:) can take either 29 | a Unit object, or a symbol representing a unit name, or a UnitValue object. 30 | If it is a symbol, it is sent as a message to Unit class to retrieve the actual 31 | unit object. If it is a UnitValue, the unitPart is taken." 32 | 33 | ^object isSymbol 34 | ifTrue: [Unit perform: object] 35 | ifFalse: [object unitPart] 36 | ] 37 | 38 | { #category : #'instance creation' } 39 | UnitValue class >> zero [ 40 | ^0 41 | ] 42 | 43 | { #category : #arithmetic } 44 | UnitValue >> % number [ 45 | self shouldNotImplement 46 | ] 47 | 48 | { #category : #arithmetic } 49 | UnitValue >> * number [ 50 | ^number multiplyByUnit: self 51 | ] 52 | 53 | { #category : #mathematical } 54 | UnitValue >> ** exponent [ 55 | ^self raisedTo: exponent 56 | ] 57 | 58 | { #category : #arithmetic } 59 | UnitValue >> + number [ 60 | ^number addToUnit: self 61 | ] 62 | 63 | { #category : #arithmetic } 64 | UnitValue >> - number [ 65 | ^number subtractFromUnit: self 66 | ] 67 | 68 | { #category : #arithmetic } 69 | UnitValue >> / number [ 70 | ^number divideIntoUnit: self 71 | ] 72 | 73 | { #category : #comparing } 74 | UnitValue >> < number [ 75 | ^number lessFromUnit: self 76 | ] 77 | 78 | { #category : #comparing } 79 | UnitValue >> = number [ 80 | ^number equalFromUnit: self 81 | ] 82 | 83 | { #category : #arithmetic } 84 | UnitValue >> @ anObject [ 85 | "Answer a new point with x being the receiver and y anObject." 86 | 87 | ^Point x: self y: anObject 88 | ] 89 | 90 | { #category : #coercing } 91 | UnitValue >> adaptToInteger: rcvr andSend: selector [ 92 | "handle coercion for mixed arithmetic 93 | uses the method already present for DD" 94 | ^(self coerce: rcvr) perform: selector with: self 95 | ] 96 | 97 | { #category : #coercing } 98 | UnitValue >> adaptToNumber: rcvr andSend: selector [ 99 | "handle coercion for mixed arithmetic 100 | uses the method already present for DD" 101 | ^(self coerce: rcvr) perform: selector with: self 102 | ] 103 | 104 | { #category : #'double dispatching' } 105 | UnitValue >> addToUnit: unitValue [ 106 | | sum | 107 | ^(self consistentWith: unitValue) 108 | ifTrue: [ 109 | sum := self uncheckedConvertTo: unitValue. 110 | sum setValue: unitValue value + sum value. 111 | sum reduced] 112 | ifFalse: [unitValue inconsistentUnits: self selector: #+] 113 | ] 114 | 115 | { #category : #converting } 116 | UnitValue >> as [ 117 | "Answer a delegate on the receiver #as: to handle the next message." 118 | 119 | ^MessageDelegate on: [:m | self as: m selector] 120 | ] 121 | 122 | { #category : #converting } 123 | UnitValue >> as: anotherUnit [ 124 | "Convert the receiver to have the same units as 'anotherUnit'. Apply any appropriate 125 | scaling factors. Gives an error if the receiver is not consistent with 'anotherUnit'." 126 | 127 | ^self convertTo: anotherUnit 128 | ] 129 | 130 | { #category : #converting } 131 | UnitValue >> baseUnits [ 132 | "Answer the receiver reduced to base units only." 133 | ^self convertTo: unit baseUnits 134 | ] 135 | 136 | { #category : #rounding } 137 | UnitValue >> ceiling [ 138 | ^self class 139 | unit: unit 140 | value: value ceiling 141 | ] 142 | 143 | { #category : #coercing } 144 | UnitValue >> coerce: number [ 145 | ^self class 146 | unit: CompoundUnit null 147 | value: number 148 | ] 149 | 150 | { #category : #consistency } 151 | UnitValue >> consistentWith: anotherUnit [ 152 | ^unit consistentWith: anotherUnit unitPart 153 | ] 154 | 155 | { #category : #converting } 156 | UnitValue >> convertTo: anotherUnit [ 157 | "Convert the receiver to have the same units as 'anotherUnit'. Apply any appropriate 158 | scaling factors. Gives an error if the receiver is not consistent with 'anotherUnit'." 159 | | u | 160 | u := self class unitFor: anotherUnit. 161 | ^(u consistentWith: unit) 162 | ifTrue: [u uncheckedConvertFrom: self] 163 | ifFalse: [self inconsistentUnits: u selector: #convertTo:] 164 | ] 165 | 166 | { #category : #printing } 167 | UnitValue >> cssString [ 168 | "CSS is picky about units directly following the value." 169 | 170 | |str| 171 | str := (String new: 20) writeStream. 172 | self value printOn: str. 173 | self unit printAbbreviationOn: str. 174 | ^str contents 175 | ] 176 | 177 | { #category : #'double dispatching' } 178 | UnitValue >> divideIntoUnit: unitValue [ 179 | "Let the VM check for division by zero." 180 | ^(self class 181 | unit: (unitValue unit dividedBy: unit) 182 | value: unitValue value / value) 183 | reduced 184 | ] 185 | 186 | { #category : #'double dispatching' } 187 | UnitValue >> equalFromUnit: unitValue [ 188 | "Is 'unitValue' equal in magnitude to the receiver?" 189 | | converted | 190 | ^(self consistentWith: unitValue) 191 | ifTrue: [ 192 | "self halt. *** why? ***" 193 | converted := self uncheckedConvertTo: unitValue. 194 | unitValue value = converted value] 195 | ifFalse: [false] 196 | ] 197 | 198 | { #category : #converting } 199 | UnitValue >> factor: unitValue [ 200 | "Factor with respect to another Unit or UnitValue." 201 | | newUnit | 202 | newUnit := unit factor: (self class unitFor: unitValue). 203 | ^self convertTo: newUnit 204 | ] 205 | 206 | { #category : #rounding } 207 | UnitValue >> floor [ 208 | ^self class 209 | unit: unit 210 | value: value floor 211 | ] 212 | 213 | { #category : #coercing } 214 | UnitValue >> generality [ 215 | ^46 216 | ] 217 | 218 | { #category : #comparing } 219 | UnitValue >> hash [ 220 | "Answer a SmallInteger whose value is related to the receiver's identity. 221 | May be overridden, and should be overridden in any classes that define = " 222 | 223 | ^self unit hash bitXor: self value hash 224 | ] 225 | 226 | { #category : #consistency } 227 | UnitValue >> inconsistentUnits: argument selector: selector [ 228 | self error: 229 | 'Inconsistent units for #', selector, ' between ', 230 | self printString, ' and ', argument printString 231 | ] 232 | 233 | { #category : #testing } 234 | UnitValue >> isZero [ 235 | ^unit isZeroAsValue: value 236 | ] 237 | 238 | { #category : #'double dispatching' } 239 | UnitValue >> lessFromUnit: unitValue [ 240 | "Does 'unitValue' have a smaller magnitude than us?" 241 | | converted | 242 | ^(self consistentWith: unitValue) 243 | ifTrue: [ 244 | converted := self uncheckedConvertTo: unitValue. 245 | unitValue value < converted value] 246 | ifFalse: [unitValue inconsistentUnits: self selector: #<] 247 | ] 248 | 249 | { #category : #'double dispatching' } 250 | UnitValue >> multiplyByUnit: unitValue [ 251 | ^(self class 252 | unit: (unit multipliedBy: unitValue unit) 253 | value: unitValue value * value) reduced 254 | ] 255 | 256 | { #category : #arithmetic } 257 | UnitValue >> negated [ 258 | ^self class 259 | unit: unit 260 | value: value negated 261 | ] 262 | 263 | { #category : #testing } 264 | UnitValue >> negative [ 265 | ^value negative 266 | ] 267 | 268 | { #category : #printing } 269 | UnitValue >> printAbbreviatedOn: stream [ 270 | "Print the receiver in abbreviated form." 271 | 272 | value printOn: stream. 273 | stream space. 274 | unit printAbbreviationOn: stream 275 | ] 276 | 277 | { #category : #printing } 278 | UnitValue >> printOn: stream [ 279 | value printOn: stream. 280 | stream space. 281 | Unit printAbbreviated 282 | ifTrue: [unit printAbbreviationOn: stream] 283 | ifFalse: [unit 284 | printFullNameOn: stream 285 | pluralized: value ~= 1] 286 | ] 287 | 288 | { #category : #printing } 289 | UnitValue >> printStringAbbreviated [ 290 | "Answer the print string in abbreviated form." 291 | 292 | |str| 293 | str := (String new: 10) writeStream. 294 | self printAbbreviatedOn: str. 295 | ^str contents 296 | ] 297 | 298 | { #category : #mathematical } 299 | UnitValue >> raisedTo: exponent [ 300 | ^self class 301 | unit: (unit raisedTo: exponent) 302 | value: (value raisedTo: exponent) 303 | ] 304 | 305 | { #category : #converting } 306 | UnitValue >> reduced [ 307 | "If the receiver's units have 'vanished' (e.g., by dividing 2 seconds by 3 seconds), 308 | answer the remaining scalar part of the receiver. Otherwise, answer the receiver 309 | unchanged." 310 | 311 | ^ unit isNull 312 | ifTrue: [ value ] 313 | ifFalse: [ 314 | (unit isComplexUnit and: [unit calculateBaseUnits isNull]) 315 | ifTrue: [ value * unit conversionFactor ] 316 | ifFalse: [ self ] ] 317 | ] 318 | 319 | { #category : #rounding } 320 | UnitValue >> roundTo: number [ 321 | ^self class 322 | unit: unit 323 | value: (value roundTo: number) 324 | ] 325 | 326 | { #category : #rounding } 327 | UnitValue >> roundUpTo: number [ 328 | ^self class 329 | unit: unit 330 | value: (value roundUpTo: number) 331 | ] 332 | 333 | { #category : #rounding } 334 | UnitValue >> rounded [ 335 | ^self class 336 | unit: unit 337 | value: value rounded 338 | ] 339 | 340 | { #category : #private } 341 | UnitValue >> setUnit: aUnit [ 342 | "Set the unit." 343 | 344 | unit := aUnit 345 | ] 346 | 347 | { #category : #private } 348 | UnitValue >> setValue: newValue [ 349 | value := newValue 350 | ] 351 | 352 | { #category : #testing } 353 | UnitValue >> sign [ 354 | ^value sign 355 | ] 356 | 357 | { #category : #mathematical } 358 | UnitValue >> sqrt [ 359 | ^self class 360 | unit: (unit raisedTo: 1/2) 361 | value: value sqrt 362 | ] 363 | 364 | { #category : #accessing } 365 | UnitValue >> storeOn: aStream [ 366 | "Store the code to lookup the receiver instead of for a new instance." 367 | 368 | |as| 369 | as := String new writeStream. 370 | self unit printAbbreviationOn: as. 371 | (Unit respondsTo: as contents asSymbol) 372 | ifTrue: [self value storeOn: aStream. 373 | aStream 374 | space; 375 | nextPutAll: as contents] 376 | ifFalse: [super storeOn: aStream] 377 | ] 378 | 379 | { #category : #'double dispatching' } 380 | UnitValue >> subtractFromUnit: unitValue [ 381 | "Subtract the receiver from the unitValue." 382 | 383 | | sum | 384 | ^(self consistentWith: unitValue) 385 | ifTrue: [ 386 | sum := self uncheckedConvertTo: unitValue. 387 | sum setValue: unitValue value - sum value. 388 | sum reduced] 389 | ifFalse: [unitValue inconsistentUnits: self selector: #-] 390 | ] 391 | 392 | { #category : #rounding } 393 | UnitValue >> truncateTo: number [ 394 | ^self class 395 | unit: unit 396 | value: (value truncateTo: number) 397 | ] 398 | 399 | { #category : #rounding } 400 | UnitValue >> truncated [ 401 | ^self class 402 | unit: unit 403 | value: value truncated 404 | ] 405 | 406 | { #category : #private } 407 | UnitValue >> uncheckedConvertTo: anotherUnit [ 408 | ^anotherUnit unitPart uncheckedConvertFrom: self 409 | ] 410 | 411 | { #category : #accessing } 412 | UnitValue >> unit [ 413 | ^unit 414 | ] 415 | 416 | { #category : #initialization } 417 | UnitValue >> unit: myUnit value: myValue [ 418 | unit := myUnit. 419 | value := myValue 420 | ] 421 | 422 | { #category : #private } 423 | UnitValue >> unitPart [ 424 | "Answer the unit part of the receiver." 425 | ^unit 426 | ] 427 | 428 | { #category : #accessing } 429 | UnitValue >> value [ 430 | "A special interpretation of #value to answer the scalar part of the unit." 431 | ^value 432 | ] 433 | -------------------------------------------------------------------------------- /src/Units-Core/Unit.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Class Unit is the superclass for all other unit classes. 3 | Instances represent units which are attached to numbers; for example 4 | 'meters' (a BaseUnit), 'meters per second' (a CompoundUnit), 5 | 'joule seconds per liter' (a ComplexUnit), 'degrees Kelvin' (a TemperatureBaseUnit), 6 | 'degrees Celsius' (a TemperatureUnit), 'kilohertz' (a PrefixedUnit), 7 | and 'moles of hydrogen' (a ModifiedUnit). 8 | 9 | A number of methods on the class side of Unit provide for easy access to built-in units. 10 | 11 | The basic unit 'kilogram' is special given that it is basic and has a 'kilo' prefix, see 12 | also http://www.bipm.org/en/si/si_brochure/chapter3/3-2.html 13 | 14 | " 15 | Class { 16 | #name : #Unit, 17 | #superclass : #Object, 18 | #classVars : [ 19 | 'PrintAbbreviated' 20 | ], 21 | #category : #'Units-Core' 22 | } 23 | 24 | { #category : #'SI base units' } 25 | Unit class >> A [ 26 | 27 | ^ self ampere 28 | ] 29 | 30 | { #category : #'miscellaneous units' } 31 | Unit class >> C [ 32 | 33 | ^ self coulomb 34 | ] 35 | 36 | { #category : #'miscellaneous units' } 37 | Unit class >> F [ 38 | 39 | ^ self farad 40 | ] 41 | 42 | { #category : #'time units' } 43 | Unit class >> GHz [ 44 | 45 | ^self gigahertz 46 | ] 47 | 48 | { #category : #'time units' } 49 | Unit class >> Hz [ 50 | 51 | ^self hertz 52 | ] 53 | 54 | { #category : #'SI base units' } 55 | Unit class >> K [ 56 | 57 | ^self kelvin 58 | ] 59 | 60 | { #category : #'time units' } 61 | Unit class >> MHz [ 62 | 63 | ^self megahertz 64 | ] 65 | 66 | { #category : #'mass units' } 67 | Unit class >> Mt [ 68 | 69 | ^self megatonne 70 | ] 71 | 72 | { #category : #'area units' } 73 | Unit class >> a [ 74 | 75 | ^ self are 76 | ] 77 | 78 | { #category : #'miscellaneous units' } 79 | Unit class >> ampere [ 80 | 81 | ^ BaseUnit ampere 82 | ] 83 | 84 | { #category : #'miscellaneous units' } 85 | Unit class >> amperes [ 86 | 87 | ^ self ampere 88 | ] 89 | 90 | { #category : #'mass units' } 91 | Unit class >> angstrom [ 92 | 93 | ^NamedUnit named: 'angstrom' 94 | ] 95 | 96 | { #category : #'mass units' } 97 | Unit class >> angstroms [ 98 | 99 | ^self angstrom 100 | ] 101 | 102 | { #category : #'miscellaneous units' } 103 | Unit class >> arcdegree [ 104 | 105 | ^ NamedUnit named: 'arcdegree' 106 | ] 107 | 108 | { #category : #'miscellaneous units' } 109 | Unit class >> arcdegrees [ 110 | 111 | ^ self arcdegree 112 | ] 113 | 114 | { #category : #'area units' } 115 | Unit class >> are [ 116 | 117 | ^ NamedUnit named: 'are' 118 | ] 119 | 120 | { #category : #'area units' } 121 | Unit class >> ares [ 122 | 123 | ^ self are 124 | ] 125 | 126 | { #category : #'miscellaneous units' } 127 | Unit class >> candela [ 128 | 129 | ^ BaseUnit candela 130 | ] 131 | 132 | { #category : #'SI base units' } 133 | Unit class >> cd [ 134 | 135 | ^self candela 136 | ] 137 | 138 | { #category : #'length units' } 139 | Unit class >> centimeter [ 140 | 141 | ^ self centimetre 142 | ] 143 | 144 | { #category : #'length units' } 145 | Unit class >> centimeters [ 146 | 147 | ^ self centimetre 148 | ] 149 | 150 | { #category : #'length units' } 151 | Unit class >> centimetre [ 152 | 153 | ^ self metre prefixedBy: 'centi' 154 | ] 155 | 156 | { #category : #'length units' } 157 | Unit class >> centimetres [ 158 | 159 | ^ self centimetre 160 | ] 161 | 162 | { #category : #'length units' } 163 | Unit class >> cm [ 164 | 165 | ^self centimetre 166 | ] 167 | 168 | { #category : #'generalized lookup' } 169 | Unit class >> commonUnits [ 170 | "Answer the common units as a collection." 171 | 172 | ^{self pixel. self point. self pica. self millimetre. 173 | self centimetre. self metre. self kilometre. 174 | self inch. self foot. self yard. self mile} 175 | ] 176 | 177 | { #category : #'miscellaneous units' } 178 | Unit class >> coulomb [ 179 | 180 | ^ NamedUnit named: 'coulomb' 181 | ] 182 | 183 | { #category : #'miscellaneous units' } 184 | Unit class >> coulombs [ 185 | 186 | ^self coulomb 187 | ] 188 | 189 | { #category : #'time units' } 190 | Unit class >> day [ 191 | 192 | ^ NamedUnit named: 'day' 193 | ] 194 | 195 | { #category : #'time units' } 196 | Unit class >> days [ 197 | 198 | ^ self day 199 | ] 200 | 201 | { #category : #'miscellaneous units' } 202 | Unit class >> deg [ 203 | 204 | ^ self arcdegree 205 | ] 206 | 207 | { #category : #'temperature units' } 208 | Unit class >> degreeCelsius [ 209 | 210 | ^ NamedUnit named: 'degree Celsius' 211 | ] 212 | 213 | { #category : #'temperature units' } 214 | Unit class >> degreeFahrenheit [ 215 | 216 | ^ NamedUnit named: 'degree Fahrenheit' 217 | ] 218 | 219 | { #category : #'temperature units' } 220 | Unit class >> degreeKelvin [ 221 | 222 | ^ self kelvin 223 | ] 224 | 225 | { #category : #'temperature units' } 226 | Unit class >> degreesCelsius [ 227 | 228 | ^ self degreeCelsius 229 | ] 230 | 231 | { #category : #'temperature units' } 232 | Unit class >> degreesFahrenheit [ 233 | 234 | ^ self degreeFahrenheit 235 | ] 236 | 237 | { #category : #'temperature units' } 238 | Unit class >> degreesKelvin [ 239 | 240 | ^ self kelvin 241 | ] 242 | 243 | { #category : #'miscellaneous units' } 244 | Unit class >> degs [ 245 | 246 | ^ self arcdegree 247 | ] 248 | 249 | { #category : #'time units' } 250 | Unit class >> dy [ 251 | 252 | ^self days 253 | ] 254 | 255 | { #category : #'miscellaneous units' } 256 | Unit class >> farad [ 257 | 258 | ^NamedUnit named: 'farad' 259 | ] 260 | 261 | { #category : #'miscellaneous units' } 262 | Unit class >> farads [ 263 | 264 | ^ self farad 265 | ] 266 | 267 | { #category : #'length units' } 268 | Unit class >> feet [ 269 | 270 | ^ self foot 271 | ] 272 | 273 | { #category : #'length units' } 274 | Unit class >> foot [ 275 | 276 | ^NamedUnit named: 'foot' 277 | ] 278 | 279 | { #category : #'length units' } 280 | Unit class >> ft [ 281 | 282 | ^self foot 283 | ] 284 | 285 | { #category : #'mass units' } 286 | Unit class >> g [ 287 | 288 | ^ self gram 289 | ] 290 | 291 | { #category : #'time units' } 292 | Unit class >> gigahertz [ 293 | 294 | ^self hertz prefixedBy: 'giga' 295 | ] 296 | 297 | { #category : #'power units' } 298 | Unit class >> gigavolt [ 299 | 300 | ^ self volt prefixedBy: 'giga' 301 | ] 302 | 303 | { #category : #'mass units' } 304 | Unit class >> gram [ 305 | 306 | ^NamedUnit named: 'gram' 307 | ] 308 | 309 | { #category : #'mass units' } 310 | Unit class >> grams [ 311 | 312 | ^ self gram 313 | ] 314 | 315 | { #category : #'miscellaneous units' } 316 | Unit class >> gramsPerMole [ 317 | 318 | ^ Unit gram / Unit mole 319 | ] 320 | 321 | { #category : #'time units' } 322 | Unit class >> h [ 323 | 324 | ^ self hour 325 | ] 326 | 327 | { #category : #'area units' } 328 | Unit class >> ha [ 329 | 330 | ^ self hectare 331 | ] 332 | 333 | { #category : #'area units' } 334 | Unit class >> hectare [ 335 | 336 | ^ self are prefixedBy: 'hecto' 337 | ] 338 | 339 | { #category : #'area units' } 340 | Unit class >> hectares [ 341 | 342 | ^ self hectare 343 | ] 344 | 345 | { #category : #'time units' } 346 | Unit class >> hertz [ 347 | ^NamedUnit named: 'hertz' 348 | ] 349 | 350 | { #category : #'time units' } 351 | Unit class >> hour [ 352 | 353 | ^ NamedUnit named: 'hour' 354 | ] 355 | 356 | { #category : #'time units' } 357 | Unit class >> hours [ 358 | 359 | ^ self hour 360 | ] 361 | 362 | { #category : #'length units' } 363 | Unit class >> in [ 364 | 365 | ^self inch 366 | ] 367 | 368 | { #category : #'length units' } 369 | Unit class >> inch [ 370 | 371 | ^ NamedUnit named: 'inch' 372 | ] 373 | 374 | { #category : #'length units' } 375 | Unit class >> inches [ 376 | 377 | ^ self inch 378 | ] 379 | 380 | { #category : #'class initialization' } 381 | Unit class >> initialize [ 382 | "Unit initialize." 383 | "This is the master 'initialize' method. It calls the #initializeClass methods of the 384 | classes in this package *in the appropriate order*. The reason I don't just have the 385 | fileout code call #initialize for each class that needs initialization is that this 386 | package depends on classes being initialized in a certain order, and the 'topological' 387 | fileout order generated by some Smalltalks does not match this order." 388 | SIPrefix initializeClass. 389 | BaseUnit initializeClass. 390 | NamedUnit initializeClass 391 | ] 392 | 393 | { #category : #'time units' } 394 | Unit class >> kHz [ 395 | 396 | ^self kilohertz 397 | ] 398 | 399 | { #category : #'temperature units' } 400 | Unit class >> kelvin [ 401 | 402 | ^ NamedUnit named: 'kelvin' 403 | ] 404 | 405 | { #category : #'temperature units' } 406 | Unit class >> kelvins [ 407 | 408 | ^self kelvin 409 | ] 410 | 411 | { #category : #'SI base units' } 412 | Unit class >> kg [ 413 | 414 | ^self kilogram 415 | ] 416 | 417 | { #category : #'mass units' } 418 | Unit class >> kilogram [ 419 | 420 | ^ BaseUnit kilogram 421 | ] 422 | 423 | { #category : #'mass units' } 424 | Unit class >> kilograms [ 425 | 426 | ^ self kilogram 427 | ] 428 | 429 | { #category : #'time units' } 430 | Unit class >> kilohertz [ 431 | 432 | ^self hertz prefixedBy: 'kilo' 433 | ] 434 | 435 | { #category : #'length units' } 436 | Unit class >> kilometer [ 437 | 438 | ^ self kilometre 439 | ] 440 | 441 | { #category : #'length units' } 442 | Unit class >> kilometers [ 443 | 444 | ^ self kilometre 445 | ] 446 | 447 | { #category : #'length units' } 448 | Unit class >> kilometre [ 449 | 450 | ^ self metre prefixedBy: 'kilo' 451 | ] 452 | 453 | { #category : #'length units' } 454 | Unit class >> kilometres [ 455 | 456 | ^ self kilometre 457 | ] 458 | 459 | { #category : #'power units' } 460 | Unit class >> kilovolt [ 461 | 462 | ^ self volt prefixedBy: 'kilo' 463 | ] 464 | 465 | { #category : #'power units' } 466 | Unit class >> kilowatt [ 467 | 468 | ^ self watt prefixedBy: 'kilo' 469 | ] 470 | 471 | { #category : #'length units' } 472 | Unit class >> km [ 473 | 474 | ^self kilometre 475 | ] 476 | 477 | { #category : #'mass units' } 478 | Unit class >> lb [ 479 | 480 | ^self pound 481 | ] 482 | 483 | { #category : #'miscellaneous units' } 484 | Unit class >> liter [ 485 | 486 | ^ self litre 487 | ] 488 | 489 | { #category : #'miscellaneous units' } 490 | Unit class >> liters [ 491 | 492 | ^ self litre 493 | ] 494 | 495 | { #category : #'miscellaneous units' } 496 | Unit class >> litre [ 497 | 498 | ^ NamedUnit named: 'litre' 499 | ] 500 | 501 | { #category : #'miscellaneous units' } 502 | Unit class >> litres [ 503 | 504 | ^ self litre 505 | ] 506 | 507 | { #category : #'SI base units' } 508 | Unit class >> m [ 509 | 510 | ^self metre 511 | ] 512 | 513 | { #category : #'time units' } 514 | Unit class >> megahertz [ 515 | 516 | ^self hertz prefixedBy: 'mega' 517 | ] 518 | 519 | { #category : #'mass units' } 520 | Unit class >> megatonnes [ 521 | 522 | ^self tonne prefixedBy: 'mega' 523 | ] 524 | 525 | { #category : #'power units' } 526 | Unit class >> megavolt [ 527 | 528 | ^ self volt prefixedBy: 'mega' 529 | ] 530 | 531 | { #category : #'length units' } 532 | Unit class >> meter [ 533 | 534 | ^ self metre 535 | ] 536 | 537 | { #category : #'length units' } 538 | Unit class >> meters [ 539 | 540 | ^ self metre 541 | ] 542 | 543 | { #category : #'length units' } 544 | Unit class >> metre [ 545 | 546 | ^ BaseUnit metre 547 | ] 548 | 549 | { #category : #'length units' } 550 | Unit class >> metres [ 551 | 552 | ^ self metre 553 | ] 554 | 555 | { #category : #'mass units' } 556 | Unit class >> mg [ 557 | 558 | ^self milligram 559 | ] 560 | 561 | { #category : #'length units' } 562 | Unit class >> mi [ 563 | 564 | ^self mile 565 | ] 566 | 567 | { #category : #'mass units' } 568 | Unit class >> microgram [ 569 | 570 | ^self gram prefixedBy: 'micro' 571 | ] 572 | 573 | { #category : #'mass units' } 574 | Unit class >> micrograms [ 575 | 576 | ^self microgram 577 | ] 578 | 579 | { #category : #'length units' } 580 | Unit class >> mile [ 581 | 582 | ^NamedUnit named: 'mile' 583 | ] 584 | 585 | { #category : #'length units' } 586 | Unit class >> miles [ 587 | 588 | ^ self mile 589 | ] 590 | 591 | { #category : #'power units' } 592 | Unit class >> milliamp [ 593 | 594 | ^ self ampere prefixedBy: 'milli' 595 | ] 596 | 597 | { #category : #'mass units' } 598 | Unit class >> milligram [ 599 | 600 | ^self gram prefixedBy: 'milli' 601 | ] 602 | 603 | { #category : #'mass units' } 604 | Unit class >> milligrams [ 605 | 606 | ^self milligram 607 | ] 608 | 609 | { #category : #'length units' } 610 | Unit class >> millimeter [ 611 | 612 | ^ self millimetre 613 | ] 614 | 615 | { #category : #'length units' } 616 | Unit class >> millimeters [ 617 | 618 | ^ self millimetre 619 | ] 620 | 621 | { #category : #'length units' } 622 | Unit class >> millimetre [ 623 | 624 | ^ self metre prefixedBy: 'milli' 625 | ] 626 | 627 | { #category : #'time units' } 628 | Unit class >> milliseconds [ 629 | 630 | ^self second prefixedBy: 'milli' 631 | ] 632 | 633 | { #category : #'power units' } 634 | Unit class >> millivolt [ 635 | 636 | ^ self volt prefixedBy: 'milli' 637 | ] 638 | 639 | { #category : #'time units' } 640 | Unit class >> min [ 641 | 642 | ^self minutes 643 | ] 644 | 645 | { #category : #'time units' } 646 | Unit class >> minute [ 647 | 648 | ^ NamedUnit named: 'minute' 649 | ] 650 | 651 | { #category : #'time units' } 652 | Unit class >> minutes [ 653 | 654 | ^ self minute 655 | ] 656 | 657 | { #category : #'length units' } 658 | Unit class >> mm [ 659 | 660 | ^self millimetre 661 | ] 662 | 663 | { #category : #'SI base units' } 664 | Unit class >> mol [ 665 | 666 | ^ self moles 667 | ] 668 | 669 | { #category : #'mass units' } 670 | Unit class >> mole [ 671 | 672 | ^ BaseUnit mole 673 | ] 674 | 675 | { #category : #'mass units' } 676 | Unit class >> moles [ 677 | 678 | ^ self mole 679 | ] 680 | 681 | { #category : #'time units' } 682 | Unit class >> ms [ 683 | 684 | ^self milliseconds 685 | ] 686 | 687 | { #category : #'generalized lookup' } 688 | Unit class >> named: unitName [ 689 | ^self 690 | withPluralName: unitName 691 | ifAbsent: [self withSingularName: unitName] 692 | ] 693 | 694 | { #category : #'generalized lookup' } 695 | Unit class >> named: unitName ifAbsent: exceptionBlock [ 696 | ^self 697 | withPluralName: unitName 698 | ifAbsent: [self withSingularName: unitName ifAbsent: exceptionBlock] 699 | ] 700 | 701 | { #category : #'power units' } 702 | Unit class >> nanovolt [ 703 | 704 | ^ self volt prefixedBy: 'nano' 705 | ] 706 | 707 | { #category : #'miscellaneous units' } 708 | Unit class >> newton [ 709 | 710 | ^NamedUnit named: 'newton' 711 | ] 712 | 713 | { #category : #'miscellaneous units' } 714 | Unit class >> newtons [ 715 | ^NamedUnit named: 'newtons' 716 | ] 717 | 718 | { #category : #'mass units' } 719 | Unit class >> ounce [ 720 | 721 | ^NamedUnit named: 'ounce' 722 | ] 723 | 724 | { #category : #'mass units' } 725 | Unit class >> ounces [ 726 | 727 | ^ self ounce 728 | ] 729 | 730 | { #category : #'mass units' } 731 | Unit class >> oz [ 732 | 733 | ^self ounce 734 | ] 735 | 736 | { #category : #'length units' } 737 | Unit class >> pc [ 738 | 739 | ^ self pica 740 | ] 741 | 742 | { #category : #'length units' } 743 | Unit class >> pica [ 744 | 745 | ^ NamedUnit named: 'pica' 746 | ] 747 | 748 | { #category : #'length units' } 749 | Unit class >> picas [ 750 | 751 | ^ self pica 752 | ] 753 | 754 | { #category : #'power units' } 755 | Unit class >> picovolt [ 756 | 757 | ^ self volt prefixedBy: 'pico' 758 | ] 759 | 760 | { #category : #'length units' } 761 | Unit class >> pixel [ 762 | 763 | ^ NamedUnit named: 'pixel' 764 | ] 765 | 766 | { #category : #'length units' } 767 | Unit class >> pixels [ 768 | 769 | ^ self pixel 770 | ] 771 | 772 | { #category : #'length units' } 773 | Unit class >> point [ 774 | 775 | ^ NamedUnit named: 'point' 776 | ] 777 | 778 | { #category : #'length units' } 779 | Unit class >> points [ 780 | 781 | ^ self point 782 | ] 783 | 784 | { #category : #'mass units' } 785 | Unit class >> pound [ 786 | 787 | ^NamedUnit named: 'pound' 788 | ] 789 | 790 | { #category : #'mass units' } 791 | Unit class >> pounds [ 792 | 793 | ^NamedUnit named: 'pounds' 794 | ] 795 | 796 | { #category : #parameters } 797 | Unit class >> printAbbreviated [ 798 | ^PrintAbbreviated 799 | ] 800 | 801 | { #category : #parameters } 802 | Unit class >> printAbbreviated: boolean [ 803 | "Should we print units in abbreviated format?" 804 | PrintAbbreviated := boolean 805 | ] 806 | 807 | { #category : #'length units' } 808 | Unit class >> pt [ 809 | 810 | ^ self point 811 | ] 812 | 813 | { #category : #'length units' } 814 | Unit class >> px [ 815 | 816 | ^ self pixel 817 | ] 818 | 819 | { #category : #'miscellaneous units' } 820 | Unit class >> rad [ 821 | 822 | ^ self radian 823 | ] 824 | 825 | { #category : #'miscellaneous units' } 826 | Unit class >> radian [ 827 | 828 | ^ BaseUnit named: 'radian' 829 | ] 830 | 831 | { #category : #'miscellaneous units' } 832 | Unit class >> radians [ 833 | 834 | ^ self rad 835 | ] 836 | 837 | { #category : #'SI base units' } 838 | Unit class >> s [ 839 | 840 | ^ self second 841 | ] 842 | 843 | { #category : #'time units' } 844 | Unit class >> second [ 845 | 846 | ^ BaseUnit second 847 | ] 848 | 849 | { #category : #'time units' } 850 | Unit class >> seconds [ 851 | 852 | ^ self second 853 | ] 854 | 855 | { #category : #'mass units' } 856 | Unit class >> st [ 857 | 858 | ^self stone 859 | ] 860 | 861 | { #category : #'mass units' } 862 | Unit class >> stone [ 863 | 864 | ^NamedUnit named: 'stone' 865 | ] 866 | 867 | { #category : #'mass units' } 868 | Unit class >> t [ 869 | 870 | ^self tonne 871 | ] 872 | 873 | { #category : #'mass units' } 874 | Unit class >> tonne [ 875 | 876 | ^NamedUnit named: 'tonne' 877 | ] 878 | 879 | { #category : #'mass units' } 880 | Unit class >> tonnes [ 881 | 882 | ^ self tonne 883 | ] 884 | 885 | { #category : #'length units' } 886 | Unit class >> ua [ 887 | ^NamedUnit named: 'astronomical unit' 888 | ] 889 | 890 | { #category : #'power units' } 891 | Unit class >> volt [ 892 | 893 | ^ BaseUnit volt 894 | ] 895 | 896 | { #category : #'power units' } 897 | Unit class >> volts [ 898 | 899 | ^ BaseUnit volts 900 | ] 901 | 902 | { #category : #'power units' } 903 | Unit class >> watt [ 904 | 905 | ^ NamedUnit named: 'watt' 906 | ] 907 | 908 | { #category : #'power units' } 909 | Unit class >> watts [ 910 | 911 | ^ NamedUnit named: 'watts' 912 | ] 913 | 914 | { #category : #'generalized lookup' } 915 | Unit class >> withAbbreviation: abbreviation [ 916 | ^self 917 | withAbbreviation: abbreviation 918 | ifAbsent: [self error: 'There is no unit with the abbreviation ', abbreviation printString, '.'] 919 | ] 920 | 921 | { #category : #'generalized lookup' } 922 | Unit class >> withAbbreviation: unitName ifAbsent: exceptionBlock [ 923 | | unit | 924 | unit := NamedUnit withAbbreviation: unitName ifAbsent: [nil]. 925 | unit isNil ifFalse: [^unit]. 926 | unit := BaseUnit withAbbreviation: unitName ifAbsent: exceptionBlock. 927 | ^unit 928 | ] 929 | 930 | { #category : #'generalized lookup' } 931 | Unit class >> withPluralName: unitName [ 932 | ^self 933 | withPluralName: unitName 934 | ifAbsent: [self error: 'There is no unit named ', unitName printString, '.'] 935 | ] 936 | 937 | { #category : #'generalized lookup' } 938 | Unit class >> withPluralName: unitName ifAbsent: exceptionBlock [ 939 | | unit | 940 | unit := NamedUnit withPluralName: unitName ifAbsent: [nil]. 941 | unit isNil ifFalse: [^unit]. 942 | unit := BaseUnit withPluralName: unitName ifAbsent: exceptionBlock. 943 | ^unit 944 | ] 945 | 946 | { #category : #'generalized lookup' } 947 | Unit class >> withSingularName: unitName [ 948 | ^self 949 | withSingularName: unitName 950 | ifAbsent: [self error: 'There is no unit named ', unitName printString, '.'] 951 | ] 952 | 953 | { #category : #'generalized lookup' } 954 | Unit class >> withSingularName: unitName ifAbsent: exceptionBlock [ 955 | | unit | 956 | unit := NamedUnit withSingularName: unitName ifAbsent: [nil]. 957 | unit isNil ifFalse: [^unit]. 958 | unit := BaseUnit withSingularName: unitName ifAbsent: exceptionBlock. 959 | ^unit 960 | ] 961 | 962 | { #category : #'length units' } 963 | Unit class >> yard [ 964 | 965 | ^ NamedUnit named: 'yard' 966 | ] 967 | 968 | { #category : #'length units' } 969 | Unit class >> yards [ 970 | 971 | ^ self yard 972 | ] 973 | 974 | { #category : #'length units' } 975 | Unit class >> yd [ 976 | 977 | ^ self yard 978 | ] 979 | 980 | { #category : #'time units' } 981 | Unit class >> year [ 982 | 983 | ^ NamedUnit named: 'year' 984 | ] 985 | 986 | { #category : #'time units' } 987 | Unit class >> years [ 988 | 989 | ^ self year 990 | ] 991 | 992 | { #category : #'time units' } 993 | Unit class >> yr [ 994 | 995 | ^self year 996 | ] 997 | 998 | { #category : #'mass units' } 999 | Unit class >> µg [ 1000 | 1001 | ^self microgram 1002 | ] 1003 | 1004 | { #category : #'unit arithmetic' } 1005 | Unit >> * anotherUnit [ 1006 | ^self multipliedBy: anotherUnit 1007 | ] 1008 | 1009 | { #category : #'unit arithmetic' } 1010 | Unit >> / anotherUnit [ 1011 | ^self dividedBy: anotherUnit 1012 | ] 1013 | 1014 | { #category : #accessing } 1015 | Unit >> abbreviation [ 1016 | "Answer an abbreviation for the receiver." 1017 | | stream | 1018 | stream := WriteStream on: String new. 1019 | self printAbbreviationOn: stream. 1020 | ^stream contents 1021 | ] 1022 | 1023 | { #category : #conversion } 1024 | Unit >> additiveFactor [ 1025 | "Only TemperatureUnits have additive factors." 1026 | ^0 1027 | ] 1028 | 1029 | { #category : #conversion } 1030 | Unit >> baseUnits [ 1031 | "Answer a Unit object that represents the receiver in reduced form (i.e., with all 'complex' units replaced by SI units)." 1032 | self subclassResponsibility 1033 | ] 1034 | 1035 | { #category : #consistency } 1036 | Unit >> consistentWith: unit [ 1037 | "Is the receiver 'consistent' with the argument? Two units must be consistent in order to be added or subtracted." 1038 | self subclassResponsibility 1039 | ] 1040 | 1041 | { #category : #consistency } 1042 | Unit >> consistentWithAnything: anotherUnit [ 1043 | ^false 1044 | ] 1045 | 1046 | { #category : #consistency } 1047 | Unit >> consistentWithBaseUnit: baseUnit [ 1048 | ^self consistentWithAnything: baseUnit 1049 | ] 1050 | 1051 | { #category : #consistency } 1052 | Unit >> consistentWithComplexUnit: compositeUnit [ 1053 | ^self consistentWithAnything: compositeUnit 1054 | ] 1055 | 1056 | { #category : #consistency } 1057 | Unit >> consistentWithCompoundUnit: compositeUnit [ 1058 | ^self consistentWithAnything: compositeUnit 1059 | ] 1060 | 1061 | { #category : #consistency } 1062 | Unit >> consistentWithModifiedUnit: modifiedUnit [ 1063 | ^self consistentWithAnything: modifiedUnit 1064 | ] 1065 | 1066 | { #category : #conversion } 1067 | Unit >> conversionFactor [ 1068 | "Answer the factor by which the receiver is larger than the base units from which it is composed." 1069 | ^self subclassResponsibility 1070 | ] 1071 | 1072 | { #category : #conversion } 1073 | Unit >> conversionFactorTo: anotherUnit [ 1074 | "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?)" 1075 | ^self conversionFactor / anotherUnit conversionFactor 1076 | ] 1077 | 1078 | { #category : #'unit arithmetic' } 1079 | Unit >> dividedBy: anotherUnit [ 1080 | ^self multipliedBy: anotherUnit reciprocal 1081 | ] 1082 | 1083 | { #category : #conversion } 1084 | Unit >> factor: anotherUnit [ 1085 | "Factor with respect 'anotherUnit'; answer a new unit equivalent to the receiver." 1086 | | thisBase argumentBase result | 1087 | thisBase := self baseUnits. 1088 | argumentBase := anotherUnit baseUnits. 1089 | result := thisBase dividedBy: argumentBase. 1090 | ^anotherUnit multipliedBy: result 1091 | ] 1092 | 1093 | { #category : #predicates } 1094 | Unit >> isBaseUnit [ 1095 | "Answer true if the receiver represents a simple SI base unit with no modifications." 1096 | ^false 1097 | ] 1098 | 1099 | { #category : #testing } 1100 | Unit >> isComplexUnit [ 1101 | ^ false 1102 | ] 1103 | 1104 | { #category : #predicates } 1105 | Unit >> isNull [ 1106 | ^false 1107 | ] 1108 | 1109 | { #category : #predicates } 1110 | Unit >> isZeroAsValue: value [ 1111 | "If 'value' were the value of a UnitValue with this unit, 1112 | would it equal zero? Most units are absolute (e.g., length) so this 1113 | answers true for zero values. Some units are not; for example 1114 | the Celsius and Fahrenheit temperature scales." 1115 | ^value isZero 1116 | ] 1117 | 1118 | { #category : #conversion } 1119 | Unit >> modify: modification [ 1120 | "Answer a ModifiedUnit with the receiver and the given modification." 1121 | self isBaseUnit ifFalse: [self error: 'You can only modify base units.']. 1122 | ^ModifiedUnit baseUnit: self modification: modification 1123 | ] 1124 | 1125 | { #category : #'unit arithmetic' } 1126 | Unit >> multipliedBy: anotherUnit [ 1127 | | unitDictionary units exponents scratch | 1128 | unitDictionary := Dictionary new. 1129 | self unitsAndExponentsDo: [:unit :exponent | 1130 | (unitDictionary includesKey: unit) 1131 | ifTrue: [unitDictionary at: unit put: (unitDictionary at: unit) + exponent] 1132 | ifFalse: [unitDictionary at: unit put: exponent]]. 1133 | anotherUnit unitsAndExponentsDo: [:unit :exponent | 1134 | (unitDictionary includesKey: unit) 1135 | ifTrue: [unitDictionary at: unit put: (unitDictionary at: unit) + exponent] 1136 | ifFalse: [unitDictionary at: unit put: exponent]]. 1137 | units := OrderedCollection new. 1138 | exponents := OrderedCollection new. 1139 | (unitDictionary keys asSortedCollection: CompoundUnit sortBlock) do: [:unit | 1140 | scratch := unitDictionary at: unit. 1141 | scratch isZero ifFalse: [ 1142 | units add: unit. 1143 | exponents add: scratch]]. 1144 | units size = 1 ifTrue: [ 1145 | (exponents at: 1) = 1 ifTrue: [^units at: 1]]. 1146 | ^ComplexUnit units: units exponents: exponents 1147 | ] 1148 | 1149 | { #category : #'unit arithmetic' } 1150 | Unit >> per: anotherUnit [ 1151 | ^self dividedBy: anotherUnit 1152 | ] 1153 | 1154 | { #category : #accessing } 1155 | Unit >> plural [ 1156 | "Answer the plural for the receiver." 1157 | 1158 | | stream | 1159 | stream := WriteStream on: String new. 1160 | self printFullNameOn: stream pluralized: true. 1161 | ^stream contents 1162 | ] 1163 | 1164 | { #category : #conversion } 1165 | Unit >> prefixedBy: prefixName [ 1166 | ^PrefixedUnit 1167 | prefixName: prefixName 1168 | unit: self 1169 | ] 1170 | 1171 | { #category : #printing } 1172 | Unit >> printAbbreviationOn: stream [ 1173 | "Print the receiver abbreviated." 1174 | self subclassResponsibility 1175 | ] 1176 | 1177 | { #category : #printing } 1178 | Unit >> printFullNameOn: stream pluralized: pluralized [ 1179 | "Print the full name of this unit, pluralized if 'pluralized' is true." 1180 | self subclassResponsibility 1181 | ] 1182 | 1183 | { #category : #printing } 1184 | Unit >> printOn: stream [ 1185 | super printOn: stream. 1186 | stream nextPutAll: ' ['. 1187 | PrintAbbreviated 1188 | ifTrue: [self printAbbreviationOn: stream] 1189 | ifFalse: [self printFullNameOn: stream pluralized: true]. 1190 | stream nextPut: $] 1191 | ] 1192 | 1193 | { #category : #'unit arithmetic' } 1194 | Unit >> raisedTo: exponent [ 1195 | "Answer the receiver raised to the given exponent." 1196 | ^ComplexUnit 1197 | units: (Array with: self) 1198 | exponents: (Array with: exponent) 1199 | ] 1200 | 1201 | { #category : #'unit arithmetic' } 1202 | Unit >> reciprocal [ 1203 | "Answer the inverse of the receiver." 1204 | ^ComplexUnit 1205 | units: (Array with: self) 1206 | exponents: (Array with: -1) 1207 | ] 1208 | 1209 | { #category : #'unit arithmetic' } 1210 | Unit >> squared [ 1211 | ^self multipliedBy: self 1212 | ] 1213 | 1214 | { #category : #conversion } 1215 | Unit >> uncheckedConvertFrom: unitValue [ 1216 | "This is a double-dispatching message used by UnitValue." 1217 | | factor | 1218 | factor := unitValue unit conversionFactorTo: self. 1219 | ^UnitValue 1220 | unit: self 1221 | value: unitValue value * factor 1222 | ] 1223 | 1224 | { #category : #private } 1225 | Unit >> unitPart [ 1226 | "For compatibility with UnitValue." 1227 | ^self 1228 | ] 1229 | 1230 | { #category : #enumerating } 1231 | Unit >> unitsAndExponentsDo: block [ 1232 | "Evaluate the block once for each unit/exponent pair contained within the receiver. 1233 | For scalar units, just evaluate the block once with 'self', exponent 1." 1234 | block value: self value: 1 1235 | ] 1236 | 1237 | { #category : #conversion } 1238 | Unit >> value: number [ 1239 | "Coerce the receiver to be a UnitValue with the given value." 1240 | ^UnitValue 1241 | unit: self 1242 | value: number 1243 | ] 1244 | 1245 | { #category : #conversion } 1246 | Unit >> veryDeepCopyWith: deepCopier [ 1247 | "Return self. Don't copy units Do not record me." 1248 | ] 1249 | --------------------------------------------------------------------------------