View Javadoc
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 }