diff --git a/src/main/java/net/imglib2/converter/RealTypeConverterInternals.java b/src/main/java/net/imglib2/converter/RealTypeConverterInternals.java new file mode 100644 index 0000000000..c4be8459c4 --- /dev/null +++ b/src/main/java/net/imglib2/converter/RealTypeConverterInternals.java @@ -0,0 +1,214 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2018 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.converter; + +import net.imglib2.loops.ClassCopyProvider; +import net.imglib2.type.BooleanType; +import net.imglib2.type.numeric.IntegerType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.GenericByteType; +import net.imglib2.type.numeric.integer.GenericIntType; +import net.imglib2.type.numeric.integer.GenericShortType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.LongType; +import net.imglib2.type.numeric.real.FloatType; + +import java.util.Arrays; + +/** + * Implementation of {@link RealTypeConverters#getConverter(RealType, RealType)}. + */ +class RealTypeConverterInternals +{ + static < S extends RealType< ? >, T extends RealType< ? > > Converter< S, T > getConverter( S inputType, T outputType ) + { + final RealTypeConverterInternals.ConverterFactory factory = RealTypeConverterInternals.getConverterFactory( inputType, outputType ); + return factory.create( inputType, outputType ); + } + + private static ConverterFactory TYPE_IDENTITY = new ConverterFactory( TypeIdentity.class ); + + private static ConverterFactory DOUBLE = new ConverterFactory( DoubleConverter.class ); + + private static ConverterFactory FLOAT = new ConverterFactory( FloatConverter.class ); + + private static ConverterFactory INTEGER = new ConverterFactory( IntegerConverter.class ); + + private static ConverterFactory LONG = new ConverterFactory( LongConverter.class ); + + private static ConverterFactory BYTE = new ConverterFactory( ByteConverter.class ); + + private static ConverterFactory SHORT = new ConverterFactory( ShortConverter.class ); + + private static ConverterFactory BOOLEAN = new ConverterFactory( BooleanConverter.class ); + + private static ConverterFactory getConverterFactory( RealType< ? > inputType, RealType< ? > outputType ) + { + if ( inputType.getClass().equals( outputType.getClass() ) ) + return TYPE_IDENTITY; + if ( ( inputType instanceof IntegerType ) && ( outputType instanceof IntegerType ) ) + return integerConverterFactory( inputType, outputType ); + return floatingPointConverterFactory( inputType, outputType ); + } + + private static ConverterFactory floatingPointConverterFactory( RealType< ? > inputType, RealType< ? > outputType ) + { + if ( ( inputType instanceof FloatType ) || ( outputType instanceof FloatType ) ) + return FLOAT; + return DOUBLE; + } + + private static ConverterFactory integerConverterFactory( RealType< ? > inputType, RealType< ? > outputType ) + { + if ( inputType instanceof LongType || outputType instanceof LongType ) + return LONG; + if ( inputType instanceof IntType || outputType instanceof IntType ) + return INTEGER; + if ( inputType instanceof GenericShortType && outputType instanceof GenericShortType ) + return SHORT; + if ( inputType instanceof GenericByteType && outputType instanceof GenericByteType ) + return BYTE; + if ( inputType instanceof BooleanType && outputType instanceof BooleanType ) + return BOOLEAN; + if ( inputType instanceof GenericIntType && smallerThanInt( outputType ) || + smallerThanInt( inputType ) && outputType instanceof GenericIntType ) + return INTEGER; + return LONG; + } + + static boolean smallerThanInt( RealType< ? > variable ) + { + // This method is package-private to allow testing. + final boolean lowerBound = variable.getMinValue() >= Integer.MIN_VALUE; + final boolean upperBound = variable.getMaxValue() <= Integer.MAX_VALUE; + return lowerBound && upperBound; + } + + /** + * The class {@link ConverterFactory} encapsulates the {@link ClassCopyProvider} + * that is used to create a {@link Converter}. + */ + private static class ConverterFactory + { + private final ClassCopyProvider< Converter > provider; + + private ConverterFactory( Class< ? extends Converter > converterClass ) + { + this.provider = new ClassCopyProvider<>( converterClass, Converter.class ); + } + + public Converter create( RealType< ? > in, RealType< ? > out ) + { + return provider.newInstanceForKey( Arrays.asList( in.getClass(), out.getClass() ) ); + } + } + + /** Converts via {@link RealType#getRealDouble()}. */ + public static class DoubleConverter implements Converter< RealType< ? >, RealType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( RealType< ? > input, RealType< ? > output ) + { + output.setReal( input.getRealDouble() ); + } + } + + /** Converts via {@link RealType#getRealFloat()}. */ + public static class FloatConverter implements Converter< RealType< ? >, RealType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( RealType< ? > input, RealType< ? > output ) + { + output.setReal( input.getRealFloat() ); + } + } + + /** Converts via {@link IntegerType#getInteger()}. */ + public static class IntegerConverter implements Converter< IntegerType< ? >, IntegerType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( IntegerType< ? > input, IntegerType< ? > output ) + { + output.setInteger( input.getInteger() ); + } + } + + /** Converts via {@link IntegerType#getInteger()}. */ + public static class LongConverter implements Converter< IntegerType< ? >, IntegerType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( IntegerType< ? > input, IntegerType< ? > output ) + { + output.setInteger( input.getIntegerLong() ); + } + } + + /** Converts via {@link GenericByteType#getByte()}. */ + public static class ByteConverter implements Converter< GenericByteType< ? >, GenericByteType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( GenericByteType< ? > input, GenericByteType< ? > output ) + { + output.setByte( input.getByte() ); + } + } + + /** Converts void {@link GenericShortType#getShort()}. */ + public static class ShortConverter implements Converter< GenericShortType< ? >, GenericShortType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( GenericShortType< ? > input, GenericShortType< ? > output ) + { + output.setShort( input.getShort() ); + } + } + + /** Converts via {@link BooleanType#get()}. */ + public static class BooleanConverter implements Converter< BooleanType< ? >, BooleanType< ? > > + { + // This class is public, in order that it can be used with ClassCopyProvider. + @Override + public void convert( BooleanType< ? > input, BooleanType< ? > output ) + { + output.set( input.get() ); + } + } +} diff --git a/src/main/java/net/imglib2/converter/RealTypeConverters.java b/src/main/java/net/imglib2/converter/RealTypeConverters.java new file mode 100644 index 0000000000..10ffeb2780 --- /dev/null +++ b/src/main/java/net/imglib2/converter/RealTypeConverters.java @@ -0,0 +1,130 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2018 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.converter; + +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.loops.LoopBuilder; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.ByteType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.LongType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; + +public final class RealTypeConverters +{ + + private RealTypeConverters() + { + // prevent from instantiation + } + + /** + * Copy the image content from a source image to a destination image. + * The area to be copied is defined by the destination. + *

+ * Both images need to be of type {@link RealType}. So for example + * {@link UnsignedByteType}, {@link IntType}, {@link FloatType} + * and many more types are supported. A type conversion is done + * if needed. + * + * @param source Image that is source of the copy operation. + * @param destination Image that is destination of the copy operation. + */ + public static void copyFromTo( + RandomAccessible< ? extends RealType< ? > > source, + RandomAccessibleInterval< ? extends RealType< ? > > destination + ) + { + IntervalView< ? extends RealType< ? > > sourceInterval = Views.interval( source, destination ); + RealType< ? > s = Util.getTypeFromInterval( sourceInterval ); + RealType< ? > d = Util.getTypeFromInterval( destination ); + Converter< RealType< ? >, RealType< ? > > copy = getConverter( s, d ); + LoopBuilder.setImages( sourceInterval, destination ).forEachPixel( copy::convert ); + } + + /** + * Convert the pixel type of the given image to a given output pixel type. + *

+ * Example, convert and image from {@link UnsignedByteType} to {@link IntType}: + *

+	 * {@code
+	 *
+	 * RandomAccessibleInterval image = ...;
+	 * RandomAccessibleInterval intImage =
+	 *     RealTypeConverters.convert( image, new IntType() );
+	 * }
+	 * 
+ * + * The conversion is done on-the-fly when a pixel value is red. + * @param image image to convert + * @param pixelType pixel type of the result image + */ + public static > RandomAccessibleInterval convert( + RandomAccessibleInterval> image, + T pixelType ) + { + RealType in = Util.getTypeFromInterval( image ); + Converter< RealType, T > converter = getConverter( in, pixelType ); + return Converters.convert( image, converter, pixelType ); + } + + /** + * Returns a converter that converts from input to output type. + *

+ * To get a converter from {@link UnsignedByteType} to {@link LongType} use: + *

+ * {@code + * Converter< UnsignedByteType, LongType > converter = + * RealConverters.getConverter(new UnsignedByteType(), new LongType()); + * } + *

+ * The functionality is equivalent to {@link RealDoubleConverter}. + * But the returned converter is faster and has higher numerical precision + * than {@link RealDoubleConverter}. + * This is because it's optimal for the given pair of types. + * A converter from {@link UnsignedByteType} to {@link ByteType} + * will for example copy the byte directly and never convert to double. + */ + public static < S extends RealType< ? >, T extends RealType< ? > > Converter< S, T > getConverter( S inputType, T outputType ) + { + return RealTypeConverterInternals.getConverter( inputType, outputType ); + } + +} diff --git a/src/main/java/net/imglib2/loops/ClassCopyProvider.java b/src/main/java/net/imglib2/loops/ClassCopyProvider.java index 4af3a2fb82..52ce411ac7 100644 --- a/src/main/java/net/imglib2/loops/ClassCopyProvider.java +++ b/src/main/java/net/imglib2/loops/ClassCopyProvider.java @@ -79,7 +79,7 @@ public ClassCopyProvider( final Class< ? extends T > clazz, final Class< T > int if ( constructors.length == 1 ) return constructors[ 0 ].getParameterTypes(); if ( constructors.length == 0 ) - throw new IllegalArgumentException( "ClassCopyProvider: Public constructor in class (" + clazz.getName() + ") needed." ); + throw new IllegalArgumentException( "ClassCopyProvider: Class and it's constructor need to be public (" + clazz.getName() + ")." ); throw new IllegalArgumentException( "ClassCopyProvider: Please specify constructor signature." ); } diff --git a/src/main/java/net/imglib2/type/numeric/integer/LongType.java b/src/main/java/net/imglib2/type/numeric/integer/LongType.java index 15b8abae96..d81edf4900 100644 --- a/src/main/java/net/imglib2/type/numeric/integer/LongType.java +++ b/src/main/java/net/imglib2/type/numeric/integer/LongType.java @@ -39,6 +39,7 @@ import net.imglib2.img.NativeImg; import net.imglib2.img.basictypeaccess.LongAccess; import net.imglib2.type.NativeTypeFactory; +import net.imglib2.util.Util; /** * TODO @@ -133,11 +134,17 @@ public void setBigInteger( final BigInteger b ) set( b.longValue() ); } + @Override + public void setReal( float real ) + { + set( Util.roundToLong( real ) ); + } + @Override public double getMaxValue() { return Long.MAX_VALUE; - } + } // imprecise @Override public double getMinValue() diff --git a/src/main/java/net/imglib2/type/numeric/integer/Unsigned128BitType.java b/src/main/java/net/imglib2/type/numeric/integer/Unsigned128BitType.java index c158f9ddc1..c4bc3d5764 100644 --- a/src/main/java/net/imglib2/type/numeric/integer/Unsigned128BitType.java +++ b/src/main/java/net/imglib2/type/numeric/integer/Unsigned128BitType.java @@ -205,6 +205,22 @@ public BigInteger getBigInteger() return get(); } + @Override + public float getRealFloat() + { + return (float) getRealDouble(); + } + + @Override + public double getRealDouble() + { + final int k = i * 2; + final long lower = dataAccess.getValue( k ); + final long upper = dataAccess.getValue( k + 1 ); + return UnsignedLongType.unsignedLongToDouble( lower ) + + UnsignedLongType.unsignedLongToDouble( upper ) * Math.pow( 2, 64 ); + } + @Override public void setInteger( final int value ) { @@ -227,6 +243,23 @@ public void setBigInteger( final BigInteger b ) set( b ); } + @Override + public void setReal( float real ) + { + setReal( (double) real ); + } + + @Override + public void setReal( double real ) + { + real = Math.floor( real + 0.5 ); + final double base = Math.pow( 2, 64 ); + double upper = Math.floor(real / base ); + double lower = real - base * upper; + set( UnsignedLongType.doubleToUnsignedLong( lower ), + UnsignedLongType.doubleToUnsignedLong( upper )); + } + /** * The maximum value that can be stored is {@code Math.pow(2, 128) -1}, * which cannot be represented with precision using a double @@ -463,4 +496,10 @@ public int hashCode() final int hash2 = Long.hashCode( dataAccess.getValue( k ) ); return Util.combineHash( hash1, hash2 ); } + + @Override + public String toString() + { + return getBigInteger().toString(); + } } diff --git a/src/main/java/net/imglib2/type/numeric/integer/UnsignedIntType.java b/src/main/java/net/imglib2/type/numeric/integer/UnsignedIntType.java index 1ed47678cf..9026e6026b 100644 --- a/src/main/java/net/imglib2/type/numeric/integer/UnsignedIntType.java +++ b/src/main/java/net/imglib2/type/numeric/integer/UnsignedIntType.java @@ -221,6 +221,12 @@ public void setBigInteger( final BigInteger b ) set( b.longValue() ); } + @Override + public void setReal( float real ) + { + set( Util.roundToLong( real ) ); + } + @Override public double getMaxValue() { diff --git a/src/main/java/net/imglib2/type/numeric/integer/UnsignedLongType.java b/src/main/java/net/imglib2/type/numeric/integer/UnsignedLongType.java index 547fbdc33d..fb479742ae 100644 --- a/src/main/java/net/imglib2/type/numeric/integer/UnsignedLongType.java +++ b/src/main/java/net/imglib2/type/numeric/integer/UnsignedLongType.java @@ -55,6 +55,8 @@ public class UnsignedLongType extends GenericLongType< UnsignedLongType > private static final double MAX_VALUE_PLUS_ONE = Math.pow( 2, 64 ); // not precise, because double is not sufficient + private static final double MAX_LONG_PLUS_ONE = Math.pow( 2, 63 ); // not precise, because double is not sufficient + // this is the constructor if you want it to read from an array public UnsignedLongType( final NativeImg< ?, ? extends LongAccess > img ) { @@ -207,7 +209,7 @@ public void dec() @Override public String toString() { - return "" + get(); + return getBigInteger().toString(); } /** @@ -265,6 +267,26 @@ public void setBigInteger( final BigInteger b ) set( b.longValue() ); } + @Override + public void setReal( double real ) + { + set( doubleToUnsignedLong( real ) ); + } + + static long doubleToUnsignedLong( double real ) + { + double value = real < MAX_LONG_PLUS_ONE ? + Math.max(0, real) : + Math.min(-1, real - MAX_VALUE_PLUS_ONE); + return Util.round( value ); + } + + @Override + public void setReal( float real ) + { + setReal( (double) real ); + } + public void set( final BigInteger bi ) { set( bi.longValue() ); @@ -336,7 +358,11 @@ public float getRealFloat() @Override public double getRealDouble() { - long l = get(); + return unsignedLongToDouble( get() ); + } + + static double unsignedLongToDouble( long l ) + { return l >= 0 ? l : (MAX_VALUE_PLUS_ONE + l); } @@ -345,4 +371,5 @@ public int compareTo( final UnsignedLongType other ) { return Long.compareUnsigned( get(), other.get() ); } + } diff --git a/src/main/java/net/imglib2/util/Util.java b/src/main/java/net/imglib2/util/Util.java index 312f731e15..4d5cdbaa89 100644 --- a/src/main/java/net/imglib2/util/Util.java +++ b/src/main/java/net/imglib2/util/Util.java @@ -500,14 +500,35 @@ else if ( a + threshold > b && a - threshold < b ) public static int round( final float value ) { - return ( int ) ( value + ( 0.5f * Math.signum( value ) ) ); + return roundToInt( value ); } public static long round( final double value ) + { + return roundToLong( value ); + } + + public static int roundToInt( final float value ) + { + return ( int ) ( value + ( 0.5f * Math.signum( value ) ) ); + } + + public static int roundToInt( final double value ) + { + return ( int ) ( value + ( 0.5d * Math.signum( value ) ) ); + } + + public static long roundToLong( final float value ) + { + return ( long ) ( value + ( 0.5f * Math.signum( value ) ) ); + } + + public static long roundToLong( final double value ) { return ( long ) ( value + ( 0.5d * Math.signum( value ) ) ); } + /** * This method creates a gaussian kernel * diff --git a/src/test/java/net/imglib2/converter/RealTypeConverterInternalsTest.java b/src/test/java/net/imglib2/converter/RealTypeConverterInternalsTest.java new file mode 100644 index 0000000000..5b3e6f9603 --- /dev/null +++ b/src/test/java/net/imglib2/converter/RealTypeConverterInternalsTest.java @@ -0,0 +1,204 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2018 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.converter; + +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.test.ImgLib2Assert; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.logic.BoolType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.ByteType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.LongType; +import net.imglib2.type.numeric.integer.ShortType; +import net.imglib2.type.numeric.integer.Unsigned128BitType; +import net.imglib2.type.numeric.integer.Unsigned12BitType; +import net.imglib2.type.numeric.integer.Unsigned2BitType; +import net.imglib2.type.numeric.integer.Unsigned4BitType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.integer.UnsignedIntType; +import net.imglib2.type.numeric.integer.UnsignedLongType; +import net.imglib2.type.numeric.integer.UnsignedShortType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.type.numeric.real.FloatType; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test {@link RealTypeConverterInternals}. + * + * @author Matthias Arzt + */ +public class RealTypeConverterInternalsTest +{ + public static final List< RealType< ? > > TYPES = Arrays.asList( + new BitType(), + new BoolType(), + new ByteType(), + new ShortType(), + new IntType(), + new LongType(), + new Unsigned2BitType(), + new Unsigned4BitType(), + new Unsigned12BitType(), + new UnsignedByteType(), + new UnsignedShortType(), + new UnsignedIntType(), + new UnsignedLongType(), + new Unsigned128BitType(), + new FloatType(), + new DoubleType() + ); + + private static final float HIGHEST_INTEGER_CORRECTLY_REPRESENTED_BY_FLOAT = ( float ) Math.pow( 2, 24 ); + + private static final float FLOAT_PRECISION = ( float ) Math.pow( 2.0, -24 ); + + @Test + public void testConvertUnsignedByteToFloatType() + { + UnsignedByteType in = new UnsignedByteType( 42 ); + FloatType out = new FloatType(); + Converter< UnsignedByteType, FloatType > converter = RealTypeConverters.getConverter( in, out ); + converter.convert( in, out ); + assertEquals( 42, out.getRealFloat(), 0 ); + } + + @Test + public void testConvertImportantValuesBetweenAllTypes() + { + for ( RealType< ? > in : TYPES ) + for ( RealType< ? > out : TYPES ) + { + Converter< RealType< ? >, RealType< ? > > converter = RealTypeConverters.getConverter( in, out ); + testMinValueConversion( converter, in.createVariable(), out.createVariable() ); + testMaxValueConversion( converter, in.createVariable(), out.createVariable() ); + testValueConversion( 0.0, converter, in, out ); + testValueConversion( 1.0, converter, in, out ); + } + } + + private void testMinValueConversion( Converter< RealType< ? >, RealType< ? > > converter, RealType< ? > in, RealType< ? > out ) + { + double value = Math.max( in.getMinValue(), out.getMinValue() ); + testValueConversion( value, converter, in, out ); + } + + private void testMaxValueConversion( Converter< RealType< ? >, RealType< ? > > converter, RealType< ? > in, RealType< ? > out ) + { + final double inMaxValue = in.getMaxValue(); + final double outMaxValue = out.getMaxValue(); + double value = decreaseMaxValue( Math.min( inMaxValue, outMaxValue ) ); + testValueConversion( value, converter, in, out ); + } + + private double decreaseMaxValue( double value ) + { + // NB: If an integer value is two high it cannot be represented + // correctly by float. This tends to round up Integer.MAX_VALUE + // Long.MAX_VALUE ... etc. The consequence is an integer overflow. + // The value is decreased by the float precision, to compensate + // for the rounding up. + if ( value <= HIGHEST_INTEGER_CORRECTLY_REPRESENTED_BY_FLOAT ) + return value; + return value * ( 1.0 - FLOAT_PRECISION ); + } + + private void testValueConversion( double value, Converter< RealType< ? >, RealType< ? > > converter, RealType< ? > in, RealType< ? > out ) + { + in.setReal( value ); + converter.convert( in, out ); + double delta = value * FLOAT_PRECISION; + assertEquals( "Conversion of value: " + value + + " from: " + in.getClass().getSimpleName() + " (" + in + ")" + + " to: " + out.getClass().getSimpleName() + " (" + out + ")", + in.getRealDouble(), out.getRealDouble(), delta ); + } + + @Test + public void testSmallerThanInt() + { + assertTrue( RealTypeConverterInternals.smallerThanInt( new IntType() ) ); + assertFalse( RealTypeConverterInternals.smallerThanInt( new UnsignedIntType() ) ); + assertFalse( RealTypeConverterInternals.smallerThanInt( new UnsignedLongType() ) ); + assertFalse( RealTypeConverterInternals.smallerThanInt( new LongType() ) ); + assertFalse( RealTypeConverterInternals.smallerThanInt( new Unsigned128BitType() ) ); + } + + @Test + public void testTypeIdentityConverters() + { + for ( RealType< ? > type : TYPES ) + testConverterType( type.createVariable(), type.createVariable(), TypeIdentity.class ); + } + + @Test + public void testOptimalConverterTypes() + { + testConverterType( new BoolType(), new BitType(), RealTypeConverterInternals.BooleanConverter.class ); + testConverterType( new UnsignedByteType(), new ByteType(), RealTypeConverterInternals.ByteConverter.class ); + testConverterType( new UnsignedShortType(), new ShortType(), RealTypeConverterInternals.ShortConverter.class ); + testConverterType( new UnsignedIntType(), new IntType(), RealTypeConverterInternals.IntegerConverter.class ); + testConverterType( new UnsignedLongType(), new LongType(), RealTypeConverterInternals.LongConverter.class ); + testConverterType( new Unsigned12BitType(), new LongType(), RealTypeConverterInternals.LongConverter.class ); + testConverterType( new Unsigned12BitType(), new UnsignedLongType(), RealTypeConverterInternals.LongConverter.class ); + testConverterType( new Unsigned12BitType(), new IntType(), RealTypeConverterInternals.IntegerConverter.class ); + testConverterType( new Unsigned12BitType(), new UnsignedIntType(), RealTypeConverterInternals.IntegerConverter.class ); + testConverterType( new Unsigned12BitType(), new ShortType(), RealTypeConverterInternals.LongConverter.class ); + testConverterType( new BoolType(), new FloatType(), RealTypeConverterInternals.FloatConverter.class ); + testConverterType( new BoolType(), new IntType(), RealTypeConverterInternals.IntegerConverter.class ); + } + + @Test + public void testConvertRandomAccessibleInterval() { + RandomAccessibleInterval ints = ArrayImgs.ints( new int[] { 42 }, 1 ); + RandomAccessibleInterval bytes = RealTypeConverters.convert( ints, new UnsignedByteType() ); + RandomAccessibleInterval expected = ArrayImgs.unsignedBytes( new byte[] { 42 }, 1); + ImgLib2Assert.assertImageEquals( expected, bytes ); + } + + private void testConverterType( RealType< ? > inputType, RealType< ? > ouputType, Class< ? extends Converter > expected ) + { + Converter< ?, ? > converter = RealTypeConverters.getConverter( inputType, ouputType ); + assertEquals( expected.getName(), converter.getClass().getName() ); + } +} diff --git a/src/test/java/net/imglib2/converter/RealTypeConvertersBenchmark.java b/src/test/java/net/imglib2/converter/RealTypeConvertersBenchmark.java new file mode 100644 index 0000000000..55c4199f4e --- /dev/null +++ b/src/test/java/net/imglib2/converter/RealTypeConvertersBenchmark.java @@ -0,0 +1,117 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2018 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.converter; + +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedLongType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.type.numeric.real.FloatType; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Measure the time it takes to call the {@link Converter} + * returned by {@link RealTypeConverters#getConverter}. + *

+ * The measurement is done for all combinations of the types + * listed in {@link #TYPES}. + */ +@State( Scope.Benchmark ) +public class RealTypeConvertersBenchmark +{ + + private static final Map< String, RealType< ? > > TYPES = Stream.of( + new DoubleType(), + new FloatType(), + new IntType(), + new UnsignedLongType() + ).collect( Collectors.toMap( x -> x.getClass().getSimpleName(), v -> v ) ); + + private RealType< ? > in; + + private RealType< ? > out; + + private Converter< RealType< ? >, RealType< ? > > converter; + + @Param( { "DoubleType" } ) + public String srcType; + + @Param( { "DoubleType" } ) + public String targetType; + + @Setup( Level.Trial ) + public void setup() + { + in = TYPES.get( srcType ); + out = TYPES.get( targetType ); + converter = RealTypeConverters.getConverter( in, out ); + } + + @Benchmark + public void converter() + { + converter.convert( in, out ); + } + + public static void main( final String... args ) throws RunnerException + { + final String[] values = TYPES.keySet().toArray( new String[ 0 ] ); + final Options opt = new OptionsBuilder() + .include( RealTypeConvertersBenchmark.class.getSimpleName() ) + .forks( 1 ) + .warmupIterations( 4 ) + .measurementIterations( 8 ) + .warmupTime( TimeValue.milliseconds( 100 ) ) + .measurementTime( TimeValue.milliseconds( 100 ) ) + .param( "srcType", values ) + .param( "targetType", values ) + .build(); + new Runner( opt ).run(); + } +} diff --git a/src/test/java/net/imglib2/converter/RealTypeConvertersTest.java b/src/test/java/net/imglib2/converter/RealTypeConvertersTest.java new file mode 100644 index 0000000000..a2760b7237 --- /dev/null +++ b/src/test/java/net/imglib2/converter/RealTypeConvertersTest.java @@ -0,0 +1,89 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2018 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.converter; + +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.test.ImgLib2Assert; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.type.numeric.real.FloatType; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests {@link RealTypeConverters}. + * + * @author Matthias Arzt + */ +public class RealTypeConvertersTest +{ + + @Test + public void testGetConverter() { + UnsignedByteType input = new UnsignedByteType( 42 ); + DoubleType output = new DoubleType(); + Converter converter = RealTypeConverters.getConverter( input, output ); + converter.convert( input, output ); + assertEquals( 42, output.getRealDouble(), 0 ); + } + + @Test + public void testConvert() { + Img input = ArrayImgs.unsignedBytes( new byte[] { 42 }, 1 ); + RandomAccessibleInterval< FloatType > result = RealTypeConverters.convert( input, new FloatType() ); + ImgLib2Assert.assertImageEqualsRealType( input, result, 0 ); + } + + @Test + public void testCopy() + { + Img< UnsignedByteType > source = ArrayImgs.unsignedBytes( new byte[] { 42 }, 1, 1 ); + Img< UnsignedByteType > destination = ArrayImgs.unsignedBytes( 1, 1 ); + RealTypeConverters.copyFromTo( source, destination ); + ImgLib2Assert.assertImageEquals( source, destination ); + } + + @Test + public void testCopyWithTypeConversion() + { + RandomAccessibleInterval< UnsignedByteType > source = ArrayImgs.unsignedBytes( new byte[] { 1, 2, 3, 4 }, 2, 2 ); + RandomAccessibleInterval< DoubleType > destination = ArrayImgs.doubles( 2, 2 ); + RealTypeConverters.copyFromTo( source, destination ); + ImgLib2Assert.assertImageEqualsRealType( source, destination, 0 ); + } +} diff --git a/src/test/java/net/imglib2/type/numeric/integer/IntegerTypeTest.java b/src/test/java/net/imglib2/type/numeric/integer/IntegerTypeTest.java new file mode 100644 index 0000000000..9c90fdcced --- /dev/null +++ b/src/test/java/net/imglib2/type/numeric/integer/IntegerTypeTest.java @@ -0,0 +1,199 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2018 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.type.numeric.integer; + +import net.imglib2.type.logic.BitType; +import net.imglib2.type.logic.BoolType; +import net.imglib2.type.numeric.IntegerType; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; + +/** + * Test basic functionality of every NumericType implementation. + *

+ * It's a parameterized test, see Junit4 + * Parameterized Test. The test is executed for every type in the list + * {@link IntegerTypeTest#types}. + * + * @param + */ +@RunWith( Parameterized.class ) +public class IntegerTypeTest< T extends IntegerType< T > > +{ + private static final List< IntegerType< ? > > types = Arrays.asList( + new BitType(), + new BoolType(), + new ByteType(), + new IntType(), + new LongType(), + new ShortType(), + new UnsignedByteType(), + new UnsignedIntType(), + new UnsignedLongType(), + new UnsignedShortType(), + new Unsigned128BitType(), + new Unsigned2BitType(), + new Unsigned4BitType(), + new Unsigned12BitType(), + new UnsignedVariableBitLengthType( 7 ) + ); + + private final T type; + + private final List< BigInteger > values; + + // NB: The class is parameterized with pairs of (className, numeric type) + // className is there for nicer error messages when a test fails. + @Parameterized.Parameters( name = "{0}" ) + public static Collection< Object > data() + { + return types.stream().map( + type -> new Object[] { type.getClass().getSimpleName(), type } + ).collect( Collectors.toList() ); + } + + public IntegerTypeTest( final String className, final T type ) + { + this.type = type; + this.values = initValues(); + } + + /** + * Return a list of positive powers of two, and negative powers of two. + * For example: 1, 2, 4, 8, -1, -2, -4, -8. + * But only contains numbers that are in the allow range between min + * value and max value. + */ + private List< BigInteger > initValues() + { + final List< BigInteger > values = new ArrayList<>(); + final boolean isSigned = type.getMinValue() < 0; + final int bits = type.getBitsPerPixel() - (isSigned ? 1 : 0); + + values.addAll( powersOfTwo( bits ) ); + if(isSigned) + values.addAll( negate( powersOfTwo( bits ) ) ); + + return values; + } + + /** Returns a list: 1, 2, 4, 8, ... , 2^(count - 1). */ + private List< BigInteger > powersOfTwo( int count ) { + BigInteger value = BigInteger.ONE; + List results = new ArrayList<>( count ); + for ( int i = 0; i < count; i++, value.multiply( BigInteger.valueOf( 2 ) ) ) + results.add( value ); + return results; + } + + /** Return a list that contains the negated values of the given list. */ + private List< BigInteger > negate( List input ) + { + return input.stream().map( BigInteger::negate ).collect(Collectors.toList()); + } + + + @Test + public void testGetAndSetBigInteger() { + T type = zero(); + BigInteger value = toBigInteger( type.getMaxValue() * 0.9 ); + type.setBigInteger( value ); + assertEquals( value, type.getBigInteger() ); + } + + @Test + public void testToString() { + for ( BigInteger value : values ) + { + final T variable = zero(); + variable.setBigInteger( value ); + assertEquals( value.toString(), value.toString() ); + } + } + + + @Test + public void testGetReal() { + for ( BigInteger value : values ) + { + final T variable = zero(); + variable.setBigInteger( value ); + assertEquals( value.doubleValue(), variable.getRealDouble(), 0); + assertEquals( value.floatValue(), variable.getRealFloat(), 0); + } + } + + @Test + public void testSetRealDouble() { + for ( BigInteger value : values ) + { + final T variable = zero(); + variable.setReal( value.doubleValue() ); + assertEquals( value.doubleValue(), variable.getRealDouble(), 0 ); + } + } + + @Test + public void testSetRealFloat() { + for ( BigInteger value : values ) + { + final T variable = zero(); + variable.setReal( value.floatValue() ); + assertEquals( value.floatValue(), variable.getRealFloat(), 0 ); + } + } + + private BigInteger toBigInteger( double v ) + { + return BigDecimal.valueOf( v ).toBigInteger(); + } + + private T zero() + { + return this.type.createVariable(); + } +} diff --git a/src/test/java/net/imglib2/type/numeric/integer/LongTypeTest.java b/src/test/java/net/imglib2/type/numeric/integer/LongTypeTest.java index f3f3797bef..cd2b6b7348 100644 --- a/src/test/java/net/imglib2/type/numeric/integer/LongTypeTest.java +++ b/src/test/java/net/imglib2/type/numeric/integer/LongTypeTest.java @@ -72,4 +72,32 @@ public void testSetBigInteger() { assertEquals( l.get(), 1093840120l ); } + @Test + public void testSetRealFloat() { + testSetRealFloat( 0.4f, 0 ); + testSetRealFloat( 0.6f, 1 ); + testSetRealFloat( -0.6f, -1 ); + testSetRealFloat( ( float ) Long.MAX_VALUE, Long.MAX_VALUE ); + testSetRealFloat( ( float ) Long.MIN_VALUE, Long.MIN_VALUE ); + } + + private void testSetRealFloat( float floatValue, long longValue ) + { + LongType type = new LongType(); + type.setReal( floatValue ); + assertEquals( longValue, type.getLong() ); + } + + @Test + public void testSetMinMaxValue() { + testSetRealDouble( new LongType().getMaxValue(), Long.MAX_VALUE ); + testSetRealDouble( new LongType().getMinValue(), Long.MIN_VALUE ); + } + + private void testSetRealDouble( double doubleValue, long longValue ) + { + LongType type = new LongType(); + type.setReal( doubleValue ); + assertEquals( longValue, type.getLong() ); + } } diff --git a/src/test/java/net/imglib2/type/numeric/integer/Unsigned128BitTypeTest.java b/src/test/java/net/imglib2/type/numeric/integer/Unsigned128BitTypeTest.java index 9b64bffc5c..81367e238e 100644 --- a/src/test/java/net/imglib2/type/numeric/integer/Unsigned128BitTypeTest.java +++ b/src/test/java/net/imglib2/type/numeric/integer/Unsigned128BitTypeTest.java @@ -163,4 +163,16 @@ public void testSetBigInteger() { assertEquals( l.get(), bi ); } + @Test + public void testGetRealDouble() { + assertEquals( 0.0, new Unsigned128BitType( 0, 0 ).getRealDouble(), 0.0 ); + assertEquals( Math.pow( 2, 128 ), new Unsigned128BitType( -1, -1 ).getRealDouble(), 0.0 ); + } + + @Test + public void testGetRealFloat() { + assertEquals( 0.0, new Unsigned128BitType( 0, 0 ).getRealFloat(), 0.0 ); + assertEquals( Math.pow( 2, 127 ), (float) Math.pow( 2, 127 ), 0.0 ); + assertEquals( Math.pow( 2, 127 ), new Unsigned128BitType(0, 1l << 63 ).getRealFloat(), 0.0 ); + } } diff --git a/src/test/java/net/imglib2/type/numeric/integer/UnsignedLongTypeTest.java b/src/test/java/net/imglib2/type/numeric/integer/UnsignedLongTypeTest.java index 3954e2cea7..558d54d853 100644 --- a/src/test/java/net/imglib2/type/numeric/integer/UnsignedLongTypeTest.java +++ b/src/test/java/net/imglib2/type/numeric/integer/UnsignedLongTypeTest.java @@ -196,4 +196,58 @@ public void testGetRealFloat() { final UnsignedLongType ul = new UnsignedLongType( -1 ); assertEquals( (float) ul.getMaxValue(), ul.getRealFloat(), 0.0f ); } + + @Test + public void testSetRealDouble() { + // simple values + testSetRealDouble( 0, 0 ); + testSetRealDouble( 42, 42 ); + // negative + testSetRealDouble( -1, 0 ); + // max unsigned long + testSetRealDouble( Math.pow( 2, 64 ) - 1, ( long ) -1 ); + // max long + 1 + testSetRealDouble( Math.pow( 2, 63 ), 1l << 63 ); + // value smaller than "max long + 1", that can be represented by double + testSetRealDouble( Math.pow( 2, 63 ) - Math.pow( 2, 10 ), (1l << 63) - (1l << 10) ); + // max unsigned long + 1 + testSetRealDouble( Math.pow( 2, 64 ) + 1, ( long ) -1 ); + } + + private void testSetRealDouble( double doubleValue, long longValue ) + { + final UnsignedLongType type = new UnsignedLongType(); + type.setReal( doubleValue ); + assertEquals( longValue, type.getLong() ); + } + + @Test + public void testSetMinMax() { + testSetRealDouble( new UnsignedLongType().getMaxValue(), -1l ); + testSetRealDouble( new UnsignedLongType().getMinValue(), 0 ); + } + + @Test + public void testSetRealFloat() { + // simple values + testSetRealFloat( 0, 0 ); + testSetRealFloat( 42, 42 ); + // negative + testSetRealFloat( -1, 0 ); + // max unsigned long + testSetRealFloat( Math.pow( 2, 64 ) - 1, ( long ) -1 ); + // max long + 1 + testSetRealFloat( Math.pow( 2, 63 ), 1l << 63 ); + // value smaller than "max long + 1", that can be represented by float + testSetRealFloat( Math.pow( 2, 63 ) - Math.pow( 2, 39 ), (1l << 63) - (1l << 39) ); + // max unsigned long + 1 + testSetRealFloat( Math.pow( 2, 64 ) + 1, ( long ) -1 ); + } + + private void testSetRealFloat( double realValue, long longValue ) + { + UnsignedLongType type = new UnsignedLongType(); + type.setReal((float) realValue ); + assertEquals( longValue, type.getLong() ); + } }