org.neo4j.values.storable.NumberValues Maven / Gradle / Ivy
Show all versions of neo4j-values Show documentation
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.values.storable;
import java.math.BigDecimal;
import java.util.Arrays;
import static org.neo4j.values.utils.ValueMath.HASH_CONSTANT;
/**
* Static methods for computing the hashCode of primitive numbers and arrays of primitive numbers.
*
* Also compares Value typed number arrays.
*/
@SuppressWarnings( "WeakerAccess" )
public final class NumberValues
{
private NumberValues()
{
}
/*
* Using the fact that the hashcode ∑x_i * 31^(i-1) can be expressed as
* a dot product, [1, v_1, v_2, v_2, ..., v_n] • [31^n, 31^{n-1}, ..., 31, 1]. By expressing
* it in that way the compiler is smart enough to better parallelize the
* computation of the hash code.
*/
static final int MAX_LENGTH = 10000;
private static final int[] COEFFICIENTS = new int[MAX_LENGTH + 1];
private static final long NON_DOUBLE_LONG = 0xFFE0_0000_0000_0000L; // doubles are exact integers up to 53 bits
static
{
//We are defining the coefficient vector backwards, [1, 31, 31^2,...]
//makes it easier and faster do find the starting position later
COEFFICIENTS[0] = 1;
for ( int i = 1; i <= MAX_LENGTH; ++i )
{
COEFFICIENTS[i] = HASH_CONSTANT * COEFFICIENTS[i - 1];
}
}
/*
* For equality semantics it is important that the hashcode of a long
* is the same as the hashcode of an int as long as the long can fit in 32 bits.
*/
public static int hash( long number )
{
int asInt = (int) number;
if ( asInt == number )
{
return asInt;
}
return Long.hashCode( number );
}
public static int hash( double number )
{
long asLong = (long) number;
if ( asLong == number )
{
return hash( asLong );
}
long bits = Double.doubleToLongBits( number );
return (int) (bits ^ (bits >>> 32));
}
/*
* This is a slightly silly optimization but by turning the computation
* of the hashcode into a dot product we trick the jit compiler to use SIMD
* instructions and performance doubles.
*/
public static int hash( byte[] values )
{
final int max = Math.min( values.length, MAX_LENGTH );
int result = COEFFICIENTS[max];
for ( int i = 0; i < values.length && i < COEFFICIENTS.length - 1; ++i )
{
result += COEFFICIENTS[max - i - 1] * values[i];
}
return result;
}
public static int hash( short[] values )
{
final int max = Math.min( values.length, MAX_LENGTH );
int result = COEFFICIENTS[max];
for ( int i = 0; i < values.length && i < COEFFICIENTS.length - 1; ++i )
{
result += COEFFICIENTS[max - i - 1] * values[i];
}
return result;
}
public static int hash( char[] values )
{
final int max = Math.min( values.length, MAX_LENGTH );
int result = COEFFICIENTS[max];
for ( int i = 0; i < values.length && i < COEFFICIENTS.length - 1; ++i )
{
result += COEFFICIENTS[max - i - 1] * ( values[i] + HASH_CONSTANT );
}
return result;
}
public static int hash( int[] values )
{
final int max = Math.min( values.length, MAX_LENGTH );
int result = COEFFICIENTS[max];
for ( int i = 0; i < values.length && i < COEFFICIENTS.length - 1; ++i )
{
result += COEFFICIENTS[max - i - 1] * values[i];
}
return result;
}
public static int hash( long[] values )
{
final int max = Math.min( values.length, MAX_LENGTH );
int result = COEFFICIENTS[max];
for ( int i = 0; i < values.length && i < COEFFICIENTS.length - 1; ++i )
{
result += COEFFICIENTS[max - i - 1] * NumberValues.hash( values[i] );
}
return result;
}
public static int hash( float[] values )
{
int result = 1;
for ( float value : values )
{
int elementHash = NumberValues.hash( value );
result = HASH_CONSTANT * result + elementHash;
}
return result;
}
public static int hash( double[] values )
{
int result = 1;
for ( double value : values )
{
int elementHash = NumberValues.hash( value );
result = HASH_CONSTANT * result + elementHash;
}
return result;
}
public static int hash( boolean[] value )
{
return Arrays.hashCode( value );
}
public static boolean numbersEqual( double fpn, long in )
{
if ( in < 0 )
{
if ( fpn < 0.0 )
{
if ( (NON_DOUBLE_LONG & in) == 0L ) // the high order bits are only sign bits
{ // no loss of precision if converting the long to a double, so it's safe to compare as double
return fpn == in;
}
else if ( fpn < Long.MIN_VALUE )
{ // the double is too big to fit in a long, they cannot be equal
return false;
}
else if ( (fpn == Math.floor( fpn )) && !Double.isInfinite( fpn ) ) // no decimals
{ // safe to compare as long
return in == (long) fpn;
}
}
}
else
{
if ( !(fpn < 0.0) )
{
if ( (NON_DOUBLE_LONG & in) == 0L ) // the high order bits are only sign bits
{ // no loss of precision if converting the long to a double, so it's safe to compare as double
return fpn == in;
}
else if ( fpn > Long.MAX_VALUE )
{ // the double is too big to fit in a long, they cannot be equal
return false;
}
else if ( (fpn == Math.floor( fpn )) && !Double.isInfinite( fpn ) ) // no decimals
{ // safe to compare as long
return in == (long) fpn;
}
}
}
return false;
}
// Tested by PropertyValueComparisonTest
public static int compareDoubleAgainstLong( double lhs, long rhs )
{
if ( (NON_DOUBLE_LONG & rhs) != 0L )
{
if ( Double.isNaN( lhs ) )
{
return +1;
}
if ( Double.isInfinite( lhs ) )
{
return lhs < 0 ? -1 : +1;
}
return BigDecimal.valueOf( lhs ).compareTo( BigDecimal.valueOf( rhs ) );
}
return Double.compare( lhs, rhs );
}
// Tested by PropertyValueComparisonTest
public static int compareLongAgainstDouble( long lhs, double rhs )
{
return -compareDoubleAgainstLong( rhs, lhs );
}
public static boolean numbersEqual( IntegralArray lhs, IntegralArray rhs )
{
int length = lhs.length();
if ( length != rhs.length() )
{
return false;
}
for ( int i = 0; i < length; i++ )
{
if ( lhs.longValue( i ) != rhs.longValue( i ) )
{
return false;
}
}
return true;
}
public static boolean numbersEqual( FloatingPointArray lhs, FloatingPointArray rhs )
{
int length = lhs.length();
if ( length != rhs.length() )
{
return false;
}
for ( int i = 0; i < length; i++ )
{
if ( lhs.doubleValue( i ) != rhs.doubleValue( i ) )
{
return false;
}
}
return true;
}
public static boolean numbersEqual( FloatingPointArray fps, IntegralArray ins )
{
int length = ins.length();
if ( length != fps.length() )
{
return false;
}
for ( int i = 0; i < length; i++ )
{
if ( !numbersEqual( fps.doubleValue( i ), ins.longValue( i ) ) )
{
return false;
}
}
return true;
}
public static int compareIntegerArrays( IntegralArray a, IntegralArray b )
{
int i = 0;
int x = 0;
int length = Math.min( a.length(), b.length() );
while ( x == 0 && i < length )
{
x = Long.compare( a.longValue( i ), b.longValue( i ) );
i++;
}
if ( x == 0 )
{
x = a.length() - b.length();
}
return x;
}
public static int compareIntegerVsFloatArrays( IntegralArray a, FloatingPointArray b )
{
int i = 0;
int x = 0;
int length = Math.min( a.length(), b.length() );
while ( x == 0 && i < length )
{
x = compareLongAgainstDouble( a.longValue( i ), b.doubleValue( i ) );
i++;
}
if ( x == 0 )
{
x = a.length() - b.length();
}
return x;
}
public static int compareFloatArrays( FloatingPointArray a, FloatingPointArray b )
{
int i = 0;
int x = 0;
int length = Math.min( a.length(), b.length() );
while ( x == 0 && i < length )
{
x = Double.compare( a.doubleValue( i ), b.doubleValue( i ) );
i++;
}
if ( x == 0 )
{
x = a.length() - b.length();
}
return x;
}
public static int compareBooleanArrays( BooleanArray a, BooleanArray b )
{
int i = 0;
int x = 0;
int length = Math.min( a.length(), b.length() );
while ( x == 0 && i < length )
{
x = Boolean.compare( a.booleanValue( i ), b.booleanValue( i ) );
i++;
}
if ( x == 0 )
{
x = a.length() - b.length();
}
return x;
}
}