summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/OrderedTuple.java
blob: 2dad2d7340df1b47c9f0d9931e00661dc3d41f5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.math3.geometry.partitioning.utilities;

import java.util.Arrays;

import org.apache.commons.math3.util.FastMath;

/** This class implements an ordering operation for T-uples.
 *
 * <p>Ordering is done by encoding all components of the T-uple into a
 * single scalar value and using this value as the sorting
 * key. Encoding is performed using the method invented by Georg
 * Cantor in 1877 when he proved it was possible to establish a
 * bijection between a line and a plane. The binary representations of
 * the components of the T-uple are mixed together to form a single
 * scalar. This means that the 2<sup>k</sup> bit of component 0 is
 * followed by the 2<sup>k</sup> bit of component 1, then by the
 * 2<sup>k</sup> bit of component 2 up to the 2<sup>k</sup> bit of
 * component {@code t}, which is followed by the 2<sup>k-1</sup>
 * bit of component 0, followed by the 2<sup>k-1</sup> bit of
 * component 1 ... The binary representations are extended as needed
 * to handle numbers with different scales and a suitable
 * 2<sup>p</sup> offset is added to the components in order to avoid
 * negative numbers (this offset is adjusted as needed during the
 * comparison operations).</p>
 *
 * <p>The more interesting property of the encoding method for our
 * purpose is that it allows to select all the points that are in a
 * given range. This is depicted in dimension 2 by the following
 * picture:</p>
 *
 * <img src="doc-files/OrderedTuple.png" />
 *
 * <p>This picture shows a set of 100000 random 2-D pairs having their
 * first component between -50 and +150 and their second component
 * between -350 and +50. We wanted to extract all pairs having their
 * first component between +30 and +70 and their second component
 * between -120 and -30. We built the lower left point at coordinates
 * (30, -120) and the upper right point at coordinates (70, -30). All
 * points smaller than the lower left point are drawn in red and all
 * points larger than the upper right point are drawn in blue. The
 * green points are between the two limits. This picture shows that
 * all the desired points are selected, along with spurious points. In
 * this case, we get 15790 points, 4420 of which really belonging to
 * the desired rectangle. It is possible to extract very small
 * subsets. As an example extracting from the same 100000 points set
 * the points having their first component between +30 and +31 and
 * their second component between -91 and -90, we get a subset of 11
 * points, 2 of which really belonging to the desired rectangle.</p>
 *
 * <p>the previous selection technique can be applied in all
 * dimensions, still using two points to define the interval. The
 * first point will have all its components set to their lower bounds
 * while the second point will have all its components set to their
 * upper bounds.</p>
 *
 * <p>T-uples with negative infinite or positive infinite components
 * are sorted logically.</p>
 *
 * <p>Since the specification of the {@code Comparator} interface
 * allows only {@code ClassCastException} errors, some arbitrary
 * choices have been made to handle specific cases. The rationale for
 * these choices is to keep <em>regular</em> and consistent T-uples
 * together.</p>
 * <ul>
 * <li>instances with different dimensions are sorted according to
 * their dimension regardless of their components values</li>
 * <li>instances with {@code Double.NaN} components are sorted
 * after all other ones (even after instances with positive infinite
 * components</li>
 * <li>instances with both positive and negative infinite components
 * are considered as if they had {@code Double.NaN}
 * components</li>
 * </ul>
 *
 * @since 3.0
 * @deprecated as of 3.4, this class is not used anymore and considered
 * to be out of scope of Apache Commons Math
 */
@Deprecated
public class OrderedTuple implements Comparable<OrderedTuple> {

    /** Sign bit mask. */
    private static final long SIGN_MASK     = 0x8000000000000000L;

    /** Exponent bits mask. */
    private static final long EXPONENT_MASK = 0x7ff0000000000000L;

    /** Mantissa bits mask. */
    private static final long MANTISSA_MASK = 0x000fffffffffffffL;

    /** Implicit MSB for normalized numbers. */
    private static final long IMPLICIT_ONE  = 0x0010000000000000L;

    /** Double components of the T-uple. */
    private double[] components;

    /** Offset scale. */
    private int offset;

    /** Least Significant Bit scale. */
    private int lsb;

