SortedCollection variableSubclass: #SortedSequence instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Payroll'! !SortedSequence methodsFor: 'accessing'! indexOfStartOfIntervalContaining: anElement "Return the index of anElement or, if it is not present, of the index of the largest element smaller than it." self isEmpty ifTrue: [^0]. ^(self indexForInserting: anElement) - firstIndex! ! Object subclass: #ValueWithHistory instanceVariableNames: 'date value ' classVariableNames: 'EarliestDate ' poolDictionaries: '' category: 'Payroll'! ValueWithHistory comment: 'I represent a (probably numerical) value that changes over time. I can answer my value at any point in time (usually a date). I can update my value at any point in time, and also add a number to my value from any point in time on into the future. My value does not change continuously, but changes at discrete points in time. Protocol: at: aDate - return value at aDate at: aDate add: anAmount - add anAmount to value at aDate and at all times in the future starting: aDate become: anAmount - set value from aDate to the next specificed time to anAmount Variables: date value The i''th element of value matches the i''th element of date. The date collection indicates when the value takes on the next element of the value collection.'! !ValueWithHistory methodsFor: 'accessing'! at: aDate "Return value at the date" | index | index := date indexOfStartOfIntervalContaining: aDate. index = 0 ifTrue: [self error: 'date is too early']. ^value at: index! at: aDate add: anAmount | start | "Return value at the date" start := (self indexForAccessing: aDate). start to: value size do: [:each | each = 0 ifTrue:[self halt]. value at: each put: (value at: each) + anAmount]! indexForAccessing: aDate "Return index of slot for aDate, creating it if necessary." | index | index := date indexOfStartOfIntervalContaining: aDate. index = 0 ifTrue: [date add: aDate. value addFirst: 0. ^1]. (date at: index) = aDate ifTrue: [^index]. date add: aDate. value add: (value at: index) beforeIndex: index + 1. ^index + 1! starting: aDate become: aValue "Change value so that it becomes aValue at aDate." | index | index := date indexForInserting: aDate. (index > 1 and: [aDate = (date at: index - 1)]) ifTrue: [^value at: index - 1 put: aValue]. date insert: aDate before: index. value add: aValue beforeIndex: index! ! !ValueWithHistory methodsFor: 'initialize-release'! initialize date := SortedSequence new. value := OrderedCollection new.! ! !ValueWithHistory methodsFor: 'printing'! printOn: aStream date with: value do: [:eachDate :eachValue | aStream nextPut: $(. eachDate printOn: aStream. aStream nextPutAll: ' -> '. eachValue printOn: aStream. aStream nextPut: $). aStream cr]! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! ValueWithHistory class instanceVariableNames: ''! !ValueWithHistory class methodsFor: 'class initialization'! initialize "ValueWithHistory initialize" EarliestDate := Date newDay: 0 year: 0.! ! !ValueWithHistory class methodsFor: 'instance creation'! new ^super new initialize! zero ^self new starting: EarliestDate become: 0! ! !ValueWithHistory class methodsFor: 'testing'! test "ValueWithHistory test" | value day1 day2 day3 | day1 := Date today. day2 := day1 subtractDays: 3. day3 := day2 subtractDays: 3. value := ValueWithHistory new. value starting: day1 become: 3. value starting: day3 become: 1. (value at: day2) = 1 ifFalse: [self error: 'test failed']. (value at: day1) = 3 ifFalse: [self error: 'test failed']. value starting: day2 become: 5. (value at: day1) = 3 ifFalse: [self error: 'test failed']. Transcript show: 'ValueWithHistory test succeeded'; cr. ^value! ! ValueWithHistory initialize!