flex_array 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YzZiYThmNDY2YTU2NGE5YmQzNjUxNjRhODdkMzJiMGEwMWNiM2MwZg==
5
+ data.tar.gz: !binary |-
6
+ M2NjZmZmMjM2ZmEzMGNkNzc1NjFiNWEwNDE4ZTc3Y2M3ZmVkZjYzOQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MmJlZDVkZTRiMzJhMmU3ZTEyNmU3MmQyM2Q0NWQ3MDAxNTRjZDkzMGRlZjE4
10
+ ZjMyY2EzYzA4N2RjY2U5YzVlNjg3NGM4ZTQwNTAxOGFlNWYzYzAyN2MyNjE1
11
+ M2FlMWI1N2YwYTk0MGQ4ZTBjMmQ3N2M1MDZjMzYzNjM1YmRmZWY=
12
+ data.tar.gz: !binary |-
13
+ MjYwNDJkODI2YzA2YTE0NTY5MmZiZmQxNGYyNzA0NzExYzI0YWI1N2U3MGVm
14
+ NDliYWIxZTQyYjJhYzQzM2I4NDljOTI0NGZlZDQwNjlhMThmYjU2NDA5OGJk
15
+ NjQxZGM3YWEzZWIyYmU0MzA4NzNjYjgyYjNlNjkwMTdmZDgwMjg=
@@ -0,0 +1,55 @@
1
+ require_relative '../flex_array'
2
+
3
+ #Extensions to Array needed to support flex array.
4
+ class Array
5
+ #Get the specifications of the array index values.
6
+ #<br>Returns
7
+ #* An array with one range in it.
8
+ def limits
9
+ [0...self.length]
10
+ end
11
+
12
+ #Quick access to the limits for internal use.
13
+ #<br>Returns
14
+ #* An array with one spec component in it.
15
+ def array_specs
16
+ [SpecComponent.new(0...self.length, 1)]
17
+ end
18
+
19
+ #Quick access to the array data for internal use.
20
+ #<br>Returns
21
+ #* An array -- self
22
+ def array_data
23
+ self
24
+ end
25
+
26
+ alias :length :count
27
+
28
+ #Convert this array to an range index against the spec.
29
+ #<br>Parameters
30
+ #* spec - The spec component used to validate this index.
31
+ #<br>Returns
32
+ #* A range.
33
+ #<br>Exceptions
34
+ #* IndexError if the range is not valid.
35
+ def to_index_range(spec)
36
+ self_min, self_max, spec_max = Integer(self[0]), Integer(self[1]), spec.max
37
+ self_min = spec_max + self_min + 1 if self_min < 0
38
+ self_max = spec_max + self_max + 1 if self_max < 0
39
+
40
+ if self.length == 2 && self_min <= self_max && spec === self_min && spec === self_max
41
+ self_min..self_max
42
+ else
43
+ fail IndexError, "Subscript invalid or out of range: #{self.inspect}"
44
+ end
45
+ end
46
+
47
+ #Return this flex array as a flex array!
48
+ #<br>Returns
49
+ #* A flex array that references this array.
50
+ #<br>Note
51
+ #* To avoid a shared reference, use my_array.dup.to_flex_array instead.
52
+ def to_flex_array
53
+ FlexArray.new_from_array(self)
54
+ end
55
+ end
@@ -0,0 +1,51 @@
1
+ #* flex_array_append.rb - The flexible array class << and related methods.
2
+ class FlexArray
3
+ #Append to the flex array.
4
+ #<br>Parameters
5
+ #* data - the data being appended to this array.
6
+ #<br>Returns
7
+ #* The array spec of the array being appended.
8
+ #<br>Exceptions
9
+ #* ArgumentError if the data may not be appended.
10
+ def << (data)
11
+ specs = get_append_specs(data = data.in_array)
12
+ data.array_data.each {|element| @array_data << element }
13
+ @array_specs[0].enlarge(specs[0].span)
14
+ @count += data.count
15
+ self
16
+ end
17
+
18
+ #Make a copy of the other's data.
19
+ #<br>Parameters
20
+ #* other - The flex array whose data is to be copied.
21
+ def copy_data(other)
22
+ fail ArgumentError, "Incompatible data copy." unless compatible?(other)
23
+ @array_data = other.array_data.dup
24
+ end
25
+
26
+ private
27
+ #Extract and validate the append array_spec
28
+ #<br>Parameters
29
+ #* data - the data being appended to this array.
30
+ #<br>Returns
31
+ #* The array spec of the array being appended.
32
+ #<br>Exceptions
33
+ #* ArgumentError if the data may not be appended.
34
+ def get_append_specs(data)
35
+ spec_len = (specs = data.array_specs).length
36
+
37
+ if @dimensions == spec_len+1
38
+ specs.insert(0, SpecComponent.new(0...1, nil))
39
+ elsif @dimensions != spec_len
40
+ fail ArgumentError, "Incompatible dimensionality error on <<."
41
+ end
42
+
43
+ (1...@dimensions).each do |index|
44
+ unless @array_specs[index] == specs[index]
45
+ fail ArgumentError, "Dimension mismatch error on <<."
46
+ end
47
+ end
48
+
49
+ specs
50
+ end
51
+ end
@@ -0,0 +1,98 @@
1
+ #* flex_array_each.rb - The flexible array class each and related methods.
2
+ class FlexArray
3
+ #Process the standard :each operator.
4
+ #<br>Parameters
5
+ #* indexes - An array with as many entries as the flexible array has
6
+ # dimensions. See [] for more details. Note that since indexes is NOT
7
+ # a splat parameter, it must be passed as an array explicitly. If omitted
8
+ # or passed in as [:all] all elements of the array are processed.
9
+ #* block - The optional block to be executed for each selected array element.
10
+ # The return value is the last value returned by the block. If the block
11
+ # is not present, an enumerator object is returned.
12
+ #<br>Block Arguments
13
+ #* value - Each value selected by the iteration.
14
+ def each(indexes=[:all], &block)
15
+ if validate_all_or_index_count(indexes)
16
+ @array_data.each(&block)
17
+ elsif block_given?
18
+ process_indexes(indexes) {|_index, posn| block.call(@array_data[posn])}
19
+ else
20
+ Enumerator.new do |yielder|
21
+ process_indexes(indexes) {|_index, posn| yielder.yield(@array_data[posn])}
22
+ end
23
+ end
24
+ end
25
+
26
+ #Retrieve data from the array endlessly repeating as needed.
27
+ #<br>Parameters
28
+ #* indexes - An array with as many entries as the flexible array has
29
+ # dimensions. See [] for more details. Note that since indexes is NOT
30
+ # a splat parameter, it must be passed as an array explicitly. If omitted
31
+ # or passed in as [:all] all elements of the array are processed.
32
+ #* block - The optional block to be executed for each selected array element.
33
+ # The return value is the last value returned by the block. If the block
34
+ # is not present, then an enumerator object is returned.
35
+ #<br>Block Arguments
36
+ #* value - Each value selected by the iteration.
37
+ def cycle(indexes=[:all], &block)
38
+ if validate_all_or_index_count(indexes)
39
+ @array_data.cycle(&block)
40
+ elsif block_given?
41
+ loop {process_indexes(indexes) {|_index, posn| block.call(@array_data[posn])}}
42
+ else
43
+ Enumerator.new do |yielder|
44
+ loop {process_indexes(indexes) {|_index, posn| yielder.yield(@array_data[posn])}}
45
+ end
46
+ end
47
+ end
48
+
49
+ #Process the standard :each_with_index operator.
50
+ #<br>Parameters
51
+ #* indexes - An array with as many entries as the flexible array has
52
+ # dimensions. See [] for more details. Note that since indexes is NOT
53
+ # a splat parameter, it must be passed as an array explicitly. If omitted
54
+ # or passed in as [:all] all elements of the array are processed.
55
+ #* block - The optional block to be executed for each selected array element.
56
+ # The return value is the last value returned by the block. If the block
57
+ # is not present, then an enumerator object is returned.
58
+ #<br>Block Arguments
59
+ #* value - Each value selected by the iteration.
60
+ #* index - An array with the full index of the selected value.
61
+ def each_with_index(indexes=[:all], &block)
62
+ validate_all_or_index_count(indexes)
63
+
64
+ if block_given?
65
+ process_indexes(indexes) {|index, posn| block.call(@array_data[posn], index)}
66
+ else
67
+ Enumerator.new do |yielder|
68
+ process_indexes(indexes) {|index, posn| yielder.yield(@array_data[posn], index)}
69
+ end
70
+ end
71
+ end
72
+
73
+ #A specialized each variant that passes the low level data, the index
74
+ #and the position to the block.
75
+ #<br>Parameters
76
+ #* indexes - An array with as many entries as the flexible array has
77
+ # dimensions. See [] for more details. Note that since indexes is NOT
78
+ # a splat parameter, it must be passed as an array explicitly. If omitted
79
+ # or passed in as [:all] all elements of the array are processed.
80
+ #* block - The optional block to be executed for each selected array element.
81
+ # The return value is the last value returned by the block. If the block
82
+ # is not present, then an enumerator object is returned.
83
+ #<br>Block Arguments
84
+ #* data - A reference to the low level data store.
85
+ #* index - An array with the full index of the selected value.
86
+ #* posn - The position of the data in the low level data store.
87
+ def _each_raw(indexes=[:all], &block)
88
+ validate_all_or_index_count(indexes)
89
+
90
+ if block_given?
91
+ process_indexes(indexes) {|index, posn| block.call(@array_data, index, posn)}
92
+ else
93
+ Enumerator.new do |yielder|
94
+ process_indexes(indexes) {|index, posn| yielder.yield(@array_data, index, posn)}
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,42 @@
1
+ #* flex_array_index.rb - The flexible array indexing and related methods.
2
+ class FlexArray
3
+ #Retrieve the selected data from the \FlexArray.
4
+ #<br>Parameters
5
+ #* indexes - An array with as many entries as the flexible array has
6
+ # dimensions. These entries may be an integer that is in the bounds of
7
+ # the range limit of that dimension, or it can be a range where the start
8
+ # and end of the range are in the bounds of the range limit of that
9
+ # dimension, or it can be the symbol :all for all of the possible values
10
+ # of that dimension.
11
+ #<br>Returns
12
+ #* The data selected by the index or an array of data if the index selects
13
+ # more than one cell.
14
+ #<br>Note
15
+ #* If the indexes parameter equals a single :all this maps to all
16
+ # elements of the array, regardless of its dimensionality.
17
+ def [](*indexes)
18
+ validate_all_or_index_count(indexes)
19
+ result = []
20
+ process_indexes(indexes) {|_index, posn| result << @array_data[posn]}
21
+ result.length == 1 ? result[0] : result
22
+ end
23
+
24
+ #Store the value data into the \FlexArray.
25
+ #<br>Parameters
26
+ #* indexes - An array with as many entries as the flexible array has
27
+ # dimensions. These entries may be an integer that is in the bounds of
28
+ # the range limit of that dimension, or it can be a range where the start
29
+ # and end of the range are in the bounds of the range limit of that
30
+ # dimension, or it can be the symbol :all for all of the possible values
31
+ # of that dimension.
32
+ #* value - A value to be stored in the selected array entries.
33
+ #<br>Returns
34
+ #* The value stored in the array.
35
+ #<br>Note
36
+ #* If the indexes parameter equals a single :all this maps to all
37
+ # elements of the array, regardless of its dimensionality.
38
+ def []=(*indexes, value)
39
+ validate_all_or_index_count(indexes)
40
+ process_indexes(indexes) {|_index, posn| @array_data[posn] = value}
41
+ end
42
+ end
@@ -0,0 +1,98 @@
1
+ #* flex_array_new.rb - The flexible array class constructor and related methods.
2
+ class FlexArray
3
+ #Construct a flexible array object.
4
+ #<br>Parameters
5
+ #* array_specs - The specification of the flex array subscript limits.
6
+ # These can either be integers, in which case the limits are 0...n or
7
+ # they can be ranges of integers, in which case the limits are those
8
+ # of the range, or they can be an array of integers and/or ranges of
9
+ # integers to represent arrays of more than one dimension.
10
+ #* default - The optional default value. Defaults to nil.
11
+ #* init_block - An optional initialization block. The return values from this
12
+ # block are used to initialize the array. This overrides the default value.
13
+ #<br>Block Arguments
14
+ #* index - An array with one element per dimension with the fully qualified
15
+ # index of the element being accessed. Note that the same array is passed
16
+ # in each time the block is called, so, if this parameter is to stored
17
+ # anywhere, it's best that it be cloned or duplicated first.
18
+ def initialize(array_specs, default=nil, &init_block)
19
+ @array_specs, @count, @transposed = [], 1, false
20
+
21
+ #Parse the array limits.
22
+ array_specs.in_array.reverse_each do |spec|
23
+ @array_specs.insert(0, spec.to_spec_component(@count))
24
+ @count *= @array_specs[0].span
25
+ end
26
+
27
+ #Cache the dimensionality of the flex array.
28
+ @dimensions = @array_specs.length
29
+
30
+ #Allocate the data for the array.
31
+ @array_data = Array.new(@count, default)
32
+
33
+ #Set up the array with the optional init_block.
34
+ if init_block
35
+ process_all {|index, posn| @array_data[posn] = init_block.call(index)}
36
+ end
37
+ end
38
+
39
+ alias_method :shallow_dup, :dup
40
+
41
+ #Create a duplicate of this array.
42
+ def dup
43
+ other = self.shallow_dup
44
+ other.array_specs = @array_specs.dup
45
+ other.array_data = @array_data.dup
46
+ other
47
+ end
48
+
49
+ #Construct a \FlexArray using other as a template or data source.
50
+ #<br>Parameters:
51
+ #* array_specs - The specification of the flex array subscript limits.
52
+ # These can either be integers, in which case the limits are 0...n or
53
+ # they can be ranges of integers, in which case the limits are those
54
+ # of the range, or they can be an array of integers and/or ranges of
55
+ # integers to represent arrays of more than one dimension.
56
+ #* other - The array or flex array that is the source of the data.
57
+ #<br>Note:
58
+ #* To make a copy of a flex array, use dup instead.
59
+ def self.new_from(array_specs, other)
60
+ iterator = other.array_data.cycle
61
+ FlexArray.new(array_specs) {iterator.next}
62
+ end
63
+
64
+ #Construct a \FlexArray using a all or a portion of portion of another
65
+ #\FlexArray as a data source.
66
+ #<br>Parameters
67
+ #* array_specs - The specification of the flex array subscript limits.
68
+ # These can either be integers, in which case the limits are 0...n or
69
+ # they can be ranges of integers, in which case the limits are those
70
+ # of the range, or they can be an array of integers and/or ranges of
71
+ # integers to represent arrays of more than one dimension.
72
+ #* other - The flex array source of the data used to build this flex
73
+ # array. If no limits or other_indexes are specified, the limits of the
74
+ # other array are used instead.
75
+ #* selection - The indexes of the required sub-set of data to use
76
+ # in setting up the new array.
77
+ #<br>Notes:
78
+ #* If the entire source array is to be used, use new_from instead.
79
+ #* To make a copy of a flex array, use dup instead.
80
+ def self.new_from_selection(array_specs, other, selection)
81
+ iterator = other.cycle(selection)
82
+ FlexArray.new(array_specs) {iterator.next}
83
+ end
84
+
85
+ #Construct a \FlexArray as a duplicate of a source array or flex array.
86
+ #<br>Parameters
87
+ #* other - the array or flex array that is the source.
88
+ #<br>Returns
89
+ #* A flex array.
90
+ #<br>Note
91
+ #* The data array of the new flex array is a reference to the source array.
92
+ def self.new_from_array(other)
93
+ result = FlexArray.new(0)
94
+ result.array_specs = other.array_specs.dup
95
+ result.array_data = other.array_data
96
+ result.update_count_and_dimensions
97
+ end
98
+ end
@@ -0,0 +1,103 @@
1
+ #* flex_array_process.rb - The flexible array class index processing routines.
2
+ class FlexArray
3
+ private
4
+ #Process a \FlexArray index array. This is the heart of the \FlexArray
5
+ #indexing process.
6
+ #<br>Parameters
7
+ #* indexes - An array with as many entries as the flexible array has
8
+ # dimensions. See [] for more details. Note that since indexes is NOT
9
+ # a splat parameter, it must be passed as an array explicitly. If passed
10
+ # in as [:all] all elements of the array are processed.
11
+ #* block - a block to be called for each value selected by this process.
12
+ #<br>Block Arguments
13
+ #* current - An array with one element per dimension with the fully qualified
14
+ # index of the element being accessed.
15
+ #* posn - The position of the element being accessed in the data array.
16
+ def process_indexes(indexes, &block)
17
+ current = Array.new(@dimensions, 0)
18
+
19
+ if indexes == [:all]
20
+ process_all_worker(0, 0, current, &block)
21
+ else
22
+ specs = @array_specs.each
23
+
24
+ checked = indexes.collect do |index|
25
+ index.to_index_range(specs.next)
26
+ end
27
+
28
+ process_indexes_worker(0, 0, checked, current, &block)
29
+ end
30
+ end
31
+
32
+ #The worker bee for process_indexes.
33
+ #<br>Parameters
34
+ #* depth - The index of the dimension being processed.
35
+ #* posn - The partially computed position of the element being accessed
36
+ # in the data array.
37
+ #* indexes - An array of array indexes. See the method [] for details on
38
+ # what sorts of things these can be.
39
+ #* current - The partial fully qualified index of the element being accessed.
40
+ #* block - A block to be called for each value selected by this process.
41
+ #<br>Block Arguments
42
+ #* current - An array with one element per dimension with the fully qualified
43
+ # index of the element being accessed.
44
+ #* posn - The position of the element being accessed in the data array.
45
+ #<br>Note
46
+ #This is a recursive function. The recursion runs for index in 0..#dimensions
47
+ def process_indexes_worker(depth, posn, indexes, current, &block)
48
+ if depth == @dimensions
49
+ block.call(current, posn)
50
+ else
51
+ spec = @array_specs[depth]
52
+ range = indexes[depth]
53
+ posn += spec.index_step(range.min)
54
+ stride = spec.stride
55
+
56
+ range.each do |index|
57
+ current[depth] = index
58
+ process_indexes_worker(depth+1, posn, indexes, current, &block)
59
+ posn += stride
60
+ end
61
+ end
62
+ end
63
+
64
+ #Special case where all of the array is being processed.
65
+ #<br>Parameters
66
+ #* block - A block to be called for each value selected by this process.
67
+ #<br>Block Arguments
68
+ #* current - An array with one element per dimension with the fully qualified
69
+ # index of the element being accessed.
70
+ #* posn - The position of the element being accessed in the data array.
71
+ def process_all(&block)
72
+ current = Array.new(@dimensions, 0)
73
+ process_all_worker(0, 0, current, &block)
74
+ end
75
+
76
+ #The worker bee for process_all.
77
+ #<br>Parameters
78
+ #* depth - The index of the dimension being processed.
79
+ #* posn - The partially computed position of the element being accessed
80
+ # in the data array.
81
+ #* current - The partial fully qualified index of the element being accessed.
82
+ #* block - A block to be called for each value selected by this process.
83
+ #<br>Block Arguments
84
+ #* current - An array with one element per dimension with the fully qualified
85
+ # index of the element being accessed.
86
+ #* posn - The position of the element being accessed in the data array.
87
+ #<br>Note
88
+ #This is a recursive function. The recursion runs for index in 0..#dimensions
89
+ def process_all_worker(depth, posn, current, &block)
90
+ if depth == @dimensions
91
+ block.call(current, posn)
92
+ else
93
+ spec = @array_specs[depth]
94
+ stride = spec.stride
95
+
96
+ spec.each do |index|
97
+ current[depth] = index
98
+ process_all_worker(depth+1, posn, current, &block)
99
+ posn += stride
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,46 @@
1
+ #* flex_array_reshape.rb - The flexible array class reshape and related methods.
2
+ class FlexArray
3
+ #Return a copy of this \FlexArray, recast in a new shape, dropping or
4
+ #repeating data elements as required.
5
+ #<br>Parameters
6
+ #* array_specs - The specification of the flex array subscript limits.
7
+ # These can either be integers, in which case the limits are 0...n or
8
+ # they can be ranges of integers, in which case the limits are those
9
+ # of the range, or they can be an array of integers and/or ranges of
10
+ # integers to represent arrays of more than one dimension.
11
+ #<br>Returns
12
+ #* The reshaped flexible array.
13
+ def reshape(array_specs)
14
+ iterator = @array_data.cycle
15
+ FlexArray.new(array_specs) {iterator.next}
16
+ end
17
+
18
+ #Recast this \FlexArray in a new shape, dropping or
19
+ #repeating data elements as required.
20
+ #<br>Parameters
21
+ #* array_specs - The specification of the flex array subscript limits.
22
+ # These can either be integers, in which case the limits are 0...n or
23
+ # they can be ranges of integers, in which case the limits are those
24
+ # of the range, or they can be an array of integers and/or ranges of
25
+ # integers to represent arrays of more than one dimension.
26
+ #<br>Returns
27
+ #* The reshaped, flexible array.
28
+ def reshape!(array_specs)
29
+ iterator = @array_data.cycle
30
+ temp = FlexArray.new(array_specs) {iterator.next}
31
+ @array_specs, @array_data, @dimensions =
32
+ temp.array_specs, temp.array_data, temp.dimensions
33
+ self
34
+ end
35
+
36
+ #Unravel the multi-dimensional structure, and any contained arrays into
37
+ #a simple array.
38
+ def flatten
39
+ @array_data.flatten
40
+ end
41
+
42
+ #Convert the flex array to a simple array. Contained arrays are not affected.
43
+ def to_a
44
+ @array_data.dup
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ #* flex_array_transpose.rb - The flexible array class transpose and related methods.
2
+ class FlexArray
3
+ #Transpose the specified dimensions. This may change the "shape" of the array
4
+ #if the transposed dimensions were of different limits.
5
+ #<br>Returns
6
+ #* The flex array transposed.
7
+ #<br>Note
8
+ #* Transposing an array disables the speed-up for processing the array with
9
+ # an index of [:all].
10
+ def transpose(dim_a, dim_b)
11
+ validate_dimension(dim_a)
12
+ validate_dimension(dim_b)
13
+ @array_specs[dim_a], @array_specs[dim_b] = @array_specs[dim_b], @array_specs[dim_a]
14
+ @transposed = true
15
+ self
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ #* flex_array_validate.rb - The flexible array class index validation routines.
2
+ class FlexArray
3
+ #Is this array compatible with other?
4
+ #<br>Parameters
5
+ #* other - The object being tested for compatibility.
6
+ #<br>Returns
7
+ #* true if the arrays are compatible.
8
+ def compatible?(other)
9
+ @array_specs == other.array_specs
10
+ end
11
+
12
+ private
13
+
14
+ #Validate the dimensionality of the indexes passed in.
15
+ #<br>Parameters
16
+ #* indexes - An array of indexes to be validated.
17
+ #<br>Returns
18
+ #* true if the indexes value is [:all] and the array is not transposed.
19
+ #<br>Exceptions
20
+ #* ArgumentError - raised on invalid dimensionality.
21
+ def validate_all_or_index_count(indexes)
22
+ if indexes == [:all]
23
+ !@transposed
24
+ elsif @dimensions != indexes.length
25
+ fail ArgumentError, "Incorrect number of indexes: #{@dimensions} expected."
26
+ end
27
+ end
28
+
29
+ #Is this a valid dimension selector?
30
+ #<br>Exceptions
31
+ #* ArgumentError - raised on invalid dimension.
32
+ def validate_dimension(dim)
33
+ unless (0...@dimensions) === dim
34
+ fail ArgumentError, "Invalid dimesnsion selector: #{dim}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'spec_component'
2
+
3
+ #Extensions to Integer needed to support flex array.
4
+ class Integer
5
+ #Convert this integer to a limits component.
6
+ #<br>Parameters
7
+ #* stride - the number of cells separating data with adjacent indexes.
8
+ #<br>Returns
9
+ #* A SpecComponent object of 0...self
10
+ def to_spec_component(stride)
11
+ if self >= 0
12
+ SpecComponent.new(0...self, stride)
13
+ else
14
+ fail ArgumentError, "Invalid flex array dimension: #{self.inspect}"
15
+ end
16
+ end
17
+
18
+ #Convert this integer to an range index against the spec.
19
+ #<br>Parameters
20
+ #* spec - The spec component used to validate this index.
21
+ #<br>Returns
22
+ #* A range.
23
+ #<br>Exceptions
24
+ #* IndexError if the range is not valid.
25
+ def to_index_range(spec)
26
+ alter_ego = (self >= 0) ? self : (spec.max + self + 1)
27
+
28
+ if spec === alter_ego
29
+ alter_ego..alter_ego
30
+ else
31
+ fail IndexError, "Subscript out of range: #{self.inspect}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ require_relative 'spec_component'
2
+
3
+ #Extensions to Object needed to support flex array.
4
+ class Object
5
+ #Fail with message since the array dimension is invalid.
6
+ #<br>Parameters
7
+ #* _stride - unused, required to maintain signature.
8
+ #<br>Returns
9
+ #* Nothing, always fails.
10
+ def to_spec_component(_stride)
11
+ fail ArgumentError, "Invalid flex array dimension: #{self.inspect}"
12
+ end
13
+
14
+ #Convert this object to an range index against the spec.
15
+ #<br>Parameters
16
+ #* spec - The spec component used to validate this index.
17
+ #<br>Returns
18
+ #* A range.
19
+ #<br>Exceptions
20
+ #* IndexError if the range is not valid.
21
+ def to_index_range(spec)
22
+ if self == :all
23
+ spec.range
24
+ else
25
+ fail IndexError, "Invalid subscript: #{self.inspect}"
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,44 @@
1
+ require_relative 'spec_component'
2
+
3
+ #Extensions to Range needed to support flex array.
4
+ class Range
5
+ #Convert this integer to a limits component.
6
+ #<br>Parameters
7
+ #* stride - the number of cells separating data with adjacent indexes.
8
+ #<br>Returns
9
+ #* A SpecComponent object with the same range as self.
10
+ def to_spec_component(stride)
11
+ min = self.min
12
+
13
+ if self == (0...0)
14
+ SpecComponent.new(0...0, stride)
15
+ elsif !self.none? && min.is_a?(Integer) && (min >= 0) && self.max.is_a?(Integer)
16
+ SpecComponent.new(self, stride)
17
+ else
18
+ fail ArgumentError, "Invalid flex array dimension: #{self.inspect}"
19
+ end
20
+ end
21
+
22
+ #Convert this range to an range index against the spec.
23
+ #<br>Parameters
24
+ #* spec - The spec component used to validate this index.
25
+ #<br>Returns
26
+ #* A range.
27
+ #<br>Exceptions
28
+ #* IndexError if the range is not valid.
29
+ def to_index_range(spec)
30
+ self_min, self_max, spec_max = self.min, self.max, spec.max
31
+
32
+ if self_min < 0 && self_max < 0
33
+ alter_ego = (spec_max + self_min + 1)..(spec_max + self_max + 1)
34
+ else
35
+ alter_ego = self
36
+ end
37
+
38
+ if spec === alter_ego.min && spec === alter_ego.max
39
+ alter_ego
40
+ else
41
+ fail IndexError, "Subscript out of range: #{self.inspect}"
42
+ end
43
+ end
44
+ end