    /** Ordering encoding of the double components. */
    private long[] encoding;

    /** Positive infinity marker. */
    private boolean posInf;

    /** Negative infinity marker. */
    private boolean negInf;

    /** Not A Number marker. */
    private boolean nan;

    /** Build an ordered T-uple from its components.
     * @param components double components of the T-uple
     */
    public OrderedTuple(final double ... components) {
        this.components = components.clone();
        int msb = Integer.MIN_VALUE;
        lsb     = Integer.MAX_VALUE;
        posInf  = false;
        negInf  = false;
        nan     = false;
        for (int i = 0; i < components.length; ++i) {
            if (Double.isInfinite(components[i])) {
                if (components[i] < 0) {
                    negInf = true;
                } else {
                    posInf = true;
                }
            } else if (Double.isNaN(components[i])) {
                nan = true;
            } else {
                final long b = Double.doubleToLongBits(components[i]);
                final long m = mantissa(b);
                if (m != 0) {
                    final int e = exponent(b);
                    msb = FastMath.max(msb, e + computeMSB(m));
                    lsb = FastMath.min(lsb, e + computeLSB(m));
                }
            }
        }

        if (posInf && negInf) {
            // instance cannot be sorted logically
            posInf = false;
            negInf = false;
            nan    = true;
        }

        if (lsb <= msb) {
            // encode the T-upple with the specified offset
            encode(msb + 16);
        } else {
            encoding = new long[] {
                0x0L
            };
        }

    }

    /** Encode the T-uple with a given offset.
     * @param minOffset minimal scale of the offset to add to all
     * components (must be greater than the MSBs of all components)
     */
    private void encode(final int minOffset) {

        // choose an offset with some margins
        offset  = minOffset + 31;
        offset -= offset % 32;

        if ((encoding != null) && (encoding.length == 1) && (encoding[0] == 0x0L)) {
            // the components are all zeroes
            return;
        }

        // allocate an integer array to encode the components (we use only
        // 63 bits per element because there is no unsigned long in Java)
        final int neededBits  = offset + 1 - lsb;
        final int neededLongs = (neededBits + 62) / 63;
        encoding = new long[components.length * neededLongs];

        // mix the bits from all components
        int  eIndex = 0;
        int  shift  = 62;
        long word   = 0x0L;
        for (int k = offset; eIndex < encoding.length; --k) {
            for (int vIndex = 0; vIndex < components.length; ++vIndex) {
                if (getBit(vIndex, k) != 0) {
                    word |= 0x1L << shift;
                }
                if (shift-- == 0) {
                    encoding[eIndex++] = word;
                    word  = 0x0L;
                    shift = 62;
                }
            }
        }

    }

