requires: {#Sequence}. provides: {#Range. #LogicRange}. collections addSlot: #Range valued: Sequence derive. "A Sequence of Numbers with a regular interval and bounding values." Range addSlot: #start valued: 0. "The number to start from; this is the first value." Range addSlot: #end valued: PositiveInfinity. "The number to end with; this may not be the last value, but no number in the Range will exceed it." Range addSlot: #step valued: 1. "The interval of stepping. Each successive element will exceed the preceeding by exactly this amount." "TODO: find some way to parametrize this on all Magnitude types and ensure that all three parameters of the Range are comparable." r@(Range traits) newFrom: start to: end by: step [| newR | newR: r clone. newR start: start. newR end: end. newR step: step. newR ]. r@(Range traits) newFrom: start to: end "This method implicitly defines a sensible default stepping value." [ r newFrom: start to: end by: (end - start) sign ]. start@(Number traits) to: end [ Range newFrom: start to: end ]. start@(Number traits) upTo: end [ Range newFrom: start to: end ]. start@(Number traits) below: end [ Range newFrom: start to: end - 1 ]. start@(Number traits) downTo: end [ Range newFrom: start to: end ]. start@(Number traits) above: end [ Range newFrom: start to: end + 1 ]. start@(Number traits) upTo: end by: step [ Range newFrom: start to: end by: step ]. start@(Number traits) below: end by: step [ Range newFrom: start to: end - 1 by: step ]. start@(Number traits) downTo: end by: step [ Range newFrom: start to: end by: step ]. start@(Number traits) above: end by: step [ Range newFrom: start to: end + 1 by: step ]. r1@(Range traits) = r2@(Range traits) [ r1 start = r2 start and: [r1 step = r2 step and: [r1 size = r2 size]] ]. r@(Range traits) hash [ (((r start hash bitShift: 2) bitOr: r end hash) bitShift: 1) bitOr: r size ]. r@(Range traits) copy [ r newFrom: r start to: r end by: r step ]. r@(Range traits) at: n [ (n isPositive and: [n < r size]) ifTrue: [r step * n + r start] ifFalse: [error: 'Subscript out of bounds.'] ]. r@(Range traits) at: n put: _ [ error: 'Ranges are not mutable.' ]. r@(Range traits) extent "Answers the magnitude of the range's extent." [ r end - r start ]. r@(Range traits) first [ r start ]. r@(Range traits) last [ r end - (r end - r start \\ r step) ]. r@(Range traits) rangeIncludes: n [ r step isNegative ifTrue: [n between: r end and: r start] ifFalse: [n between: r start and: r end] ]. r@(Range traits) includes: n [ (r rangeIncludes: n) and: [| val | val: ((n - r start) as: Float) / r step. val fractionPart abs < (r step * 1.0e-10)] ]. r@(Range traits) size "Check the 'direction' of the Range to make sure that it's possible to have elements at all." [ (r step isNegative xor: r start > r end) ifTrue: [0] ifFalse: [r end - r start // r step + 1] ]. r@(Range traits) collect: block [| each result | result: (r newSize: r size). each: r start. 0 below: r size do: [| :i | result at: i put: (block value: each). each: each + r step]. result ]. r@(Range traits) do: block [| each | each: r start. r step isNegative ifTrue: [[r end <= each] whileTrue: [block value: each. each: each + step]] ifFalse: [[r end >= each] whileTrue: [block value: each. each: each + step]]. r ]. r@(Range traits) reverseDo: block [| each | each: r start. r step isNegative ifTrue: [[r end >= each] whileTrue: [block value: each. each: each - step]] ifFalse: [[r end <= each] whileTrue: [block value: each. each: each - step]]. r ]. r@(Range traits) printOn: s [ s nextPut: $(. r start printOn: s. s ; ' to: '; r end printName. r step ~= 1 ifTrue: [s ; ' by: ' ; r step printName]. s nextPut: $). r ]. r1@(Range traits) intersects: r2@(Range traits) "Whether the Ranges have overlap in their extents." [ (r1 start between: r2 start and: r2 end) or: [r2 start between: r1 start and: r1 end] ]. collections addSlot: #LogicRange valued: Range derive. "A Range which uses blocks for the end and step aspects." LogicRange addSlot: #start valued: 0. "The starting value." LogicRange addSlot: #end valued: [| :x | x > PositiveInfinity]. "A Block which should halt iteration when reaching True." LogicRange addSlot: #step valued: [| :x | x + 1]. "A Block returning the next value in the Sequence based on the current one." r@(LogicRange traits) newFrom: start to: end by: step "Coerces any numerical parameters into blocks where necessary." [| newR goesUp | goesUp: end > start. newR: r clone. newR start: start. newR end: ((end is: Number) ifTrue: [goesUp ifTrue: [[| :x | x >= end]] ifFalse: [[| :x | x <= end]]] ifFalse: [end]). newR step: ((step is: Number) ifTrue: [goesUp ifTrue: [[| :x | x + step]] ifFalse: [[| :x | x - step]]] ifFalse: [step]). newR ]. start@(Number traits) until: end by: step "Creates a new LogicRange." [ LogicRange newFrom: start to: end by: step ]. r@(LogicRange traits) size "This should obviously be avoided." [| size | size: 0. r do: [| :each | size: size + 1]. size ]. r@(LogicRange traits) at: n [| result | result: start. 0 below: n do: [| :each | result: (r step valued: result). (r end value: result) ifTrue: [error: 'Subscript out of bounds.']. result ]. r@(LogicRange traits) do: block "Iterates through values from the start until the end-test is satisfied." [| each | each: r start. [end value: each] whileFalse: [block value: each. each: (r step value: each)]. r ]. r@(LogicRange traits) reverseDo: block "Since the end is defined by a test and not a value, this is ambiguous." [ notImplementable ]. r@(LogicRange traits) isAscending "Whether the Range's progression is positive." [ (r step value: r start) > r start ]. r@(LogicRange traits) collect: block "There is no way to predict the size, so this wastes a lot of space. TODO: Implement with a tree-based Sequence for the result?" [| each result | result: ExtensibleSequence newEmpty. r do: [| :each | result addLast: (block value: each)]. result ]. r@(LogicRange traits) rangeIncludes: n "Whether the value is in the direction of the Range and between the start while not exceeding the end test." [ (r end value: n) not and: [r isAscending ifTrue: [n >= r start] ifFalse: [n <= r start]] ]. r@(LogicRange traits) includes: n "Simply iterate through the values and compare." [ r do: [| : each | n = each ifTrue: [^ True]]. False ]. r@(LogicRange traits) printOn: s [ s nextPut: $(. r start printOn: s. s ; ' until: '; r end printName. r step ~= 1 ifTrue: [s ; ' by: ' ; r step printName]. s nextPut: $). r ].