1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.numbers.examples.jmh.arrays;
19
20 /**
21 * An {@link UpdatingInterval} and {@link SplittingInterval} backed by an array of ordered keys.
22 *
23 * @since 1.2
24 */
25 final class KeyUpdatingInterval implements UpdatingInterval, SplittingInterval {
26 /** Size to use a scan of the keys when splitting instead of binary search.
27 * Note binary search has an overhead on small size due to the random left/right
28 * branching per iteration. It is much faster on very large sizes. */
29 private static final int SCAN_SIZE = 256;
30
31 /** The ordered keys. */
32 private final int[] keys;
33 /** Index of the left key. */
34 private int l;
35 /** Index of the right key. */
36 private int r;
37
38 /**
39 * Create an instance with the provided {@code indices}.
40 * Indices must be sorted.
41 *
42 * @param indices Indices.
43 * @param n Number of indices.
44 */
45 KeyUpdatingInterval(int[] indices, int n) {
46 this(indices, 0, n - 1);
47 }
48
49 /**
50 * @param indices Indices.
51 * @param l Index of left key.
52 * @param r Index of right key.
53 */
54 private KeyUpdatingInterval(int[] indices, int l, int r) {
55 keys = indices;
56 this.l = l;
57 this.r = r;
58 }
59
60 /**
61 * Initialise an instance with the {@code indices}. The indices are used in place.
62 *
63 * @param indices Indices.
64 * @param n Number of indices.
65 * @return the interval
66 * @throws IllegalArgumentException if the indices are not unique and ordered;
67 * or {@code n <= 0}
68 */
69 static KeyUpdatingInterval of(int[] indices, int n) {
70 // Check the indices are uniquely ordered
71 if (n <= 0) {
72 throw new IllegalArgumentException("No indices to define the range");
73 }
74 int p = indices[0];
75 for (int i = 0; ++i < n;) {
76 final int c = indices[i];
77 if (c <= p) {
78 throw new IllegalArgumentException("Indices are not unique and ordered");
79 }
80 p = c;
81 }
82 if (indices[0] < 0) {
83 throw new IllegalArgumentException("Unsupported min value: " + indices[0]);
84 }
85 if (indices[n - 1] == Integer.MAX_VALUE) {
86 throw new IllegalArgumentException("Unsupported max value: " + Integer.MAX_VALUE);
87 }
88 return new KeyUpdatingInterval(indices, n);
89 }
90
91 @Override
92 public int left() {
93 return keys[l];
94 }
95
96 @Override
97 public int right() {
98 return keys[r];
99 }
100
101 @Override
102 public int updateLeft(int k) {
103 // Assume left < k <= right (i.e. we must move left at least 1)
104 // Search using a scan on the assumption that k is close to the end
105 int i = l;
106 do {
107 ++i;
108 } while (keys[i] < k);
109 l = i;
110 return keys[i];
111 }
112
113 @Override
114 public int updateRight(int k) {
115 // Assume left <= k < right (i.e. we must move right at least 1)
116 // Search using a scan on the assumption that k is close to the end
117 int i = r;
118 do {
119 --i;
120 } while (keys[i] > k);
121 r = i;
122 return keys[i];
123 }
124
125 @Override
126 public UpdatingInterval splitLeft(int ka, int kb) {
127 // left < ka <= kb < right
128
129 // Find the new left bound for the upper interval.
130 // Switch to a linear scan if length is small.
131 int i;
132 if (r - l < SCAN_SIZE) {
133 i = r;
134 do {
135 --i;
136 } while (keys[i] > kb);
137 } else {
138 // Binary search
139 i = Partition.searchLessOrEqual(keys, l, r, kb);
140 }
141 final int lowerLeft = l;
142 l = i + 1;
143
144 // Find the new right bound for the lower interval using a scan since a
145 // typical use case has ka == kb and this is faster than a second binary search.
146 while (keys[i] >= ka) {
147 --i;
148 }
149 // return left
150 return new KeyUpdatingInterval(keys, lowerLeft, i);
151 }
152
153 @Override
154 public UpdatingInterval splitRight(int ka, int kb) {
155 // left < ka <= kb < right
156
157 // Find the new left bound for the upper interval.
158 // Switch to a linear scan if length is small.
159 int i;
160 if (r - l < SCAN_SIZE) {
161 i = r;
162 do {
163 --i;
164 } while (keys[i] > kb);
165 } else {
166 // Binary search
167 i = Partition.searchLessOrEqual(keys, l, r, kb);
168 }
169 final int upperLeft = i + 1;
170
171 // Find the new right bound for the lower interval using a scan since a
172 // typical use case has ka == kb and this is faster than a second binary search.
173 while (keys[i] >= ka) {
174 --i;
175 }
176 final int upperRight = r;
177 r = i;
178 // return right
179 return new KeyUpdatingInterval(keys, upperLeft, upperRight);
180 }
181
182 /**
183 * Return the current number of indices in the interval.
184 * This is undefined when {@link #empty()}.
185 *
186 * @return the size
187 */
188 int size() {
189 return r - l + 1;
190 }
191
192 @Override
193 public boolean empty() {
194 // Empty when the interval is invalid. Signalled by a negative right index.
195 return r < 0;
196 }
197
198 @Override
199 public SplittingInterval split(int ka, int kb) {
200 if (ka <= left()) {
201 // No left interval
202 if (kb >= right()) {
203 // No right interval
204 invalidate();
205 } else if (kb >= left()) {
206 // Update the left bound.
207 // Search using a scan on the assumption that kb is close to the end
208 // given that ka is less then the end.
209 int i = l;
210 do {
211 ++i;
212 } while (keys[i] < kb);
213 l = i;
214 }
215 return null;
216 }
217 if (kb >= right()) {
218 // No right interval.
219 // Find new right bound for the left-side.
220 // Search using a scan on the assumption that ka is close to the end
221 // given that kb is greater then the end.
222 int i = r;
223 if (ka <= keys[i]) {
224 do {
225 --i;
226 } while (keys[i] > ka);
227 }
228 invalidate();
229 return new KeyUpdatingInterval(keys, l, i);
230 }
231 // Split
232 return (SplittingInterval) splitLeft(ka, kb);
233 }
234
235 /**
236 * Invalidate the interval and mark as empty.
237 */
238 private void invalidate() {
239 r = -1;
240 }
241 }