    /** Compares this ordered T-uple with the specified object.

     * <p>The ordering method is detailed in the general description of
     * the class. Its main property is to be consistent with distance:
     * geometrically close T-uples stay close to each other when stored
     * in a sorted collection using this comparison method.</p>

     * <p>T-uples with negative infinite, positive infinite are sorted
     * logically.</p>

     * <p>Some arbitrary choices have been made to handle specific
     * cases. The rationale for these choices is to keep
     * <em>normal</em> and consistent T-uples together.</p>
     * <ul>
     * <li>instances with different dimensions are sorted according to
     * their dimension regardless of their components values</li>
     * <li>instances with {@code Double.NaN} components are sorted
     * after all other ones (evan after instances with positive infinite
     * components</li>
     * <li>instances with both positive and negative infinite components
     * are considered as if they had {@code Double.NaN}
     * components</li>
     * </ul>

     * @param ot T-uple to compare instance with
     * @return a negative integer if the instance is less than the
     * object, zero if they are equal, or a positive integer if the
     * instance is greater than the object

     */
    public int compareTo(final OrderedTuple ot) {
        if (components.length == ot.components.length) {
            if (nan) {
                return +1;
            } else if (ot.nan) {
                return -1;
            } else if (negInf || ot.posInf) {
                return -1;
            } else if (posInf || ot.negInf) {
                return +1;
            } else {

                if (offset < ot.offset) {
                    encode(ot.offset);
                } else if (offset > ot.offset) {
                    ot.encode(offset);
                }

                final int limit = FastMath.min(encoding.length, ot.encoding.length);
                for (int i = 0; i < limit; ++i) {
                    if (encoding[i] < ot.encoding[i]) {
                        return -1;
                    } else if (encoding[i] > ot.encoding[i]) {
                        return +1;
                    }
                }

                if (encoding.length < ot.encoding.length) {
                    return -1;
                } else if (encoding.length > ot.encoding.length) {
                    return +1;
                } else {
                    return 0;
                }

            }
        }

        return components.length - ot.components.length;

    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(final Object other) {
        if (this == other) {
            return true;
        } else if (other instanceof OrderedTuple) {
            return compareTo((OrderedTuple) other) == 0;
        } else {
            return false;
        }
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        // the following constants are arbitrary small primes
        final int multiplier = 37;
        final int trueHash   = 97;
        final int falseHash  = 71;

        // hash fields and combine them
        // (we rely on the multiplier to have different combined weights
        //  for all int fields and all boolean fields)
        int hash = Arrays.hashCode(components);
        hash = hash * multiplier + offset;
        hash = hash * multiplier + lsb;
        hash = hash * multiplier + (posInf ? trueHash : falseHash);
        hash = hash * multiplier + (negInf ? trueHash : falseHash);
        hash = hash * multiplier + (nan    ? trueHash : falseHash);

        return hash;

    }

    /** Get the components array.
     * @return array containing the T-uple components
     */
    public double[] getComponents() {
        return components.clone();
    }

    /** Extract the sign from the bits of a double.
     * @param bits binary representation of the double
     * @return sign bit (zero if positive, non zero if negative)
     */
    private static long sign(final long bits) {
        return bits & SIGN_MASK;
    }

    /** Extract the exponent from the bits of a double.
     * @param bits binary representation of the double
     * @return exponent
     */
    private static int exponent(final long bits) {
        return ((int) ((bits & EXPONENT_MASK) >> 52)) - 1075;
    }

    /** Extract the mantissa from the bits of a double.
     * @param bits binary representation of the double
     * @return mantissa
     */
    private static long mantissa(final long bits) {
        return ((bits & EXPONENT_MASK) == 0) ?
               ((bits & MANTISSA_MASK) << 1) :          // subnormal number
               (IMPLICIT_ONE | (bits & MANTISSA_MASK)); // normal number
    }

    /** Compute the most significant bit of a long.
     * @param l long from which the most significant bit is requested
     * @return scale of the most significant bit of {@code l},
     * or 0 if {@code l} is zero
     * @see #computeLSB
     */
    private static int computeMSB(final long l) {

        long ll = l;
        long mask  = 0xffffffffL;
        int  scale = 32;
        int  msb   = 0;

        while (scale != 0) {
            if ((ll & mask) != ll) {
                msb |= scale;
                ll >>= scale;
            }
            scale >>= 1;
            mask >>= scale;
        }

        return msb;

    }

    /** Compute the least significant bit of a long.
     * @param l long from which the least significant bit is requested
     * @return scale of the least significant bit of {@code l},
     * or 63 if {@code l} is zero
     * @see #computeMSB
     */
    private static int computeLSB(final long l) {

        long ll = l;
        long mask  = 0xffffffff00000000L;
        int  scale = 32;
        int  lsb   = 0;

        while (scale != 0) {
            if ((ll & mask) == ll) {
                lsb |= scale;
                ll >>= scale;
            }
            scale >>= 1;
            mask >>= scale;
        }

        return lsb;

    }

    /** Get a bit from the mantissa of a double.
     * @param i index of the component
     * @param k scale of the requested bit
     * @return the specified bit (either 0 or 1), after the offset has
     * been added to the double
     */
    private int getBit(final int i, final int k) {
        final long bits = Double.doubleToLongBits(components[i]);
        final int e = exponent(bits);
        if ((k < e) || (k > offset)) {
            return 0;
        } else if (k == offset) {
            return (sign(bits) == 0L) ? 1 : 0;
        } else if (k > (e + 52)) {
            return (sign(bits) == 0L) ? 0 : 1;
        } else {
            final long m = (sign(bits) == 0L) ? mantissa(bits) : -mantissa(bits);
            return (int) ((m >> (k - e)) & 0x1L);
        }
    }

}