diff --git a/jar/EigenPSF_Extractor-0.0.3.jar b/jar/EigenPSF_Extractor-0.0.3.jar
index 4083cd65759b6ef0bd70c5e33d84096068dbaf6f..4dcdecfec910ea7a47f179f1007353aa081bbab1 100644
Binary files a/jar/EigenPSF_Extractor-0.0.3.jar and b/jar/EigenPSF_Extractor-0.0.3.jar differ
diff --git a/javaworkspace/EigenPSF_Extractor/.classpath b/javaworkspace/EigenPSF_Extractor/.classpath
index f96c12c9b6b688792fde90370f4f7f8ef161608f..e6a9ad7c578e466c088a1d0c063c38ccabe906e9 100644
--- a/javaworkspace/EigenPSF_Extractor/.classpath
+++ b/javaworkspace/EigenPSF_Extractor/.classpath
@@ -2,7 +2,6 @@
 <classpath>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="lib" path="lib/bilib-commons.jar"/>
 	<classpathentry kind="lib" path="lib/ij.jar"/>
 	<classpathentry kind="lib" path="lib/jblas-1.2.5.jar"/>
 	<classpathentry kind="lib" path="lib/JTransforms-3.1-with-dependencies.jar"/>
diff --git a/javaworkspace/EigenPSF_Extractor/.project b/javaworkspace/EigenPSF_Extractor/.project
index 4f63843730f30413911ed7c961131300fcf73a5f..45fba7c793ac84e09188bb6962ad60407c59742c 100644
--- a/javaworkspace/EigenPSF_Extractor/.project
+++ b/javaworkspace/EigenPSF_Extractor/.project
@@ -40,11 +40,6 @@
 			<type>1</type>
 			<locationURI>PARENT-2-PROJECT_LOC/src/lib/JTransforms-3.1-with-dependencies.jar</locationURI>
 		</link>
-		<link>
-			<name>lib/bilib-commons.jar</name>
-			<type>1</type>
-			<locationURI>PARENT-2-PROJECT_LOC/src/lib/bilib-commons.jar</locationURI>
-		</link>
 		<link>
 			<name>lib/ij.jar</name>
 			<type>1</type>
@@ -85,6 +80,11 @@
 			<type>1</type>
 			<locationURI>PARENT-2-PROJECT_LOC/src/src/Unit_Tests.java</locationURI>
 		</link>
+		<link>
+			<name>src/bilib</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
 		<link>
 			<name>src/eigenpsf</name>
 			<type>2</type>
@@ -120,6 +120,16 @@
 			<type>1</type>
 			<locationURI>PARENT-2-PROJECT_LOC/src/src/RefineCodeCpp/libEigenPSF_Refine.so</locationURI>
 		</link>
+		<link>
+			<name>bin/bilib/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons.zip</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons.zip</locationURI>
+		</link>
 		<link>
 			<name>bin/resources/about.png</name>
 			<type>1</type>
@@ -255,6 +265,31 @@
 			<type>1</type>
 			<locationURI>PARENT-2-PROJECT_LOC/src/src/RefineCodeCpp/libEigenPSF_Refine.so</locationURI>
 		</link>
+		<link>
+			<name>src/bilib/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons.zip</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons.zip</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/fft</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/optimization</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
 		<link>
 			<name>src/eigenpsf/AdvancedSettingsPanel.java</name>
 			<type>1</type>
@@ -460,6 +495,81 @@
 			<type>1</type>
 			<locationURI>PARENT-2-PROJECT_LOC/src/src/resources/update.png</locationURI>
 		</link>
+		<link>
+			<name>bin/bilib/commons/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/optimization/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/components</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/fft</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/math</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/random</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/settings</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/table</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/utils</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/fft/AcademicFourierTransform.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/fft/AcademicFourierTransform.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/optimization/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/optimization/levenbergmarquardt</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
 		<link>
 			<name>src/eigenpsf/data/Convolution.java</name>
 			<type>1</type>
@@ -660,5 +770,385 @@
 			<type>1</type>
 			<locationURI>PARENT-2-PROJECT_LOC/src/src/eigenpsf/stack/ZStack.java</locationURI>
 		</link>
+		<link>
+			<name>bin/bilib/commons/buttons/about.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/about.png</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons/buttons/close.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/close.png</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons/buttons/help.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/help.png</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons/buttons/prefs.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/prefs.png</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons/buttons/run.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/run.png</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons/buttons/save.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/save.png</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons/buttons/snapshot.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/snapshot.png</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons/buttons/stop.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/stop.png</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons/job/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>bin/bilib/commons/math/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/math/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons/ButtonFactory.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/ButtonFactory.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons/about.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/about.png</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons/close.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/close.png</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons/help.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/help.png</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons/prefs.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/prefs.png</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons/run.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/run.png</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons/save.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/save.png</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons/snapshot.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/snapshot.png</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/buttons/stop.png</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/stop.png</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/components/BorderToggledButton.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/BorderToggledButton.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/components/DoubleScrollablePanel.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/DoubleScrollablePanel.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/components/GridPanel.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/GridPanel.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/components/GridToolbar.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/GridToolbar.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/components/HTMLPane.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/HTMLPane.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/components/SpinnerRangeDouble.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/SpinnerRangeDouble.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/components/SpinnerRangeFloat.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/SpinnerRangeFloat.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/components/SpinnerRangeInteger.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/SpinnerRangeInteger.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/fft/BasicFFT.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/fft/BasicFFT.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/ExecutionMode.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/ExecutionMode.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/JobAbstract.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/JobAbstract.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/JobEvent.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/JobEvent.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/MonitorAbstract.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorAbstract.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/MonitorConsole.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorConsole.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/MonitorProgressBar.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorProgressBar.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/MonitorTimedLog.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorTimedLog.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/MonitorTimedProgressBar.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorTimedProgressBar.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/PoolAbstract.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/PoolAbstract.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/callable</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/runnable</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/worker</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/math/.DS_Store</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/math/.DS_Store</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/math/bessel</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/math/windowing</name>
+			<type>2</type>
+			<locationURI>virtual:/virtual</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/random/Noise.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/Noise.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/random/NoiseExponential.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoiseExponential.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/random/NoiseGaussian.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoiseGaussian.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/random/NoisePoisson.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoisePoisson.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/random/NoiseRayleigh.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoiseRayleigh.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/random/NoiseUniform.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoiseUniform.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/settings/Settings.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/settings/Settings.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/settings/SettingsFileDialog.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/settings/SettingsFileDialog.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/table/CustomizedColumn.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/table/CustomizedColumn.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/table/CustomizedTable.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/table/CustomizedTable.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/utils/Chrono.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/Chrono.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/utils/Files.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/Files.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/utils/Log.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/Log.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/utils/NumFormat.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/NumFormat.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/utils/WebBrowser.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/WebBrowser.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/optimization/levenbergmarquardt/Cholesky.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/levenbergmarquardt/Cholesky.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/optimization/levenbergmarquardt/Function.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/levenbergmarquardt/Function.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/callable/CallableDemo.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/callable/CallableDemo.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/callable/Job.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/callable/Job.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/callable/Pool.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/callable/Pool.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/callable/PoolResponder.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/callable/PoolResponder.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/runnable/Job.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/runnable/Job.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/runnable/Pool.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/runnable/Pool.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/runnable/PoolResponder.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/runnable/PoolResponder.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/runnable/RunnableDemo.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/runnable/RunnableDemo.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/worker/Job.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/worker/Job.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/worker/Pool.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/worker/Pool.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/worker/PoolResponder.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/worker/PoolResponder.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/job/worker/WorkerDemo.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/worker/WorkerDemo.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/math/bessel/Bessel.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/math/bessel/Bessel.java</locationURI>
+		</link>
+		<link>
+			<name>src/bilib/commons/math/windowing/Windowing.java</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/math/windowing/Windowing.java</locationURI>
+		</link>
 	</linkedResources>
 </projectDescription>
diff --git a/javaworkspace/EigenPSF_Extractor/tt/BackgroundPatches.tif b/javaworkspace/EigenPSF_Extractor/tt/BackgroundPatches.tif
new file mode 100644
index 0000000000000000000000000000000000000000..e830a35cefe845cc6a460fc4aa1bf687d1d16e84
Binary files /dev/null and b/javaworkspace/EigenPSF_Extractor/tt/BackgroundPatches.tif differ
diff --git a/javaworkspace/EigenPSF_Extractor/tt/EigenPSF.tif b/javaworkspace/EigenPSF_Extractor/tt/EigenPSF.tif
new file mode 100644
index 0000000000000000000000000000000000000000..29282da5bc828418a5917dea51a7c2c905584085
Binary files /dev/null and b/javaworkspace/EigenPSF_Extractor/tt/EigenPSF.tif differ
diff --git a/javaworkspace/EigenPSF_Extractor/tt/ProcessedPatches.tif b/javaworkspace/EigenPSF_Extractor/tt/ProcessedPatches.tif
new file mode 100644
index 0000000000000000000000000000000000000000..5619a00edb29f1d5e30c64013936ff41d0379ad3
Binary files /dev/null and b/javaworkspace/EigenPSF_Extractor/tt/ProcessedPatches.tif differ
diff --git a/javaworkspace/EigenPSF_Extractor/tt/ProjectedPatches.tif b/javaworkspace/EigenPSF_Extractor/tt/ProjectedPatches.tif
new file mode 100644
index 0000000000000000000000000000000000000000..2bde6e0b9407e602960761d7cac5c7cc00358d89
Binary files /dev/null and b/javaworkspace/EigenPSF_Extractor/tt/ProjectedPatches.tif differ
diff --git a/javaworkspace/EigenPSF_Extractor/tt/RawPatches.tif b/javaworkspace/EigenPSF_Extractor/tt/RawPatches.tif
new file mode 100644
index 0000000000000000000000000000000000000000..f863fb9f8ce0a70d6e1115484b83889ed794a530
Binary files /dev/null and b/javaworkspace/EigenPSF_Extractor/tt/RawPatches.tif differ
diff --git a/javaworkspace/EigenPSF_Extractor/tt/config.csv b/javaworkspace/EigenPSF_Extractor/tt/config.csv
new file mode 100644
index 0000000000000000000000000000000000000000..fb61b8a244432a346f32a7947cda6ac104e39238
--- /dev/null
+++ b/javaworkspace/EigenPSF_Extractor/tt/config.csv
@@ -0,0 +1,18 @@
+detectionMethod, SIFT
+backgroundMethod, Polynomial Fit
+registrationMethod, Scale Space
+normalize, l2
+dim, 2
+Nscale, 3
+NbasisVectSIFT, 3
+NeigenElements, 1
+nthin, 7
+scaleBack, 4
+factorDiamBack, 2.0
+thresholdDetection, 0.5
+quantile, 2.0
+smin, 0.5
+smax, 1.5
+psf_visible_width_0, 17
+psf_visible_width_1, 17
+psf_visible_width_2, 1
diff --git a/javaworkspace/EigenPSF_Extractor/tt/listfiles.csv b/javaworkspace/EigenPSF_Extractor/tt/listfiles.csv
new file mode 100644
index 0000000000000000000000000000000000000000..2c705dff213cc5acbb264baa8b168174d2ee70ec
--- /dev/null
+++ b/javaworkspace/EigenPSF_Extractor/tt/listfiles.csv
@@ -0,0 +1,2 @@
+Name,Beads,Show,Mask,
+1,2D_widefield_astig-1.tif,/home/esoubies/Bureau/GitHub/eigenpsf-extractor/demo/2D_widefield_astig-1.tif,
diff --git a/javaworkspace/EigenPSF_Extractor/tt/patchesTable.csv b/javaworkspace/EigenPSF_Extractor/tt/patchesTable.csv
new file mode 100644
index 0000000000000000000000000000000000000000..a7a581fad12511bb4580a33afe48905410e2cd7a
--- /dev/null
+++ b/javaworkspace/EigenPSF_Extractor/tt/patchesTable.csv
@@ -0,0 +1,60 @@
+Image name 	 ID 	 X 	 Y 	 Z 	 Quality 	 Dist 	 Max 	 Valid  
+2D_widefield_astig-1.tif	 52	 231	 334	 0	 2.6368922825871293	 138.74076545846214	 8,161E+03	 
+2D_widefield_astig-1.tif	 40	 289	 183	 0	 1.9621766979946318	 92.39588735436226	 1,104E+04	 Ok
+2D_widefield_astig-1.tif	 57	 101	 162	 0	 3.4731905008960595	 104.06248123122954	 9,983E+03	 
+2D_widefield_astig-1.tif	 34	 60	 207	 0	 1.3833614005738457	 138.2931668593933	 5,789E+03	 Ok
+2D_widefield_astig-1.tif	 36	 376	 248	 0	 1.639697072655175	 184.6212338816963	 6,704E+03	 Ok
+2D_widefield_astig-1.tif	 13	 315	 222	 0	 0.9588205913708715	 119.4361754243663	 8,999E+03	 Ok
+2D_widefield_astig-1.tif	 42	 110	 263	 0	 2.051581162503771	 108.60018416190647	 7,072E+03	 
+2D_widefield_astig-1.tif	 15	 169	 99	 0	 1.039431340797815	 104.12012293500234	 8,953E+03	 Ok
+2D_widefield_astig-1.tif	 39	 98	 333	 0	 1.9097765431261826	 168.6001186239203	 1,715E+04	 Ok
+2D_widefield_astig-1.tif	 50	 117	 201	 0	 2.476141207912426	 82.02438661763951	 2,001E+04	 
+2D_widefield_astig-1.tif	 49	 133	 218	 0	 2.43296303234266	 68.00735254367721	 1,296E+04	 
+2D_widefield_astig-1.tif	 48	 291	 375	 0	 2.1925531446676088	 200.83077453418338	 1,181E+04	 
+2D_widefield_astig-1.tif	 38	 111	 308	 0	 1.7730343406949798	 140.24621207005913	 7,977E+03	 Ok
+2D_widefield_astig-1.tif	 37	 141	 327	 0	 1.7044494201054463	 141.0319112825179	 8,899E+03	 Ok
+2D_widefield_astig-1.tif	 16	 133	 34	 0	 1.1020035515862272	 176.41145087550296	 6,234E+03	 Ok
+2D_widefield_astig-1.tif	 35	 373	 159	 0	 1.6114569779425758	 179.2930561957155	 8,358E+03	 Ok
+2D_widefield_astig-1.tif	 47	 224	 249	 0	 2.175836056596235	 58.137767414994535	 1,444E+04	 
+2D_widefield_astig-1.tif	 55	 193	 344	 0	 2.9265074368094908	 145.1240848377691	 9,201E+03	 
+2D_widefield_astig-1.tif	 43	 193	 173	 0	 2.1160033926768933	 26.476404589747453	 1,273E+04	 
+2D_widefield_astig-1.tif	 22	 152	 305	 0	 1.1690761198439292	 116.46887996370532	 1,775E+04	 Ok
+2D_widefield_astig-1.tif	 21	 232	 290	 0	 1.1684242470791302	 98.08159868191383	 1,104E+04	 Ok
+2D_widefield_astig-1.tif	 53	 322	 242	 0	 2.7845849677774575	 132.18925826253812	 8,761E+03	 
+2D_widefield_astig-1.tif	 46	 220	 307	 0	 2.1602337204229864	 111.9866063420086	 1,174E+04	 
+2D_widefield_astig-1.tif	 7	 61	 257	 0	 0.898166793749632	 149.164338901763	 6,674E+03	 Ok
+2D_widefield_astig-1.tif	 10	 272	 179	 0	 0.9308440447335254	 76.6550715869472	 3,827E+04	 Ok
+2D_widefield_astig-1.tif	 2	 372	 202	 0	 0.8365680637800508	 174.04597093871493	 7,782E+03	 Ok
+2D_widefield_astig-1.tif	 14	 90	 210	 0	 0.9607983531660049	 108.664621657649	 1,720E+04	 Ok
+2D_widefield_astig-1.tif	 51	 308	 256	 0	 2.602329097485422	 123.47064428438041	 1,146E+04	 
+2D_widefield_astig-1.tif	 11	 315	 190	 0	 0.9396605727284989	 117.27318534089538	 2,809E+04	 Ok
+2D_widefield_astig-1.tif	 9	 252	 192	 0	 0.9093135207899893	 53.46026561849464	 2,802E+04	 Ok
+2D_widefield_astig-1.tif	 27	 108	 229	 0	 1.2895707808526016	 95.1892851112981	 8,811E+03	 Ok
+2D_widefield_astig-1.tif	 17	 180	 321	 0	 1.121353980827542	 124.31009613060397	 2,224E+04	 Ok
+2D_widefield_astig-1.tif	 29	 200	 156	 0	 1.3195480677918965	 42.04759208325728	 1,071E+04	 Ok
+2D_widefield_astig-1.tif	 56	 184	 292	 0	 3.2042571166320943	 94.89467845985885	 1,406E+04	 
+2D_widefield_astig-1.tif	 41	 367	 115	 0	 2.0246417215407626	 186.9438418349211	 7,738E+03	 
+2D_widefield_astig-1.tif	 24	 89	 308	 0	 1.2037193785876887	 155.56349186104046	 1,193E+04	 Ok
+2D_widefield_astig-1.tif	 1	 356	 167	 0	 0.7821929662752916	 160.0312469488381	 1,100E+04	 Ok
+2D_widefield_astig-1.tif	 59	 263	 296	 0	 4.72015806113066	 116.50321883965266	 1,144E+04	 
+2D_widefield_astig-1.tif	 3	 257	 89	 0	 0.8522766100250591	 123.94353553130554	 1,033E+04	 Ok
+2D_widefield_astig-1.tif	 44	 163	 326	 0	 2.1181977831426106	 131.73458164050928	 9,948E+03	 
+2D_widefield_astig-1.tif	 12	 263	 8	 0	 0.9454884362991656	 200.48940121612415	 8,717E+03	 Ok
+2D_widefield_astig-1.tif	 18	 256	 210	 0	 1.1327329132058517	 59.22837157984339	 1,164E+04	 Ok
+2D_widefield_astig-1.tif	 28	 283	 272	 0	 1.3065621529883933	 112.69871339105873	 8,977E+03	 Ok
+2D_widefield_astig-1.tif	 45	 251	 331	 0	 2.1194805664581393	 144.10065926289164	 8,908E+03	 
+2D_widefield_astig-1.tif	 54	 257	 271	 0	 2.8405014851743613	 93.08598175880189	 2,937E+04	 
+2D_widefield_astig-1.tif	 20	 145	 238	 0	 1.151344362258985	 65.80273550544841	 2,230E+04	 Ok
+2D_widefield_astig-1.tif	 30	 276	 307	 0	 1.3200147695348847	 133.2216198670471	 1,042E+04	 Ok
+2D_widefield_astig-1.tif	 58	 165	 197	 0	 4.009037772596921	 31.016124838541646	 2,136E+04	 
+2D_widefield_astig-1.tif	 33	 298	 241	 0	 1.3808148976687682	 108.85311203635843	 1,323E+04	 Ok
+2D_widefield_astig-1.tif	 31	 276	 210	 0	 1.3501578905120262	 78.91767862779544	 1,517E+04	 Ok
+2D_widefield_astig-1.tif	 25	 352	 250	 0	 1.2414301893684534	 162.22515218054195	 2,044E+04	 Ok
+2D_widefield_astig-1.tif	 32	 162	 356	 0	 1.3788240341940585	 162.04937519163718	 1,149E+04	 Ok
+2D_widefield_astig-1.tif	 8	 146	 124	 0	 0.9082565892758712	 90.44335243676011	 8,439E+03	 Ok
+2D_widefield_astig-1.tif	 4	 170	 121	 0	 0.8702071389742275	 82.87339742040264	 4,710E+04	 Ok
+2D_widefield_astig-1.tif	 5	 210	 191	 0	 0.8757715793006416	 13.892443989449804	 4,780E+04	 Ok
+2D_widefield_astig-1.tif	 26	 252	 240	 0	 1.2811400940397435	 68.41052550594829	 2,894E+04	 Ok
+2D_widefield_astig-1.tif	 6	 349	 119	 0	 0.8867731113538374	 170.88300090997933	 1,735E+04	 Ok
+2D_widefield_astig-1.tif	 23	 263	 160	 0	 1.1942832656560562	 75.29276193632427	 2,333E+04	 Ok
+2D_widefield_astig-1.tif	 19	 197	 96	 0	 1.1511892666591277	 102.00490184299969	 1,702E+04	 Ok
diff --git a/src/bilib/src/.DS_Store b/src/bilib/src/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..6b9fe70d098578936fb60f7f68699ac925a87938
Binary files /dev/null and b/src/bilib/src/.DS_Store differ
diff --git a/src/bilib/src/additionaluserinterface.zip b/src/bilib/src/additionaluserinterface.zip
new file mode 100644
index 0000000000000000000000000000000000000000..c0afaeb8e9bb683213def10a9810079f6f03a981
Binary files /dev/null and b/src/bilib/src/additionaluserinterface.zip differ
diff --git a/src/bilib/src/additionaluserinterface/Chrono.java b/src/bilib/src/additionaluserinterface/Chrono.java
new file mode 100644
index 0000000000000000000000000000000000000000..178ea58447c9297792f88a3ffb468b2461d3d02c
--- /dev/null
+++ b/src/bilib/src/additionaluserinterface/Chrono.java
@@ -0,0 +1,54 @@
+package additionaluserinterface;
+
+import java.text.DecimalFormat;
+
+/**
+ * This class provides static methods to measures the elapsed time. It is a
+ * equivalent to the function tic and toc of Matlab.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class Chrono {
+
+	static private double	chrono	= 0;
+
+	/**
+	 * Register the current time.
+	 */
+	public static void tic() {
+		chrono = System.currentTimeMillis();
+	}
+
+	/**
+	 * Returns a string that indicates the elapsed time since the last tic()
+	 * call.
+	 */
+	public static String toc() {
+		return toc("");
+	}
+
+	/**
+	 * Returns a string that indicates the elapsed time since the last tic()
+	 * call.
+	 * 
+	 * @param msg
+	 *            message to print
+	 */
+	public static String toc(String msg) {
+		double te = System.currentTimeMillis() - chrono;
+		String s = msg + " ";
+		DecimalFormat df = new DecimalFormat("####.##");
+		if (te < 3000.0)
+			return s + df.format(te) + " ms";
+		te /= 1000;
+		if (te < 600.1)
+			return s + df.format(te) + " s";
+		te /= 60;
+		if (te < 240.1)
+			return s + df.format(te) + " min.";
+		te /= 24;
+		return s + df.format(te) + " h.";
+	}
+
+}
diff --git a/src/bilib/src/additionaluserinterface/GridPanel.java b/src/bilib/src/additionaluserinterface/GridPanel.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fa3fe0164c824e4b038c3419e072fc5aa3f58de
--- /dev/null
+++ b/src/bilib/src/additionaluserinterface/GridPanel.java
@@ -0,0 +1,128 @@
+package additionaluserinterface;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+/**
+ * This class extends the JPanel to create grid panel given the possibility to
+ * place Java compoments in an organized manner in the dialog box.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class GridPanel extends JPanel {
+
+	private GridBagLayout		layout			= new GridBagLayout();
+	private GridBagConstraints	constraint		= new GridBagConstraints();
+	private int					defaultSpace	= 3;
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel() {
+		super();
+		setLayout(layout);
+		setBorder(BorderFactory.createEtchedBorder());
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(int defaultSpace) {
+		super();
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createEtchedBorder());
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(boolean border) {
+		super();
+		setLayout(layout);
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(String title) {
+		super();
+		setLayout(layout);
+		setBorder(BorderFactory.createTitledBorder(title));
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(boolean border, int defaultSpace) {
+		super();
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(String title, int defaultSpace) {
+		super();
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createTitledBorder(title));
+	}
+
+	/**
+	 * Specify the defaultSpace.
+	 */
+	public void setSpace(int defaultSpace) {
+		this.defaultSpace = defaultSpace;
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, JComponent comp) {
+		place(row, col, 1, 1, defaultSpace, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int space, JComponent comp) {
+		place(row, col, 1, 1, space, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, JComponent comp) {
+		place(row, col, width, height, defaultSpace, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, int space, JComponent comp) {
+		constraint.gridx = col;
+		constraint.gridy = row;
+		constraint.gridwidth = width;
+		constraint.gridheight = height;
+		constraint.anchor = GridBagConstraints.NORTHWEST;
+		constraint.insets = new Insets(space, space, space, space);
+		constraint.fill = GridBagConstraints.HORIZONTAL;
+		layout.setConstraints(comp, constraint);
+		add(comp);
+	}
+
+}
diff --git a/src/bilib/src/additionaluserinterface/GridToolbar.java b/src/bilib/src/additionaluserinterface/GridToolbar.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2066f261ece7fe5e99a2560bbfa64a99da8a09d
--- /dev/null
+++ b/src/bilib/src/additionaluserinterface/GridToolbar.java
@@ -0,0 +1,191 @@
+package additionaluserinterface;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JToolBar;
+
+/**
+ * This class extends the JToolbar to create grid panel given the possibility to
+ * place Java compoments in an organized manner in the dialog box.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class GridToolbar extends JToolBar {
+
+	private GridBagLayout		layout			= new GridBagLayout();
+	private GridBagConstraints	constraint		= new GridBagConstraints();
+	private int					defaultSpace	= 3;
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar() {
+		super("Control");
+		setLayout(layout);
+		setBorder(BorderFactory.createEtchedBorder());
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(boolean border) {
+		super("Control");
+		setLayout(layout);
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(String title) {
+		super(title);
+		setLayout(layout);
+		setBorder(BorderFactory.createTitledBorder(title));
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(int defaultSpace) {
+		super("Control");
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createEtchedBorder());
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(boolean border, int defaultSpace) {
+		super("Control");
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(boolean border, int defaultSpace, boolean floatable) {
+		super("Control");
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(boolean border, boolean floatable) {
+		super("Control");
+		setLayout(layout);
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(String title, boolean floatable) {
+		super(title);
+		setLayout(layout);
+		setBorder(BorderFactory.createTitledBorder(title));
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(int defaultSpace, boolean floatable) {
+		super("Control");
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createEtchedBorder());
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(String title, int defaultSpace) {
+		super(title);
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createTitledBorder(title));
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(String title, int defaultSpace, boolean floatable) {
+		super(title);
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createTitledBorder(title));
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Specify the defaultSpace.
+	 */
+	public void setSpace(int defaultSpace) {
+		this.defaultSpace = defaultSpace;
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, JComponent comp) {
+		place(row, col, 1, 1, defaultSpace, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int space, JComponent comp) {
+		place(row, col, 1, 1, space, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, JComponent comp) {
+		place(row, col, width, height, defaultSpace, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, int space, JComponent comp) {
+		constraint.gridx = col;
+		constraint.gridy = row;
+		constraint.gridwidth = width;
+		constraint.gridheight = height;
+		constraint.anchor = GridBagConstraints.NORTHWEST;
+		constraint.insets = new Insets(space, space, space, space);
+		constraint.fill = GridBagConstraints.HORIZONTAL;
+		layout.setConstraints(comp, constraint);
+		add(comp);
+	}
+
+}
diff --git a/src/bilib/src/additionaluserinterface/NumericTable.java b/src/bilib/src/additionaluserinterface/NumericTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c8a3b922ddbfff7f115668679a13c74778dfa89
--- /dev/null
+++ b/src/bilib/src/additionaluserinterface/NumericTable.java
@@ -0,0 +1,82 @@
+package additionaluserinterface;
+
+import java.awt.Dimension;
+import java.awt.Point;
+import java.text.DecimalFormat;
+
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableColumn;
+
+/**
+ * This class extends JFrame and draw a simple table. All values are in 2D
+ * double arrays
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+
+public class NumericTable extends JFrame {
+
+	private JTable				table;
+	private DefaultTableModel	model;
+
+	public NumericTable(String title, String[] headings, Dimension dim) {
+		super(title);
+		setMinimumSize(dim);
+		setSize(dim);
+		setPreferredSize(dim);
+
+		JScrollPane pane = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		model = new DefaultTableModel();
+		table = new JTable(model);
+		for (int i = 0; i < headings.length; i++) {
+			model.addColumn(headings[i]);
+		}
+
+		table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+		pane.getViewport().add(table, null);
+		add(pane);
+	}
+
+	public void setData(double data[][]) {
+		int nrow = data.length;
+		int ncol = data[0].length;
+		String s[] = new String[ncol];
+		for (int r = 0; r < nrow; r++) {
+			for (int c = 0; c < ncol; c++)
+				s[c] = "" + data[r][c];
+			model.addRow(s);
+		}
+
+	}
+
+	public void setData(double data[][], String[] formats) {
+		int nrow = data.length;
+		int ncol = data[0].length;
+		String s[] = new String[ncol];
+		for (int r = 0; r < nrow; r++) {
+			for (int c = 0; c < ncol; c++)
+				s[c] = (new DecimalFormat(formats[c])).format(data[r][c]);
+			model.addRow(s);
+		}
+
+	}
+
+	public void setColumnSize(int width[]) {
+		for (int i = 0; i < width.length; i++) {
+			TableColumn column = table.getColumnModel().getColumn(i);
+			column.setPreferredWidth(width[i]);
+		}
+	}
+
+	public void show(int posx, int posy) {
+		pack();
+		setLocation(new Point(posx, posy));
+		setVisible(true);
+	}
+
+}
diff --git a/src/bilib/src/additionaluserinterface/Settings.java b/src/bilib/src/additionaluserinterface/Settings.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b694fee5490cf6c95132097b41f2d908d77a16c
--- /dev/null
+++ b/src/bilib/src/additionaluserinterface/Settings.java
@@ -0,0 +1,479 @@
+package additionaluserinterface;
+
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Properties;
+import java.util.Vector;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JSlider;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.JToggleButton;
+import javax.swing.Timer;
+
+/**
+ * This class allows to store and load key-associated values in a text file. The
+ * class has methods to load and store single value linked to a string key
+ * describing the value. Futhermore, this class has methods to record a GUI
+ * component to a specified key. By this way this class allows to load and store
+ * all recorded items.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class Settings {
+
+	private String			filename;
+	private String			project;
+	private Vector<Item>	items;
+	private Properties		props;
+
+	/**
+	 * Constructors a Settings abject for a given project name and a given
+	 * filename.
+	 * 
+	 * @param project
+	 *            a string describing the project
+	 * @param filename
+	 *            a string give the full name of the file, including the path
+	 */
+	public Settings(String project, String filename) {
+		this.filename = filename;
+		this.project = project;
+		items = new Vector<Item>();
+		props = new Properties();
+	}
+
+	/**
+	 * Records a JTextField component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JTextField component, String defaultValue) {
+		Item item = new Item(key, component, defaultValue);
+		items.add(item);
+	}
+
+	/**
+	 * Records a JComboBox component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JComboBox component, String defaultValue) {
+		Item item = new Item(key, component, defaultValue);
+		items.add(item);
+	}
+
+	/**
+	 * Records a JSpinner component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JSpinner component, String defaultValue) {
+		Item item = new Item(key, component, defaultValue);
+		items.add(item);
+	}
+
+	/**
+	 * Records a JToggleButton component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JToggleButton component, boolean defaultValue) {
+		Item item = new Item(key, component, (defaultValue ? "on" : "off"));
+		items.add(item);
+	}
+
+	/**
+	 * Records a JCheckBox component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JCheckBox component, boolean defaultValue) {
+		Item item = new Item(key, component, (defaultValue ? "on" : "off"));
+		items.add(item);
+	}
+
+	/**
+	 * Records a JSlider component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a int value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JSlider component, String defaultValue) {
+		Item item = new Item(key, component, defaultValue);
+		items.add(item);
+	}
+
+	/**
+	 * Load an individual double value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param defaultValue
+	 *            the default value
+	 * @return the value get from the file
+	 */
+	public String loadValue(String key, String defaultValue) {
+		String s = "";
+		try {
+			FileInputStream in = new FileInputStream(filename);
+			props.load(in);
+			s = props.getProperty(key, "" + defaultValue);
+		}
+		catch (Exception e) {
+			s = defaultValue;
+		}
+		return s;
+	}
+
+	/**
+	 * Load an individual double value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param defaultValue
+	 *            the default value
+	 * @return the value get from the file
+	 */
+	public double loadValue(String key, double defaultValue) {
+		double d = 0;
+		try {
+			FileInputStream in = new FileInputStream(filename);
+			props.load(in);
+			String value = props.getProperty(key, "" + defaultValue);
+			d = (new Double(value)).doubleValue();
+		}
+		catch (Exception e) {
+			d = defaultValue;
+		}
+		return d;
+	}
+
+	/**
+	 * Load an individual integer value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param defaultValue
+	 *            the default value
+	 * @return the value get from the file
+	 */
+	public int loadValue(String key, int defaultValue) {
+		int i = 0;
+		try {
+			FileInputStream in = new FileInputStream(filename);
+			props.load(in);
+			String value = props.getProperty(key, "" + defaultValue);
+			i = (new Integer(value)).intValue();
+		}
+		catch (Exception e) {
+			i = defaultValue;
+		}
+		return i;
+	}
+
+	/**
+	 * Load an individual boolean value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param defaultValue
+	 *            the default value
+	 * @return the value get from the file
+	 */
+	public boolean loadValue(String key, boolean defaultValue) {
+		boolean b = false;
+		try {
+			FileInputStream in = new FileInputStream(filename);
+			props.load(in);
+			String value = props.getProperty(key, "" + defaultValue);
+			b = (new Boolean(value)).booleanValue();
+		}
+		catch (Exception e) {
+			b = defaultValue;
+		}
+		return b;
+	}
+
+	/**
+	 * Store an individual double value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param value
+	 *            the value to store
+	 */
+	public void storeValue(String key, String value) {
+		props.setProperty(key, value);
+		try {
+			FileOutputStream out = new FileOutputStream(filename);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			new Msg(project, "Impossible to store settings in (" + filename + ")");
+		}
+	}
+
+	/**
+	 * Store an individual double value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param value
+	 *            the value to store
+	 */
+	public void storeValue(String key, double value) {
+		props.setProperty(key, "" + value);
+		try {
+			FileOutputStream out = new FileOutputStream(filename);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			new Msg(project, "Impossible to store settings in (" + filename + ")");
+		}
+	}
+
+	/**
+	 * Store an individual integer value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param value
+	 *            the value to store
+	 */
+	public void storeValue(String key, int value) {
+		props.setProperty(key, "" + value);
+		try {
+			FileOutputStream out = new FileOutputStream(filename);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			new Msg(project, "Impossible to store settings in (" + filename + ")");
+		}
+	}
+
+	/**
+	 * Store an individual boolean value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param value
+	 *            the value to store
+	 */
+	public void storeValue(String key, boolean value) {
+		props.setProperty(key, "" + value);
+		try {
+			FileOutputStream out = new FileOutputStream(filename);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			new Msg(project, "Impossible to store settings in (" + filename + ")");
+		}
+	}
+
+	/**
+	 * Load all recorded values.
+	 */
+	public void loadRecordedItems() {
+		loadRecordedItems(filename);
+	}
+
+	/**
+	 * Load all recorded values from a specified filename.
+	 */
+	public void loadRecordedItems(String fname) {
+		try {
+			FileInputStream in = new FileInputStream(fname);
+			props.load(in);
+		}
+		catch (Exception e) {
+			new Msg(project, "Loading default value. No settings file (" + fname + ")");
+			return;
+		}
+
+		for (int i = 0; i < items.size(); i++) {
+			Item item = (Item) items.get(i);
+			String value = props.getProperty(item.key, item.defaultValue);
+			if (item.component instanceof JTextField) {
+				((JTextField) item.component).setText(value);
+			}
+			else if (item.component instanceof JComboBox) {
+				((JComboBox) item.component).setSelectedItem(value);
+			}
+			else if (item.component instanceof JCheckBox) {
+				((JCheckBox) item.component).setSelected(value.equals("on") ? true : false);
+			}
+			else if (item.component instanceof JToggleButton) {
+				((JToggleButton) item.component).setSelected(value.equals("on") ? true : false);
+			}
+			else if (item.component instanceof SpinnerInteger) {
+				((SpinnerInteger) item.component).set(Math.round((new Double(value)).intValue()));
+			}
+			else if (item.component instanceof SpinnerDouble) {
+				((SpinnerDouble) item.component).set((new Double(value)).doubleValue());
+			}
+			else if (item.component instanceof SpinnerFloat) {
+				((SpinnerFloat) item.component).set((new Float(value)).floatValue());
+			}
+			else if (item.component instanceof JSlider) {
+				((JSlider) item.component).setValue((new Integer(value)).intValue());
+			}
+		}
+	}
+
+	/**
+	 * Store all recorded values.
+	 */
+	public void storeRecordedItems() {
+		storeRecordedItems(filename);
+	}
+
+	/**
+	 * Store all recorded values into a specified filename
+	 */
+	public void storeRecordedItems(String fname) {
+
+		for (int i = 0; i < items.size(); i++) {
+			Item item = (Item) items.get(i);
+			if (item.component instanceof JTextField) {
+				String value = ((JTextField) item.component).getText();
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JComboBox) {
+				String value = (String) ((JComboBox) item.component).getSelectedItem();
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JCheckBox) {
+				String value = (((JCheckBox) item.component).isSelected() ? "on" : "off");
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JToggleButton) {
+				String value = (((JToggleButton) item.component).isSelected() ? "on" : "off");
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JSpinner) {
+				String value = "" + ((JSpinner) item.component).getValue();
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JSlider) {
+				String value = "" + ((JSlider) item.component).getValue();
+				props.setProperty(item.key, value);
+			}
+		}
+
+		try {
+			FileOutputStream out = new FileOutputStream(fname);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			new Msg(project, "Impossible to store settings in (" + fname + ")");
+
+		}
+	}
+
+	/**
+	 * Private class to store one component and its key.
+	 */
+	private class Item {
+		public Object	component;
+		public String	defaultValue;
+		public String	key;
+
+		public Item(String key, Object component, String defaultValue) {
+			this.component = component;
+			this.defaultValue = defaultValue;
+			this.key = key;
+		}
+	}
+
+	/**
+	 * Private class to display an alert message when the file is not found.
+	 */
+	private class Msg extends JFrame {
+
+		public Msg(String project, String msg) {
+			super(project);
+			GridBagLayout layout = new GridBagLayout();
+			GridBagConstraints constraints = new GridBagConstraints();
+			Container contentPane = getContentPane();
+			contentPane.setLayout(layout);
+			constraints.weightx = 0.0;
+			constraints.weighty = 1.0;
+			constraints.gridx = 0;
+			constraints.gridy = 0;
+			constraints.gridwidth = 1;
+			constraints.gridheight = 1;
+			constraints.insets = new Insets(10, 10, 10, 10);
+			constraints.anchor = GridBagConstraints.CENTER;
+			JLabel newLabel = new JLabel(msg);
+			layout.setConstraints(newLabel, constraints);
+			contentPane.add(newLabel);
+			setResizable(false);
+			pack();
+			setVisible(true);
+			Dimension dim = getToolkit().getScreenSize();
+			Rectangle abounds = getBounds();
+			setLocation((dim.width - abounds.width) / 2, (dim.height - abounds.height) / 2);
+			Timer timer = new Timer(1000, new DelayListener(this));
+			timer.start();
+		}
+	}
+
+	/**
+	 * Private class to dispose the message after 1 second.
+	 */
+	private class DelayListener implements ActionListener {
+		private Msg	msg;
+
+		public DelayListener(Msg msg) {
+			this.msg = msg;
+		}
+
+		public void actionPerformed(ActionEvent evt) {
+			msg.dispose();
+		}
+	}
+
+}
diff --git a/src/bilib/src/additionaluserinterface/SpinnerDouble.java b/src/bilib/src/additionaluserinterface/SpinnerDouble.java
new file mode 100644
index 0000000000000000000000000000000000000000..94a5909d9ec2eb4aa70ffecffbd5d6d186a4633c
--- /dev/null
+++ b/src/bilib/src/additionaluserinterface/SpinnerDouble.java
@@ -0,0 +1,105 @@
+package additionaluserinterface;
+
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+
+/**
+ * This class extends the generic JSpinner of Java for a specific JSpinner for
+ * double. It handles double type.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class SpinnerDouble extends JSpinner {
+
+	private SpinnerNumberModel	model;
+
+	private double				defValue;
+	private double				minValue;
+	private double				maxValue;
+	private double				incValue;
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerDouble(double defValue, double minValue, double maxValue, double incValue) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Double def = new Double(defValue);
+		Double min = new Double(minValue);
+		Double max = new Double(maxValue);
+		Double inc = new Double(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Set the minimal and the maximal limit.
+	 */
+	public void setLimit(double minValue, double maxValue) {
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		double value = get();
+		Double min = new Double(minValue);
+		Double max = new Double(maxValue);
+		Double inc = new Double(incValue);
+		defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		Double def = new Double(defValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Set the incremental step.
+	 */
+	public void setIncrement(double incValue) {
+		this.incValue = incValue;
+		Double def = (Double) getModel().getValue();
+		Double min = new Double(minValue);
+		Double max = new Double(maxValue);
+		Double inc = new Double(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Returns the incremental step.
+	 */
+	public double getIncrement() {
+		return incValue;
+	}
+
+	/**
+	 * Set the value in the JSpinner with clipping in the range [min..max].
+	 */
+	public void set(double value) {
+		value = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		model.setValue(value);
+	}
+
+	/**
+	 * Return the value with clipping the value in the range [min..max].
+	 */
+	public double get() {
+		if (model.getValue() instanceof Integer) {
+			Integer i = (Integer) model.getValue();
+			double ii = i.intValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Double) {
+			Double i = (Double) model.getValue();
+			double ii = i.doubleValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Float) {
+			Float i = (Float) model.getValue();
+			double ii = i.floatValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		return 0.0;
+	}
+}
diff --git a/src/bilib/src/additionaluserinterface/SpinnerFloat.java b/src/bilib/src/additionaluserinterface/SpinnerFloat.java
new file mode 100644
index 0000000000000000000000000000000000000000..1796e732a5e9d693588cd277c96e37f78c11ed80
--- /dev/null
+++ b/src/bilib/src/additionaluserinterface/SpinnerFloat.java
@@ -0,0 +1,106 @@
+package additionaluserinterface;
+
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+
+/**
+ * This class extends the generic JSpinner of Java for a specific JSpinner for
+ * float. It handles float type.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class SpinnerFloat extends JSpinner {
+
+	private SpinnerNumberModel	model;
+
+	private float				defValue;
+	private float				minValue;
+	private float				maxValue;
+	private float				incValue;
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerFloat(float defValue, float minValue, float maxValue, float incValue) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Float def = new Float(defValue);
+		Float min = new Float(minValue);
+		Float max = new Float(maxValue);
+		Float inc = new Float(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Set the minimal and the maximal limit.
+	 */
+	public void setLimit(float minValue, float maxValue) {
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		float value = get();
+		Float min = new Float(minValue);
+		Float max = new Float(maxValue);
+		Float inc = new Float(incValue);
+		defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		Float def = new Float(defValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Set the incremental step.
+	 */
+	public void setIncrement(float incValue) {
+		this.incValue = incValue;
+		Float def = (Float) getModel().getValue();
+		Float min = new Float(minValue);
+		Float max = new Float(maxValue);
+		Float inc = new Float(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Returns the incremental step.
+	 */
+	public float getIncrement() {
+		return incValue;
+	}
+
+	/**
+	 * Set the value in the JSpinner with clipping in the range [min..max].
+	 */
+	public void set(float value) {
+		value = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		model.setValue(value);
+	}
+
+	/**
+	 * Return the value without clipping the value in the range [min..max].
+	 */
+	public float get() {
+		if (model.getValue() instanceof Integer) {
+			Integer i = (Integer) model.getValue();
+			float ii = (float) i.intValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Double) {
+			Double i = (Double) model.getValue();
+			float ii = (float) i.doubleValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Float) {
+			Float i = (Float) model.getValue();
+			float ii = i.floatValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		return 0f;
+	}
+
+}
diff --git a/src/bilib/src/additionaluserinterface/SpinnerInteger.java b/src/bilib/src/additionaluserinterface/SpinnerInteger.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4c7cb9b8b022bd205c6b5fc98c29687f4cc5280
--- /dev/null
+++ b/src/bilib/src/additionaluserinterface/SpinnerInteger.java
@@ -0,0 +1,104 @@
+package additionaluserinterface;
+
+import javax.swing.*;
+
+/**
+ * This class extends the generic JSpinner of Java for a specific JSpinner for
+ * integer. It handles int type.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class SpinnerInteger extends JSpinner {
+
+	private SpinnerNumberModel	model;
+
+	private int					defValue;
+	private int					minValue;
+	private int					maxValue;
+	private int					incValue;
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerInteger(int defValue, int minValue, int maxValue, int incValue) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Integer def = new Integer(defValue);
+		Integer min = new Integer(minValue);
+		Integer max = new Integer(maxValue);
+		Integer inc = new Integer(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Set the minimal and the maximal limit.
+	 */
+	public void setLimit(int minValue, int maxValue) {
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		int value = get();
+		Integer min = new Integer(minValue);
+		Integer max = new Integer(maxValue);
+		Integer inc = new Integer(incValue);
+		defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		Integer def = new Integer(defValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Set the incremental step.
+	 */
+	public void setIncrement(int incValue) {
+		this.incValue = incValue;
+		Integer def = (Integer) getModel().getValue();
+		Integer min = new Integer(minValue);
+		Integer max = new Integer(maxValue);
+		Integer inc = new Integer(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Returns the incremental step.
+	 */
+	public int getIncrement() {
+		return incValue;
+	}
+
+	/**
+	 * Set the value in the JSpinner with clipping in the range [min..max].
+	 */
+	public void set(int value) {
+		value = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		model.setValue(value);
+	}
+
+	/**
+	 * Return the value without clipping the value in the range [min..max].
+	 */
+	public int get() {
+		if (model.getValue() instanceof Integer) {
+			Integer i = (Integer) model.getValue();
+			int ii = i.intValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Double) {
+			Double i = (Double) model.getValue();
+			int ii = (int) i.doubleValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Float) {
+			Float i = (Float) model.getValue();
+			int ii = (int) i.floatValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		return 0;
+	}
+}
diff --git a/src/bilib/src/additionaluserinterface/WalkBar.java b/src/bilib/src/additionaluserinterface/WalkBar.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd9496df19aa8bc36ee71d640acbebb4c98fa26b
--- /dev/null
+++ b/src/bilib/src/additionaluserinterface/WalkBar.java
@@ -0,0 +1,356 @@
+package additionaluserinterface;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JEditorPane;
+import javax.swing.JFrame;
+import javax.swing.JProgressBar;
+import javax.swing.JScrollPane;
+import javax.swing.JToolBar;
+import javax.swing.SwingUtilities;
+import javax.swing.text.DefaultCaret;
+
+/**
+ * This class extends the JToolbar of Java to create a status bar including some
+ * of the following component ProgressBar, Help Button About Button and Close
+ * Button
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class WalkBar extends JToolBar implements ActionListener {
+
+	private JProgressBar	progress	= new JProgressBar();
+	private JButton			bnHelp		= new JButton("Help");
+	private JButton			bnAbout		= new JButton("About");
+	private JButton			bnClose		= new JButton("Close");
+	private String			about[]		= { "About", "Version", "Description", "Author", "Biomedical Image Group", "2008", "http://bigwww.epfl.ch" };
+	private String			help;
+	private double			chrono;
+	private int				xSizeAbout	= 400;
+	private int				ySizeAbout	= 400;
+	private int				xSizeHelp	= 400;
+	private int				ySizeHelp	= 400;
+
+	/**
+	 * Class SetValue in the swing thread.
+	 */
+	private static class SetValue implements Runnable {
+		private int				value;
+		private JProgressBar	progress;
+
+		public SetValue(JProgressBar progress, int value) {
+			this.progress = progress;
+			this.value = value;
+		}
+
+		public void run() {
+			progress.setValue(value);
+		}
+	}
+
+	/**
+	 * Class IncValue in the swing thread.
+	 */
+	private static class IncValue implements Runnable {
+		private double			inc;
+		private JProgressBar	progress;
+
+		public IncValue(JProgressBar progress, double inc) {
+			this.progress = progress;
+			this.inc = inc;
+		}
+
+		public void run() {
+			progress.setValue((int) Math.round(progress.getValue() + inc));
+		}
+	}
+
+	/**
+	 * Class SetMessage in the swing thread.
+	 */
+	private static class SetMessage implements Runnable {
+		private String			msg;
+		private JProgressBar	progress;
+
+		public SetMessage(JProgressBar progress, String msg) {
+			this.progress = progress;
+			this.msg = msg;
+		}
+
+		public void run() {
+			progress.setString(msg);
+		}
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public WalkBar() {
+		super("Walk Bar");
+		build("", false, false, false, 100);
+	}
+
+	public WalkBar(String initialMessage, boolean isAbout, boolean isHelp, boolean isClose) {
+		super("Walk Bar");
+		build(initialMessage, isAbout, isHelp, isClose, 100);
+	}
+
+	public WalkBar(String initialMessage, boolean isAbout, boolean isHelp, boolean isClose, int size) {
+		super("Walk Bar");
+		build(initialMessage, isAbout, isHelp, isClose, size);
+
+	}
+
+	private void build(String initialMessage, boolean isAbout, boolean isHelp, boolean isClose, int size) {
+		if (isAbout)
+			add(bnAbout);
+		if (isHelp)
+			add(bnHelp);
+		addSeparator();
+		add(progress);
+		addSeparator();
+		if (isClose)
+			add(bnClose);
+
+		progress.setStringPainted(true);
+		progress.setString(initialMessage);
+		// progress.setFont(new Font("Arial", Font.PLAIN, 20));
+		progress.setMinimum(0);
+		progress.setMaximum(100);
+		progress.setPreferredSize(new Dimension(size, 20));
+		bnAbout.addActionListener(this);
+		bnHelp.addActionListener(this);
+
+		setFloatable(false);
+		setRollover(true);
+		setBorderPainted(false);
+		chrono = System.currentTimeMillis();
+	}
+
+	/**
+	 * Implements the actionPerformed for the ActionListener.
+	 */
+	public synchronized void actionPerformed(ActionEvent e) {
+		if (e.getSource() == bnHelp) {
+			showHelp();
+		}
+		else if (e.getSource() == bnAbout) {
+			showAbout();
+		}
+		else if (e.getSource() == bnClose) {
+		}
+	}
+
+	/**
+	 * Return a reference to the Close button.
+	 */
+	public JButton getButtonClose() {
+		return bnClose;
+	}
+
+	/**
+	 * Set a value and a message in the progress bar.
+	 */
+	public void progress(String msg, int value) {
+		double elapsedTime = System.currentTimeMillis() - chrono;
+		String t = " [" + (elapsedTime > 3000 ? Math.round(elapsedTime / 10) / 100.0 + "s." : elapsedTime + "ms") + "]";
+		SwingUtilities.invokeLater(new SetValue(progress, value));
+		SwingUtilities.invokeLater(new SetMessage(progress, msg + t));
+	}
+
+	/**
+	 * Set a value and a message in the progress bar.
+	 */
+	public void increment(double inc) {
+		SwingUtilities.invokeLater(new IncValue(progress, inc));
+	}
+
+	/**
+	 * Set a value in the progress bar.
+	 */
+	public void setValue(int value) {
+		SwingUtilities.invokeLater(new SetValue(progress, value));
+	}
+
+	/**
+	 * Set a message in the progress bar.
+	 */
+	public void setMessage(String msg) {
+		SwingUtilities.invokeLater(new SetMessage(progress, msg));
+	}
+
+	/**
+	 * Set a value and a message in the progress bar.
+	 */
+	public void progress(String msg, double value) {
+		progress(msg, (int) Math.round(value));
+	}
+
+	/**
+	 * Set to 0 the progress bar.
+	 */
+	public void reset() {
+		chrono = System.currentTimeMillis();
+		progress("Start", 0);
+	}
+
+	/**
+	 * Set to 100 the progress bar.
+	 */
+	public void finish() {
+		progress("End", 100);
+	}
+
+	/**
+	 * Set to 100 the progress bar with an additional message.
+	 */
+	public void finish(String msg) {
+		progress(msg, 100);
+	}
+
+	/**
+	 * Specify the content of the About window.
+	 */
+	public void fillAbout(String name, String version, String description, String author, String organisation, String date, String info) {
+		this.about[0] = name;
+		this.about[1] = version;
+		this.about[2] = description;
+		this.about[3] = author;
+		this.about[4] = organisation;
+		this.about[5] = date;
+		this.about[6] = info;
+	}
+
+	/**
+	 * Specify the content of the Help window.
+	 */
+	public void fillHelp(String help) {
+		this.help = help;
+	}
+
+	/**
+	 * Show the content of the About window.
+	 */
+	public void showAbout() {
+
+		final JFrame frame = new JFrame("About " + about[0]);
+		JEditorPane pane = new JEditorPane();
+		pane.setEditable(false);
+		pane.setContentType("text/html; charset=ISO-8859-1");
+		pane.setText("<html><head><title>" + about[0] + "</title>" + getStyle() + "</head><body>" + (about[0] == "" ? "" : "<p class=\"name\">" + about[0] + "</p>")
+				+ // Name
+				(about[1] == "" ? "" : "<p class=\"vers\">" + about[1] + "</p>")
+				+ // Version
+				(about[2] == "" ? "" : "<p class=\"desc\">" + about[2] + "</p><hr>")
+				+ // Description
+				(about[3] == "" ? "" : "<p class=\"auth\">" + about[3] + "</p>")
+				+ // author
+				(about[4] == "" ? "" : "<p class=\"orga\">" + about[4] + "</p>") + (about[5] == "" ? "" : "<p class=\"date\">" + about[5] + "</p>")
+				+ (about[6] == "" ? "" : "<p class=\"more\">" + about[6] + "</p>") + "</html>");
+
+		final JButton bnClose = new JButton("Close");
+		bnClose.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				frame.dispose();
+			}
+		});
+		pane.setCaret(new DefaultCaret());
+		JScrollPane scrollPane = new JScrollPane(pane);
+		// helpScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+		scrollPane.setPreferredSize(new Dimension(xSizeAbout, ySizeAbout));
+		frame.getContentPane().add(scrollPane, BorderLayout.NORTH);
+		frame.getContentPane().add(bnClose, BorderLayout.CENTER);
+
+		frame.pack();
+		frame.setResizable(false);
+		frame.setVisible(true);
+		center(frame);
+	}
+
+	/**
+	 * Show the content of the Help window of a given size.
+	 */
+	public void showHelp() {
+		final JFrame frame = new JFrame("Help " + about[0]);
+		JEditorPane pane = new JEditorPane();
+		pane.setEditable(false);
+		pane.setContentType("text/html; charset=ISO-8859-1");
+		pane.setText("<html><head><title>" + about[0] + "</title>" + getStyle() + "</head><body>" + (about[0] == "" ? "" : "<p class=\"name\">" + about[0] + "</p>") + // Name
+				(about[1] == "" ? "" : "<p class=\"vers\">" + about[1] + "</p>") + // Version
+				(about[2] == "" ? "" : "<p class=\"desc\">" + about[2] + "</p>") + // Description
+				"<hr><p class=\"help\">" + help + "</p>" + "</html>");
+		final JButton bnClose = new JButton("Close");
+		bnClose.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				frame.dispose();
+			}
+		});
+		pane.setCaret(new DefaultCaret());
+		JScrollPane scrollPane = new JScrollPane(pane);
+		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+		scrollPane.setPreferredSize(new Dimension(xSizeHelp, ySizeHelp));
+		frame.setPreferredSize(new Dimension(xSizeHelp, ySizeHelp));
+		frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
+		frame.getContentPane().add(bnClose, BorderLayout.SOUTH);
+		frame.setVisible(true);
+		frame.pack();
+		center(frame);
+	}
+
+	/*
+	 * Place the window in the center of the screen.
+	 */
+	private void center(Window w) {
+		Dimension screenSize = new Dimension(0, 0);
+		boolean isWin = System.getProperty("os.name").startsWith("Windows");
+		if (isWin) { // GraphicsEnvironment.getConfigurations is *very* slow on
+						// Windows
+			screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+		}
+		if (GraphicsEnvironment.isHeadless())
+			screenSize = new Dimension(0, 0);
+		else {
+			// Can't use Toolkit.getScreenSize() on Linux because it returns
+			// size of all displays rather than just the primary display.
+			GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+			GraphicsDevice[] gd = ge.getScreenDevices();
+			GraphicsConfiguration[] gc = gd[0].getConfigurations();
+			Rectangle bounds = gc[0].getBounds();
+			if (bounds.x == 0 && bounds.y == 0)
+				screenSize = new Dimension(bounds.width, bounds.height);
+			else
+				screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+		}
+		Dimension window = w.getSize();
+		if (window.width == 0)
+			return;
+		int left = screenSize.width / 2 - window.width / 2;
+		int top = (screenSize.height - window.height) / 4;
+		if (top < 0)
+			top = 0;
+		w.setLocation(left, top);
+	}
+
+	/*
+	 * Defines the CSS style for the help and about window.
+	 */
+	private String getStyle() {
+		return "<style type=text/css>" + "body {backgroud-color:#222277}" + "hr {width:80% color:#333366; padding-top:7px }"
+				+ "p, li {margin-left:10px;margin-right:10px; color:#000000; font-size:1em; font-family:Verdana,Helvetica,Arial,Geneva,Swiss,SunSans-Regular,sans-serif}"
+				+ "p.name {color:#ffffff; font-size:1.2em; font-weight: bold; background-color: #333366; text-align:center;}" + "p.vers {color:#333333; text-align:center;}"
+				+ "p.desc {color:#333333; font-weight: bold; text-align:center;}" + "p.auth {color:#333333; font-style: italic; text-align:center;}"
+				+ "p.orga {color:#333333; text-align:center;}" + "p.date {color:#333333; text-align:center;}" + "p.more {color:#333333; text-align:center;}"
+				+ "p.help {color:#000000; text-align:left;}" + "</style>";
+	}
+}
diff --git a/src/bilib/src/bilib.zip b/src/bilib/src/bilib.zip
new file mode 100644
index 0000000000000000000000000000000000000000..2ef6e196c2d25c8eb2717582cab0ae148956c022
Binary files /dev/null and b/src/bilib/src/bilib.zip differ
diff --git a/src/bilib/src/bilib/.DS_Store b/src/bilib/src/bilib/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..2dae4405c103007dd70cd4f46b3f7f99458b776a
Binary files /dev/null and b/src/bilib/src/bilib/.DS_Store differ
diff --git a/src/bilib/src/bilib/commons.zip b/src/bilib/src/bilib/commons.zip
new file mode 100644
index 0000000000000000000000000000000000000000..cd3f5e1a05f420e086e25203b7b333f7fabb00a1
Binary files /dev/null and b/src/bilib/src/bilib/commons.zip differ
diff --git a/src/bilib/src/bilib/commons/.DS_Store b/src/bilib/src/bilib/commons/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..05370983747f7d8fa1271a67dd854d587f24703c
Binary files /dev/null and b/src/bilib/src/bilib/commons/.DS_Store differ
diff --git a/src/bilib/src/bilib/commons/buttons/ButtonFactory.java b/src/bilib/src/bilib/commons/buttons/ButtonFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..67bb752061649188f6bdd7093e8cd53ab2a01db0
--- /dev/null
+++ b/src/bilib/src/bilib/commons/buttons/ButtonFactory.java
@@ -0,0 +1,89 @@
+package bilib.commons.buttons;
+
+import java.awt.Dimension;
+import java.net.URL;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+
+public class ButtonFactory {
+
+	private static JButton icon(JButton bn) {
+		URL url = ButtonFactory.class.getResource(bn.getText().toLowerCase() +".png");
+		if (url != null) {
+			ImageIcon icon = new ImageIcon(url, "");
+			if (icon != null) {
+				JButton bni = new JButton(icon);
+				Dimension d = new Dimension(icon.getIconWidth(), icon.getIconHeight()+2);
+				bni.setMaximumSize(d);
+				bni.setPreferredSize(d);
+				return bni;
+			}
+		}
+		return bn;
+	}
+	
+	public static JButton about(boolean tryIcon) {
+		JButton bn = new JButton("About");
+		if (tryIcon == true)
+			bn = icon(bn);
+		bn.setToolTipText("About ...");
+		return bn;
+	}
+
+	public static JButton close(boolean tryIcon) {
+		JButton bn = new JButton("Close");
+		if (tryIcon == true)
+			bn = icon(bn);
+		bn.setToolTipText("Save the settings and close");
+		return bn;
+	}
+
+	public static JButton prefs(boolean tryIcon) {
+		JButton bn = new JButton("Prefs");
+		if (tryIcon == true)
+			bn = icon(bn);
+		bn.setToolTipText("Manage the settings");
+		return bn;
+	}
+
+	public static JButton help(boolean tryIcon) {
+		JButton bn = new JButton("Help");
+		if (tryIcon == true)
+			bn = icon(bn);
+		bn.setToolTipText("Get online help");
+		return bn;
+	}
+
+	public static JButton run(boolean tryIcon) {
+		JButton bn = new JButton("Run");
+		if (tryIcon == true)
+			bn = icon(bn);
+		bn.setToolTipText("Start the processing");
+		return bn;
+	}
+
+	public static JButton save(boolean tryIcon) {
+		JButton bn = new JButton("Save");
+		if (tryIcon == true)
+			bn = icon(bn);
+		bn.setToolTipText("Save");
+		return bn;
+	}
+
+	public static JButton snapshot(boolean tryIcon) {
+		JButton bn = new JButton("Snapshot");
+		if (tryIcon == true)
+			bn = icon(bn);
+		bn.setToolTipText("Snapshot");
+		return bn;
+	}
+
+	public static JButton stop(boolean tryIcon) {
+		JButton bn = new JButton("Stop");
+		if (tryIcon == true)
+			bn = icon(bn);
+		bn.setToolTipText("Abort the processing");
+		return bn;
+	}
+}
diff --git a/src/bilib/src/bilib/commons/buttons/about.png b/src/bilib/src/bilib/commons/buttons/about.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b79af592e1b717d318a6d70da2a964e608a24ab
Binary files /dev/null and b/src/bilib/src/bilib/commons/buttons/about.png differ
diff --git a/src/bilib/src/bilib/commons/buttons/close.png b/src/bilib/src/bilib/commons/buttons/close.png
new file mode 100644
index 0000000000000000000000000000000000000000..253ac8afeb193e2825e59d50da8a80e8d763a925
Binary files /dev/null and b/src/bilib/src/bilib/commons/buttons/close.png differ
diff --git a/src/bilib/src/bilib/commons/buttons/help.png b/src/bilib/src/bilib/commons/buttons/help.png
new file mode 100644
index 0000000000000000000000000000000000000000..3549c560cdb5700c0abc0c0caedcfb2149c72554
Binary files /dev/null and b/src/bilib/src/bilib/commons/buttons/help.png differ
diff --git a/src/bilib/src/bilib/commons/buttons/prefs.png b/src/bilib/src/bilib/commons/buttons/prefs.png
new file mode 100644
index 0000000000000000000000000000000000000000..611406224177d9adcce8bf124ffca437c55db12b
Binary files /dev/null and b/src/bilib/src/bilib/commons/buttons/prefs.png differ
diff --git a/src/bilib/src/bilib/commons/buttons/run.png b/src/bilib/src/bilib/commons/buttons/run.png
new file mode 100644
index 0000000000000000000000000000000000000000..b5f15e97e4ac0a80ea5c571b7e202948dfdb0417
Binary files /dev/null and b/src/bilib/src/bilib/commons/buttons/run.png differ
diff --git a/src/bilib/src/bilib/commons/buttons/save.png b/src/bilib/src/bilib/commons/buttons/save.png
new file mode 100644
index 0000000000000000000000000000000000000000..3133d5a0e2654587d7151057b1355d27cc4b006e
Binary files /dev/null and b/src/bilib/src/bilib/commons/buttons/save.png differ
diff --git a/src/bilib/src/bilib/commons/buttons/snapshot.png b/src/bilib/src/bilib/commons/buttons/snapshot.png
new file mode 100644
index 0000000000000000000000000000000000000000..6cd56b514729b3e114e688cab15fe1f02fa81ab7
Binary files /dev/null and b/src/bilib/src/bilib/commons/buttons/snapshot.png differ
diff --git a/src/bilib/src/bilib/commons/buttons/stop.png b/src/bilib/src/bilib/commons/buttons/stop.png
new file mode 100644
index 0000000000000000000000000000000000000000..ed24ea55c7d256a163ccc32d2d93cb5686371e75
Binary files /dev/null and b/src/bilib/src/bilib/commons/buttons/stop.png differ
diff --git a/src/bilib/src/bilib/commons/components/BorderToggledButton.java b/src/bilib/src/bilib/commons/components/BorderToggledButton.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3f4ccb41214c34e1c1d8bbb95fc1b6f92417eee
--- /dev/null
+++ b/src/bilib/src/bilib/commons/components/BorderToggledButton.java
@@ -0,0 +1,54 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.components;
+
+import java.awt.Insets;
+
+import javax.swing.JButton;
+
+public class BorderToggledButton extends JButton {
+
+	private String text = "";
+	public BorderToggledButton(String text) {
+		super(text);
+		this.text = text;
+		setMargin(new Insets(1, 1, 1, 1));
+	}
+
+	@Override
+	public void setSelected(boolean selected) {
+		if (selected)
+			setText("<html><b>" + text + "</b></html>");
+		else
+			setText(text);
+	}
+}
diff --git a/src/bilib/src/bilib/commons/components/DoubleScrollablePanel.java b/src/bilib/src/bilib/commons/components/DoubleScrollablePanel.java
new file mode 100644
index 0000000000000000000000000000000000000000..c91aedca90920d8c474466b412434a7a04fdfa84
--- /dev/null
+++ b/src/bilib/src/bilib/commons/components/DoubleScrollablePanel.java
@@ -0,0 +1,73 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.components;
+
+import java.awt.Dimension;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+
+/**
+ * This class extends the JSplitPane to create two scrollable panel splitted in
+ * the vertical direction.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+
+public class DoubleScrollablePanel extends JSplitPane {
+
+	public DoubleScrollablePanel(int hpref1, JComponent panel1, int hpref2, JComponent panel2) {
+		super(JSplitPane.VERTICAL_SPLIT);
+		int hmin = 70;
+		JScrollPane scroll1 = new JScrollPane(panel1);
+		scroll1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		scroll1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+		scroll1.setBorder(BorderFactory.createEmptyBorder());
+		scroll1.setMinimumSize(new Dimension(200, hmin));
+		scroll1.setPreferredSize(new Dimension(200, hpref1));
+		add(scroll1, 1);
+
+		JScrollPane scroll2 = new JScrollPane(panel2);
+		scroll2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		scroll2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+		scroll2.setBorder(BorderFactory.createEmptyBorder());
+		scroll2.setMinimumSize(new Dimension(200, hmin));
+		scroll2.setPreferredSize(new Dimension(200, hpref2));
+		add(scroll2, 1);
+		setOneTouchExpandable(true);
+		setBorder(BorderFactory.createEtchedBorder());
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/components/GridPanel.java b/src/bilib/src/bilib/commons/components/GridPanel.java
new file mode 100644
index 0000000000000000000000000000000000000000..3bdcba25def121a806d7ea24c4f2b334dba0ea0c
--- /dev/null
+++ b/src/bilib/src/bilib/commons/components/GridPanel.java
@@ -0,0 +1,200 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.components;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+/**
+ * This class extends the JPanel to create grid panel given the possibility to
+ * place Java components in an organized manner in the dialog box.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class GridPanel extends JPanel {
+
+	private static final long	serialVersionUID	= 1L;
+	private GridBagLayout		layout				= new GridBagLayout();
+	private GridBagConstraints	constraint			= new GridBagConstraints();
+	private int					defaultSpace		= 3;
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel() {
+		super();
+		setLayout(layout);
+		setBorder(BorderFactory.createEtchedBorder());
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(int defaultSpace) {
+		super();
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createEtchedBorder());
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(boolean border) {
+		super();
+		setLayout(layout);
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(String title) {
+		super();
+		setLayout(layout);
+		setBorder(BorderFactory.createTitledBorder(title));
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(boolean border, int defaultSpace) {
+		super();
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridPanel(String title, int defaultSpace) {
+		super();
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createTitledBorder(title));
+	}
+
+	/**
+	 * Specify the defaultSpace.
+	 */
+	public void setSpace(int defaultSpace) {
+		this.defaultSpace = defaultSpace;
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, String label) {
+		place(row, col, 1, 1, defaultSpace, new JLabel(label));
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int space, String label) {
+		place(row, col, 1, 1, space, new JLabel(label));
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, String label) {
+		place(row, col, width, height, defaultSpace, new JLabel(label));
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, JComponent comp) {
+		place(row, col, 1, 1, defaultSpace, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int space, JComponent comp) {
+		place(row, col, 1, 1, space, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, JComponent comp) {
+		place(row, col, width, height, defaultSpace, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, int space, JComponent comp) {
+		if (comp == null)
+			return;
+		constraint.gridx = col;
+		constraint.gridy = row;
+		constraint.gridwidth = width;
+		constraint.gridheight = height;
+		constraint.anchor = GridBagConstraints.NORTHWEST;
+		constraint.insets = new Insets(space, space, space, space);
+		constraint.fill = GridBagConstraints.HORIZONTAL;
+		layout.setConstraints(comp, constraint);
+		add(comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, int spaceHorizontal, int spaceVertical, JComponent comp) {
+		if (comp == null)
+			return;
+		constraint.gridx = col;
+		constraint.gridy = row;
+		constraint.gridwidth = width;
+		constraint.gridheight = height;
+		constraint.anchor = GridBagConstraints.NORTHWEST;
+		constraint.insets = new Insets(spaceVertical, spaceHorizontal, spaceHorizontal, spaceVertical);
+		constraint.fill = GridBagConstraints.HORIZONTAL;
+		layout.setConstraints(comp, constraint);
+		add(comp);
+	}
+}
diff --git a/src/bilib/src/bilib/commons/components/GridToolbar.java b/src/bilib/src/bilib/commons/components/GridToolbar.java
new file mode 100644
index 0000000000000000000000000000000000000000..ffb8d5e13f6a26b7143badae50191eb38aa18e44
--- /dev/null
+++ b/src/bilib/src/bilib/commons/components/GridToolbar.java
@@ -0,0 +1,246 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.components;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JToolBar;
+
+/**
+ * This class extends the JToolbar to create grid panel given the possibility to
+ * place Java components in an organized manner in the dialog box.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class GridToolbar extends JToolBar {
+
+	private GridBagLayout		layout			= new GridBagLayout();
+	private GridBagConstraints	constraint		= new GridBagConstraints();
+	private int					defaultSpace	= 3;
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar() {
+		super("Control");
+		setLayout(layout);
+		setBorder(BorderFactory.createEtchedBorder());
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(boolean border) {
+		super("Control");
+		setLayout(layout);
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(String title) {
+		super(title);
+		setLayout(layout);
+		setBorder(BorderFactory.createTitledBorder(title));
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(int defaultSpace) {
+		super("Control");
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createEtchedBorder());
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(boolean border, int defaultSpace) {
+		super("Control");
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(boolean border, int defaultSpace, boolean floatable) {
+		super("Control");
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(boolean border, boolean floatable) {
+		super("Control");
+		setLayout(layout);
+		if (border) {
+			setBorder(BorderFactory.createEtchedBorder());
+		}
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(String title, boolean floatable) {
+		super(title);
+		setLayout(layout);
+		setBorder(BorderFactory.createTitledBorder(title));
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(int defaultSpace, boolean floatable) {
+		super("Control");
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createEtchedBorder());
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(String title, int defaultSpace) {
+		super(title);
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createTitledBorder(title));
+		setFloatable(false);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public GridToolbar(String title, int defaultSpace, boolean floatable) {
+		super(title);
+		setLayout(layout);
+		this.defaultSpace = defaultSpace;
+		setBorder(BorderFactory.createTitledBorder(title));
+		setFloatable(floatable);
+	}
+
+	/**
+	 * Specify the defaultSpace.
+	 */
+	public void setSpace(int defaultSpace) {
+		this.defaultSpace = defaultSpace;
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, String label) {
+		place(row, col, 1, 1, defaultSpace, new JLabel(label));
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int space, String label) {
+		place(row, col, 1, 1, space, new JLabel(label));
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, String label) {
+		place(row, col, width, height, defaultSpace, new JLabel(label));
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, JComponent comp) {
+		place(row, col, 1, 1, defaultSpace, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int space, JComponent comp) {
+		place(row, col, 1, 1, space, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, JComponent comp) {
+		place(row, col, width, height, defaultSpace, comp);
+	}
+
+	/**
+	 * Place a component in the northwest of the cell.
+	 */
+	public void place(int row, int col, int width, int height, int space, JComponent comp) {
+		if (comp == null)
+			return;
+		constraint.gridx = col;
+		constraint.gridy = row;
+		constraint.gridwidth = width;
+		constraint.gridheight = height;
+		constraint.anchor = GridBagConstraints.NORTHWEST;
+		constraint.insets = new Insets(space, space, space, space);
+		constraint.fill = GridBagConstraints.HORIZONTAL;
+		layout.setConstraints(comp, constraint);
+		add(comp);
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/components/HTMLPane.java b/src/bilib/src/bilib/commons/components/HTMLPane.java
new file mode 100644
index 0000000000000000000000000000000000000000..676e07915b67b0376cb7fa8bfe785fc4f3c1bc6e
--- /dev/null
+++ b/src/bilib/src/bilib/commons/components/HTMLPane.java
@@ -0,0 +1,141 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.components;
+
+import java.awt.Dimension;
+
+import javax.swing.JEditorPane;
+import javax.swing.JScrollPane;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+/**
+ * This class extends the Java JEditorPane to make a easy to use panel to
+ * display HTML information.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class HTMLPane extends JEditorPane {
+
+	private String		html		= "";
+	private String		header		= "";
+	private String		footer		= "";
+	private Dimension	dim;
+	private String		font		= "verdana";
+	private String		color		= "#222222";
+	private String		background	= "#f8f8f8";
+
+	public HTMLPane() {
+		create();
+	}
+
+	public HTMLPane(String font) {
+		this.font = font;
+		create();
+	}
+
+	public HTMLPane(int width, int height) {
+		this.dim = new Dimension(width, height);
+		create();
+	}
+
+	public HTMLPane(String font, int width, int height) {
+		this.font = font;
+		this.dim = new Dimension(width, height);
+		create();
+	}
+
+	public HTMLPane(String font, String color, String background, int width, int height) {
+		this.font = font;
+		this.dim = new Dimension(width, height);
+		this.color = color;
+		this.background = background;
+		create();
+	}
+
+	@Override
+	public String getText() {
+		Document doc = this.getDocument();
+		try {
+			return doc.getText(0, doc.getLength());
+		}
+		catch (BadLocationException e) {
+			e.printStackTrace();
+			return getText();
+		}
+	}
+	
+	public void clear() {
+		html = "";
+		append("");
+	}
+
+	private void create() {
+		header += "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n";
+		header += "<html><head>\n";
+		header += "<style>body {background-color:" + background + "; color:" + color + "; font-family: " + font + ";margin:4px}</style>\n";
+		header += "<style>h1 {color:#555555; font-size:1.0em; font-weight:bold; padding:1px; margin:1px;}</style>\n";
+		header += "<style>h2 {color:#333333; font-size:0.9em; font-weight:bold; padding:1px; margin:1px;}</style>\n";
+		header += "<style>h3 {color:#000000; font-size:0.9em; font-weight:italic; padding:1px; margin:1px;}</style>\n";
+		header += "<style>p  {color:" + color + "; font-size:0.9em; padding:1px; margin:0px;}</style>\n";
+		header += "</head>\n";
+		header += "<body>\n";
+		footer += "</body></html>\n";
+		setEditable(false);
+		setContentType("text/html; charset=ISO-8859-1");
+	}
+
+	public void append(String content) {
+		html += content;
+		setText(header + html + footer);
+		if (dim != null) {
+			setPreferredSize(dim);
+		}
+		setCaretPosition(0);
+	}
+
+	public void append(String tag, String content) {
+		html += "<" + tag + ">" + content + "</" + tag + ">";
+		setText(header + html + footer);
+		if (dim != null) {
+			setPreferredSize(dim);
+		}
+		setCaretPosition(0);
+	}
+
+	public JScrollPane getPane() {
+		JScrollPane scroll = new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		scroll.setPreferredSize(dim);
+		return scroll;
+	}
+}
diff --git a/src/bilib/src/bilib/commons/components/SpinnerRangeDouble.java b/src/bilib/src/bilib/commons/components/SpinnerRangeDouble.java
new file mode 100644
index 0000000000000000000000000000000000000000..f67a8cb51a9cffa3dd0033cfdcb53440e70ce422
--- /dev/null
+++ b/src/bilib/src/bilib/commons/components/SpinnerRangeDouble.java
@@ -0,0 +1,197 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.components;
+
+import javax.swing.JFormattedTextField;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+
+/**
+ * This class extends the Java Swing JSpinner to make a easy to use spinner for
+ * double. It handles double type. The size can be control by the number of
+ * visible chars or by a specific format (NumberEditor).
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class SpinnerRangeDouble extends JSpinner {
+
+	private SpinnerNumberModel	model;
+
+	private double				defValue;
+	private double				minValue;
+	private double				maxValue;
+	private double				incValue;
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerRangeDouble(double defValue, double minValue, double maxValue, double incValue) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Double def = new Double(defValue);
+		Double min = new Double(minValue);
+		Double max = new Double(maxValue);
+		Double inc = new Double(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+		JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
+		tf.setColumns(7);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerRangeDouble(double defValue, double minValue, double maxValue, double incValue, String format) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Double def = new Double(defValue);
+		Double min = new Double(minValue);
+		Double max = new Double(maxValue);
+		Double inc = new Double(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+		setEditor(new JSpinner.NumberEditor(this, format));
+		JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
+		tf.setColumns(7);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerRangeDouble(double defValue, double minValue, double maxValue, double incValue, int visibleChars) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Double def = new Double(defValue);
+		Double min = new Double(minValue);
+		Double max = new Double(maxValue);
+		Double inc = new Double(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+		JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
+		tf.setColumns(visibleChars);
+	}
+
+	/**
+	 * Set the format of the numerical value.
+	 */
+	public void setFormat(String format) {
+		setEditor(new JSpinner.NumberEditor(this, format));
+	}
+
+	/**
+	 * Set the minimal and the maximal limit.
+	 */
+	public void setLimit(double minValue, double maxValue) {
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		double value = get();
+		Double min = new Double(minValue);
+		Double max = new Double(maxValue);
+		Double inc = new Double(incValue);
+		defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		Double def = new Double(defValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Set the incremental step.
+	 */
+	public void setIncrement(double incValue) {
+		this.incValue = incValue;
+		Double def = (Double) getModel().getValue();
+		Double min = new Double(minValue);
+		Double max = new Double(maxValue);
+		Double inc = new Double(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Returns the incremental step.
+	 */
+	public double getIncrement() {
+		return incValue;
+	}
+
+	/**
+	 * Set the value in the JSpinner with clipping in the range [min..max].
+	 */
+	public void set(double value) {
+		value = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		model.setValue(new Double(value));
+	}
+
+	/**
+	 * Return the value with clipping the value in the range [min..max].
+	 */
+	public double get() {
+		if (model.getValue() instanceof Integer) {
+			Integer i = (Integer) model.getValue();
+			double ii = i.intValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Double) {
+			Double i = (Double) model.getValue();
+			double ii = i.doubleValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Float) {
+			Float i = (Float) model.getValue();
+			double ii = i.floatValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		return 0.0;
+	}
+	
+	public double getRangeMaximum() {
+		return maxValue;
+	}
+	
+	public double getRangeMinimum() {
+		return minValue;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/components/SpinnerRangeFloat.java b/src/bilib/src/bilib/commons/components/SpinnerRangeFloat.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ee802ad8c4fdd02d13d1d8e3ed3a173b0f58914
--- /dev/null
+++ b/src/bilib/src/bilib/commons/components/SpinnerRangeFloat.java
@@ -0,0 +1,197 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.components;
+
+import javax.swing.JFormattedTextField;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+
+/**
+ * This class extends the Java Swing JSpinner to make a easy to use spinner for
+ * float. It handles float type. The size can be control by the number of
+ * visible chars or by a specific format (NumberEditor).
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class SpinnerRangeFloat extends JSpinner {
+
+	private SpinnerNumberModel	model;
+
+	private float				defValue;
+	private float				minValue;
+	private float				maxValue;
+	private float				incValue;
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerRangeFloat(float defValue, float minValue, float maxValue, float incValue) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Float def = new Float(defValue);
+		Float min = new Float(minValue);
+		Float max = new Float(maxValue);
+		Float inc = new Float(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+		JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
+		tf.setColumns(7);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerRangeFloat(float defValue, float minValue, float maxValue, float incValue, String format) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Double def = new Double(defValue);
+		Double min = new Double(minValue);
+		Double max = new Double(maxValue);
+		Double inc = new Double(incValue);
+		this.model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+		setEditor(new JSpinner.NumberEditor(this, format));
+		JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
+		tf.setColumns(7);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerRangeFloat(float defValue, float minValue, float maxValue, float incValue, int visibleChars) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Float def = new Float(defValue);
+		Float min = new Float(minValue);
+		Float max = new Float(maxValue);
+		Float inc = new Float(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+		JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
+		tf.setColumns(visibleChars);
+	}
+
+	/**
+	 * Set the format of the numerical value.
+	 */
+	public void setFormat(String format) {
+		setEditor(new JSpinner.NumberEditor(this, format));
+	}
+
+	/**
+	 * Set the minimal and the maximal limit.
+	 */
+	public void setLimit(float minValue, float maxValue) {
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		float value = get();
+		Float min = new Float(minValue);
+		Float max = new Float(maxValue);
+		Float inc = new Float(incValue);
+		defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		Float def = new Float(defValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Set the incremental step.
+	 */
+	public void setIncrement(float incValue) {
+		this.incValue = incValue;
+		Float def = (Float) getModel().getValue();
+		Float min = new Float(minValue);
+		Float max = new Float(maxValue);
+		Float inc = new Float(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Returns the incremental step.
+	 */
+	public float getIncrement() {
+		return incValue;
+	}
+
+	/**
+	 * Set the value in the JSpinner with clipping in the range [min..max].
+	 */
+	public void set(float value) {
+		value = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		model.setValue(new Float(value));
+	}
+
+	/**
+	 * Return the value without clipping the value in the range [min..max].
+	 */
+	public float get() {
+		if (model.getValue() instanceof Integer) {
+			Integer i = (Integer) model.getValue();
+			float ii = (float) i.intValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Double) {
+			Double i = (Double) model.getValue();
+			float ii = (float) i.doubleValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Float) {
+			Float i = (Float) model.getValue();
+			float ii = i.floatValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		return 0f;
+	}
+	
+	public float getRangeMaximum() {
+		return maxValue;
+	}
+	
+	public float getRangeMinimum() {
+		return minValue;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/components/SpinnerRangeInteger.java b/src/bilib/src/bilib/commons/components/SpinnerRangeInteger.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9b345f0d5416dd183467e6395db18705c36e732
--- /dev/null
+++ b/src/bilib/src/bilib/commons/components/SpinnerRangeInteger.java
@@ -0,0 +1,196 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.components;
+
+import javax.swing.JFormattedTextField;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+
+/**
+ * This class extends the Java Swing JSpinner to make a easy to use spinner for
+ * integer. It handles int type. The size can be control by the number of
+ * visible chars or by a specific format (NumberEditor).
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class SpinnerRangeInteger extends JSpinner {
+
+	private SpinnerNumberModel	model;
+
+	private int					defValue;
+	private int					minValue;
+	private int					maxValue;
+	private int					incValue;
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerRangeInteger(int defValue, int minValue, int maxValue, int incValue) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Integer def = new Integer(defValue);
+		Integer min = new Integer(minValue);
+		Integer max = new Integer(maxValue);
+		Integer inc = new Integer(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+		JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
+		tf.setColumns(7);
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerRangeInteger(int defValue, int minValue, int maxValue, int incValue, String format) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Integer def = new Integer(defValue);
+		Integer min = new Integer(minValue);
+		Integer max = new Integer(maxValue);
+		Integer inc = new Integer(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+		setEditor(new JSpinner.NumberEditor(this, format));
+		JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
+		tf.setColumns(format.length());
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public SpinnerRangeInteger(int defValue, int minValue, int maxValue, int incValue, int visibleChars) {
+		super();
+		this.defValue = defValue;
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		this.incValue = incValue;
+
+		Integer def = new Integer(defValue);
+		Integer min = new Integer(minValue);
+		Integer max = new Integer(maxValue);
+		Integer inc = new Integer(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+		JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
+		tf.setColumns(visibleChars);
+	}
+
+	/**
+	 * Set the format of the numerical value.
+	 */
+	public void setFormat(String format) {
+		setEditor(new JSpinner.NumberEditor(this, format));
+	}
+
+	/**
+	 * Set the minimal and the maximal limit.
+	 */
+	public void setLimit(int minValue, int maxValue) {
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+		int value = get();
+		Integer min = new Integer(minValue);
+		Integer max = new Integer(maxValue);
+		Integer inc = new Integer(incValue);
+		defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		Integer def = new Integer(defValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Set the incremental step.
+	 */
+	public void setIncrement(int incValue) {
+		this.incValue = incValue;
+		Integer def = (Integer) getModel().getValue();
+		Integer min = new Integer(minValue);
+		Integer max = new Integer(maxValue);
+		Integer inc = new Integer(incValue);
+		model = new SpinnerNumberModel(def, min, max, inc);
+		setModel(model);
+	}
+
+	/**
+	 * Returns the incremental step.
+	 */
+	public int getIncrement() {
+		return incValue;
+	}
+
+	/**
+	 * Set the value in the JSpinner with clipping in the range [min..max].
+	 */
+	public void set(int value) {
+		value = (value > maxValue ? maxValue : (value < minValue ? minValue : value));
+		model.setValue(new Integer(value));
+	}
+
+	/**
+	 * Return the value without clipping the value in the range [min..max].
+	 */
+	public int get() {
+		if (model.getValue() instanceof Integer) {
+			Integer i = (Integer) model.getValue();
+			int ii = i.intValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Double) {
+			Double i = (Double) model.getValue();
+			int ii = (int) i.doubleValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		else if (model.getValue() instanceof Float) {
+			Float i = (Float) model.getValue();
+			int ii = (int) i.floatValue();
+			return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii));
+		}
+		return 0;
+	}
+	
+	public int getRangeMaximum() {
+		return maxValue;
+	}
+	
+	public int getRangeMinimum() {
+		return minValue;
+	}
+}
diff --git a/src/bilib/src/bilib/commons/fft/BasicFFT.java b/src/bilib/src/bilib/commons/fft/BasicFFT.java
new file mode 100644
index 0000000000000000000000000000000000000000..c60e1012b6a36b28b339e6118a4fe1deda14cab3
--- /dev/null
+++ b/src/bilib/src/bilib/commons/fft/BasicFFT.java
@@ -0,0 +1,234 @@
+package bilib.commons.fft;
+
+/**
+ * Performs a 2D or 1D (double) Fourier transformation using a basic FFT
+ * algorithm. It is only a a size of signal which in a power of 2. The 2D signal
+ * are store in a 1D double array in row-major convention. The complex value are
+ * store in a additional dimension [2].
+ * 
+ * @author Daniel Sage
+ */
+
+public class BasicFFT {
+
+	/**
+	 * Creates the real frequency part of the Fourier space with the array
+	 * realFunction using hermitian symmetry. The imaginary part is set to zero.
+	 */
+	public double[] fillHermitian2D(double[][] realFunction) {
+		int xsize = (realFunction.length - 1);
+		int ysize = (realFunction[0].length - 1);
+		int nx = xsize * 2;
+		int ny = ysize * 2;
+		double[] signal = new double[nx * ny];
+		for (int y = 0; y <= ysize; y++)
+			for (int x = 0; x <= xsize; x++) {
+				signal[x + nx * (y)] = realFunction[x][y];
+			}
+		for (int y = 0; y < ysize; y++)
+			for (int x = 0; x < xsize; x++) {
+				signal[nx - 1 - x + nx * (y)] = realFunction[x + 1][y];
+				signal[nx - 1 - x + nx * (ny - 1 - y)] = realFunction[x + 1][y + 1];
+				signal[x + nx * (ny - 1 - y)] = realFunction[x][y + 1];
+			}
+		return signal;
+	}
+
+	/**
+	 * Performs the 2D FFT (Cooley Tukey). Signal's size has to be a power of
+	 * two : 2^n. Return a complex signal.
+	 */
+	public double[][] transform2D(double real[], double imag[], int nx, int ny) {
+
+		double[][] signal = new double[2][nx * ny];
+
+		double rowReal[] = new double[nx];
+		double rowImag[] = new double[nx];
+
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < nx; x++) {
+				rowReal[x] = real[x + y * nx];
+				rowImag[x] = imag[x + y * nx];
+			}
+			transform1D(rowReal, rowImag);
+			for (int x = 0; x < nx; x++) {
+				signal[0][x + nx * (y)] = rowReal[x];
+				signal[1][x + nx * (y)] = rowImag[x];
+			}
+		}
+		double colReal[] = new double[ny];
+		double colImag[] = new double[ny];
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < ny; y++) {
+				colReal[y] = signal[0][x + nx * (y)];
+				colImag[y] = signal[1][x + nx * (y)];
+			}
+			transform1D(colReal, colImag);
+			for (int y = 0; y < ny; y++) {
+				signal[0][x + nx * (y)] = colReal[y];
+				signal[1][x + nx * (y)] = colImag[y];
+			}
+		}
+		return signal;
+	}
+
+	/**
+	 * Performs the 2D inverse FFT (Cooley Tukey). Signal's size has to be a
+	 * power of two : 2^n.
+	 */
+	public double[][] inverse2D(double real[], double imag[], int nx, int ny) {
+
+		double[][] tmpreal = new double[nx][ny];
+		double[][] tmpimag = new double[nx][ny];
+
+		double colReal[] = new double[ny];
+		double colImag[] = new double[ny];
+
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < ny; y++) {
+				colReal[y] = real[x + nx * (y)];
+				colImag[y] = imag[x + nx * (y)];
+			}
+			inverseFFT1D(colReal, colImag);
+			for (int y = 0; y < ny; y++) {
+				tmpreal[x][y] = colReal[y];
+				tmpimag[x][y] = colImag[y];
+			}
+		}
+
+		double rowReal[] = new double[nx];
+		double rowImag[] = new double[nx];
+
+		double signal[][] = new double[2][nx * ny];
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < nx; x++) {
+				rowReal[x] = tmpreal[x][y];
+				rowImag[x] = tmpimag[x][y];
+			}
+			inverseFFT1D(rowReal, rowImag);
+			for (int x = 0; x < nx; x++) {
+				signal[0][x + nx * y] = rowReal[x];
+				signal[1][x + nx * y] = rowImag[x];
+			}
+		}
+		return signal;
+	}
+
+	/**
+	 * Perform the Fourier transform of the FFT 1D.
+	 * 
+	 * 2D shift of the half size of the input.
+	 */
+	public void transform1D(double real[], double imag[]) {
+		fft1D(real, imag);
+	}
+
+	/**
+	 * Perform the Inverse of the FFT 1D.
+	 * 
+	 * Signal's size has to be a power of two : 2^m
+	 */
+	public void inverseFFT1D(double real[], double imag[]) {
+		int size = real.length;
+		for (int i = 0; i < size; i++) {
+			imag[i] = -imag[i];
+		}
+
+		fft1D(real, imag);
+
+		for (int i = 0; i < size; i++) {
+			real[i] = real[i] / size;
+			imag[i] = -imag[i] / size;
+		}
+	}
+
+	/**
+	 * 2D shift of the half size of the input.
+	 */
+	public void shift2D(double plane[], int nx, int ny) {
+		int nx2 = nx / 2;
+		int ny2 = ny / 2;
+		double tmp[] = new double[nx * ny];
+
+		for (int x = 0; x < nx; x++) {
+			int i = (x >= nx2 ? x - nx2 : x + nx2);
+			for (int y = 0; y < ny; y++)
+				tmp[i + nx * y] = plane[x + nx * y];
+		}
+
+		for (int y = 0; y < ny; y++) {
+			int j = (y >= ny2 ? y - ny2 : y + ny2);
+			for (int x = 0; x < nx; x++)
+				plane[x + nx * j] = tmp[x + nx * y];
+		}
+	}
+
+	/**
+	 * Perform the FFT 1D Method Decimation in time (Cooley Tukey)
+	 * 
+	 * Signal's size has to be a power of two : 2^m
+	 */
+	private void fft1D(double real[], double imaginary[]) {
+		int shift = 0; // start of the computation
+		int size = real.length; // length of the FFT
+
+		int m = (int) Math.floor((Math.log((double) size) / Math.log(2.0)));
+
+		double Retmp, Imtmp, arg;
+		int i, j, k, stepsize, shifter, n;
+		int i_j, i_j_s;
+
+		n = 1 << m;
+
+		double[] Imarg = new double[n];
+		double[] Rearg = new double[n];
+
+		// compute W coefficients
+		double arg0 = 2.0 * Math.PI / (double) n;
+		for (i = 0; i < n; i++) {
+			arg = arg0 * (float) i;
+			Rearg[i] = Math.cos(arg);
+			Imarg[i] = -Math.sin(arg);
+		}
+
+		// bit inversion
+		for (i = j = shift; i < shift + n - 1; i++) {
+			if (i < j) {
+				Retmp = real[i];
+				Imtmp = imaginary[i];
+				real[i] = real[j];
+				imaginary[i] = imaginary[j];
+				real[j] = Retmp;
+				imaginary[j] = Imtmp;
+			}
+			k = n >> 1;
+			while (k + shift <= j) {
+				j -= k;
+				k /= 2;
+			}
+			j += k;
+		}
+
+		// Perform the FFT
+		for (stepsize = 1, shifter = m - 1; stepsize < n; stepsize <<= 1, --shifter) {
+			for (j = shift; j < shift + n; j += stepsize << 1) {
+				for (i = 0; i < stepsize; i++) {
+					i_j = i + j;
+					i_j_s = i_j + stepsize;
+					if (i > 0) {
+						Retmp = Rearg[i << shifter] * real[i_j_s] - Imarg[i << shifter] * imaginary[i_j_s];
+						imaginary[i_j_s] = Rearg[i << shifter] * imaginary[i_j_s] + Imarg[i << shifter] * real[i_j_s];
+						real[i_j_s] = Retmp;
+					}
+					Retmp = real[i_j] - real[i_j_s];
+					Imtmp = imaginary[i_j] - imaginary[i_j_s];
+					real[i_j] += real[i_j_s];
+					imaginary[i_j] += imaginary[i_j_s];
+					real[i_j_s] = Retmp;
+					imaginary[i_j_s] = Imtmp;
+				}
+			}
+		}
+
+	}
+}
diff --git a/src/bilib/src/bilib/commons/job/.DS_Store b/src/bilib/src/bilib/commons/job/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..b853ace37669751e41d0bd03e4c7f851f8d05f5c
Binary files /dev/null and b/src/bilib/src/bilib/commons/job/.DS_Store differ
diff --git a/src/bilib/src/bilib/commons/job/ExecutionMode.java b/src/bilib/src/bilib/commons/job/ExecutionMode.java
new file mode 100644
index 0000000000000000000000000000000000000000..675b94956ee17bca8c965bc697b57bef1dd36459
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/ExecutionMode.java
@@ -0,0 +1,17 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job;
+
+public enum ExecutionMode {
+	MULTITHREAD_NO, MULTITHREAD_SYNCHRONIZED, MULTITHREAD_ASYNCHRONIZED
+}
diff --git a/src/bilib/src/bilib/commons/job/JobAbstract.java b/src/bilib/src/bilib/commons/job/JobAbstract.java
new file mode 100644
index 0000000000000000000000000000000000000000..eab12ad27d17e9fa3547df5186c8127a415952f2
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/JobAbstract.java
@@ -0,0 +1,69 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job;
+
+import java.util.ArrayList;
+
+/**
+ * This class is an interface for three implementations of the Job concept:
+ * Runnable Job, Callable Job, SwingWorker Job.
+ * 
+ * @author sage
+ * 
+ */
+public interface JobAbstract {
+
+	public abstract void addMonitor(MonitorAbstract monitor);
+
+	public abstract void removeMonitor(MonitorAbstract monitor);
+
+	public abstract void clearMonitor();
+
+	public abstract ArrayList<MonitorAbstract> getMonitor();
+
+	public abstract boolean isNotTimeOut();
+
+	public abstract boolean isJobLive();
+
+	public abstract boolean isJobDone();
+
+	public abstract boolean isJobIdle();
+
+	public abstract boolean isJobProcessing();
+
+	public abstract boolean isJobIncomplete();
+
+	@Override
+	public abstract String toString();
+
+	public abstract String getName();
+
+	public abstract void setName(String name);
+
+	public abstract void setTimeOut(double timeoutms);
+
+	public abstract void abort();
+
+	public abstract void interrupt();
+
+	public abstract void abort(String message);
+
+	public abstract void interrupt(String message);
+
+	public abstract void init();
+
+	public abstract PoolAbstract getPool();
+
+	public abstract void setPool(PoolAbstract pool);
+
+}
diff --git a/src/bilib/src/bilib/commons/job/JobEvent.java b/src/bilib/src/bilib/commons/job/JobEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6ee541a7302c6bb3d44d5fcd7cf441676b09035
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/JobEvent.java
@@ -0,0 +1,94 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job;
+
+import java.util.EventObject;
+
+public class JobEvent extends EventObject {
+
+	public static byte	VOID		= 0;
+	public static byte	STARTED		= 1;
+	public static byte	COMPLETED	= 2;
+	public static byte	ABORTED		= 3;
+	public static byte	TIMEOUT		= 4;
+	public static byte	EXCEPTION	= 5;
+	public static byte	INTERRUPTED	= 6;
+
+	private byte		typeEvent	= VOID;
+	private String		message		= "";
+	private Exception	exception;
+	private JobAbstract	job;
+
+	public JobEvent(JobAbstract source, byte typeEvent, String message) {
+		super(source);
+		this.job = source;
+		this.typeEvent = typeEvent;
+		this.exception = null;
+		this.message = message;
+	}
+
+	public JobEvent(JobAbstract source, Exception exception, String message) {
+		super(source);
+		this.job = source;
+		this.typeEvent = EXCEPTION;
+		this.exception = exception;
+		this.message = message;
+	}
+
+	public JobEvent(JobAbstract source, byte typeEvent) {
+		super(source);
+		this.job = source;
+		this.typeEvent = typeEvent;
+		this.message = "";
+	}
+
+	public JobAbstract getJob() {
+		return job;
+	}
+
+	public String getMessage() {
+		return message;
+	}
+
+	public Exception getException() {
+		return exception;
+	}
+
+	public byte getTypeEvent() {
+		return typeEvent;
+	}
+
+	public String getEventString() {
+		if (typeEvent == VOID)
+			return "VOID";
+		if (typeEvent == STARTED)
+			return "STARTED";
+		if (typeEvent == COMPLETED)
+			return "COMPLETED";
+		if (typeEvent == ABORTED)
+			return "ABORTED";
+		if (typeEvent == TIMEOUT)
+			return "TIMEOUT";
+		if (typeEvent == EXCEPTION)
+			return "EXCEPTION";
+		if (typeEvent == INTERRUPTED)
+			return "INTERRUPTED";
+		return "UNDEFINED";
+	}
+
+	@Override
+	public String toString() {
+		return "JobEvent, type=(" + getEventString() + "), source=(" + source.toString() + "), message=(" + message + ") " + source.getClass().getName();
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/job/MonitorAbstract.java b/src/bilib/src/bilib/commons/job/MonitorAbstract.java
new file mode 100644
index 0000000000000000000000000000000000000000..da98b572213a3f648da6ea62c1f3a468a2740960
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/MonitorAbstract.java
@@ -0,0 +1,32 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job;
+
+public interface MonitorAbstract {
+
+	public abstract void rewind();
+
+	public abstract void print(String message);
+
+	public abstract void error(String message);
+
+	public abstract void warning(String message);
+
+	public abstract void progress(double percentage);
+
+	public abstract void progress(double percentage, String message);
+
+	public abstract void increment(double percentageIncrement);
+
+	public abstract void increment(double percentageIncrement, String message);
+}
diff --git a/src/bilib/src/bilib/commons/job/MonitorConsole.java b/src/bilib/src/bilib/commons/job/MonitorConsole.java
new file mode 100644
index 0000000000000000000000000000000000000000..3453533df13571c1397d99e2343aea4f827bab1c
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/MonitorConsole.java
@@ -0,0 +1,55 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job;
+
+public class MonitorConsole implements MonitorAbstract {
+
+	@Override
+	public void rewind() {
+	}
+
+	@Override
+	public void print(String message) {
+		System.out.println(message);
+	}
+
+	@Override
+	public void progress(double percentage) {
+		System.out.println("Progression:" + percentage);
+	}
+
+	@Override
+	public void progress(double percentage, String message) {
+		System.out.println("Progression:" + percentage + " Message:" + message);
+	}
+
+	@Override
+	public void increment(double percentageIncrement) {
+		System.out.println("+ Progression:" + percentageIncrement);
+	}
+
+	@Override
+	public void increment(double percentageIncrement, String message) {
+		System.out.println("+ Progression:" + percentageIncrement + " Message:" + message);
+	}
+
+	@Override
+	public void error(String message) {
+		System.out.println("Error:" + message);
+	}
+
+	@Override
+	public void warning(String message) {
+		System.out.println("Warning:" + message);
+	}
+}
diff --git a/src/bilib/src/bilib/commons/job/MonitorProgressBar.java b/src/bilib/src/bilib/commons/job/MonitorProgressBar.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1a91e750da6805b523a9b351ceb1187c43c1fc4
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/MonitorProgressBar.java
@@ -0,0 +1,76 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job;
+
+import javax.swing.JProgressBar;
+
+public class MonitorProgressBar extends JProgressBar implements MonitorAbstract {
+
+	private double	percentage	= 0.0;
+
+	public MonitorProgressBar() {
+		setStringPainted(true);
+		setMaximum(100);
+		setPercentage(0);
+	}
+
+	@Override
+	public void rewind() {
+		setPercentage(0);
+		setString("");
+	}
+
+	@Override
+	public void print(String message) {
+		setString(message);
+	}
+
+	@Override
+	public void progress(double percentage) {
+		setPercentage(percentage);
+	}
+
+	@Override
+	public void progress(double percentage, String message) {
+		setPercentage(percentage);
+		setString(message);
+	}
+
+	@Override
+	public void increment(double percentageIncrement) {
+		setPercentage(percentageIncrement + percentage);
+	}
+
+	@Override
+	public void increment(double percentageIncrement, String message) {
+		setPercentage(percentageIncrement + percentage);
+		setString(message);
+	}
+
+	@Override
+	public void error(String message) {
+		setString("Error:" + message);
+	}
+
+	@Override
+	public void warning(String message) {
+		setString("Warning:" + message);
+	}
+
+	private void setPercentage(double percentage) {
+		this.percentage = percentage;
+		setValue((int) Math.max(0, Math.min(100, percentage)));
+
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/job/MonitorTimedLog.java b/src/bilib/src/bilib/commons/job/MonitorTimedLog.java
new file mode 100644
index 0000000000000000000000000000000000000000..e14784e83f57ee22e162e39394c283f6f7818bcf
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/MonitorTimedLog.java
@@ -0,0 +1,100 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job;
+
+import java.awt.Insets;
+import java.text.DecimalFormat;
+
+import javax.swing.JTextArea;
+import javax.swing.text.DefaultCaret;
+
+public class MonitorTimedLog extends JTextArea implements MonitorAbstract {
+
+	private double	chrono	= System.nanoTime();
+
+	public MonitorTimedLog(int rows, int columns) {
+		super(rows, columns);
+		setMargin(new Insets(5, 5, 5, 5));
+		setEditable(false);
+		DefaultCaret caret = (DefaultCaret) getCaret();
+		caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+	}
+
+	@Override
+	public void rewind() {
+		chrono = System.nanoTime();
+		setText("");
+	}
+
+	@Override
+	public void print(String message) {
+		update(message);
+	}
+
+	@Override
+	public void error(String message) {
+		update(message);
+	}
+
+	@Override
+	public void warning(String message) {
+		update(message);
+	}
+
+	@Override
+	public void progress(double percentage) {
+		update("Progression: " + percentage);
+	}
+
+	@Override
+	public void progress(double percentage, String message) {
+		update("Progression: " + percentage + " - ");
+	}
+
+	@Override
+	public void increment(double percentage) {
+		update("+ Progression: " + percentage);
+	}
+
+	@Override
+	public void increment(double percentage, String message) {
+		update("+ Progression: " + percentage + " - ");
+	}
+
+	private void update(String message) {
+		append(toc() + " " + message + "\n");
+		setCaretPosition(getDocument().getLength());
+	}
+
+	private String toc() {
+		double te = System.nanoTime() - chrono;
+		String s = " ";
+		DecimalFormat df = new DecimalFormat("####.##");
+		if (te < 1000.0)
+			return s + df.format(te) + " ns";
+		te /= 1000;
+		if (te < 1000.0)
+			return s + df.format(te) + " us";
+		te /= 1000;
+		if (te < 3000.0)
+			return s + df.format(te) + " ms";
+		te /= 1000;
+		if (te < 600.1)
+			return s + df.format(te) + " s";
+		te /= 60;
+		if (te < 600.1)
+			return s + df.format(te) + " min.";
+		te /= 60;
+		return s + df.format(te) + " h.";
+	}
+}
diff --git a/src/bilib/src/bilib/commons/job/MonitorTimedProgressBar.java b/src/bilib/src/bilib/commons/job/MonitorTimedProgressBar.java
new file mode 100644
index 0000000000000000000000000000000000000000..9431a910eb1f387e3a204e23cf7a340ee95e497e
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/MonitorTimedProgressBar.java
@@ -0,0 +1,106 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job;
+
+import java.text.DecimalFormat;
+
+import javax.swing.JProgressBar;
+
+public class MonitorTimedProgressBar extends JProgressBar implements MonitorAbstract {
+
+	private double	chrono		= System.nanoTime();
+	private double	percentage	= 0.0;
+
+	public MonitorTimedProgressBar() {
+		setStringPainted(true);
+		setMaximum(100);
+		setPercentage(0);
+	}
+
+	@Override
+	public void rewind() {
+		setPercentage(0);
+		chrono = System.nanoTime();
+		setText("");
+	}
+
+	@Override
+	public void print(String message) {
+		setText(message);
+	}
+
+	@Override
+	public void progress(double percentage) {
+		setPercentage(percentage);
+	}
+
+	@Override
+	public void progress(double percentage, String message) {
+		setPercentage(percentage);
+		setText(message);
+	}
+
+	@Override
+	public void increment(double percentageIncrement) {
+		setPercentage(percentageIncrement + percentage);
+	}
+
+	@Override
+	public void increment(double percentageIncrement, String message) {
+		setPercentage(percentageIncrement + percentage);
+		setText(message);
+	}
+
+	@Override
+	public void error(String message) {
+		setText("Error:" + message);
+	}
+
+	@Override
+	public void warning(String message) {
+		setText("Warning:" + message);
+	}
+
+	private void setText(String message) {
+		setString(toc() + " - " + message);
+	}
+
+	private String toc() {
+		double te = System.nanoTime() - chrono;
+		String s = " ";
+		DecimalFormat df = new DecimalFormat("####.##");
+		if (te < 1000.0)
+			return s + df.format(te) + " ns";
+		te /= 1000;
+		if (te < 1000.0)
+			return s + df.format(te) + " us";
+		te /= 1000;
+		if (te < 3000.0)
+			return s + df.format(te) + " ms";
+		te /= 1000;
+		if (te < 600.1)
+			return s + df.format(te) + " s";
+		te /= 60;
+		if (te < 600.1)
+			return s + df.format(te) + " min.";
+		te /= 60;
+		return s + df.format(te) + " h.";
+	}
+
+	private void setPercentage(double percentage) {
+		this.percentage = percentage;
+		setValue((int) Math.max(0, Math.min(100, percentage)));
+
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/job/PoolAbstract.java b/src/bilib/src/bilib/commons/job/PoolAbstract.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa7e3e8ea3b0835811f38bfbcbfc3373730efb9e
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/PoolAbstract.java
@@ -0,0 +1,52 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job;
+
+import java.util.concurrent.Future;
+
+public interface PoolAbstract {
+
+	public abstract String getName();
+
+	public abstract void execute(ExecutionMode mode);
+
+	public abstract void execute(ExecutionMode mode, int nThreads);
+
+	public abstract Future<Object> getFuture(String jobName);
+
+	public abstract int size();
+
+	public abstract void cancel();
+
+	public abstract void register(JobAbstract job);
+
+	public abstract void register(JobAbstract job, double timeoutms);
+
+	public abstract void unregister(JobAbstract job);
+
+	public abstract void unregisterAll();
+
+	public abstract boolean isJobDone();
+
+	public abstract boolean isJobLive();
+
+	public abstract boolean isJobIdle();
+
+	public abstract void waitTermination();
+
+	public abstract void die();
+
+	public abstract void fire(JobEvent event);
+
+	public abstract void fire(JobAbstract parent, JobEvent event);
+}
diff --git a/src/bilib/src/bilib/commons/job/callable/CallableDemo.java b/src/bilib/src/bilib/commons/job/callable/CallableDemo.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e2bf6254a58aeb00abb5fa75b84896ae6e7f614
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/callable/CallableDemo.java
@@ -0,0 +1,303 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.callable;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+
+import bilib.commons.job.ExecutionMode;
+import bilib.commons.job.JobEvent;
+import bilib.commons.job.MonitorAbstract;
+import bilib.commons.job.MonitorProgressBar;
+import bilib.commons.job.MonitorTimedLog;
+import bilib.commons.job.MonitorTimedProgressBar;
+import bilib.commons.job.callable.PoolResponder;
+
+public class CallableDemo extends JFrame implements PoolResponder, ActionListener {
+
+	public JRadioButton				rbSequential			= new JRadioButton("Sequential", false);
+	public JRadioButton				rbAsynchronized			= new JRadioButton("Parallel - Asynchronized jobs (not wait)", false);
+	public JRadioButton				rbSynchronized			= new JRadioButton("Parallel - Synchronized jobs (wait termination)", true);
+
+	public JButton					execute					= new JButton("Execute");
+	public JTextField				nthreads				= new JTextField("0");
+	public JTextField				njobs					= new JTextField("10");
+	public JButton					simulateAbort			= new JButton("Abort");
+	public JButton					simulateException		= new JButton("Simulate an Exception");
+	public JButton					simulateJobException	= new JButton("Simulate an JobException");
+	public JButton					simulateInterruption	= new JButton("Simulate an Interruption");
+	public JButton					simulateCancellation	= new JButton("Simulate an Cancellation");
+	public JButton					simulateTimeOut			= new JButton("Simulate an TimeOut");
+
+	public MonitorTimedProgressBar	bar						= new MonitorTimedProgressBar();
+	public MonitorProgressBar		bar1					= new MonitorProgressBar();
+	public MonitorTimedLog			log						= new MonitorTimedLog(10, 10);
+
+	private double					chrono;
+	private MainProcessing			main;
+
+	public static void main(String args[]) {
+		new CallableDemo();
+	}
+
+	public CallableDemo() {
+		super("Callable Demo");
+		main = new MainProcessing(this);
+
+		ButtonGroup bg1 = new ButtonGroup();
+		bg1.add(rbSequential);
+		bg1.add(rbAsynchronized);
+		bg1.add(rbSynchronized);
+
+		JPanel panel1 = new JPanel(new GridLayout(20, 1));
+
+		panel1.add(rbSequential);
+		panel1.add(rbAsynchronized);
+		panel1.add(rbSynchronized);
+
+		panel1.add(new JLabel("Number of threads (0 = nb of jobs)"));
+		panel1.add(nthreads);
+		panel1.add(new JLabel("Number of jobs"));
+		panel1.add(njobs);
+		panel1.add(execute);
+		panel1.add(new JLabel(""));
+		panel1.add(simulateAbort);
+		panel1.add(simulateException);
+		panel1.add(simulateJobException);
+		panel1.add(simulateInterruption);
+		panel1.add(simulateCancellation);
+		panel1.add(simulateTimeOut);
+
+		JPanel panel3 = new JPanel(new BorderLayout());
+		panel3.add(bar, BorderLayout.NORTH);
+		panel3.add(bar1, BorderLayout.SOUTH);
+
+		JPanel panel = new JPanel(new BorderLayout());
+		panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
+		panel.add(panel1, BorderLayout.NORTH);
+		panel.add(panel3, BorderLayout.SOUTH);
+		panel.add(log, BorderLayout.CENTER);
+
+		execute.addActionListener(this);
+		simulateAbort.addActionListener(this);
+		simulateException.addActionListener(this);
+		simulateJobException.addActionListener(this);
+		simulateInterruption.addActionListener(this);
+		simulateCancellation.addActionListener(this);
+		simulateTimeOut.addActionListener(this);
+		getContentPane().add(panel);
+		pack();
+		setVisible(true);
+	}
+
+	private ExecutionMode getMode() {
+		ExecutionMode mode = ExecutionMode.MULTITHREAD_NO;
+		if (rbSynchronized.isSelected())
+			mode = ExecutionMode.MULTITHREAD_SYNCHRONIZED;
+		if (rbAsynchronized.isSelected())
+			mode = ExecutionMode.MULTITHREAD_ASYNCHRONIZED;
+		return mode;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent event) {
+
+		if (event.getSource() == execute) {
+			main.setMode(getMode(), Integer.parseInt(nthreads.getText()), Integer.parseInt(njobs.getText()));
+			System.out.println("\n\n");
+			Pool pool = new Pool("Main", this);
+			pool.register(main);
+			pool.execute(ExecutionMode.MULTITHREAD_ASYNCHRONIZED);
+			chrono = System.nanoTime();
+		}
+
+		if (event.getSource() == simulateAbort)
+			main.simulateAbort();
+
+		if (event.getSource() == simulateException)
+			main.simulateException();
+
+		if (event.getSource() == simulateJobException)
+			main.simulateJobException();
+
+		if (event.getSource() == simulateInterruption)
+			main.simulateInterruption();
+
+		if (event.getSource() == simulateCancellation)
+			main.simulateCancellation();
+
+		if (event.getSource() == simulateTimeOut)
+			main.simulateTimeOut();
+	}
+
+	//
+	// MainProcessing
+	//
+	public class MainProcessing extends Job implements PoolResponder {
+		private ExecutionMode	mode;
+		private int				nthreads	= 0;
+		private int				njobs;
+		private PoolResponder	responder;
+		private Pool			pool;
+		private int				niter		= 15;
+
+		public MainProcessing(PoolResponder responder) {
+			super();
+			this.responder = responder;
+		}
+
+		public void setMode(ExecutionMode mode, int nthreads, int njobs) {
+			this.mode = mode;
+			this.njobs = njobs;
+			this.nthreads = nthreads;
+		}
+
+		@Override
+		public Object process() {
+			bar.rewind();
+			bar1.rewind();
+			log.rewind();
+
+			pool = new Pool("my", responder);
+			MyCallable p1 = new MyCallable(niter, bar);
+			p1.addMonitor(log);
+			p1.addMonitor(bar1);
+			pool.register(p1);
+			for (int i = 0; i < njobs; i++)
+				pool.register(new MyCallable(niter, bar));
+			pool.execute(mode, nthreads);
+
+			System.out.println("End of main");
+			return "End";
+
+		}
+
+		public void simulateAbort() {
+			if (pool != null)
+				pool.getRegisteredJob(pool.size() - 1).abort();
+		}
+
+		public void simulateCancellation() {
+			if (pool != null)
+				pool.cancel();
+		}
+
+		public void simulateException() {
+			if (pool != null)
+				((MyCallable) pool.getRegisteredJob(pool.size() / 2 - 1)).withException = true;
+		}
+
+		public void simulateJobException() {
+			if (pool != null)
+				((MyCallable) pool.getRegisteredJob(pool.size() - 1)).withJobException = true;
+		}
+
+		public void simulateInterruption() {
+			if (pool != null)
+				((MyCallable) pool.getRegisteredJob(pool.size() - 1)).withInterruption = true;
+		}
+
+		public void simulateTimeOut() {
+			if (pool != null)
+				((MyCallable) pool.getRegisteredJob(pool.size() - 1)).withTimeOut = true;
+		}
+
+		@Override
+		public void onEvent(Pool pool, JobEvent event) {
+			responder.onEvent(pool, event);
+		}
+
+		@Override
+		public void onSuccess(Pool pool, JobEvent event) {
+			responder.onSuccess(pool, event);
+		}
+
+		@Override
+		public void onFailure(Pool pool, JobEvent event) {
+			responder.onFailure(pool, event);
+		}
+
+	}
+
+	//
+	// MyCallable
+	//
+	public class MyCallable extends Job {
+		public boolean	withTimeOut			= false;
+		public boolean	withInterruption	= false;
+		public boolean	withException		= false;
+		public boolean	withJobException	= false;
+		private int		niter				= 15;
+
+		public MyCallable(int niter, MonitorAbstract monitor) {
+			super(monitor);
+			this.niter = niter;
+		}
+
+		@Override
+		public Object process() throws RuntimeException {
+			int i = 0;
+			for (i = 0; i < niter && live; i++) {
+				increment(2, getName() + " " + live + " " + done);
+				for (int j = 0; j < 600000 && live; j++)
+					Math.round(Math.cos(Math.exp(Math.pow(j, -3))));
+				if (withJobException)
+					throw new RuntimeException("It is a programming exception");
+				if (withInterruption) {
+					error("Interrupted messsage");
+					interrupt();
+				}
+				if (withTimeOut)
+					niter += 2;
+				if (withException) {
+					double a[] = new double[2];
+					a[3] = 1;
+				}
+				if (!live)
+					return "no live";
+			}
+			System.out.println("End of " + getName() + " iterations:" + i + " " + live + " " + done);
+			return "End of " + getName() + " iterations:" + i + " " + live + " " + done;
+		}
+	}
+
+	@Override
+	public void onEvent(Pool pool, JobEvent event) {
+		System.out.println("Main side: Job Event " + event);
+	}
+
+	@Override
+	public void onSuccess(Pool pool, JobEvent event) {
+		System.out.println(">>> Success " + event + " from pool " + pool.getName());
+		System.out.println("Computation time: " + (System.nanoTime() - chrono) / 1000000.0);
+	}
+
+	@Override
+	public void onFailure(Pool pool, JobEvent event) {
+		System.out.println(">>> Failure " + event + " from pool " + pool.getName());
+		if (event.getException() != null)
+			event.getException().printStackTrace();
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/job/callable/Job.java b/src/bilib/src/bilib/commons/job/callable/Job.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f35fc54b37294b7087ae1ca2f767423c7705d1c
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/callable/Job.java
@@ -0,0 +1,253 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.callable;
+
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+
+import bilib.commons.job.JobAbstract;
+import bilib.commons.job.JobEvent;
+import bilib.commons.job.MonitorAbstract;
+import bilib.commons.job.PoolAbstract;
+
+public abstract class Job implements JobAbstract, MonitorAbstract, Callable<Object> {
+
+	public boolean						live		= false;
+	public boolean						done		= false;
+	public boolean						idle		= false;
+	private double						chrono		= -1;
+	private double						timeoutns	= -1;
+	private boolean						stopTimeOut	= false;
+	private ArrayList<MonitorAbstract>	monitors	= new ArrayList<MonitorAbstract>();
+
+	public PoolAbstract					pool;
+
+	public abstract Object process() throws RuntimeException;
+
+	private String	name;
+
+	public Job() {
+	}
+
+	public Job(MonitorAbstract monitor) {
+		monitors.add(monitor);
+	}
+
+	@Override
+	public String getName() {
+		return name;
+	}
+
+	@Override
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	@Override
+	public PoolAbstract getPool() {
+		return pool;
+	}
+
+	@Override
+	public void setPool(PoolAbstract pool) {
+		this.pool = pool;
+	}
+
+	@Override
+	public void setTimeOut(double timeoutms) {
+		this.timeoutns = timeoutms * 1000000.0;
+		this.stopTimeOut = true;
+	}
+
+	@Override
+	public void abort() {
+		idle = false;
+		live = false;
+		done = false;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.ABORTED));
+	}
+
+	@Override
+	public void interrupt() {
+		idle = false;
+		live = false;
+		done = false;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.INTERRUPTED));
+	}
+
+	@Override
+	public void abort(String message) {
+		idle = false;
+		live = false;
+		done = false;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.ABORTED, message));
+	}
+
+	@Override
+	public void interrupt(String message) {
+		idle = false;
+		live = false;
+		done = false;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.INTERRUPTED, message));
+	}
+
+	@Override
+	public void init() {
+		idle = true;
+		live = true;
+		done = false;
+		chrono = System.nanoTime();
+	}
+
+	@Override
+	public Object call() {
+		Object o = null;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.STARTED));
+		try {
+			idle = false;
+			o = process();
+			if (live) {
+				done = true;
+				live = false;
+				if (pool != null)
+					pool.fire(new JobEvent(this, JobEvent.COMPLETED));
+			}
+		}
+		catch (RuntimeException ex) {
+			if (pool != null)
+				pool.fire(new JobEvent(this, ex, ex.getMessage()));
+		}
+		live = false;
+		idle = true;
+		return o;
+	}
+
+	@Override
+	public boolean isNotTimeOut() {
+		if (!live)
+			return false;
+		if (stopTimeOut)
+			if ((System.nanoTime() - chrono) > timeoutns) {
+				if (pool != null)
+					pool.fire(new JobEvent(this, JobEvent.TIMEOUT));
+				live = false;
+				done = false;
+				return false;
+			}
+		return true;
+	}
+
+	@Override
+	public boolean isJobLive() {
+		return live;
+	}
+
+	@Override
+	public boolean isJobDone() {
+		return done;
+	}
+
+	@Override
+	public boolean isJobIdle() {
+		return idle;
+	}
+
+	@Override
+	public boolean isJobProcessing() {
+		return !idle;
+	}
+
+	@Override
+	public boolean isJobIncomplete() {
+		return !done;
+	}
+
+	@Override
+	public String toString() {
+		return "JobCallable: " + getName();
+	}
+
+	@Override
+	public void rewind() {
+		for (MonitorAbstract monitor : monitors)
+			monitor.rewind();
+	}
+
+	@Override
+	public void print(String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.print(message);
+	}
+
+	@Override
+	public void error(String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.error(message);
+	}
+
+	@Override
+	public void warning(String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.warning(message);
+	}
+
+	@Override
+	public void progress(double percentage) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.progress(percentage);
+	}
+
+	@Override
+	public void progress(double percentage, String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.progress(percentage, message);
+	}
+
+	@Override
+	public void increment(double percentageIncrement) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.increment(percentageIncrement);
+	}
+
+	@Override
+	public void increment(double percentageIncrement, String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.increment(percentageIncrement, message);
+	}
+
+	@Override
+	public void addMonitor(MonitorAbstract monitor) {
+		monitors.add(monitor);
+	}
+
+	@Override
+	public void removeMonitor(MonitorAbstract monitor) {
+		monitors.remove(monitor);
+	}
+
+	@Override
+	public void clearMonitor() {
+		monitors.clear();
+	}
+
+	@Override
+	public ArrayList<MonitorAbstract> getMonitor() {
+		return monitors;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/job/callable/Pool.java b/src/bilib/src/bilib/commons/job/callable/Pool.java
new file mode 100644
index 0000000000000000000000000000000000000000..7448679cf9b7198fb8740f523f9d9781c3292106
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/callable/Pool.java
@@ -0,0 +1,288 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.callable;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import bilib.commons.job.ExecutionMode;
+import bilib.commons.job.JobAbstract;
+import bilib.commons.job.JobEvent;
+import bilib.commons.job.PoolAbstract;
+import bilib.commons.job.callable.PoolResponder;
+import bilib.commons.job.callable.Job;
+
+public class Pool implements PoolAbstract {
+
+	private ArrayList<Job>						jobs;
+	protected String							name;
+	protected PoolResponder						responder;
+
+	protected HashMap<String, Future<Object>>	results;
+
+	/**
+	 * Create a new pool with one registered job (Callable).
+	 * 
+	 * @param job
+	 *            the job to register
+	 * @param responder
+	 *            the responder handling the job event
+	 */
+	public Pool(Job job, PoolResponder responder) {
+		jobs = new ArrayList<Job>();
+		results = new HashMap<String, Future<Object>>();
+		this.name = job.getName();
+		this.responder = responder;
+		register(job);
+	}
+
+	/**
+	 * Create a new pool without any registered job (Callable).
+	 * 
+	 * @param name
+	 *            name of the pool
+	 * @param responder
+	 *            the responder handling the job event
+	 */
+	public Pool(String name, PoolResponder responder) {
+		jobs = new ArrayList<Job>();
+		results = new HashMap<String, Future<Object>>();
+		this.name = name;
+		this.responder = responder;
+	}
+
+	@Override
+	public void fire(JobEvent event) {
+
+		int t = event.getTypeEvent();
+		if (responder != null)
+			responder.onEvent(this, event);
+
+		if (t == JobEvent.EXCEPTION) {
+			die();
+			if (responder != null) {
+				responder.onFailure(this, event);
+			}
+		}
+		else if (t == JobEvent.ABORTED) {
+			die();
+			if (responder != null)
+				responder.onFailure(this, event);
+		}
+		else if (t == JobEvent.TIMEOUT) {
+			die();
+			if (responder != null)
+				responder.onFailure(this, event);
+		}
+		else if (t == JobEvent.COMPLETED) {
+			if (isJobDone()) {
+				die();
+				if (responder != null)
+					responder.onSuccess(this, event);
+			}
+		}
+	}
+
+	@Override
+	public void fire(JobAbstract parent, JobEvent event) {
+		if (parent == null)
+			return;
+		if (parent.getPool() == null)
+			return;
+		if (event.getTypeEvent() == JobEvent.EXCEPTION)
+			parent.getPool().fire(new JobEvent(parent, event.getException(), event.getMessage()));
+		else
+			parent.getPool().fire(new JobEvent(parent, event.getTypeEvent(), event.getMessage()));
+
+	}
+
+	public ArrayList<Job> getRegisteredJobs() {
+		return jobs;
+	}
+
+	public Job getRegisteredJob(int i) {
+		return jobs.get(i);
+	}
+
+	@Override
+	public void execute(ExecutionMode mode) {
+		execute(mode, jobs.size());
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public void execute(ExecutionMode mode, int nThreads) {
+		for (Job job : jobs)
+			job.init();
+		if (mode == ExecutionMode.MULTITHREAD_NO) {
+			for (Job job : jobs) {
+				job.call();
+			}
+		}
+		else {
+			if (nThreads <= 0)
+				nThreads = jobs.size();
+			ExecutorService executor = Executors.newFixedThreadPool(nThreads);
+			for (Job job : jobs) {
+				Future<?> future = executor.submit(job);
+				results.put(job.getName(), (Future<Object>) future);
+			}
+			executor.shutdown();
+			if (mode == ExecutionMode.MULTITHREAD_SYNCHRONIZED)
+				waitTermination();
+		}
+	}
+
+	@Override
+	public Future<Object> getFuture(String jobName) {
+		if (jobs != null)
+			for (int i = 0; i < jobs.size(); i++) {
+				if (jobs.get(i).getName().equals(jobName))
+					return results.get(jobs.get(i).getName());
+			}
+		return null;
+	}
+
+	@Override
+	public int size() {
+		int s = 0;
+		if (jobs != null)
+			s += jobs.size();
+		return s;
+	}
+
+	@Override
+	public void cancel() {
+		if (jobs != null)
+			for (Job job : jobs)
+				results.get(job.getName()).cancel(true);
+	}
+
+	@Override
+	public void register(JobAbstract job) {
+		if (jobs == null)
+			return;
+		if (job instanceof Job)
+			if (jobs.add((Job) job)) {
+				job.setName(name + "-" + jobs.size());
+				job.setPool(this);
+			}
+	}
+
+	@Override
+	public void register(JobAbstract job, double timeoutms) {
+		if (jobs == null)
+			return;
+		if (job instanceof Job)
+			if (jobs.add((Job) job)) {
+				job.setTimeOut(timeoutms);
+				job.setName(name + "-" + jobs.size());
+				job.setPool(this);
+			}
+	}
+
+	@Override
+	public void unregister(JobAbstract job) {
+		if (jobs == null)
+			return;
+		if (job instanceof Job) {
+			jobs.remove((Job) job);
+			job.setPool(null);
+		}
+	}
+
+	@Override
+	public void unregisterAll() {
+		if (jobs != null)
+			jobs.clear();
+	}
+
+	@Override
+	public boolean isJobDone() {
+		int count = 0;
+		if (jobs != null) {
+			for (Job job : jobs) {
+				if (job.done)
+					count++;
+			}
+			return (count == jobs.size());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean isJobLive() {
+		int count = 0;
+		if (jobs != null) {
+			for (Job job : jobs) {
+				if (job.live)
+					count++;
+			}
+			return (count == jobs.size());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean isJobIdle() {
+		int count = 0;
+		if (jobs != null) {
+			for (Job job : jobs) {
+				if (job.idle)
+					count++;
+			}
+			return (count == jobs.size());
+		}
+		return false;
+	}
+
+	@Override
+	public void waitTermination() {
+		boolean termination = false;
+
+		if (jobs != null)
+			while (!termination) {
+				int count = 0;
+				for (Job job : jobs)
+					if (!job.live)
+						count++;
+				termination = (count >= jobs.size());
+			}
+	}
+
+	@Override
+	public synchronized void die() {
+		if (jobs != null)
+			for (Job job : jobs) {
+				job.live = false;
+				job.done = true;
+			}
+	}
+
+	@Override
+	public String toString() {
+		String form = "";
+		if (jobs != null)
+			form = jobs.getClass().getName();
+		return " Pool=(" + name + ", " + size() + " " + form + ") ";
+	}
+
+	@Override
+	public String getName() {
+		return name;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/job/callable/PoolResponder.java b/src/bilib/src/bilib/commons/job/callable/PoolResponder.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9d59c852789e1c2813ebe5cd26a3e65b5d83571
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/callable/PoolResponder.java
@@ -0,0 +1,24 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.callable;
+
+import bilib.commons.job.JobEvent;
+
+public interface PoolResponder {
+
+	public abstract void onEvent(Pool pool, JobEvent event);
+
+	public abstract void onSuccess(Pool pool, JobEvent event);
+
+	public abstract void onFailure(Pool pool, JobEvent event);
+}
diff --git a/src/bilib/src/bilib/commons/job/runnable/Job.java b/src/bilib/src/bilib/commons/job/runnable/Job.java
new file mode 100644
index 0000000000000000000000000000000000000000..00ddb9528737e0e4a7f1a9c8fd77391065499ea4
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/runnable/Job.java
@@ -0,0 +1,251 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.runnable;
+
+import java.util.ArrayList;
+
+import bilib.commons.job.JobAbstract;
+import bilib.commons.job.JobEvent;
+import bilib.commons.job.MonitorAbstract;
+import bilib.commons.job.PoolAbstract;
+
+public abstract class Job implements JobAbstract, MonitorAbstract, Runnable {
+
+	public boolean						live		= false;
+	public boolean						done		= false;
+	public boolean						idle		= false;
+
+	private double						chrono		= -1;
+	private double						timeoutns	= -1;
+	private boolean						stopTimeOut	= false;
+	private ArrayList<MonitorAbstract>	monitors	= new ArrayList<MonitorAbstract>();
+
+	private PoolAbstract				pool;
+	private String						name;
+
+	public abstract void process() throws RuntimeException;
+
+	public Job() {
+	}
+
+	public Job(MonitorAbstract monitor) {
+		monitors.add(monitor);
+	}
+
+	@Override
+	public String getName() {
+		return name;
+	}
+
+	@Override
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	@Override
+	public PoolAbstract getPool() {
+		return pool;
+	}
+
+	@Override
+	public void setPool(PoolAbstract pool) {
+		this.pool = pool;
+	}
+
+	@Override
+	public void setTimeOut(double timeoutms) {
+		this.timeoutns = timeoutms * 1000000.0;
+		this.stopTimeOut = true;
+	}
+
+	@Override
+	public void abort() {
+		idle = false;
+		live = false;
+		done = true;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.ABORTED));
+	}
+
+	@Override
+	public void interrupt() {
+		idle = false;
+		live = false;
+		done = true;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.INTERRUPTED, "Interruption"));
+	}
+
+	@Override
+	public void abort(String message) {
+		idle = false;
+		live = false;
+		done = true;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.ABORTED, message));
+	}
+
+	@Override
+	public void interrupt(String message) {
+		idle = false;
+		live = false;
+		done = true;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.INTERRUPTED, message));
+	}
+
+	@Override
+	public void init() {
+		idle = true;
+		live = true;
+		done = false;
+		chrono = System.nanoTime();
+	}
+
+	@Override
+	public void run() {
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.STARTED));
+		try {
+			idle = false;
+			process();
+			if (live) {
+				done = true;
+				live = false;
+				if (pool != null)
+					pool.fire(new JobEvent(this, JobEvent.COMPLETED));
+			}
+		}
+		catch (IllegalArgumentException ex) { // Workaround a unexplained Icy exception
+		}
+		catch (RuntimeException ex) {
+			if (pool != null)
+				pool.fire(new JobEvent(this, ex, ex.getMessage()));
+		}
+		live = false;
+		idle = true;
+	}
+
+	@Override
+	public boolean isNotTimeOut() {
+		if (!live)
+			return false;
+		if (stopTimeOut)
+			if ((System.nanoTime() - chrono) > timeoutns) {
+				if (pool != null)
+					pool.fire(new JobEvent(this, JobEvent.TIMEOUT));
+				live = false;
+				done = true;
+				return false;
+			}
+		return true;
+	}
+
+	@Override
+	public boolean isJobLive() {
+		return live;
+	}
+
+	@Override
+	public boolean isJobDone() {
+		return done;
+	}
+
+	@Override
+	public boolean isJobIdle() {
+		return idle;
+	}
+
+	@Override
+	public boolean isJobProcessing() {
+		return !idle;
+	}
+
+	@Override
+	public boolean isJobIncomplete() {
+		return !done;
+	}
+
+	@Override
+	public String toString() {
+		return "JobRunnable: " + getName();
+	}
+
+	@Override
+	public void rewind() {
+		for (MonitorAbstract monitor : monitors)
+			monitor.rewind();
+	}
+
+	@Override
+	public void print(String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.print(message);
+	}
+
+	@Override
+	public void error(String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.error(message);
+	}
+
+	@Override
+	public void warning(String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.warning(message);
+	}
+
+	@Override
+	public void progress(double percentage) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.progress(percentage);
+	}
+
+	@Override
+	public void progress(double percentage, String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.progress(percentage, message);
+	}
+
+	@Override
+	public void increment(double percentageIncrement) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.increment(percentageIncrement);
+	}
+
+	@Override
+	public void increment(double percentageIncrement, String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.increment(percentageIncrement, message);
+	}
+
+	@Override
+	public void addMonitor(MonitorAbstract monitor) {
+		monitors.add(monitor);
+	}
+
+	@Override
+	public void removeMonitor(MonitorAbstract monitor) {
+		monitors.remove(monitor);
+	}
+
+	@Override
+	public void clearMonitor() {
+		monitors.clear();
+	}
+
+	@Override
+	public ArrayList<MonitorAbstract> getMonitor() {
+		return monitors;
+	}
+}
diff --git a/src/bilib/src/bilib/commons/job/runnable/Pool.java b/src/bilib/src/bilib/commons/job/runnable/Pool.java
new file mode 100644
index 0000000000000000000000000000000000000000..23fbd24ab1d0fe592afebd9de2b79b9f572912d6
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/runnable/Pool.java
@@ -0,0 +1,286 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.runnable;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import bilib.commons.job.ExecutionMode;
+import bilib.commons.job.JobAbstract;
+import bilib.commons.job.JobEvent;
+import bilib.commons.job.PoolAbstract;
+
+public class Pool implements PoolAbstract {
+
+	private ArrayList<Job>						jobs;
+	protected String							name;
+	protected PoolResponder						responder;
+
+	protected HashMap<String, Future<Object>>	results;
+
+	/**
+	 * Create a new pool with one registered job (Runnable).
+	 * 
+	 * @param job
+	 *            the job to register
+	 * @param responder
+	 *            the responder handling the job event
+	 */
+	public Pool(Job job, PoolResponder responder) {
+		jobs = new ArrayList<Job>();
+		results = new HashMap<String, Future<Object>>();
+		this.name = job.getName();
+		this.responder = responder;
+		register(job);
+	}
+
+	/**
+	 * Create a new pool without any registered job (Runnable).
+	 * 
+	 * @param name
+	 *            name of the pool
+	 * @param responder
+	 *            the responder handling the job event
+	 */
+	public Pool(String name, PoolResponder responder) {
+		jobs = new ArrayList<Job>();
+		results = new HashMap<String, Future<Object>>();
+		this.name = name;
+		this.responder = responder;
+	}
+
+	@Override
+	public void fire(JobEvent event) {
+
+		int t = event.getTypeEvent();
+		if (responder != null)
+			responder.onEvent(this, event);
+
+		if (t == JobEvent.EXCEPTION) {
+			die();
+			if (responder != null) {
+				responder.onFailure(this, event);
+			}
+		}
+		else if (t == JobEvent.ABORTED) {
+			die();
+			if (responder != null)
+				responder.onFailure(this, event);
+		}
+		else if (t == JobEvent.TIMEOUT) {
+			die();
+			if (responder != null)
+				responder.onFailure(this, event);
+		}
+		else if (t == JobEvent.COMPLETED) {
+			if (isJobDone()) {
+				die();
+				if (responder != null)
+					responder.onSuccess(this, event);
+			}
+		}
+	}
+
+	@Override
+	public void fire(JobAbstract parent, JobEvent event) {
+		if (parent == null)
+			return;
+		if (parent.getPool() == null)
+			return;
+		if (event.getTypeEvent() == JobEvent.EXCEPTION)
+			parent.getPool().fire(new JobEvent(parent, event.getException(), event.getMessage()));
+		else
+			parent.getPool().fire(new JobEvent(parent, event.getTypeEvent(), event.getMessage()));
+
+	}
+
+	public ArrayList<Job> getRegisteredJobs() {
+		return jobs;
+	}
+
+	public Job getRegisteredJob(int i) {
+		return jobs.get(i);
+	}
+
+	@Override
+	public void execute(ExecutionMode mode) {
+		execute(mode, jobs.size());
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public void execute(ExecutionMode mode, int nThreads) {
+		for (Job job : jobs)
+			job.init();
+		if (mode == ExecutionMode.MULTITHREAD_NO) {
+			for (Job job : jobs) {
+				job.run();
+			}
+		}
+		else {
+			if (nThreads <= 0)
+				nThreads = jobs.size();
+			ExecutorService executor = Executors.newFixedThreadPool(nThreads);
+			for (Job job : jobs) {
+				Future<?> future = executor.submit(job);
+				results.put(job.getName(), (Future<Object>) future);
+			}
+			executor.shutdown();
+			if (mode == ExecutionMode.MULTITHREAD_SYNCHRONIZED)
+				waitTermination();
+		}
+	}
+
+	@Override
+	public Future<Object> getFuture(String jobName) {
+		if (jobs != null)
+			for (int i = 0; i < jobs.size(); i++) {
+				if (jobs.get(i).getName().equals(jobName))
+					return results.get(jobs.get(i).getName());
+			}
+		return null;
+	}
+
+	@Override
+	public int size() {
+		int s = 0;
+		if (jobs != null)
+			s += jobs.size();
+		return s;
+	}
+
+	@Override
+	public void cancel() {
+		if (jobs != null)
+			for (Job job : jobs)
+				results.get(job.getName()).cancel(true);
+	}
+
+	@Override
+	public void register(JobAbstract job) {
+		if (jobs == null)
+			return;
+		if (job instanceof Job)
+			if (jobs.add((Job) job)) {
+				job.setName(name + "-" + jobs.size());
+				job.setPool(this);
+			}
+	}
+
+	@Override
+	public void register(JobAbstract job, double timeoutms) {
+		if (jobs == null)
+			return;
+		if (job instanceof Job)
+			if (jobs.add((Job) job)) {
+				job.setTimeOut(timeoutms);
+				job.setName(name + "-" + jobs.size());
+				job.setPool(this);
+			}
+	}
+
+	@Override
+	public void unregister(JobAbstract job) {
+		if (jobs == null)
+			return;
+		if (job instanceof Job) {
+			jobs.remove((Job) job);
+			job.setPool(null);
+		}
+	}
+
+	@Override
+	public void unregisterAll() {
+		if (jobs != null)
+			jobs.clear();
+	}
+
+	@Override
+	public boolean isJobDone() {
+		int count = 0;
+		if (jobs != null) {
+			for (Job job : jobs) {
+				if (job.done)
+					count++;
+			}
+			return (count == jobs.size());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean isJobLive() {
+		int count = 0;
+		if (jobs != null) {
+			for (Job job : jobs) {
+				if (job.live)
+					count++;
+			}
+			return (count == jobs.size());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean isJobIdle() {
+		int count = 0;
+		if (jobs != null) {
+			for (Job job : jobs) {
+				if (job.idle)
+					count++;
+			}
+			return (count == jobs.size());
+		}
+		return false;
+	}
+
+	@Override
+	public void waitTermination() {
+		boolean termination = false;
+
+		if (jobs != null)
+			while (!termination) {
+				int count = 0;
+				for (Job job : jobs)
+					if (!job.live)
+						count++;
+				termination = (count >= jobs.size());
+			}
+	}
+
+	@Override
+	public synchronized void die() {
+		if (jobs != null)
+			for (Job job : jobs) {
+				job.live = false;
+				job.done = true;
+			}
+	}
+
+	@Override
+	public String toString() {
+		String form = "";
+		if (jobs != null)
+			form = jobs.getClass().getName();
+		return " Pool=(" + name + ", " + size() + " " + form + ") ";
+	}
+
+	@Override
+	public String getName() {
+		return name;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/job/runnable/PoolResponder.java b/src/bilib/src/bilib/commons/job/runnable/PoolResponder.java
new file mode 100644
index 0000000000000000000000000000000000000000..640c786413f9cb65cb79b91cf179fc4ffaccf2c0
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/runnable/PoolResponder.java
@@ -0,0 +1,24 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.runnable;
+
+import bilib.commons.job.JobEvent;
+
+public interface PoolResponder {
+
+	public abstract void onEvent(Pool pool, JobEvent event);
+
+	public abstract void onSuccess(Pool pool, JobEvent event);
+
+	public abstract void onFailure(Pool pool, JobEvent event);
+}
diff --git a/src/bilib/src/bilib/commons/job/runnable/RunnableDemo.java b/src/bilib/src/bilib/commons/job/runnable/RunnableDemo.java
new file mode 100644
index 0000000000000000000000000000000000000000..80a4340373a6210032b01ad15f8761f2b482b79d
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/runnable/RunnableDemo.java
@@ -0,0 +1,301 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.runnable;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+
+import bilib.commons.job.ExecutionMode;
+import bilib.commons.job.JobEvent;
+import bilib.commons.job.MonitorAbstract;
+import bilib.commons.job.MonitorProgressBar;
+import bilib.commons.job.MonitorTimedLog;
+import bilib.commons.job.MonitorTimedProgressBar;
+import bilib.commons.job.runnable.Pool;
+
+public class RunnableDemo extends JFrame implements PoolResponder, ActionListener {
+
+	public JRadioButton				rbSequential			= new JRadioButton("Sequential", false);
+	public JRadioButton				rbAsynchronized			= new JRadioButton("Parallel - Asynchronized jobs (not wait)", false);
+	public JRadioButton				rbSynchronized			= new JRadioButton("Parallel - Synchronized jobs (wait termination)", true);
+
+	public JButton					execute					= new JButton("Execute");
+	public JTextField				nthreads				= new JTextField("0");
+	public JTextField				njobs					= new JTextField("10");
+	public JButton					simulateAbort			= new JButton("Abort");
+	public JButton					simulateException		= new JButton("Simulate an Exception");
+	public JButton					simulateJobException	= new JButton("Simulate an JobException");
+	public JButton					simulateInterruption	= new JButton("Simulate an Interruption");
+	public JButton					simulateCancellation	= new JButton("Simulate an Cancellation");
+	public JButton					simulateTimeOut			= new JButton("Simulate an TimeOut");
+
+	public MonitorTimedProgressBar	bar						= new MonitorTimedProgressBar();
+	public MonitorProgressBar		bar1					= new MonitorProgressBar();
+	public MonitorTimedLog			log						= new MonitorTimedLog(10, 10);
+
+	private double					chrono;
+	private MainProcessing			main;
+
+	public static void main(String args[]) {
+		new RunnableDemo();
+	}
+
+	public RunnableDemo() {
+		super("Runnable Demo");
+		main = new MainProcessing(this);
+
+		ButtonGroup bg1 = new ButtonGroup();
+		bg1.add(rbSequential);
+		bg1.add(rbAsynchronized);
+		bg1.add(rbSynchronized);
+
+		JPanel panel1 = new JPanel(new GridLayout(20, 1));
+
+		panel1.add(rbSequential);
+		panel1.add(rbAsynchronized);
+		panel1.add(rbSynchronized);
+
+		panel1.add(new JLabel("Number of threads (0 = nb of jobs)"));
+		panel1.add(nthreads);
+		panel1.add(new JLabel("Number of jobs"));
+		panel1.add(njobs);
+		panel1.add(execute);
+		panel1.add(new JLabel(""));
+		panel1.add(simulateAbort);
+		panel1.add(simulateException);
+		panel1.add(simulateJobException);
+		panel1.add(simulateInterruption);
+		panel1.add(simulateCancellation);
+		panel1.add(simulateTimeOut);
+
+		JPanel panel3 = new JPanel(new BorderLayout());
+		panel3.add(bar, BorderLayout.NORTH);
+		panel3.add(bar1, BorderLayout.SOUTH);
+
+		JPanel panel = new JPanel(new BorderLayout());
+		panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
+		panel.add(panel1, BorderLayout.NORTH);
+		panel.add(panel3, BorderLayout.SOUTH);
+		panel.add(log, BorderLayout.CENTER);
+
+		execute.addActionListener(this);
+		simulateAbort.addActionListener(this);
+		simulateException.addActionListener(this);
+		simulateJobException.addActionListener(this);
+		simulateInterruption.addActionListener(this);
+		simulateCancellation.addActionListener(this);
+		simulateTimeOut.addActionListener(this);
+		getContentPane().add(panel);
+		pack();
+		setVisible(true);
+	}
+
+	private ExecutionMode getMode() {
+		ExecutionMode mode = ExecutionMode.MULTITHREAD_NO;
+		if (rbSynchronized.isSelected())
+			mode = ExecutionMode.MULTITHREAD_SYNCHRONIZED;
+		if (rbAsynchronized.isSelected())
+			mode = ExecutionMode.MULTITHREAD_ASYNCHRONIZED;
+		return mode;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent event) {
+
+		if (event.getSource() == execute) {
+			main.setMode(getMode(), Integer.parseInt(nthreads.getText()), Integer.parseInt(njobs.getText()));
+			System.out.println("\n\n");
+			Thread thread = new Thread(main);
+			thread.start();
+			chrono = System.nanoTime();
+		}
+
+		if (event.getSource() == simulateAbort)
+			main.simulateAbort();
+
+		if (event.getSource() == simulateException)
+			main.simulateException();
+
+		if (event.getSource() == simulateJobException)
+			main.simulateJobException();
+
+		if (event.getSource() == simulateInterruption)
+			main.simulateInterruption();
+
+		if (event.getSource() == simulateCancellation)
+			main.simulateCancellation();
+
+		if (event.getSource() == simulateTimeOut)
+			main.simulateTimeOut();
+	}
+
+	//
+	// MainProcessing
+	//
+	public class MainProcessing extends Job implements PoolResponder {
+		private ExecutionMode	mode;
+		private int				nthreads	= 0;
+		private int				njobs;
+		private PoolResponder	responder;
+		private Pool			pool;
+		private int				niter		= 15;
+
+		public MainProcessing(PoolResponder responder) {
+			super();
+			this.responder = responder;
+		}
+
+		public void setMode(ExecutionMode mode, int nthreads, int njobs) {
+			this.mode = mode;
+			this.njobs = njobs;
+			this.nthreads = nthreads;
+		}
+
+		@Override
+		public void process() {
+			bar.rewind();
+			bar1.rewind();
+			log.rewind();
+			pool = new Pool("my", responder);
+			MyRunnable p1 = new MyRunnable(niter, bar);
+			p1.addMonitor(log);
+			p1.addMonitor(bar1);
+			pool.register(p1);
+			for (int i = 0; i < njobs; i++)
+				pool.register(new MyRunnable(niter, bar));
+			pool.execute(mode, nthreads);
+			System.out.println("End of main");
+		}
+
+		public void simulateAbort() {
+			if (pool != null)
+				pool.getRegisteredJob(pool.size() - 1).abort();
+		}
+
+		public void simulateCancellation() {
+			if (pool != null)
+				pool.cancel();
+		}
+
+		public void simulateException() {
+			if (pool != null)
+				((MyRunnable) pool.getRegisteredJob(pool.size() / 2 - 1)).withException = true;
+		}
+
+		public void simulateJobException() {
+			if (pool != null)
+				((MyRunnable) pool.getRegisteredJob(pool.size() - 1)).withJobException = true;
+		}
+
+		public void simulateInterruption() {
+			if (pool != null)
+				((MyRunnable) pool.getRegisteredJob(pool.size() - 1)).withInterruption = true;
+		}
+
+		public void simulateTimeOut() {
+			if (pool != null)
+				((MyRunnable) pool.getRegisteredJob(pool.size() - 1)).withTimeOut = true;
+		}
+
+		@Override
+		public void onEvent(Pool pool, JobEvent event) {
+			responder.onEvent(pool, event);
+		}
+
+		@Override
+		public void onSuccess(Pool pool, JobEvent event) {
+			responder.onSuccess(pool, event);
+		}
+
+		@Override
+		public void onFailure(Pool pool, JobEvent event) {
+			responder.onFailure(pool, event);
+		}
+
+	}
+
+	//
+	// MyRunnable
+	//
+	public class MyRunnable extends Job {
+		public boolean	withTimeOut			= false;
+		public boolean	withInterruption	= false;
+		public boolean	withException		= false;
+		public boolean	withJobException	= false;
+		private int		niter				= 15;
+
+		public MyRunnable(int niter, MonitorAbstract monitor) {
+			super(monitor);
+			this.niter = niter;
+		}
+
+		@Override
+		public void process() {
+			int i = 0;
+			for (i = 0; i < niter && live; i++) {
+				increment(2, getName() + " " + live + " " + done);
+				for (int j = 0; j < 600000 && live; j++)
+					Math.round(Math.cos(Math.exp(Math.pow(j, -3))));
+				if (withJobException)
+					throw new RuntimeException("It is a programming exception");
+				if (withInterruption)
+					interrupt();
+				if (withTimeOut)
+					this.setTimeOut(1000);
+				if (withException) {
+					double a[] = new double[2];
+					a[3] = 1;
+				}
+				if (!live)
+					return;
+				if (!isNotTimeOut()) {
+					return;
+				}
+			}
+			System.out.println("End of " + getName() + " iterations:" + i + " " + live + " " + done);
+		}
+
+	}
+
+	@Override
+	public void onEvent(Pool pool, JobEvent event) {
+		if (event.getTypeEvent() == JobEvent.INTERRUPTED)
+			System.out.println("Main side: Job Event " + event);
+	}
+
+	@Override
+	public void onSuccess(Pool pool, JobEvent event) {
+		System.out.println(">>> Success " + event + " from pool " + pool.getName());
+		System.out.println("Computation time: " + (System.nanoTime() - chrono) / 1000000.0);
+	}
+
+	@Override
+	public void onFailure(Pool pool, JobEvent event) {
+		System.out.println(">>> Failure " + event + " from pool " + pool.getName());
+		if (event.getException() != null)
+			event.getException().printStackTrace();
+	}
+
+
+}
diff --git a/src/bilib/src/bilib/commons/job/worker/Job.java b/src/bilib/src/bilib/commons/job/worker/Job.java
new file mode 100644
index 0000000000000000000000000000000000000000..f71a1216bea316e1606269558fe58825a1dead69
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/worker/Job.java
@@ -0,0 +1,255 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.worker;
+
+import java.util.ArrayList;
+
+import javax.swing.SwingWorker;
+
+import bilib.commons.job.JobAbstract;
+import bilib.commons.job.JobEvent;
+import bilib.commons.job.MonitorAbstract;
+import bilib.commons.job.PoolAbstract;
+
+public abstract class Job extends SwingWorker<Object, String> implements JobAbstract, MonitorAbstract {
+
+	protected boolean					live		= false;
+	protected boolean					done		= false;
+	protected boolean					idle		= false;
+
+	private double						chrono		= -1;
+	private double						timeoutns	= -1;
+	private boolean						stopTimeOut	= false;
+	private ArrayList<MonitorAbstract>	monitors	= new ArrayList<MonitorAbstract>();
+
+	private PoolAbstract				pool;
+
+	public abstract Object process() throws RuntimeException;
+
+	private String	name;
+
+	public Job() {
+	}
+
+	public Job(MonitorAbstract monitor) {
+		monitors.add(monitor);
+	}
+
+	@Override
+	public String getName() {
+		return name;
+	}
+
+	@Override
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	@Override
+	public PoolAbstract getPool() {
+		return pool;
+	}
+
+	@Override
+	public void setPool(PoolAbstract pool) {
+		this.pool = pool;
+	}
+
+	@Override
+	public void setTimeOut(double timeoutms) {
+		this.timeoutns = timeoutms * 1000000.0;
+		this.stopTimeOut = true;
+	}
+
+	@Override
+	public void abort() {
+		idle = false;
+		live = false;
+		done = false;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.ABORTED));
+	}
+
+	@Override
+	public void interrupt() {
+		idle = false;
+		live = false;
+		done = false;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.INTERRUPTED));
+	}
+
+	@Override
+	public void abort(String message) {
+		idle = false;
+		live = false;
+		done = false;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.ABORTED, message));
+	}
+
+	@Override
+	public void interrupt(String message) {
+		idle = false;
+		live = false;
+		done = false;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.INTERRUPTED, message));
+	}
+
+	@Override
+	public void init() {
+		idle = true;
+		live = true;
+		done = false;
+		chrono = System.nanoTime();
+	}
+
+	@Override
+	public Object doInBackground() {
+		Object o = null;
+		if (pool != null)
+			pool.fire(new JobEvent(this, JobEvent.STARTED));
+		try {
+			idle = false;
+			o = process();
+			if (live) {
+				done = true;
+				live = false;
+				if (pool != null)
+					pool.fire(new JobEvent(this, JobEvent.COMPLETED));
+			}
+		}
+		catch (RuntimeException ex) {
+			if (pool != null)
+				pool.fire(new JobEvent(this, ex, ex.getMessage()));
+		}
+		live = false;
+		idle = true;
+		return o;
+	}
+
+	@Override
+	public boolean isNotTimeOut() {
+		if (!live)
+			return false;
+		if (stopTimeOut)
+			if ((System.nanoTime() - chrono) > timeoutns) {
+				if (pool != null)
+					pool.fire(new JobEvent(this, JobEvent.TIMEOUT));
+				live = false;
+				done = false;
+				return false;
+			}
+		return true;
+	}
+
+	@Override
+	public boolean isJobLive() {
+		return live;
+	}
+
+	@Override
+	public boolean isJobDone() {
+		return done;
+	}
+
+	@Override
+	public boolean isJobIdle() {
+		return idle;
+	}
+
+	@Override
+	public boolean isJobProcessing() {
+		return !idle;
+	}
+
+	@Override
+	public boolean isJobIncomplete() {
+		return !done;
+	}
+
+	@Override
+	public String toString() {
+		return "JobWorker: " + getName();
+	}
+
+	@Override
+	public void rewind() {
+		for (MonitorAbstract monitor : monitors)
+			monitor.rewind();
+	}
+
+	@Override
+	public void print(String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.print(message);
+	}
+
+	@Override
+	public void error(String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.error(message);
+	}
+
+	@Override
+	public void warning(String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.warning(message);
+	}
+
+	@Override
+	public void progress(double percentage) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.progress(percentage);
+	}
+
+	@Override
+	public void progress(double percentage, String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.progress(percentage, message);
+	}
+
+	@Override
+	public void increment(double percentageIncrement) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.increment(percentageIncrement);
+	}
+
+	@Override
+	public void increment(double percentageIncrement, String message) {
+		for (MonitorAbstract monitor : monitors)
+			monitor.increment(percentageIncrement, message);
+	}
+
+	@Override
+	public void addMonitor(MonitorAbstract monitor) {
+		monitors.add(monitor);
+	}
+
+	@Override
+	public void removeMonitor(MonitorAbstract monitor) {
+		monitors.remove(monitor);
+	}
+
+	@Override
+	public void clearMonitor() {
+		monitors.clear();
+	}
+
+	@Override
+	public ArrayList<MonitorAbstract> getMonitor() {
+		return monitors;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/job/worker/Pool.java b/src/bilib/src/bilib/commons/job/worker/Pool.java
new file mode 100644
index 0000000000000000000000000000000000000000..30fd246fedb79c93eaea39f627993bec0c229830
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/worker/Pool.java
@@ -0,0 +1,292 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.worker;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import bilib.commons.job.ExecutionMode;
+import bilib.commons.job.JobAbstract;
+import bilib.commons.job.JobEvent;
+import bilib.commons.job.PoolAbstract;
+import bilib.commons.job.worker.Job;
+import bilib.commons.job.worker.PoolResponder;
+
+public class Pool implements PoolAbstract {
+
+	private ArrayList<Job>						jobs;
+	protected String							name;
+	protected PoolResponder						responder;
+
+	protected HashMap<String, Future<Object>>	results;
+
+	/**
+	 * Create a new pool with one registered job (Worker).
+	 * 
+	 * @param job
+	 *            the job to register
+	 * @param responder
+	 *            the responder handling the job event
+	 */
+	public Pool(Job job, PoolResponder responder) {
+		jobs = new ArrayList<Job>();
+		results = new HashMap<String, Future<Object>>();
+		this.name = job.getName();
+		this.responder = responder;
+		register(job);
+	}
+
+	/**
+	 * Create a new pool without any registered job (Worker).
+	 * 
+	 * @param name
+	 *            name of the pool
+	 * @param responder
+	 *            the responder handling the job event
+	 */
+	public Pool(String name, PoolResponder responder) {
+		jobs = new ArrayList<Job>();
+		results = new HashMap<String, Future<Object>>();
+		this.name = name;
+		this.responder = responder;
+	}
+
+	@Override
+	public void fire(JobEvent event) {
+
+		int t = event.getTypeEvent();
+		if (responder != null)
+			responder.onEvent(this, event);
+
+		if (t == JobEvent.EXCEPTION) {
+			die();
+			if (responder != null) {
+				responder.onFailure(this, event);
+			}
+		}
+		else if (t == JobEvent.ABORTED) {
+			die();
+			if (responder != null)
+				responder.onFailure(this, event);
+		}
+		else if (t == JobEvent.TIMEOUT) {
+			die();
+			if (responder != null)
+				responder.onFailure(this, event);
+		}
+		else if (t == JobEvent.COMPLETED) {
+			if (isJobDone()) {
+				die();
+				if (responder != null)
+					responder.onSuccess(this, event);
+			}
+		}
+	}
+
+	@Override
+	public void fire(JobAbstract parent, JobEvent event) {
+		if (parent == null)
+			return;
+		if (parent.getPool() == null)
+			return;
+		if (event.getTypeEvent() == JobEvent.EXCEPTION)
+			parent.getPool().fire(new JobEvent(parent, event.getException(), event.getMessage()));
+		else
+			parent.getPool().fire(new JobEvent(parent, event.getTypeEvent(), event.getMessage()));
+
+	}
+
+	public ArrayList<Job> getRegisteredJobs() {
+		return jobs;
+	}
+
+	public Job getRegisteredJob(int i) {
+		return jobs.get(i);
+	}
+
+	@Override
+	public void execute(ExecutionMode mode) {
+		execute(mode, jobs.size());
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public void execute(ExecutionMode mode, int nThreads) {
+		for (Job job : jobs)
+			job.init();
+		if (mode == ExecutionMode.MULTITHREAD_NO) {
+			try {
+				for (Job job : jobs)
+					job.doInBackground();
+			}
+			catch (Exception ex) {
+				ex.printStackTrace();
+			}
+		}
+		else {
+			if (nThreads <= 0)
+				nThreads = jobs.size();
+			ExecutorService executor = Executors.newFixedThreadPool(nThreads);
+			for (Job job : jobs) {
+				Future<?> future = executor.submit(job);
+				results.put(job.getName(), (Future<Object>) future);
+			}
+			executor.shutdown();
+			if (mode == ExecutionMode.MULTITHREAD_SYNCHRONIZED)
+				waitTermination();
+		}
+	}
+
+	@Override
+	public Future<Object> getFuture(String jobName) {
+		if (jobs != null)
+			for (int i = 0; i < jobs.size(); i++) {
+				if (jobs.get(i).getName().equals(jobName))
+					return results.get(jobs.get(i).getName());
+			}
+		return null;
+	}
+
+	@Override
+	public int size() {
+		int s = 0;
+		if (jobs != null)
+			s += jobs.size();
+		return s;
+	}
+
+	@Override
+	public void cancel() {
+		if (jobs != null)
+			for (Job job : jobs)
+				results.get(job.getName()).cancel(true);
+	}
+
+	@Override
+	public void register(JobAbstract job) {
+		if (jobs == null)
+			return;
+		if (job instanceof Job)
+			if (jobs.add((Job) job)) {
+				job.setName(name + "-" + jobs.size());
+				job.setPool(this);
+			}
+	}
+
+	@Override
+	public void register(JobAbstract job, double timeoutms) {
+		if (jobs == null)
+			return;
+		if (job instanceof Job)
+			if (jobs.add((Job) job)) {
+				job.setTimeOut(timeoutms);
+				job.setName(name + "-" + jobs.size());
+				job.setPool(this);
+			}
+	}
+
+	@Override
+	public void unregister(JobAbstract job) {
+		if (jobs == null)
+			return;
+		if (job instanceof Job) {
+			jobs.remove((Job) job);
+			job.setPool(null);
+		}
+	}
+
+	@Override
+	public void unregisterAll() {
+		if (jobs != null)
+			jobs.clear();
+	}
+
+	@Override
+	public boolean isJobDone() {
+		int count = 0;
+		if (jobs != null) {
+			for (Job job : jobs) {
+				if (job.done)
+					count++;
+			}
+			return (count == jobs.size());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean isJobLive() {
+		int count = 0;
+		if (jobs != null) {
+			for (Job job : jobs) {
+				if (job.live)
+					count++;
+			}
+			return (count == jobs.size());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean isJobIdle() {
+		int count = 0;
+		if (jobs != null) {
+			for (Job job : jobs) {
+				if (job.idle)
+					count++;
+			}
+			return (count == jobs.size());
+		}
+		return false;
+	}
+
+	@Override
+	public void waitTermination() {
+		boolean termination = false;
+
+		if (jobs != null)
+			while (!termination) {
+				int count = 0;
+				for (Job job : jobs)
+					if (!job.live)
+						count++;
+				termination = (count >= jobs.size());
+			}
+	}
+
+	@Override
+	public synchronized void die() {
+		if (jobs != null)
+			for (Job job : jobs) {
+				job.live = false;
+				job.done = true;
+			}
+	}
+
+	@Override
+	public String toString() {
+		String form = "";
+		if (jobs != null)
+			form = jobs.getClass().getName();
+		return " Pool=(" + name + ", " + size() + " " + form + ") ";
+	}
+
+	@Override
+	public String getName() {
+		return name;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/job/worker/PoolResponder.java b/src/bilib/src/bilib/commons/job/worker/PoolResponder.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5c23ade9ed98f93cba08ded8c19ca9924ecfdfd
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/worker/PoolResponder.java
@@ -0,0 +1,24 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.worker;
+
+import bilib.commons.job.JobEvent;
+
+public interface PoolResponder {
+
+	public abstract void onEvent(Pool pool, JobEvent event);
+
+	public abstract void onSuccess(Pool pool, JobEvent event);
+
+	public abstract void onFailure(Pool pool, JobEvent event);
+}
diff --git a/src/bilib/src/bilib/commons/job/worker/WorkerDemo.java b/src/bilib/src/bilib/commons/job/worker/WorkerDemo.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a76d0356689ed8a81c96167be5a41d2a35a876c
--- /dev/null
+++ b/src/bilib/src/bilib/commons/job/worker/WorkerDemo.java
@@ -0,0 +1,303 @@
+//====================================================================================================
+// Project: Bilib Job
+// 
+// Authors: Daniel Sage
+// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne
+// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland
+//
+// Conditions of use:
+// You'll be free to use this software for research purposes, but you should not redistribute 
+// it without our consent. In addition, we expect you to include a citation whenever you 
+// present or publish results that are based on it.
+//====================================================================================================
+package bilib.commons.job.worker;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+
+import bilib.commons.job.ExecutionMode;
+import bilib.commons.job.JobEvent;
+import bilib.commons.job.MonitorAbstract;
+import bilib.commons.job.MonitorProgressBar;
+import bilib.commons.job.MonitorTimedLog;
+import bilib.commons.job.MonitorTimedProgressBar;
+import bilib.commons.job.worker.PoolResponder;
+import bilib.commons.job.worker.Job;
+
+public class WorkerDemo extends JFrame implements PoolResponder, ActionListener {
+
+	public JRadioButton				rbSequential			= new JRadioButton("Sequential", false);
+	public JRadioButton				rbAsynchronized			= new JRadioButton("Parallel - Asynchronized jobs (not wait)", false);
+	public JRadioButton				rbSynchronized			= new JRadioButton("Parallel - Synchronized jobs (wait termination)", true);
+
+	public JButton					execute					= new JButton("Execute");
+	public JTextField				nthreads				= new JTextField("0");
+	public JTextField				njobs					= new JTextField("10");
+	public JButton					simulateAbort			= new JButton("Abort");
+	public JButton					simulateException		= new JButton("Simulate an Exception");
+	public JButton					simulateJobException	= new JButton("Simulate an JobException");
+	public JButton					simulateInterruption	= new JButton("Simulate an Interruption");
+	public JButton					simulateCancellation	= new JButton("Simulate an Cancellation");
+	public JButton					simulateTimeOut			= new JButton("Simulate an TimeOut");
+
+	public MonitorTimedProgressBar	bar						= new MonitorTimedProgressBar();
+	public MonitorProgressBar		bar1					= new MonitorProgressBar();
+	public MonitorTimedLog			log						= new MonitorTimedLog(10, 10);
+
+	private double					chrono;
+	private MainProcessing			main;
+
+	public static void main(String args[]) {
+		new WorkerDemo();
+	}
+
+	public WorkerDemo() {
+		super("Worker Demo");
+		main = new MainProcessing(this);
+
+		ButtonGroup bg1 = new ButtonGroup();
+		bg1.add(rbSequential);
+		bg1.add(rbAsynchronized);
+		bg1.add(rbSynchronized);
+
+		JPanel panel1 = new JPanel(new GridLayout(20, 1));
+
+		panel1.add(rbSequential);
+		panel1.add(rbAsynchronized);
+		panel1.add(rbSynchronized);
+
+		panel1.add(new JLabel("Number of threads (0 = nb of jobs)"));
+		panel1.add(nthreads);
+		panel1.add(new JLabel("Number of jobs"));
+		panel1.add(njobs);
+		panel1.add(execute);
+		panel1.add(new JLabel(""));
+		panel1.add(simulateAbort);
+		panel1.add(simulateException);
+		panel1.add(simulateJobException);
+		panel1.add(simulateInterruption);
+		panel1.add(simulateCancellation);
+		panel1.add(simulateTimeOut);
+
+		JPanel panel3 = new JPanel(new BorderLayout());
+		panel3.add(bar, BorderLayout.NORTH);
+		panel3.add(bar1, BorderLayout.SOUTH);
+
+		JPanel panel = new JPanel(new BorderLayout());
+		panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
+		panel.add(panel1, BorderLayout.NORTH);
+		panel.add(panel3, BorderLayout.SOUTH);
+		panel.add(log, BorderLayout.CENTER);
+
+		execute.addActionListener(this);
+		simulateAbort.addActionListener(this);
+		simulateException.addActionListener(this);
+		simulateJobException.addActionListener(this);
+		simulateInterruption.addActionListener(this);
+		simulateCancellation.addActionListener(this);
+		simulateTimeOut.addActionListener(this);
+		getContentPane().add(panel);
+		pack();
+		setVisible(true);
+	}
+
+	private ExecutionMode getMode() {
+		ExecutionMode mode = ExecutionMode.MULTITHREAD_NO;
+		if (rbSynchronized.isSelected())
+			mode = ExecutionMode.MULTITHREAD_SYNCHRONIZED;
+		if (rbAsynchronized.isSelected())
+			mode = ExecutionMode.MULTITHREAD_ASYNCHRONIZED;
+		return mode;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent event) {
+
+		if (event.getSource() == execute) {
+			main.setMode(getMode(), Integer.parseInt(nthreads.getText()), Integer.parseInt(njobs.getText()));
+			System.out.println("\n\n");
+			Pool pool = new Pool("Main", this);
+			pool.register(main);
+			pool.execute(ExecutionMode.MULTITHREAD_ASYNCHRONIZED);
+			chrono = System.nanoTime();
+		}
+
+		if (event.getSource() == simulateAbort)
+			main.simulateAbort();
+
+		if (event.getSource() == simulateException)
+			main.simulateException();
+
+		if (event.getSource() == simulateJobException)
+			main.simulateJobException();
+
+		if (event.getSource() == simulateInterruption)
+			main.simulateInterruption();
+
+		if (event.getSource() == simulateCancellation)
+			main.simulateCancellation();
+
+		if (event.getSource() == simulateTimeOut)
+			main.simulateTimeOut();
+	}
+
+	//
+	// MainProcessing
+	//
+	public class MainProcessing extends Job implements PoolResponder {
+		private ExecutionMode	mode;
+		private int				nthreads	= 0;
+		private int				njobs;
+		private PoolResponder	responder;
+		private Pool			pool;
+		private int				niter		= 15;
+
+		public MainProcessing(PoolResponder responder) {
+			super();
+			this.responder = responder;
+		}
+
+		public void setMode(ExecutionMode mode, int nthreads, int njobs) {
+			this.mode = mode;
+			this.njobs = njobs;
+			this.nthreads = nthreads;
+		}
+
+		@Override
+		public Object process() {
+			bar.rewind();
+			bar1.rewind();
+			log.rewind();
+
+			pool = new Pool("my", responder);
+			MyWorker p1 = new MyWorker(niter, bar);
+			p1.addMonitor(log);
+			p1.addMonitor(bar1);
+			pool.register(p1);
+			for (int i = 0; i < njobs; i++)
+				pool.register(new MyWorker(niter, bar));
+			pool.execute(mode, nthreads);
+
+			System.out.println("End of main");
+			return "End";
+
+		}
+
+		public void simulateAbort() {
+			if (pool != null)
+				pool.getRegisteredJob(pool.size() - 1).abort();
+		}
+
+		public void simulateCancellation() {
+			if (pool != null)
+				pool.cancel();
+		}
+
+		public void simulateException() {
+			if (pool != null)
+				((MyWorker) pool.getRegisteredJob(pool.size() / 2 - 1)).withException = true;
+		}
+
+		public void simulateJobException() {
+			if (pool != null)
+				((MyWorker) pool.getRegisteredJob(pool.size() - 1)).withJobException = true;
+		}
+
+		public void simulateInterruption() {
+			if (pool != null)
+				((MyWorker) pool.getRegisteredJob(pool.size() - 1)).withInterruption = true;
+		}
+
+		public void simulateTimeOut() {
+			if (pool != null)
+				((MyWorker) pool.getRegisteredJob(pool.size() - 1)).withTimeOut = true;
+		}
+
+		@Override
+		public void onEvent(Pool pool, JobEvent event) {
+			responder.onEvent(pool, event);
+		}
+
+		@Override
+		public void onSuccess(Pool pool, JobEvent event) {
+			responder.onSuccess(pool, event);
+		}
+
+		@Override
+		public void onFailure(Pool pool, JobEvent event) {
+			responder.onFailure(pool, event);
+		}
+
+	}
+
+	//
+	// MyWorker
+	//
+	public class MyWorker extends Job {
+		public boolean	withTimeOut			= false;
+		public boolean	withInterruption	= false;
+		public boolean	withException		= false;
+		public boolean	withJobException	= false;
+		private int		niter				= 15;
+
+		public MyWorker(int niter, MonitorAbstract monitor) {
+			super(monitor);
+			this.niter = niter;
+		}
+
+		@Override
+		public Object process() throws RuntimeException {
+			int i = 0;
+			for (i = 0; i < niter && live; i++) {
+				increment(2, getName() + " " + live + " " + done);
+				for (int j = 0; j < 600000 && live; j++)
+					Math.round(Math.cos(Math.exp(Math.pow(j, -3))));
+				if (withJobException)
+					throw new RuntimeException("It is a programming exception");
+				if (withInterruption)
+					interrupt();
+				if (withTimeOut)
+					niter += 2;
+				if (withException) {
+					double a[] = new double[2];
+					a[3] = 1;
+				}
+				if (!live)
+					return "KO";
+			}
+			System.out.println("End of " + getName() + " iterations:" + i + " " + live + " " + done);
+			return "End of " + getName() + " iterations:" + i + " " + live + " " + done;
+		}
+	}
+
+	@Override
+	public void onEvent(Pool pool, JobEvent event) {
+		if (event.getTypeEvent() == JobEvent.INTERRUPTED)
+			System.out.println("Main side: Job Event " + event);
+	}
+
+	@Override
+	public void onSuccess(Pool pool, JobEvent event) {
+		System.out.println(">>> Success " + event + " from pool " + pool.getName());
+		System.out.println("Computation time: " + (System.nanoTime() - chrono) / 1000000.0);
+	}
+
+	@Override
+	public void onFailure(Pool pool, JobEvent event) {
+		System.out.println(">>> Failure " + event + " from pool " + pool.getName());
+		if (event.getException() != null)
+			event.getException().printStackTrace();
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/math/.DS_Store b/src/bilib/src/bilib/commons/math/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..c9486404a74a7352f8b49aa3dd1e300876d9d298
Binary files /dev/null and b/src/bilib/src/bilib/commons/math/.DS_Store differ
diff --git a/src/bilib/src/bilib/commons/math/bessel/Bessel.java b/src/bilib/src/bilib/commons/math/bessel/Bessel.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0bb9870d027051e0c8b268ade63c3017237eb69
--- /dev/null
+++ b/src/bilib/src/bilib/commons/math/bessel/Bessel.java
@@ -0,0 +1,82 @@
+package bilib.commons.math.bessel;
+
+/**
+ * This class evaluates the Bessel function J0(x). It uses the polynomial
+ * approximations on p. 369-70 of Abramowitz & Stegun. The error in J0 is
+ * supposed to be less than or equal to 5 x 10^-8. The error in J1 is supposed
+ * to be less than or equal to 4 x 10^-8, relative to the value of x. The error
+ * in J2 depends on the error of J0 and J1, as J2 = 2*J1/x + J0.
+ * 
+ * @author Hagai Kirshner, Biomedical Imaging Group, Ecole Polytechnique
+ *         Federale de Lausanne (EPFL)
+ */
+
+public class Bessel {
+
+	/**
+	 * Constants for Bessel function approximation according Abramowitz & Stegun
+	 */
+	private static double[]	tJ0	= { 1.0, -2.2499997, 1.2656208, -0.3163866, 0.0444479, -0.0039444, 0.0002100 };
+	private static double[]	pJ0	= { -.78539816, -.04166397, -.00003954, 0.00262573, -.00054125, -.00029333, .00013558 };
+	private static double[]	fJ0	= { .79788456, -0.00000077, -.00552740, -.00009512, 0.00137237, -0.00072805, 0.00014476 };
+
+	private static double[]	tJ1	= { 0.5, -0.56249985, 0.21093573, -0.03954289, 0.0443319, -0.00031761, 0.0001109 };
+	private static double[]	pJ1	= { -2.35619449, 0.12499612, 0.00005650, -0.00637879, 0.00074348, 0.00079824, -0.00029166 };
+	private static double[]	fJ1	= { 0.79788456, 0.00000156, 0.01689667, 0.00017105, -0.00249511, 0.00113653, -0.00020033 };
+
+	/**
+	 * Returns the value of the Bessel function of the first kind of order zero
+	 * (J0) at x.
+	 */
+	public static double J0(double x) {
+		if (x < 0.0)
+			x *= -1.0;
+		if (x <= 3.0) {
+			double y = x * x / 9.0;
+			return tJ0[0] + y * (tJ0[1] + y * (tJ0[2] + y * (tJ0[3] + y * (tJ0[4] + y * (tJ0[5] + y * tJ0[6])))));
+		}
+
+		double y = 3.0 / x;
+		double theta0 = x + pJ0[0] + y * (pJ0[1] + y * (pJ0[2] + y * (pJ0[3] + y * (pJ0[4] + y * (pJ0[5] + y * pJ0[6])))));
+		double f0 = fJ0[0] + y * (fJ0[1] + y * (fJ0[2] + y * (fJ0[3] + y * (fJ0[4] + y * (fJ0[5] + y * fJ0[6])))));
+		return Math.sqrt(1.0 / x) * f0 * Math.cos(theta0);
+	}
+
+	/**
+	 * Returns the value of the Bessel function of the first kind of order one
+	 * (J1) at x.
+	 */
+	public static double J1(double x) {
+
+		int sign = 1;
+
+		if (x < 0.0) {
+			x *= -1.0;
+			sign = -1;
+		}
+
+		if (x <= 3.0) {
+			double y = x * x / 9.0;
+			return sign * x * (tJ1[0] + y * (tJ1[1] + y * (tJ1[2] + y * (tJ1[3] + y * (tJ1[4] + y * (tJ1[5] + y * tJ1[6]))))));
+		}
+
+		double y = 3.0 / x;
+		double theta1 = x + pJ1[0] + y * (pJ1[1] + y * (pJ1[2] + y * (pJ1[3] + y * (pJ1[4] + y * (pJ1[5] + y * pJ1[6])))));
+		double f1 = fJ1[0] + y * (fJ1[1] + y * (fJ1[2] + y * (fJ1[3] + y * (fJ1[4] + y * (fJ1[5] + y * fJ1[6])))));
+		return sign * Math.sqrt(1.0 / x) * f1 * Math.cos(theta1);
+	}
+
+	/**
+	 * Returns the value of the Bessel function of the first kind of order two
+	 * (J2) at x.
+	 */
+	public static double J2(double x) {
+
+		double value0 = J0(x);
+		double value1 = J1(x);
+		if (x == 0.0)
+			return 0.0;
+		else
+			return 2.0 * value1 / x + value0;
+	}
+}
\ No newline at end of file
diff --git a/src/bilib/src/bilib/commons/math/windowing/Windowing.java b/src/bilib/src/bilib/commons/math/windowing/Windowing.java
new file mode 100644
index 0000000000000000000000000000000000000000..de517f2a797591eb0a22f20ec7985752d104a525
--- /dev/null
+++ b/src/bilib/src/bilib/commons/math/windowing/Windowing.java
@@ -0,0 +1,177 @@
+package bilib.commons.math.windowing;
+
+public class Windowing {
+
+	static public double rectangle(double x, double N) {
+		return elliptical(x, N);
+	}
+
+	static public double elliptical(double x, double N) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		return 1.0;
+	}
+
+	static public double bartlett(double x, double N) {
+		return triangle(x, N);
+	}
+
+	static public double triangle(double x, double N) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		return 1.0 - Math.abs(2.0 * x / N);
+	}
+
+	static public double gaussian(double x, double N, double sigma) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		return Math.exp(-0.5 * x * x / (sigma * sigma));
+	}
+
+	static public double gaussian(double x, double N) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		double sigma = N / 3.0;
+		return Math.exp(-0.5 * x * x / (sigma * sigma));
+	}
+
+	static public double supergaussian(double x, double N, double sigma) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		return Math.exp(-0.5 * x * x * x * x * x * x / (sigma * sigma));
+	}
+
+	static public double supergaussian(double x, double N) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		double sigma = N / 3.0;
+		return Math.exp(-0.5 * x * x * x * x * x * x / (sigma * sigma));
+	}
+
+	static public double cosine(double x, double N) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		double t = 2.0 * x / N;
+		return Math.cos(Math.PI * 0.5 * t);
+	}
+
+	static public double raisedcosine(double x, double N, double power) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		double t = 2.0 * x / N;
+		return Math.pow(Math.cos(Math.PI * 0.5 * t), power);
+	}
+
+	static public double cubicspline(double x, double N) {
+		double t = 2.0 * x / (0.5 * N);
+		if (t < -2.0)
+			return 0.0;
+		if (t < -1.0)
+			return 0.25 * (t + 2.0) * (t + 2.0) * (t + 2.0);
+		if (t < 0.0)
+			return 0.25 * (-3.0 * t * t * t - 6.0 * t * t + 4.0);
+		if (t < 1.0)
+			return 0.25 * (3.0 * t * t * t - 6.0 * t * t + 4.0);
+		if (t < 2.0)
+			return 0.25 * (2.0 - t) * (2.0 - t) * (2.0 - t);
+		return 0;
+	}
+
+	static public double parzen(double x, double N) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		double t = 2.0 * (x < 0 ? -x : x) / N;
+		if (t < 0.5)
+			return 1.0 - 6.0 * t * t + 6.0 * t * t * t;
+		if (t < 1.0)
+			return 2.0 * (1.0 - t) * (1.0 - t) * (1.0 - t);
+		return 0;
+	}
+
+	static public double sigmoid(double x, double N, double slope) {
+		return 1.0 / (1.0 + Math.exp(-slope * (x + N * 0.5))) - 1.0 / (1.0 + Math.exp(-slope * (x - N * 0.5)));
+	}
+
+	static public double hann(double x, double N) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		double t = 2.0 * x / N;
+		return Math.cos(0.5 * Math.PI * t);
+	}
+
+	static public double hamming(double x, double N, double alpha) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		double t = 2.0 * x / N;
+		return alpha + (1.0 - alpha) * Math.cos(Math.PI * t);
+	}
+
+	static public double tukey(double x, double N, double alpha) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		if (x > -0.5 * N + alpha && x < 0.5 * N - alpha)
+			return 1.0;
+		return Math.cos(Math.PI * ((2 * x - N) / (4 * alpha) + 0.5));
+	}
+
+	static public double exponential(double x, double N) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		double k = 4.60517018599 / N;
+		return Math.exp(-((x < 0 ? -x : x)) * k);
+	}
+
+	static public double lanczos(double x, double N) {
+		if (x < -0.5 * N)
+			return 0;
+		if (x > 0.5 * N)
+			return 0;
+		double t = 2.0 * x / N;
+		if (t == 0)
+			return 1.0;
+		return Math.sin(Math.PI * t) / (Math.PI * t);
+	}
+
+	public static void main(String args[]) {
+
+		double N = 20;
+
+		String title = "t\trectangle\ttriangle\tgaussian3\tgaussian\tsupergaussian3\tsupergaussian\tcosine\traisedcosine\tcubicspline\tparzen\tsigmoid\thann\thamming\ttukey\texponential\tlanczos";
+		System.out.println(title);
+		for (double x = -25; x < 25; x += 0.1) {
+			double[] results = new double[] { x, rectangle(x, N), triangle(x, N), gaussian(x, N, 3), gaussian(x, N), supergaussian(x, N, 3), supergaussian(x, N), cosine(x, N),
+					raisedcosine(x, N, 6), cubicspline(x, N), parzen(x, N), sigmoid(x, N, 10), hann(x, N), hamming(x, N, 0.5), tukey(x, N, 2), exponential(x, N), lanczos(x, N) };
+			String s = "";
+			for (int i = 0; i < results.length; i++)
+				s += String.format("%2.3f\t ", results[i]);
+			System.out.println(s);
+		}
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/random/Noise.java b/src/bilib/src/bilib/commons/random/Noise.java
new file mode 100644
index 0000000000000000000000000000000000000000..e33bf7a93bc43eb858808e174c8125032355f7cf
--- /dev/null
+++ b/src/bilib/src/bilib/commons/random/Noise.java
@@ -0,0 +1,29 @@
+package bilib.commons.random;
+
+import java.util.Random;
+
+import javax.swing.JPanel;
+
+import bilib.commons.settings.Settings;
+
+public abstract class Noise {
+
+	public Random	random	= new Random();
+
+	public abstract void fetchParameters();
+
+	public abstract String getName();
+
+	public abstract double nextValue();
+
+	public abstract JPanel buildPanel(Settings settings);
+
+	public void load(Settings settings) {
+		settings.loadRecordedItems();
+	}
+
+	public void store(Settings settings) {
+		settings.storeRecordedItems();
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/random/NoiseExponential.java b/src/bilib/src/bilib/commons/random/NoiseExponential.java
new file mode 100644
index 0000000000000000000000000000000000000000..ecb1be2bdeda74bf40b64162f01afee66f2b0b7a
--- /dev/null
+++ b/src/bilib/src/bilib/commons/random/NoiseExponential.java
@@ -0,0 +1,50 @@
+package bilib.commons.random;
+
+import javax.swing.JPanel;
+
+import bilib.commons.components.GridPanel;
+import bilib.commons.components.SpinnerRangeDouble;
+import bilib.commons.settings.Settings;
+
+public class NoiseExponential extends Noise {
+
+	private double			mean	= 0.0;
+	private double			stdev	= 1.0;
+	private SpinnerRangeDouble	spnMean	= new SpinnerRangeDouble(0, -Double.MAX_VALUE, Double.MAX_VALUE, 1, "0.000");
+	private SpinnerRangeDouble	spnStd	= new SpinnerRangeDouble(1, 0, Double.MAX_VALUE, 0.5, "0.000");
+
+	public NoiseExponential(double mean, double stdev) {
+		this.mean = mean;
+		this.stdev = stdev;
+	}
+
+	@Override
+	public String getName() {
+		return "Exponential";
+	}
+
+	@Override
+	public void fetchParameters() {
+		this.mean = spnMean.get();
+		this.stdev = spnStd.get();
+	}
+
+	@Override
+	public double nextValue() {
+		return mean + stdev * random.nextGaussian();
+	}
+
+	@Override
+	public JPanel buildPanel(Settings settings) {
+		GridPanel panel = new GridPanel(false);
+		panel.place(1, 0, "Mean");
+		panel.place(1, 1, spnMean);
+		panel.place(2, 0, "Standard deviation");
+		panel.place(2, 1, spnStd);
+		settings.record("random-" + getName() + "-mean", spnMean, "0");
+		settings.record("random-" + getName() + "-stdev", spnStd, "1");
+		settings.loadRecordedItems();
+		return panel;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/random/NoiseGaussian.java b/src/bilib/src/bilib/commons/random/NoiseGaussian.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8d4b2ab4f32833559171c1969b02c47d2e14bea
--- /dev/null
+++ b/src/bilib/src/bilib/commons/random/NoiseGaussian.java
@@ -0,0 +1,50 @@
+package bilib.commons.random;
+
+import javax.swing.JPanel;
+
+import bilib.commons.components.GridPanel;
+import bilib.commons.components.SpinnerRangeDouble;
+import bilib.commons.settings.Settings;
+
+public class NoiseGaussian extends Noise {
+
+	private double			mean	= 0.0;
+	private double			stdev	= 1.0;
+	private SpinnerRangeDouble	spnMean	= new SpinnerRangeDouble(0, -Double.MAX_VALUE, Double.MAX_VALUE, 1);
+	private SpinnerRangeDouble	spnStd	= new SpinnerRangeDouble(1, 0, Double.MAX_VALUE, 0.5);
+
+	public NoiseGaussian(double mean, double stdev) {
+		this.mean = mean;
+		this.stdev = stdev;
+	}
+
+	@Override
+	public String getName() {
+		return "Gaussian";
+	}
+
+	@Override
+	public void fetchParameters() {
+		this.mean = spnMean.get();
+		this.stdev = spnStd.get();
+	}
+
+	@Override
+	public double nextValue() {
+		return mean + stdev * random.nextGaussian();
+	}
+
+	@Override
+	public JPanel buildPanel(Settings settings) {
+		GridPanel panel = new GridPanel(false);
+		panel.place(1, 0, "Mean");
+		panel.place(1, 1, spnMean);
+		panel.place(2, 0, "Standard deviation");
+		panel.place(2, 1, spnStd);
+		settings.record("random-" + getName() + "-mean", spnMean, "0");
+		settings.record("random-" + getName() + "-stdev", spnStd, "1");
+		settings.loadRecordedItems();
+		return panel;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/random/NoisePoisson.java b/src/bilib/src/bilib/commons/random/NoisePoisson.java
new file mode 100644
index 0000000000000000000000000000000000000000..930d666190819562b2dcf171fc4286f024369cae
--- /dev/null
+++ b/src/bilib/src/bilib/commons/random/NoisePoisson.java
@@ -0,0 +1,98 @@
+package bilib.commons.random;
+
+import javax.swing.JPanel;
+
+import bilib.commons.components.GridPanel;
+import bilib.commons.components.SpinnerRangeDouble;
+import bilib.commons.settings.Settings;
+
+public class NoisePoisson extends Noise {
+
+	private double			mean	= 0.0;
+	private SpinnerRangeDouble	spnMean	= new SpinnerRangeDouble(1, -Double.MAX_VALUE, Double.MAX_VALUE, 1, "0.000");
+
+	public NoisePoisson(double mean) {
+		this.mean = mean;
+	}
+
+	@Override
+	public String getName() {
+		return "Poisson";
+	}
+
+	@Override
+	public void fetchParameters() {
+		this.mean = spnMean.get();
+	}
+
+	@Override
+	public double nextValue() {
+		return poidev(mean);
+	}
+
+	@Override
+	public JPanel buildPanel(Settings settings) {
+		GridPanel panel = new GridPanel(false);
+		panel.place(1, 0, "Mean/Variance");
+		panel.place(1, 1, spnMean);
+		settings.record("random-" + getName() + "-mean", spnMean, "1");
+		settings.loadRecordedItems();
+		return panel;
+	}
+
+	private double poidev(double xm) {
+
+		double sq = 0, alxm = 0, g = 0, oldm = (-1.0);
+		double em, t, y;
+		if (xm < 12.0) {
+			if (xm != oldm) {
+				oldm = xm;
+				g = Math.exp(-xm);
+			}
+			em = -1;
+			t = 1.0;
+			do {
+				++em;
+				t *= random.nextDouble();
+				;
+			}
+			while (t > g);
+		}
+		else {
+			if (xm != oldm) {
+				oldm = xm;
+				sq = Math.sqrt(2.0 * xm);
+				alxm = Math.log(xm);
+				g = xm * alxm - gammln(xm + 1.0);
+			}
+			do {
+				do {
+					y = Math.tan(Math.PI * random.nextDouble());
+					em = sq * y + xm;
+				}
+				while (em < 0.0);
+				em = Math.floor(em);
+				t = 0.9 * (1.0 + y * y) * Math.exp(em * alxm - gammln(em + 1.0) - g);
+
+			}
+			while (random.nextDouble() > t);
+		}
+		return em;
+	}
+
+	// Internal arithmetic will be done in double precision, a nicety that you
+	// can omit if five-figure
+	// accuracy is good enough.
+	double gammln(double xx) {
+		double x, y, tmp, ser;
+		double cof[] = new double[] { 76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5 };
+		int j;
+		y = x = xx;
+		tmp = x + 5.5;
+		tmp -= (x + 0.5) * Math.log(tmp);
+		ser = 1.000000000190015;
+		for (j = 0; j <= 5; j++)
+			ser += cof[j] / ++y;
+		return -tmp + Math.log(2.5066282746310005 * ser / x);
+	}
+}
diff --git a/src/bilib/src/bilib/commons/random/NoiseRayleigh.java b/src/bilib/src/bilib/commons/random/NoiseRayleigh.java
new file mode 100644
index 0000000000000000000000000000000000000000..c547f63a528a439f63bb2c5f0578e3d9cffd0ef6
--- /dev/null
+++ b/src/bilib/src/bilib/commons/random/NoiseRayleigh.java
@@ -0,0 +1,43 @@
+package bilib.commons.random;
+
+import javax.swing.JPanel;
+
+import bilib.commons.components.GridPanel;
+import bilib.commons.components.SpinnerRangeDouble;
+import bilib.commons.settings.Settings;
+
+public class NoiseRayleigh extends Noise {
+
+	private double			sigma		= 1.0;
+	private SpinnerRangeDouble	spnSigma	= new SpinnerRangeDouble(1, -Double.MAX_VALUE, Double.MAX_VALUE, 1);
+
+	public NoiseRayleigh(double sigma) {
+		this.sigma = sigma;
+	}
+
+	@Override
+	public String getName() {
+		return "Rayleigh";
+	}
+
+	@Override
+	public void fetchParameters() {
+		this.sigma = spnSigma.get();
+	}
+
+	@Override
+	public double nextValue() {
+		return Math.sqrt(-2.0D * Math.log(1.0D - random.nextDouble())) * sigma;
+	}
+
+	@Override
+	public JPanel buildPanel(Settings settings) {
+		GridPanel panel = new GridPanel(false);
+		panel.place(1, 0, "Sigma");
+		panel.place(1, 1, spnSigma);
+		settings.record("random-" + getName() + "-sigma", spnSigma, "1");
+		settings.loadRecordedItems();
+		return panel;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/random/NoiseUniform.java b/src/bilib/src/bilib/commons/random/NoiseUniform.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3f1fbbcd5237269761d5cf363f0c4cafe3335d9
--- /dev/null
+++ b/src/bilib/src/bilib/commons/random/NoiseUniform.java
@@ -0,0 +1,51 @@
+package bilib.commons.random;
+
+import javax.swing.JPanel;
+
+import bilib.commons.components.GridPanel;
+import bilib.commons.components.SpinnerRangeDouble;
+import bilib.commons.settings.Settings;
+
+public class NoiseUniform extends Noise {
+
+	private double			lower		= 0.0;
+	private double			upper		= 1.0;
+	private SpinnerRangeDouble	spnLower	= new SpinnerRangeDouble(0.0, -Double.MAX_VALUE, Double.MAX_VALUE, 1, "0.000");
+	private SpinnerRangeDouble	spnUpper	= new SpinnerRangeDouble(1.0, -Double.MAX_VALUE, Double.MAX_VALUE, 1, "0.000");
+
+	public NoiseUniform(double lower, double upper) {
+		this.lower = lower;
+		this.upper = upper;
+	}
+
+	@Override
+	public String getName() {
+		return "Uniform";
+	}
+
+	@Override
+	public void fetchParameters() {
+		this.lower = spnLower.get();
+		this.upper = spnUpper.get();
+	}
+
+	@Override
+	public double nextValue() {
+		return (upper - lower) * random.nextDouble() + upper;
+	}
+
+	@Override
+	public JPanel buildPanel(Settings settings) {
+		GridPanel panel = new GridPanel(false);
+		panel.place(1, 0, "Lower value");
+		panel.place(1, 1, spnLower);
+		panel.place(2, 0, "Upper value");
+		panel.place(2, 1, spnUpper);
+		settings.record("random-" + getName() + "-lower", spnLower, "0");
+		settings.record("random-" + getName() + "-upper", spnUpper, "1");
+		settings.loadRecordedItems();
+
+		return panel;
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/settings/Settings.java b/src/bilib/src/bilib/commons/settings/Settings.java
new file mode 100644
index 0000000000000000000000000000000000000000..489b45288075498715fe197f0a836f34f5e2b3ae
--- /dev/null
+++ b/src/bilib/src/bilib/commons/settings/Settings.java
@@ -0,0 +1,430 @@
+package bilib.commons.settings;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.Properties;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JSlider;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.JToggleButton;
+
+import bilib.commons.components.SpinnerRangeDouble;
+import bilib.commons.components.SpinnerRangeFloat;
+import bilib.commons.components.SpinnerRangeInteger;
+
+/**
+ * This class allows to store and load key-associated values in a text file. The
+ * class has methods to load and store single value linked to a string key
+ * describing the value. Furthermore, this class has methods to record a GUI
+ * component to a specified key. By this way this class allows to load and store
+ * all recorded items.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class Settings {
+
+	private String			filename;
+	private String			project;
+	private ArrayList<Item>	items;
+	private Properties		props;
+
+	/**
+	 * Constructors a Settings abject for a given project name and a given
+	 * filename.
+	 * 
+	 * @param project
+	 *            a string describing the project
+	 * @param filename
+	 *            a string give the full name of the file, including the path
+	 */
+	public Settings(String project, String filename) {
+		this.filename = filename;
+		this.project = project;
+		items = new ArrayList<Item>();
+		props = new Properties();
+	}
+
+	public String getProject() {
+		return project;
+	}
+
+	public String getFilename() {
+		return filename;
+	}
+
+	/**
+	 * Records a JTextField component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JTextField component, String defaultValue) {
+		Item item = new Item(key, component, defaultValue);
+		items.add(item);
+	}
+
+	/**
+	 * Records a JComboBox component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JComboBox component, String defaultValue) {
+		Item item = new Item(key, component, defaultValue);
+		items.add(item);
+	}
+
+	/**
+	 * Records a JSpinner component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JSpinner component, String defaultValue) {
+		Item item = new Item(key, component, defaultValue);
+		items.add(item);
+	}
+
+	/**
+	 * Records a JToggleButton component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JToggleButton component, boolean defaultValue) {
+		Item item = new Item(key, component, (defaultValue ? "on" : "off"));
+		items.add(item);
+	}
+
+	/**
+	 * Records a JCheckBox component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JCheckBox component, boolean defaultValue) {
+		Item item = new Item(key, component, (defaultValue ? "on" : "off"));
+		items.add(item);
+	}
+
+	/**
+	 * Records a JSlider component to store/load automatically.
+	 * 
+	 * @param key
+	 *            a int value
+	 * @param component
+	 *            the component to record
+	 * @param defaultValue
+	 *            the default value
+	 */
+	public void record(String key, JSlider component, String defaultValue) {
+		Item item = new Item(key, component, defaultValue);
+		items.add(item);
+	}
+
+	/**
+	 * Load an individual double value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param defaultValue
+	 *            the default value
+	 * @return the value get from the file
+	 */
+	public String loadValue(String key, String defaultValue) {
+		String s = "";
+		try {
+			FileInputStream in = new FileInputStream(filename);
+			props.load(in);
+			s = props.getProperty(key, "" + defaultValue);
+		}
+		catch (Exception e) {
+			s = defaultValue;
+		}
+		return s;
+	}
+
+	/**
+	 * Load an individual double value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param defaultValue
+	 *            the default value
+	 * @return the value get from the file
+	 */
+	public double loadValue(String key, double defaultValue) {
+		double d = 0;
+		try {
+			FileInputStream in = new FileInputStream(filename);
+			props.load(in);
+			String value = props.getProperty(key, "" + defaultValue);
+			d = (new Double(value)).doubleValue();
+		}
+		catch (Exception e) {
+			d = defaultValue;
+		}
+		return d;
+	}
+
+	/**
+	 * Load an individual integer value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param defaultValue
+	 *            the default value
+	 * @return the value get from the file
+	 */
+	public int loadValue(String key, int defaultValue) {
+		int i = 0;
+		try {
+			FileInputStream in = new FileInputStream(filename);
+			props.load(in);
+			String value = props.getProperty(key, "" + defaultValue);
+			i = (new Integer(value)).intValue();
+		}
+		catch (Exception e) {
+			i = defaultValue;
+		}
+		return i;
+	}
+
+	/**
+	 * Load an individual boolean value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param defaultValue
+	 *            the default value
+	 * @return the value get from the file
+	 */
+	public boolean loadValue(String key, boolean defaultValue) {
+		boolean b = false;
+		try {
+			FileInputStream in = new FileInputStream(filename);
+			props.load(in);
+			String value = props.getProperty(key, "" + defaultValue);
+			b = (new Boolean(value)).booleanValue();
+		}
+		catch (Exception e) {
+			b = defaultValue;
+		}
+		return b;
+	}
+
+	/**
+	 * Store an individual double value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param value
+	 *            the value to store
+	 */
+	public void storeValue(String key, String value) {
+		props.setProperty(key, value);
+		try {
+			FileOutputStream out = new FileOutputStream(filename);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			System.out.println(project + ": Impossible to store settings in (" + filename + ")");
+		}
+	}
+
+	/**
+	 * Store an individual double value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param value
+	 *            the value to store
+	 */
+	public void storeValue(String key, double value) {
+		props.setProperty(key, "" + value);
+		try {
+			FileOutputStream out = new FileOutputStream(filename);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			System.out.println(project + ": Impossible to store settings in (" + filename + ")");
+		}
+	}
+
+	/**
+	 * Store an individual integer value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param value
+	 *            the value to store
+	 */
+	public void storeValue(String key, int value) {
+		props.setProperty(key, "" + value);
+		try {
+			FileOutputStream out = new FileOutputStream(filename);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			System.out.println(project + ": Impossible to store settings in (" + filename + ")");
+		}
+	}
+
+	/**
+	 * Store an individual boolean value given a specified key
+	 * 
+	 * @param key
+	 *            a string describing the value
+	 * @param value
+	 *            the value to store
+	 */
+	public void storeValue(String key, boolean value) {
+		props.setProperty(key, "" + value);
+		try {
+			FileOutputStream out = new FileOutputStream(filename);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			System.out.println(project + ": Impossible to store settings in (" + filename + ")");
+		}
+	}
+
+	/**
+	 * Load all recorded values.
+	 */
+	public void loadRecordedItems() {
+		loadRecordedItems(filename);
+	}
+
+	/**
+	 * Load all recorded values from a specified filename.
+	 */
+	public void loadRecordedItems(String fname) {
+		try {
+			FileInputStream in = new FileInputStream(fname);
+			props.load(in);
+		}
+		catch (Exception e) {
+			props = new Properties();
+		}
+
+		for (int i = 0; i < items.size(); i++) {
+			Item item = (Item) items.get(i);
+			String value = props.getProperty(item.key, item.defaultValue);
+			if (item.component instanceof JTextField) {
+				((JTextField) item.component).setText(value);
+			}
+			else if (item.component instanceof JComboBox) {
+				((JComboBox) item.component).setSelectedItem(value);
+			}
+			else if (item.component instanceof JCheckBox) {
+				((JCheckBox) item.component).setSelected(value.equals("on") ? true : false);
+			}
+			else if (item.component instanceof JToggleButton) {
+				((JToggleButton) item.component).setSelected(value.equals("on") ? true : false);
+			}
+			else if (item.component instanceof SpinnerRangeInteger) {
+				((SpinnerRangeInteger) item.component).set(Math.round((new Double(value)).intValue()));
+			}
+			else if (item.component instanceof SpinnerRangeDouble) {
+				((SpinnerRangeDouble) item.component).set((new Double(value)).doubleValue());
+			}
+			else if (item.component instanceof SpinnerRangeFloat) {
+				((SpinnerRangeFloat) item.component).set((new Float(value)).floatValue());
+			}
+			else if (item.component instanceof JSlider) {
+				((JSlider) item.component).setValue((new Integer(value)).intValue());
+			}
+		}
+	}
+
+	/**
+	 * Store all recorded values.
+	 */
+	public void storeRecordedItems() {
+		storeRecordedItems(filename);
+	}
+
+	/**
+	 * Store all recorded values into a specified filename
+	 */
+	public void storeRecordedItems(String fname) {
+
+		for (int i = 0; i < items.size(); i++) {
+			Item item = (Item) items.get(i);
+			if (item.component instanceof JTextField) {
+				String value = ((JTextField) item.component).getText();
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JComboBox) {
+				String value = (String) ((JComboBox) item.component).getSelectedItem();
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JCheckBox) {
+				String value = (((JCheckBox) item.component).isSelected() ? "on" : "off");
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JToggleButton) {
+				String value = (((JToggleButton) item.component).isSelected() ? "on" : "off");
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JSpinner) {
+				String value = "" + ((JSpinner) item.component).getValue();
+				props.setProperty(item.key, value);
+			}
+			else if (item.component instanceof JSlider) {
+				String value = "" + ((JSlider) item.component).getValue();
+				props.setProperty(item.key, value);
+			}
+		}
+
+		try {
+			FileOutputStream out = new FileOutputStream(fname);
+			props.store(out, project);
+		}
+		catch (Exception e) {
+			System.out.println(project + ": Impossible to store settings in (" + fname + ")");
+		}
+	}
+
+	/**
+	 * Private class to store one component and its key.
+	 */
+	private class Item {
+		public Object	component;
+		public String	defaultValue;
+		public String	key;
+
+		public Item(String key, Object component, String defaultValue) {
+			this.component = component;
+			this.defaultValue = defaultValue;
+			this.key = key;
+		}
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/settings/SettingsFileDialog.java b/src/bilib/src/bilib/commons/settings/SettingsFileDialog.java
new file mode 100644
index 0000000000000000000000000000000000000000..3fda912489d431ac376782e802e515a7a91e96df
--- /dev/null
+++ b/src/bilib/src/bilib/commons/settings/SettingsFileDialog.java
@@ -0,0 +1,107 @@
+package bilib.commons.settings;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.filechooser.FileNameExtensionFilter;
+
+public class SettingsFileDialog extends JDialog implements ActionListener {
+	private JTextField	txt;
+	private JButton		bnCancel	= new JButton("Cancel");
+	private JButton		bnReset		= new JButton("Default");
+	private JButton		bnSaveAs	= new JButton("Save As");
+	private JButton		bnLoad		= new JButton("Load");
+	private JButton		bnSave		= new JButton("Save");
+
+	private Settings	settings;
+
+	public SettingsFileDialog(Settings settings) {
+		super(new JFrame(), "Settings of " + settings.getProject());
+		this.settings = settings;
+		txt = new JTextField(settings.getFilename());
+
+		txt.setEditable(false);
+		Container contentPane = getContentPane();
+		contentPane.setLayout(new BorderLayout());
+
+		JPanel pn1 = new JPanel(new FlowLayout());
+		pn1.add(txt);
+
+		JPanel pn2 = new JPanel(new FlowLayout());
+		pn2.add(bnCancel);
+		pn2.add(bnReset);
+		pn2.add(bnSave);
+		pn2.add(bnSaveAs);
+		pn2.add(bnLoad);
+
+		contentPane.add(pn1, BorderLayout.NORTH);
+		contentPane.add(pn2, BorderLayout.SOUTH);
+		bnCancel.addActionListener(this);
+		bnReset.addActionListener(this);
+		bnSaveAs.addActionListener(this);
+		bnSave.addActionListener(this);
+		bnLoad.addActionListener(this);
+		pack();
+
+		setModalityType(JDialog.ModalityType.APPLICATION_MODAL);
+		setResizable(false);
+		Dimension dim = getToolkit().getScreenSize();
+		Rectangle abounds = getBounds();
+		setLocation((dim.width - abounds.width) / 2, (dim.height - abounds.height) / 2);
+		setVisible(true);
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		if (e.getSource() == bnSaveAs) {
+			JFileChooser chooser = new JFileChooser(txt.getText());
+			chooser.setSelectedFile(new File("config.txt"));
+			chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+			chooser.setFileFilter(new FileNameExtensionFilter("Configuration file", "txt"));
+			int returnVal = chooser.showSaveDialog(new JFrame());
+			if (returnVal == JFileChooser.APPROVE_OPTION) {
+				String name = chooser.getSelectedFile().getAbsolutePath();
+				if (!name.endsWith(".txt"))
+					name += ".txt";
+				txt.setText(name);
+				settings.storeRecordedItems(txt.getText());
+			}
+		}
+		else if (e.getSource() == bnLoad) {
+			JFileChooser chooser = new JFileChooser(txt.getText());
+			chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+			chooser.setFileFilter(new FileNameExtensionFilter("Configuration file", "txt"));
+			int returnVal = chooser.showOpenDialog(new JFrame());
+			if (returnVal == JFileChooser.APPROVE_OPTION) {
+				String name = chooser.getSelectedFile().getAbsolutePath();
+				txt.setText(name);
+			}
+			settings.loadRecordedItems(txt.getText());
+		}
+		else if (e.getSource() == bnSave) {
+			settings.storeRecordedItems(txt.getText());
+		}
+		else if (e.getSource() == bnReset) {
+			settings.loadRecordedItems("default-no-file");
+		}
+
+		bnCancel.removeActionListener(this);
+		bnReset.removeActionListener(this);
+		bnLoad.removeActionListener(this);
+		bnSave.removeActionListener(this);
+		bnSaveAs.removeActionListener(this);
+		dispose();
+	}
+}
\ No newline at end of file
diff --git a/src/bilib/src/bilib/commons/table/CustomizedColumn.java b/src/bilib/src/bilib/commons/table/CustomizedColumn.java
new file mode 100644
index 0000000000000000000000000000000000000000..f37537a32efe6e022f567576d2d7d9ddde699905
--- /dev/null
+++ b/src/bilib/src/bilib/commons/table/CustomizedColumn.java
@@ -0,0 +1,102 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.table;
+
+/**
+ * This class allows to customized the columns of the CustomizedTable tables.
+ * 
+ * @author Daniel Sage
+ * 
+ */
+public class CustomizedColumn {
+
+	private Class<?>	columnClasse; // usually it is a String
+	private String	 	header;
+	private boolean		editable;
+	private int	     	width;
+	private String[]	choices;	  // ComboBox
+	private String	 	button;	      // Button
+	private String	 	tooltip;
+
+	public CustomizedColumn(String header, Class<?> columnClasse, int width, boolean editable) {
+		this.columnClasse = columnClasse;
+		this.header = header;
+		this.width = width;
+		this.editable = editable;
+	}
+
+	public CustomizedColumn(String header, Class<?> classe, int width, String[] choices, String tooltip) {
+		this.columnClasse = classe;
+		this.header = header;
+		this.width = width;
+		this.editable = true;
+		this.choices = choices;
+		this.tooltip = tooltip;
+	}
+
+	public CustomizedColumn(String header, Class<?> columnClasse, int width, String button, String tooltip) {
+		this.columnClasse = columnClasse;
+		this.header = header;
+		this.width = width;
+		this.editable = false;
+		this.button = button;
+		this.tooltip = tooltip;
+	}
+
+	public Class<?> getColumnClass() {
+		return columnClasse;
+	}
+	
+	public String getHeader() {
+		return header;
+	}
+	
+	public boolean isEditable() {
+		return editable;
+	}
+	
+	public int	getWidth()  {
+		return width;
+	}
+	
+	public String[] getChoices()  {
+		return choices;
+	}
+	
+	public String getButton()  {
+		return button;
+	} 
+	
+	public String getTooltip()  {
+		return tooltip;
+	} 
+}
diff --git a/src/bilib/src/bilib/commons/table/CustomizedTable.java b/src/bilib/src/bilib/commons/table/CustomizedTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f8dfa77484a61d550b26e8a4a411df46dcfe215
--- /dev/null
+++ b/src/bilib/src/bilib/commons/table/CustomizedTable.java
@@ -0,0 +1,336 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.table;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.swing.DefaultCellEditor;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+
+/**
+ * This class build a table by extending JTable. Usually the contructor expects
+ * a list of CustomizedColumn objects that defines the columns of the table.
+ * 
+ * @author Daniel Sage
+ * 
+ */
+
+public class CustomizedTable extends JTable {
+
+	private JScrollPane	                pane	= null;
+	private ArrayList<CustomizedColumn>	columns;
+
+	public CustomizedTable(ArrayList<CustomizedColumn> columns, boolean sortable) {
+		create(columns);
+		setAutoCreateRowSorter(sortable);
+		setRowHeight(20);
+	}
+
+
+	public CustomizedTable(String headers[], boolean sortable) {
+		ArrayList<CustomizedColumn> colums = new ArrayList<CustomizedColumn>();
+		for (int i = 0; i < headers.length; i++)
+			colums.add(new CustomizedColumn(headers[i], String.class, 150, false));
+		create(colums);
+		setAutoCreateRowSorter(sortable);
+		setRowHeight(20);
+	}
+
+	private void create(ArrayList<CustomizedColumn> column) {
+		columns = column;
+		DefaultTableModel model = new DefaultTableModel() {
+			@Override
+			public boolean isCellEditable(int row, int col) {
+				return columns.get(col).isEditable();
+			}
+
+			@Override
+			public Class<?> getColumnClass(int col) {
+				return columns.get(col).getColumnClass();
+			}
+		};
+
+		setModel(model);
+		int n = columns.size();
+		String headers[] = new String[n];
+		for (int col = 0; col < n; col++)
+			headers[col] = columns.get(col).getHeader();
+
+		model.setColumnIdentifiers(headers);
+		setFillsViewportHeight(true);
+
+		for (int col = 0; col < n; col++) {
+			TableColumn tc = getColumnModel().getColumn(col);
+			tc.setPreferredWidth(columns.get(col).getWidth());
+
+			if (columns.get(col).getChoices() != null) {
+				JComboBox cmb = new JComboBox();
+				for (String p : columns.get(col).getChoices()) {
+					cmb.addItem(p);
+					cmb.setToolTipText(columns.get(col).getTooltip());
+					tc.setCellEditor(new DefaultCellEditor(cmb));
+				}
+			}
+			if (columns.get(col).getButton() != null) {
+				ButtonRenderer bn = new ButtonRenderer();
+				bn.setToolTipText(columns.get(col).getTooltip());
+				tc.setCellRenderer(bn);
+			}
+		}
+		getTableHeader().setReorderingAllowed(false);
+	}
+
+	public void setPreferredSize(int width, int height) {
+		if (pane != null)
+			pane.setPreferredSize(new Dimension(width, height));
+	}
+
+	/**
+	 * Removes one specify row from the table.
+	 * 
+	 * @param row	Row to remove
+	 */
+	public void removeRow(int row) {
+		if (row >= 0 && row < getRowCount())
+			((DefaultTableModel) getModel()).removeRow(row);
+	}
+
+	/**
+	 * Removes all rows of the table.
+	 */
+	public void removeRows() {
+		while (getRowCount() > 0)
+			((DefaultTableModel) getModel()).removeRow(0);
+	}
+
+	public String[] getRow(int row) {
+		if (row >= 0) {
+			int ncol = getColumnCount();
+			String items[] = new String[ncol];
+			for (int col = 0; col < ncol; col++)
+				items[col] = (String) getModel().getValueAt(row, col);
+			return items;
+		}
+		return new String[1];
+	}
+
+	public String getCell(int row, int col) {
+		if (row >= 0 && col >= 0) {
+			return (String) getModel().getValueAt(row, col);
+		}
+		return "";
+	}
+
+	public void setCell(int row, int col, String value) {
+		if (row >= 0 && col >= 0) {
+			getModel().setValueAt(value, row, col);
+		}
+	}
+
+	public String getRowCSV(int row, String seperator) {
+		if (row >= 0) {
+			int ncol = getColumnCount();
+			String items = "";
+			for (int col = 0; col < ncol - 1; col++) {
+				if ((String) getModel().getValueAt(row, col) == null)
+					items += "" + seperator;
+				else
+					items += (String) getModel().getValueAt(row, col) + seperator;
+			}
+			if (ncol >= 1)
+				items += (String) getModel().getValueAt(row, ncol - 1);
+			return items;
+		}
+		return "";
+	}
+
+	/**
+	 * Saves the table in a CSV file
+	 * 
+	 * @param filename	Complete path and filename
+	 */
+	public void storeCSV(String filename) {
+		File file = new File(filename);
+		try {
+			BufferedWriter buffer = new BufferedWriter(new FileWriter(file));
+			int nrows = getRowCount();
+			int ncols = getColumnCount();
+
+			String row = "";
+			for (int c = 0; c < columns.size(); c++)
+				row += columns.get(c).getHeader() + (c == columns.size() - 1 ? "" : ", ");
+			buffer.write(row + "\n");
+
+			for (int r = 0; r < nrows; r++) {
+				row = "";
+				for (int c = 0; c < ncols; c++)
+					row += this.getCell(r, c) + (c == ncols - 1 ? "" : ", ");
+				buffer.write(row + "\n");
+			}
+			buffer.close();
+		}
+		catch (IOException ex) {
+		}
+	}
+
+	public String getSelectedAtColumn(int col) {
+		int row = getSelectedRow();
+		if (row >= 0)
+			return (String) getModel().getValueAt(row, col);
+		else
+			return "";
+	}
+
+	public void setSelectedAtColumn(int col, String selection) {
+		int nrows = this.getRowCount();
+		for (int i = 0; i < nrows; i++) {
+			String name = (String) getModel().getValueAt(i, col);
+			if (name.equals(selection))
+				this.setRowSelectionInterval(i, i + 1);
+		}
+	}
+
+	/**
+	 * Add a row at the end of the table.
+	 * 
+	 * @param row
+	 */
+	public void append(Object[] row) {
+		DefaultTableModel model = (DefaultTableModel) getModel();
+		int i = 0;
+		try {
+			model.addRow(row);
+			i = getRowCount() - 1;
+			if (i >= 0) {
+				getSelectionModel().setSelectionInterval(i, i);
+				scrollRectToVisible(new Rectangle(getCellRect(i, 0, true)));
+			}
+		}
+		catch (Exception e) {
+		}
+		repaint();
+	}
+
+	/**
+	 * Add a row at the top of the table.
+	 * 
+	 * @param row
+	 */
+	public void insert(Object[] row) {
+		DefaultTableModel model = (DefaultTableModel) getModel();
+		int i = 0;
+		try {
+			model.insertRow(0, row);
+			getSelectionModel().setSelectionInterval(i, i);
+			scrollRectToVisible(new Rectangle(getCellRect(i, 0, true)));
+		}
+		catch (Exception e) {
+		}
+		repaint();
+	}
+
+	@Override
+	public int getSelectedRow() {
+		int row = super.getSelectedRow();
+		if (row < 0) {
+			if (getRowCount() > 0) {
+				setRowSelectionInterval(0, 0);
+				row = super.getSelectedRow();
+			}
+			return row;
+		}
+		return row;
+	}
+
+	/**
+	 * Replaces all the content of the table by a content private as list of String[].
+	 * 
+	 * @param data
+	 */
+	public void update(ArrayList<String[]> data) {
+		DefaultTableModel model = (DefaultTableModel) getModel();
+		model.getDataVector().removeAllElements();
+		for (String[] row : data)
+			model.addRow(row);
+		repaint();
+	}
+
+	public JScrollPane getPane(int width, int height) {
+		setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+		setPreferredScrollableViewportSize(new Dimension(width, height));
+		setFillsViewportHeight(true);
+		pane = new JScrollPane(this);
+		return pane;
+	}
+
+	public JScrollPane getMinimumPane(int width, int height) {
+		setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+		setMinimumSize(new Dimension(width, height));
+		setShowVerticalLines(true);
+		setPreferredScrollableViewportSize(new Dimension(width, height));
+		setFillsViewportHeight(true);
+		return new JScrollPane(this);
+	}
+
+	public JFrame show(String title, int w, int h) {
+		JFrame frame = new JFrame(title);
+		frame.add(getPane(w, h));
+		frame.pack();
+		frame.setVisible(true);
+		return frame;
+	}
+
+	public class ButtonRenderer extends JButton implements TableCellRenderer {
+		@Override
+		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+			setText((String) value);
+			setMargin(new Insets(1, 1, 1, 1));
+			return this;
+		}
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/utils/Chrono.java b/src/bilib/src/bilib/commons/utils/Chrono.java
new file mode 100644
index 0000000000000000000000000000000000000000..516d09320d1d9b46d2fe006020e177bac258b806
--- /dev/null
+++ b/src/bilib/src/bilib/commons/utils/Chrono.java
@@ -0,0 +1,91 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.utils;
+
+import java.text.DecimalFormat;
+
+/**
+ * This class provides static methods to measures the elapsed time. It is a
+ * equivalent to the function tic and toc of Matlab.
+ * 
+ * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland.
+ * 
+ */
+public class Chrono {
+
+	static private double	chrono	= 0;
+
+	/**
+	 * Register the current time.
+	 */
+	public static void tic() {
+		chrono = System.nanoTime();
+	}
+
+	/**
+	 * Returns a string that indicates the elapsed time since the last tic()
+	 * call.
+	 */
+	public static String toc() {
+		return toc("");
+	}
+
+	/**
+	 * Returns a string that indicates the elapsed time since the last tic()
+	 * call.
+	 * 
+	 * @param msg
+	 *            message to print
+	 */
+	public static String toc(String msg) {
+		double te = System.nanoTime() - chrono;
+		String s = msg + " ";
+		DecimalFormat df = new DecimalFormat("####.##");
+		if (te < 1000.0)
+			return s + df.format(te) + " ns";
+		te /= 1000;
+		if (te < 1000.0)
+			return s + df.format(te) + " us";
+		te /= 1000;
+		if (te < 3000.0)
+			return s + df.format(te) + " ms";
+		te /= 1000;
+		if (te < 600.1)
+			return s + df.format(te) + " s";
+		te /= 60;
+		if (te < 600.1)
+			return s + df.format(te) + " min.";
+		te /= 60;
+		return s + df.format(te) + " h.";
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/utils/Files.java b/src/bilib/src/bilib/commons/utils/Files.java
new file mode 100644
index 0000000000000000000000000000000000000000..786228c2ed7dd51704de2a73ea555c96e5786d92
--- /dev/null
+++ b/src/bilib/src/bilib/commons/utils/Files.java
@@ -0,0 +1,84 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.utils;
+
+import java.io.File;
+
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileSystemView;
+
+public class Files {
+
+	public static String getWorkingDirectory() {
+		return System.getProperty("user.dir");
+	}
+
+	public static String getHomeDirectory() {
+		return FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath() + File.separator;	
+	}
+	
+	public static String getDesktopDirectory() {
+		return getHomeDirectory() + "Desktop" + File.separator;
+	}
+	
+	public static File browseFile(String path) {
+		JFileChooser fc = new JFileChooser(); 
+		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
+		File dir = new File(path);
+		if (dir.exists())
+			fc.setCurrentDirectory(dir);
+		
+		int ret = fc.showOpenDialog(null); 
+		if (ret == JFileChooser.APPROVE_OPTION) {
+			File file = new File(fc.getSelectedFile().getAbsolutePath());
+			if (file.exists())
+				return file;
+		}
+		return null;
+	}
+	
+	public static File browseDirectory(String path) {
+		JFileChooser fc = new JFileChooser(); 
+		fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+		File dir = new File(path);
+		if (dir.exists())
+			fc.setCurrentDirectory(dir);
+
+		int ret = fc.showOpenDialog(null); 
+		if (ret == JFileChooser.APPROVE_OPTION) {
+			File file = new File(fc.getSelectedFile().getAbsolutePath());
+			if (file.exists())
+				return file;
+		}
+		return null;
+	}
+}
diff --git a/src/bilib/src/bilib/commons/utils/Log.java b/src/bilib/src/bilib/commons/utils/Log.java
new file mode 100644
index 0000000000000000000000000000000000000000..379049f16cc6e3d8c5fd9bb55afbc3c3709cddb5
--- /dev/null
+++ b/src/bilib/src/bilib/commons/utils/Log.java
@@ -0,0 +1,59 @@
+package bilib.commons.utils;
+
+public class Log {
+
+	static public void dash() {
+		System.out.println("------------------------------------------");
+	}
+
+	static public void print() {
+		System.out.println("");
+	}
+
+	static public void print(String a) {
+		System.out.println(">" + a);
+	}
+
+	static public void print(double a) {
+		System.out.println(">" + a);
+	}
+
+	static public void print(float a) {
+		System.out.println(">" + a);
+	}
+
+	static public void print(long a) {
+		System.out.println(">" + a);
+	}
+
+	static public void print(int a) {
+		System.out.println(">" + a);
+	}
+
+	static public void print(short a) {
+		System.out.println(">" + a);
+	}
+
+	static public void print(byte a) {
+		System.out.println(">" + a);
+	}
+
+	static public void print(double a[]) {
+		if (a == null) {
+			System.out.println("> null double 1D array");
+			return;
+		}
+
+		if (a.length < 10) {
+			for (int i = 0; i < a.length; i++)
+				System.out.println("> [" + i + "] = " + a[i]);
+			return;
+		}
+		for (int i = 0; i < 4; i++)
+			System.out.println("> [" + i + "] = " + a[i]);
+		System.out.println("> ......");
+		for (int i = a.length - 4; i < a.length; i++)
+			System.out.println("> [" + i + "] = " + a[i]);
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/utils/NumFormat.java b/src/bilib/src/bilib/commons/utils/NumFormat.java
new file mode 100644
index 0000000000000000000000000000000000000000..1773cb2dcb7109169b7dfd54911c2eabfbb1fdbb
--- /dev/null
+++ b/src/bilib/src/bilib/commons/utils/NumFormat.java
@@ -0,0 +1,157 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.utils;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class NumFormat {
+
+	public static double parseNumber(String line, double def) {
+		double[] numbers = parseNumbers(line);
+		if (numbers.length >= 1)
+			return numbers[0];
+		else
+			return def;
+	}
+
+	public static double[] parseNumbersAfter(String keyword, String line) {
+		String parts[] = line.trim().toLowerCase().split(keyword.toLowerCase());
+		if (parts.length == 2)
+			return parseNumbers(parts[1]);
+		else
+			return new double[0];
+	}
+
+	public static double[] parseNumbers(String line) {
+		ArrayList<String> num = new ArrayList<String>();
+		Pattern p = Pattern.compile("[-+]?[0-9]+[.]?[0-9]*([eE][-+]?[0-9]+)?");
+		Matcher m = p.matcher(line);
+		while (m.find()) {
+			num.add(m.group());
+		}
+		double number[] = new double[num.size()];
+		for (int i = 0; i < num.size(); i++)
+			number[i] = Double.parseDouble(num.get(i));
+		return number;
+	}
+
+	public static String sci(double a) {
+		double b = Math.abs(a);
+		String n = a < 0 ? "-" : "";
+		if (a == 0)
+			return "0.0";
+		if (b > 3000.0)
+			return String.format(n + "%6.3E", b);
+		if (b > 300.0)
+			return String.format(n + "%4.1f", b);
+		if (b > 30.0)
+			return String.format(n + "%3.2f", b);
+		if (b > 3.0)
+			return String.format(n + "%2.4f", b);
+		if (b > 0.003)
+			return String.format(n + "%1.4f", b);
+		
+		return String.format(n + "%6.3E", b).trim();
+	}
+	
+	public static String unit(int n, String unit) {
+		double dn = n;
+		if (dn < 0.000000001)
+			return new DecimalFormat("0.00 p").format(dn*1000000000.) + unit;
+		if (dn < 0.000001)
+			return new DecimalFormat("0.00 n").format(dn*1000000.) + unit;
+		if (dn < 0.001)
+			return new DecimalFormat("0.00 m").format(dn*1000.) + unit;
+		if (dn < 1)
+			return new DecimalFormat("0.00 ").format(dn) + unit;			
+		if (dn < 1000.)
+			return new DecimalFormat("0.00 K").format(dn*0.001) + unit;
+		if (dn < 1000000.)
+			return new DecimalFormat("0.00 M").format(dn*0.000001) + unit;
+		if (dn < 1000000000.)
+			return new DecimalFormat("0.00 G").format(dn*0.000000001) + unit;
+		return new DecimalFormat("0.00 T").format(dn*0.000000000001) + unit;
+	}
+
+	public static String seconds(double ns) {
+		return String.format("%5.1f s", ns * 1e-9);
+	}
+
+	public static String time(double ns) {
+		if (ns < 3000.0)
+			return String.format("%3.2f ns", ns);
+		ns *= 0.001;
+		if (ns < 3000.0)
+			return String.format("%3.2f us", ns);
+		ns *= 0.001;
+		if (ns < 3000.0)
+			return String.format("%3.2f ms", ns);
+		ns *= 0.001;
+		if (ns < 3600.0 * 3)
+			return String.format("%3.2f s", ns);
+		ns /= 3600;
+		return String.format("%3.2f h", ns);
+	}
+
+	public static String bytes(double bytes) {
+		if (bytes < 3000)
+			return String.format("%3.0f", bytes);
+		bytes /= 1024.0;
+		if (bytes < 3000)
+			return String.format("%3.1f Kb", bytes);
+		bytes /= 1024.0;
+		if (bytes < 3000)
+			return String.format("%3.1f Mb", bytes);
+		bytes /= 1024.0;
+		if (bytes < 3000)
+			return String.format("%3.1f Gb", bytes);
+		bytes /= 1024.0;
+		return String.format("%3.1f Tb", bytes);
+	}
+
+	public static String toPercent(String value) {
+		try {
+			return toPercent(Double.parseDouble(value));
+		}
+		catch (Exception ex) {
+		}
+		return value;
+	}
+
+	public static String toPercent(double value) {
+		return String.format("%5.3f", value * 100) + "%";
+	}
+
+}
diff --git a/src/bilib/src/bilib/commons/utils/WebBrowser.java b/src/bilib/src/bilib/commons/utils/WebBrowser.java
new file mode 100644
index 0000000000000000000000000000000000000000..70fef852acd54d8a7c12dea7b23de96b752782b3
--- /dev/null
+++ b/src/bilib/src/bilib/commons/utils/WebBrowser.java
@@ -0,0 +1,74 @@
+/*
+ * bilib --- Java Bioimaging Library ---
+ * 
+ * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland
+ * 
+ * Conditions of use: You are free to use this software for research or
+ * educational purposes. In addition, we expect you to include adequate
+ * citations and acknowledgments whenever you present or publish results that
+ * are based on it.
+ * 
+ * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java"
+ * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003.
+ * http://bigwww.epfl.ch/publications/sage0303.html
+ */
+
+/*
+ * Copyright 2007-2017 Biomedical Imaging Group at the EPFL.
+ * 
+ * bilib 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.
+ * 
+ * bilib 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
+ * bilib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package bilib.commons.utils;
+
+import java.awt.Desktop;
+import java.net.URL;
+
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+
+public class WebBrowser {
+
+	public static boolean open(String url) {
+		Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
+		if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
+			try {
+				desktop.browse(new URL(url).toURI());
+				return true;
+			}
+			catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+		return false;
+	}
+
+	public static boolean openInFrame(String url) {
+		Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
+		if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
+			try {
+				desktop.browse(new URL(url).toURI());
+				return true;
+			}
+			catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+		JFrame frame = new JFrame("Help");
+		JLabel lbl = new JLabel(url);
+		frame.add(lbl);
+		frame.pack();
+		frame.setVisible(true);	
+		return false;
+	}
+}
diff --git a/src/bilib/src/bilib/fft/AcademicFourierTransform.java b/src/bilib/src/bilib/fft/AcademicFourierTransform.java
new file mode 100644
index 0000000000000000000000000000000000000000..e37a53e274d853e464b8260a5f644367e247732c
--- /dev/null
+++ b/src/bilib/src/bilib/fft/AcademicFourierTransform.java
@@ -0,0 +1,21796 @@
+package bilib.fft;
+
+/*====================================================================
+| Version: May 31, 2014
+\===================================================================*/
+
+/*====================================================================
+| Philippe Thevenaz
+| EPFL/STI/IMT-LS/LIB/BM.4.137
+| Station 17
+| CH-1015 Lausanne VD
+| Switzerland
+|
+| phone (CET): +41(21)693.51.61
+| fax: +41(21)693.37.01
+| RFC-822: philippe.thevenaz@epfl.ch
+| X-400: /C=ch/A=400net/P=switch/O=epfl/S=thevenaz/G=philippe/
+| URL: http://bigwww.epfl.ch/
+\===================================================================*/
+
+// import ij.IJ;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.TreeMap;
+import java.util.Vector;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.Math.PI;
+import static java.lang.Math.atan2;
+import static java.lang.Math.cos;
+import static java.lang.Math.floor;
+import static java.lang.Math.sin;
+import static java.lang.Math.sqrt;
+
+/*********************************************************************
+ <p>
+ The purpose of this class is to encapsulate operations related to the
+ discrete Fourier transform of periodic sequences, images, and
+ volumes. If <i>x</i> is a discrete sequence assumed to be periodic
+ over <i>K</i> samples, then its discrete Fourier transform is <i>X</i>
+ while the inverse discrete Fourier transform of <i>X</i> is <i>x</i>.
+ The transforms are defined as the pair
+ </p>
+ <ul>
+ <li>&#8704;<i>n</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]:
+ <i>X</i>[<i>n</i>] = &#8497;{<i>x</i>}[<i>n</i>]
+ = &#8721;<sub><i>k</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]</sub>
+ <i>x</i>[<i>k</i>]
+ e<sup>&#8722;j <i>n</i> (2 &#960; &#8725; <i>K</i>) <i>k</i></sup></li>
+ <li>&#8704;<i>k</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]:
+ <i>x</i>[<i>k</i>] = &#8497;<sup>&#8722;1</sup>{<i>X</i>}[<i>k</i>]
+ = (1 &#8725; <i>K</i>)
+ &#8721;<sub><i>n</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]</sub>
+ <i>X</i>[<i>n</i>]
+ e<sup>j <i>n</i> (2 &#960; &#8725; <i>K</i>) <i>k</i></sup></li>
+ </ul>
+ <p>
+ A few relevant relations are
+ </p>
+ <ul>
+ <li>j<sup>2</sup> = &#8722;1</li>
+ <li>&#8704;<i>k</i>, <i>n</i> &#8712; [0&#8230;<i>K</i> &#8722; 1],
+ <i>m</i> &#8712; &#8484;:
+ <i>x</i>[<i>k</i>] = <i>x</i>[<i>k</i> + <i>m</i> <i>K</i>],
+ <i>X</i>[<i>n</i>] = <i>X</i>[<i>n</i> + <i>m</i> <i>K</i>]</li>
+ <li>&#8704;<i>&#955;</i> &#8712; &#8450;: &#8497;{<i>&#955;</i> <i>x</i> + <i>y</i>}
+ = <i>&#955;</i> <i>X</i> + <i>Y</i></li>
+ <li>&#8497;{<i>X</i>}[0] = <i>x</i>[0];
+ <i>m</i> &#8712; [1&#8230;<i>K</i> &#8722; 1]:
+ &#8497;{<i>X</i>}[<i>m</i>] = <i>x</i>[<i>K</i> &#8722; <i>m</i>]</li>
+ <li><i>k</i><sub>0</sub> &#8712; [1&#8230;<i>K</i> &#8722; 1]:
+ ((<i>k</i> &#8712; [0&#8230;<i>k</i><sub>0</sub> &#8722; 1]:
+ <i>y</i>[<i>k</i>] = <i>x</i>[<i>K</i> + <i>k</i>
+ &#8722; <i>k</i><sub>0</sub>]),
+ (<i>k</i> &#8712; [<i>k</i><sub>0</sub>&#8230;<i>K</i> &#8722; 1]:
+ <i>y</i>[<i>k</i>] = <i>x</i>[<i>k</i> &#8722; <i>k</i><sub>0</sub>]));
+ <i>Y</i>[<i>n</i>] = e<sup>&#8722;j <i>k</i><sub>0</sub>
+ (2 &#960; &#8725; <i>K</i>) <i>n</i></sup> <i>X</i>[<i>n</i>]</li>
+ <li><i>n</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]:
+ (<i>k</i><sub>0</sub> &#8712; [0&#8230;<i>K</i> &#8722; 2]:
+ (<i>x</i> &#8727; <i>y</i>)[<i>k</i><sub>0</sub>]
+ = &#8721;<sub><i>k</i> &#8712; [0&#8230;<i>k</i><sub>0</sub>]</sub>
+ <i>x</i>[<i>k</i>] <i>y</i>[<i>k</i><sub>0</sub> &#8722; <i>k</i>]
+ + &#8721;<sub><i>k</i> &#8712; [<i>k</i><sub>0</sub> + 1&#8230;<i>K</i> &#8722; 1]</sub>
+ <i>x</i>[<i>k</i>] <i>y</i>[<i>K</i> + <i>k</i><sub>0</sub> &#8722; <i>k</i>]),
+ ((<i>x</i> &#8727; <i>y</i>)[<i>K</i> &#8722; 1]
+ = &#8721;<sub><i>k</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]</sub>
+ <i>x</i>[<i>k</i>] <i>y</i>[<i>K</i> &#8722; 1 &#8722; <i>k</i>]);
+ &#8497;{<i>x</i> &#8727; <i>y</i>}[<i>n</i>]
+ = <i>X</i>[<i>n</i>] <i>Y</i>[<i>n</i>]</li>
+ <li>&#12296;<i>x</i>, <i>y</i>&#12297;
+ = &#8721;<sub><i>k</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]</sub>
+ <i>x</i>[<i>k</i>] (<i>y</i>[<i>k</i>])<sup>*</sup>
+ = (1 &#8725; <i>K</i>) &#12296;<i>X</i>, <i>Y</i>&#12297;
+ = (1 &#8725; <i>K</i>) &#8721;<sub><i>n</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]</sub>
+ <i>X</i>[<i>n</i>] (<i>Y</i>[<i>n</i>])<sup>*</sup></li>
+ <li><i>x</i>[0] = 1, <i>k</i> &#8712; [1&#8230;<i>K</i> &#8722; 1]:
+ <i>x</i>[<i>k</i>] = 0; <i>n</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]:
+ <i>X</i>[<i>n</i>] = 1</li>
+ <li><i>k</i> &#8712; [0&#8230;<i>K</i> &#8722; 1]:
+ <i>x</i>[<i>k</i>] = 1; <i>X</i>[0] = <i>K</i>,
+ <i>n</i> &#8712; [1&#8230;<i>K</i> &#8722; 1]: <i>X</i>[<i>n</i>] = 0</li>
+ <li><i>x</i> = &#8476;(<i>x</i>); &#8465;(<i>X</i>[0]) = 0,
+ (2 &#8739; <i>K</i>: &#8465;(<i>X</i>[<i>K</i> &#8725; 2]) = 0),
+ <i>n</i> &#8712; [1&#8230;<i>K</i> &#8722; 1]: <i>X</i>[<i>n</i>]
+ = (<i>X</i>[<i>K</i> &#8722; <i>n</i>])<sup>*</sup></li>
+ <li><i>x</i> = &#8476;(<i>x</i>); <i>y</i> = &#8476;(<i>y</i>);
+ F = &#8497;{<i>x</i> + j <i>y</i>}; <i>X</i>[0] = &#8476;(<i>F</i>[0]);
+ <i>Y</i>[0] = &#8465;(<i>F</i>[0]);
+ <i>n</i> &#8712; [1&#8230;<i>K</i> &#8722; 1]: <i>X</i>[<i>n</i>]
+ = (<i>F</i>[<i>n</i>] + (<i>F</i>[<i>K</i> &#8722; <i>n</i>])<sup>*</sup>)
+ &#8725; 2, <i>Y</i>[<i>n</i>] = &#8722;j (<i>F</i>[<i>n</i>]
+ &#8722; (<i>F</i>[<i>K</i> &#8722; <i>n</i>])<sup>*</sup>) &#8725; 2</li>
+ </ul>
+ <p>
+ In two dimensions
+ </p>
+ <ul>
+ <li>&#8497;{<i>x</i>}[<i>n</i><sub>1</sub>, <i>n</i><sub>2</sub>]
+ = &#8497;<sub><i>k</i><sub>2</sub></sub>{&#8497;<sub><i>k</i><sub>1</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>]}[<i>n</i><sub>1</sub>, <i>k</i><sub>2</sub>]}[<i>n</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>]
+ = &#8497;<sub><i>k</i><sub>1</sub></sub>{&#8497;<sub><i>k</i><sub>2</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>]}[<i>k</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>]}[<i>n</i><sub>1</sub>, <i>n</i><sub>2</sub>]</li>
+ </ul>
+ <p>
+ In three dimensions
+ </p>
+ <ul>
+ <li>&#8497;{<i>x</i>}[<i>n</i><sub>1</sub>, <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]
+ = &#8497;<sub><i>k</i><sub>3</sub></sub>{&#8497;<sub><i>k</i><sub>2</sub></sub>{&#8497;<sub><i>k</i><sub>1</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]
+ = &#8497;<sub><i>k</i><sub>3</sub></sub>{&#8497;<sub><i>k</i><sub>1</sub></sub>{&#8497;<sub><i>k</i><sub>2</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]
+ = &#8497;<sub><i>k</i><sub>2</sub></sub>{&#8497;<sub><i>k</i><sub>3</sub></sub>{&#8497;<sub><i>k</i><sub>1</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]
+ = &#8497;<sub><i>k</i><sub>2</sub></sub>{&#8497;<sub><i>k</i><sub>1</sub></sub>{&#8497;<sub><i>k</i><sub>3</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]
+ = &#8497;<sub><i>k</i><sub>1</sub></sub>{&#8497;<sub><i>k</i><sub>3</sub></sub>{&#8497;<sub><i>k</i><sub>2</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]
+ = &#8497;<sub><i>k</i><sub>1</sub></sub>{&#8497;<sub><i>k</i><sub>2</sub></sub>{&#8497;<sub><i>k</i><sub>3</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>,
+ <i>k</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>k</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>,
+ <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]</li>
+ </ul>
+ <p>
+ Data are provided as the pair (real part, imaginary part). The real
+ part is assumed to be provided as an array that contains raster data,
+ with the convention that the indexing for the horizontal dimension is
+ faster than the indexing for the vertical dimension, itself faster
+ than the indexing for the depth dimension. The imaginary part follows
+ the same organization.
+ </p>
+ <p>
+ The two buffers that are provided to some methods must have the same
+ length as the data. They are used internally as temporary storage for
+ the computations. They are there as a convenience to avoid repeated
+ allocations.
+ </p>
+ ********************************************************************/
+public class AcademicFourierTransform
+
+{ /* begin class SlowFourierTransform */
+
+/*....................................................................
+	SlowFourierTransform public enum constants
+....................................................................*/
+/*********************************************************************
+ <p>
+ This enumeration provides the constants that describe the type of
+ input (complex or real) of a Fourier transform.
+ </p>
+ @see #makeHermitian(double[],double[],double[],double[])
+ @see #makeHermitian(float[],float[],float[],float[])
+ ********************************************************************/
+public enum InputDataType {
+/*********************************************************************
+ <p>
+ The input of the Fourier transform is complex, made of a real and an
+ imaginary part. The two parts must be of equal length; moreover, this
+ length must match the length inferred at creation time of this
+ object.
+ </p>
+ ********************************************************************/
+	COMPLEXINPUT,
+/*********************************************************************
+ <p>
+ The input of the Fourier transform is real, although the storage for
+ the imaginary part must still be provided to receive the output of
+ the Fourier transform. The two parts must be of equal length;
+ moreover, this length must match the length inferred at creation time
+ of this object.
+ </p>
+ ********************************************************************/
+	REALINPUT
+}
+
+/*....................................................................
+	SlowFourierTransform protected variables
+....................................................................*/
+protected double[] imBufferDouble;
+protected double[] imDataDouble;
+protected double[] reBufferDouble;
+protected double[] reDataDouble;
+protected float[] imBufferFloat;
+protected float[] imDataFloat;
+protected float[] reBufferFloat;
+protected float[] reDataFloat;
+
+/*....................................................................
+	SlowFourierTransform private variables
+....................................................................*/
+private Integer depth;
+private Integer height;
+private Integer width;
+private int dataLength;
+private int dimensions;
+private int firstDimension;
+private int fourierOrigin1;
+private int fourierOrigin2;
+private int fourierOrigin3;
+
+/*....................................................................
+	SlowFourierTransform private enum constants
+....................................................................*/
+private enum Algorithm {
+	BRUTEFORCE,
+	COPRIMEFACTOR,
+	DUOREAL,
+	EVENREAL,
+	LENGTH1,
+	LENGTH2,
+	LENGTH3,
+	LENGTH4,
+	LENGTH5,
+	LENGTH6,
+	LENGTH8,
+	MIXEDRADIX,
+	PADDEDRADER,
+	RADER,
+	RADIX2,
+	SPLITRADIX
+}
+
+private final boolean PARALLELPROCESSING = true;
+
+/*....................................................................
+	SlowFourierTransform inner classes
+....................................................................*/
+/*====================================================================
+|	DFTDouble
+\===================================================================*/
+static class DFTDouble
+
+{ /* begin class DFTDouble */
+
+/*....................................................................
+	DFTDouble variables
+....................................................................*/
+protected double[] imData;
+protected double[] reData;
+protected int startIndex;
+protected int stride;
+
+/*....................................................................
+	DFTDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTDouble (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	this.reData = reData;
+	this.imData = imData;
+	this.startIndex = startIndex;
+	this.stride = stride;
+} /* end DFTDouble */
+
+} /* end class DFTDouble */
+
+/*====================================================================
+|	DFTFloat
+\===================================================================*/
+static class DFTFloat
+
+{ /* begin class DFTFloat */
+
+/*....................................................................
+	DFTFloat variables
+....................................................................*/
+protected float[] imData;
+protected float[] reData;
+protected int startIndex;
+protected int stride;
+
+/*....................................................................
+	DFTFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTFloat (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	this.reData = reData;
+	this.imData = imData;
+	this.startIndex = startIndex;
+	this.stride = stride;
+} /* end DFTFloat */
+
+} /* end class DFTFloat */
+
+/*====================================================================
+|	DFTBruteForce
+\===================================================================*/
+static class DFTBruteForce
+
+{ /* begin class DFTBruteForce */
+
+/*....................................................................
+	DFTBruteForce static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(transformLength))) {
+		return(-1L);
+	}
+	final long K = (long)transformLength;
+	return(FFTSetup.FLALLOC * 2L
+		+ FFTSetup.FLOP * (K * 2L + (K - 1L) * (K - 1L) * 8L)
+		+ FFTSetup.FLASSIGN * (2L + K * 2L + 2L + (K- 1L) * (2L
+			+ (K - 1L) * 2L + 2L) + K * 2L)
+		+ FFTSetup.INTALLOC * 9L
+		+ FFTSetup.INTOP * (K * 3L + 1L + (K - 1L) * (3L + (K - 1L) * 7L + 1L)
+			+ K * 3L)
+		+ FFTSetup.INTASSIGN * (5L + K * 2L + 2L + (K - 1L) * (4L
+			+ (K - 1L) * 4L + 1L) + 2L + K * 2L)
+		+ FFTSetup.IDX * (K * 2L + 2L + (K - 1L) * (2L + (K - 1L) * 8L + 2L)
+			+ K * 4L)
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTBruteForce */
+
+/*====================================================================
+|	DFTBruteForceDouble
+\===================================================================*/
+static class DFTBruteForceDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTBruteForceDouble */
+
+/*....................................................................
+	DFTBruteForceDouble private variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imUnitRoot;
+private double[] reBuffer;
+private double[] reUnitRoot;
+
+/*....................................................................
+	DFTBruteForceDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTBruteForceDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reUnitRoot,
+	final double[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTBruteForceDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = reUnitRoot.length;
+//;IJ.log("DFTBruteForceDouble " + transformLength);
+	final int d1 = stride;
+	int n0 = startIndex;
+	double re = 0.0;
+	double im = 0.0;
+	int k1 = startIndex;
+	for (int n = 0; (n < transformLength); n++) {
+		re += reData[k1];
+		im += imData[k1];
+		k1 += d1;
+	}
+	reBuffer[n0] = re;
+	imBuffer[n0] = im;
+	n0 += d1;
+	for (int m = 1; (m < transformLength); m++) {
+		re = reData[startIndex];
+		im = imData[startIndex];
+		int m1 = m;
+		k1 = startIndex + d1;
+		for (int n = 1; (n < transformLength); n++) {
+			re += reData[k1] * reUnitRoot[m1] - imData[k1] * imUnitRoot[m1];
+			im += reData[k1] * imUnitRoot[m1] + imData[k1] * reUnitRoot[m1];
+			m1 += m;
+			m1 -= transformLength * (m1 / transformLength);
+			k1 += d1;
+		}
+		reBuffer[n0] = re;
+		imBuffer[n0] = im;
+		n0 += d1;
+	}
+	n0 = startIndex;
+	for (int m = 0; (m < transformLength); m++) {
+		reData[n0] = reBuffer[n0];
+		imData[n0] = imBuffer[n0];
+		n0 += d1;
+	}
+} /* end run */
+
+/*....................................................................
+	DFTBruteForceDouble static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static double[] getImUnitRoot (
+	final int transformLength
+) {
+	final double[] imUnitRoot = new double[transformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < transformLength); k++) {
+		imUnitRoot[k] = sin((double)k * angularStep);
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static double[] getReUnitRoot (
+	final int transformLength
+) {
+	final double[] reUnitRoot = new double[transformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < transformLength); k++) {
+		reUnitRoot[k] = cos((double)k * angularStep);
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTBruteForceDouble */
+
+/*====================================================================
+|	DFTBruteForceFloat
+\===================================================================*/
+static class DFTBruteForceFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTBruteForceFloat */
+
+/*....................................................................
+	DFTBruteForceFloat private variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imUnitRoot;
+private float[] reBuffer;
+private float[] reUnitRoot;
+
+/*....................................................................
+	DFTBruteForceFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTBruteForceFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reUnitRoot,
+	final float[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTBruteForceFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = reUnitRoot.length;
+//;IJ.log("DFTBruteForceFloat " + transformLength);
+	final int d1 = stride;
+	int n0 = startIndex;
+	float re = 0.0F;
+	float im = 0.0F;
+	int k1 = startIndex;
+	for (int n = 0; (n < transformLength); n++) {
+		re += reData[k1];
+		im += imData[k1];
+		k1 += d1;
+	}
+	reBuffer[n0] = re;
+	imBuffer[n0] = im;
+	n0 += d1;
+	for (int m = 1; (m < transformLength); m++) {
+		re = reData[startIndex];
+		im = imData[startIndex];
+		int m1 = m;
+		k1 = startIndex + d1;
+		for (int n = 1; (n < transformLength); n++) {
+			re += reData[k1] * reUnitRoot[m1] - imData[k1] * imUnitRoot[m1];
+			im += reData[k1] * imUnitRoot[m1] + imData[k1] * reUnitRoot[m1];
+			m1 += m;
+			m1 -= transformLength * (m1 / transformLength);
+			k1 += d1;
+		}
+		reBuffer[n0] = re;
+		imBuffer[n0] = im;
+		n0 += d1;
+	}
+	n0 = startIndex;
+	for (int m = 0; (m < transformLength); m++) {
+		reData[n0] = reBuffer[n0];
+		imData[n0] = imBuffer[n0];
+		n0 += d1;
+	}
+} /* end run */
+
+/*....................................................................
+	DFTBruteForceFloat static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static float[] getImUnitRoot (
+	final int transformLength
+) {
+	final float[] imUnitRoot = new float[transformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < transformLength); k++) {
+		imUnitRoot[k] = (float)sin((double)((float)k * angularStep));
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static float[] getReUnitRoot (
+	final int transformLength
+) {
+	final float[] reUnitRoot = new float[transformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < transformLength); k++) {
+		reUnitRoot[k] = (float)cos((double)((float)k * angularStep));
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTBruteForceFloat */
+
+/*====================================================================
+|	DFTBruteForceReal
+\===================================================================*/
+static class DFTBruteForceReal
+
+{ /* begin class DFTBruteForceReal */
+
+/*....................................................................
+	DFTBruteForceReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(transformLength))) {
+		return(-1L);
+	}
+	final long K = (long)transformLength;
+	final long k = (long)(transformLength >> 1);
+	return(FFTSetup.FLALLOC * 2L
+		+ FFTSetup.FLOP * (K * 1L + k * (K * 4L))
+		+ FFTSetup.FLASSIGN * (2L + K * 1L + 1L + k * (2L + K * 2L + 2L)
+			+ k * 2L + 2L)
+		+ FFTSetup.INTALLOC * 11L
+		+ FFTSetup.INTOP * (4L + K * 3L + k * (2L + K * 7L + 1L) + 1L + k * 3L)
+		+ FFTSetup.INTASSIGN * (6L + K * 2L + 1L + k * (4L + K * 4L + 1L) + 2L
+			+ k * 2L)
+		+ FFTSetup.IDX * (K * 1L + 1L + k * (K * 4L + 2L) + k * 4L + 3L)
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTBruteForceReal */
+
+/*====================================================================
+|	DFTBruteForceRealDouble
+\===================================================================*/
+static class DFTBruteForceRealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTBruteForceRealDouble */
+
+/*....................................................................
+	DFTBruteForceRealDouble private variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imUnitRoot;
+private double[] reBuffer;
+private double[] reUnitRoot;
+
+/*....................................................................
+	DFTBruteForceRealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTBruteForceRealDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reUnitRoot,
+	final double[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTBruteForceRealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	double re = 0.0;
+	double im = 0.0;
+	final int transformLength = reUnitRoot.length;
+//;IJ.log("DFTBruteForceRealDouble " + transformLength);
+	final int halfTransformLength = transformLength >> 1;
+	final int d1 = stride;
+	int k1 = startIndex;
+	int n0 = startIndex + d1;
+	if (0 == (transformLength & 1)) {
+		for (int n = 0; (n < halfTransformLength); n++) {
+			re += reData[k1];
+			im += reData[k1];
+			k1 += d1;
+			re += reData[k1];
+			im -= reData[k1];
+			k1 += d1;
+		}
+		reBuffer[startIndex] = re;
+		int n1 = startIndex + halfTransformLength * d1;
+		reBuffer[n1] = im;
+		for (int m = 1; (m < halfTransformLength); m++) {
+			re = 0.0;
+			im = 0.0;
+			int m1 = 0;
+			k1 = startIndex;
+			for (int n = 0; (n < transformLength); n++) {
+				re += reData[k1] * reUnitRoot[m1];
+				im += reData[k1] * imUnitRoot[m1];
+				m1 += m;
+				m1 -= transformLength * (m1 / transformLength);
+				k1 += d1;
+			}
+			reBuffer[n0] = re;
+			imBuffer[n0] = im;
+			n0 += d1;
+		}
+		reData[n0] = reBuffer[n0];
+		imData[n0] = 0.0;
+		n0 -= d1;
+		for (int m = 1; (m < halfTransformLength); m++) {
+			reData[n0] = reBuffer[n0];
+			imData[n0] = imBuffer[n0];
+			n0 -= d1;
+		}
+		reData[n0] = reBuffer[n0];
+		imData[n0] = 0.0;
+	}
+	else {
+		for (int n = 0; (n < transformLength); n++) {
+			re += reData[k1];
+			k1 += d1;
+		}
+		reBuffer[startIndex] = re;
+		for (int m = 1; (m <= halfTransformLength); m++) {
+			re = 0.0;
+			im = 0.0;
+			int m1 = 0;
+			k1 = startIndex;
+			for (int n = 0; (n < transformLength); n++) {
+				re += reData[k1] * reUnitRoot[m1];
+				im += reData[k1] * imUnitRoot[m1];
+				m1 += m;
+				m1 -= transformLength * (m1 / transformLength);
+				k1 += d1;
+			}
+			reBuffer[n0] = re;
+			imBuffer[n0] = im;
+			n0 += d1;
+		}
+		int n1 = n0 - d1;
+		for (int m = 1; (m <= halfTransformLength); m++) {
+			reData[n1] = reBuffer[n1];
+			imData[n1] = imBuffer[n1];
+			n1 -= d1;
+		}
+		reData[n1] = reBuffer[n1];
+		imData[n1] = 0.0;
+	}
+} /* end run */
+
+} /* end class DFTBruteForceRealDouble */
+
+/*====================================================================
+|	DFTBruteForceRealFloat
+\===================================================================*/
+static class DFTBruteForceRealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTBruteForceRealFloat */
+
+/*....................................................................
+	DFTBruteForceRealFloat private variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imUnitRoot;
+private float[] reBuffer;
+private float[] reUnitRoot;
+
+/*....................................................................
+	DFTBruteForceRealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTBruteForceRealFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reUnitRoot,
+	final float[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTBruteForceRealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	float re = 0.0F;
+	float im = 0.0F;
+	final int transformLength = reUnitRoot.length;
+//;IJ.log("DFTBruteForceRealFloat " + transformLength);
+	final int halfTransformLength = transformLength >> 1;
+	final int d1 = stride;
+	int k1 = startIndex;
+	int n0 = startIndex + d1;
+	if (0 == (transformLength & 1)) {
+		for (int n = 0; (n < halfTransformLength); n++) {
+			re += reData[k1];
+			im += reData[k1];
+			k1 += d1;
+			re += reData[k1];
+			im -= reData[k1];
+			k1 += d1;
+		}
+		reBuffer[startIndex] = re;
+		int n1 = startIndex + halfTransformLength * d1;
+		reBuffer[n1] = im;
+		for (int m = 1; (m < halfTransformLength); m++) {
+			re = 0.0F;
+			im = 0.0F;
+			int m1 = 0;
+			k1 = startIndex;
+			for (int n = 0; (n < transformLength); n++) {
+				re += reData[k1] * reUnitRoot[m1];
+				im += reData[k1] * imUnitRoot[m1];
+				m1 += m;
+				m1 -= transformLength * (m1 / transformLength);
+				k1 += d1;
+			}
+			reBuffer[n0] = re;
+			imBuffer[n0] = im;
+			n0 += d1;
+		}
+		reData[n0] = reBuffer[n0];
+		imData[n0] = 0.0F;
+		n0 -= d1;
+		for (int m = 1; (m < halfTransformLength); m++) {
+			reData[n0] = reBuffer[n0];
+			imData[n0] = imBuffer[n0];
+			n0 -= d1;
+		}
+		reData[n0] = reBuffer[n0];
+		imData[n0] = 0.0F;
+	}
+	else {
+		for (int n = 0; (n < transformLength); n++) {
+			re += reData[k1];
+			k1 += d1;
+		}
+		reBuffer[startIndex] = re;
+		for (int m = 1; (m <= halfTransformLength); m++) {
+			re = 0.0F;
+			im = 0.0F;
+			int m1 = 0;
+			k1 = startIndex;
+			for (int n = 0; (n < transformLength); n++) {
+				re += reData[k1] * reUnitRoot[m1];
+				im += reData[k1] * imUnitRoot[m1];
+				m1 += m;
+				m1 -= transformLength * (m1 / transformLength);
+				k1 += d1;
+			}
+			reBuffer[n0] = re;
+			imBuffer[n0] = im;
+			n0 += d1;
+		}
+		int n1 = n0 - d1;
+		for (int m = 1; (m <= halfTransformLength); m++) {
+			reData[n1] = reBuffer[n1];
+			imData[n1] = imBuffer[n1];
+			n1 -= d1;
+		}
+		reData[n1] = reBuffer[n1];
+		imData[n1] = 0.0F;
+	}
+} /* end run */
+
+} /* end class DFTBruteForceRealFloat */
+
+/*====================================================================
+|	DFTCoprimeFactor
+\===================================================================*/
+static class DFTCoprimeFactor
+
+{ /* begin class DFTCoprimeFactor */
+
+/*....................................................................
+	DFTCoprimeFactor static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int K1,
+	final int K2
+) {
+	if (FFTSetup.taboos.contains(new Integer(K1))
+		|| FFTSetup.taboos.contains(new Integer(K2))) {
+		return(-1L);
+	}
+	final long K = (long)K1 * (long)K2;
+	return(FFTSetup.FLALLOC * 0L
+		+ FFTSetup.FLOP * 0L
+		+ FFTSetup.FLASSIGN * (K * 2L + K * 2L)
+		+ FFTSetup.INTALLOC * 11L
+		+ FFTSetup.INTOP * (1L + K * 5L + 2L + K2 * 3L + 1L + K1 * 3L + K * 5L)
+		+ FFTSetup.INTASSIGN * (4L + K * 3L + 4L + K2 * 2L + 2L + K1 * 2L + 2L
+			+ K * 3L)
+		+ FFTSetup.IDX * (K * 5L + K * 5L)
+		+ FFTSetup.NEWOBJ * (K2 * 1L + K1 * 1L)
+		+ (long)K2 * FFTSetup.cost(K1) + (long)K1 * FFTSetup.cost(K2)
+	);
+} /* end cost */
+
+/*------------------------------------------------------------------*/
+static int[] getChineseRemainderShuffling (
+	final int K1,
+	final int K2
+) {
+	final int K1K2 = K1 * K2;
+	int[] chineseRemainderShuffling = new int[K1K2];
+	final int p1 = modularMultiplicativeInverse(K1, K2);
+	final int p2 = modularMultiplicativeInverse(K2, K1);
+	int p = 0;
+	for (int k2 = 0; (k2 < K2); k2++) {
+		int q = p * p1;
+		for (int k1 = 0; (k1 < K1); k1++) {
+			chineseRemainderShuffling[q - K1K2 * (q / K1K2)] = p++;
+			q += p2 * K2;
+		}
+	}
+	return(chineseRemainderShuffling);
+} /* end getChineseRemainderShuffling */
+
+/*------------------------------------------------------------------*/
+static int[] getRuritanianShuffling (
+	final int K1,
+	final int K2
+) {
+	final int K1K2 = K1 * K2;
+	int[] ruritanianShuffling = new int[K1K2];
+	int p = 0;
+	for (int k2 = 0; (k2 < K2); k2++) {
+		int q = p;
+		for (int k1 = 0; (k1 < K1); k1++) {
+			ruritanianShuffling[p++] = q - K1K2 * (q / K1K2);
+			q += K2;
+		}
+	}
+	return(ruritanianShuffling);
+} /* end getRuritanianShuffling */
+
+/*....................................................................
+	DFTCoprimeFactor private methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static private int[] extendedGreatestCommonDivisor (
+	int m,
+	int n
+) {
+	if ((m < 1) || (n < 1)) {
+		return(null);
+	}
+	int a = 1;
+	int b = 0;
+	int p = 0;
+	int q = 1;
+	while (0 != n) {
+		final int i = m;
+		final int j = a;
+		final int k = b;
+		m = n;
+		a = p;
+		b = q;
+		final int f = i / n;
+		n = i - f * n;
+		p = j - f * p;
+		q = k - f * q;
+	}
+	return(new int[] { // a * m + b * n = gcd(m, n)
+		m, // gcd
+		a,
+		b
+	});
+} /* end extendedGreatestCommonDivisor */
+
+/*------------------------------------------------------------------*/
+static private int modularMultiplicativeInverse (
+	int n,
+	final int modulo
+) {
+	if ((n < 1) || (modulo < 1)) {
+		return(0);
+	}
+	n = extendedGreatestCommonDivisor(n, modulo)[1];
+	return((n < 0) ? (n + modulo) : (n));
+} /* end modularMultiplicativeInverse */
+
+} /* end class DFTCoprimeFactor */
+
+/*====================================================================
+|	DFTCoprimeFactorDouble
+\===================================================================*/
+static class DFTCoprimeFactorDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTCoprimeFactorDouble */
+
+/*....................................................................
+	DFTCoprimeFactorDouble private variables
+....................................................................*/
+private double[] imBuffer;
+private double[] reBuffer;
+private int[] ruritanian;
+private int[] chinese;
+private int K1;
+
+/*....................................................................
+	DFTCoprimeFactorDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTCoprimeFactorDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final int[] ruritanian,
+	final int[] chinese,
+	final int K1
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.ruritanian = ruritanian;
+	this.chinese = chinese;
+	this.K1 = K1;
+} /* end DFTCoprimeFactorDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = ruritanian.length;
+//;IJ.log("DFTCoprimeFactorDouble " + transformLength + " / " + K1);
+	final int K2 = transformLength / K1;
+	int p = startIndex;
+	for (int k = 0; (k < transformLength); k++) {
+		final int q = startIndex + ruritanian[k] * stride;
+		reBuffer[p] = reData[q];
+		imBuffer[p] = imData[q];
+		p += stride;
+	}
+	final int d1 = stride;
+	final int d2 = K1 * d1;
+	p = startIndex;
+	final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1));
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData,
+					p, d1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				p += d2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData,
+					p, d1,
+					fft1.ruritanian, fft1.chinese, fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength2Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength3Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength4Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength5Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength6Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength8Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble,
+					fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTPaddedRaderDouble(reBuffer, imBuffer,
+					p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTRaderDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTRadix2Double(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				p += d2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				p += d2;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				p += d1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength2Double(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength3Double(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength4Double(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength5Double(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength6Double(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength8Double(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+					fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTPaddedRaderDouble(reBuffer, imBuffer,
+					p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADER: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTRaderDouble(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTRadix2Double(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				p += d1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				p += d1;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	for (int k = 0; (k < transformLength); k++) {
+		final int q = startIndex + chinese[k] * stride;
+		reData[p] = reBuffer[q];
+		imData[p] = imBuffer[q];
+		p += stride;
+	}
+} /* end run */
+
+} /* end class DFTCoprimeFactorDouble */
+
+/*====================================================================
+|	DFTCoprimeFactorFloat
+\===================================================================*/
+static class DFTCoprimeFactorFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTCoprimeFactorFloat */
+
+/*....................................................................
+	DFTCoprimeFactorFloat private variables
+....................................................................*/
+private float[] imBuffer;
+private float[] reBuffer;
+private int[] ruritanian;
+private int[] chinese;
+private int K1;
+
+/*....................................................................
+	DFTCoprimeFactorFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTCoprimeFactorFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final int[] ruritanian,
+	final int[] chinese,
+	final int K1
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.ruritanian = ruritanian;
+	this.chinese = chinese;
+	this.K1 = K1;
+} /* end DFTCoprimeFactorFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = ruritanian.length;
+//;IJ.log("DFTCoprimeFactorFloat " + transformLength + " / " + K1);
+	final int K2 = transformLength / K1;
+	int p = startIndex;
+	for (int k = 0; (k < transformLength); k++) {
+		final int q = startIndex + ruritanian[k] * stride;
+		reBuffer[p] = reData[q];
+		imBuffer[p] = imData[q];
+		p += stride;
+	}
+	final int d1 = stride;
+	final int d2 = K1 * d1;
+	p = startIndex;
+	final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1));
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				p += d2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength2Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength3Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength4Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength5Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength6Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength8Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat,
+					fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTPaddedRaderFloat(reBuffer, imBuffer,
+					p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTRaderFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTRadix2Float(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				p += d2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				p += d2;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				p += d1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength2Float(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength3Float(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength4Float(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength5Float(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength6Float(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength8Float(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+					fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTPaddedRaderFloat(reBuffer, imBuffer,
+					p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADER: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTRaderFloat(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTRadix2Float(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				p += d1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				p += d1;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	for (int k = 0; (k < transformLength); k++) {
+		final int q = startIndex + chinese[k] * stride;
+		reData[p] = reBuffer[q];
+		imData[p] = imBuffer[q];
+		p += stride;
+	}
+} /* end run */
+
+} /* end class DFTCoprimeFactorFloat */
+
+/*====================================================================
+|	DFTCoprimeFactorReal
+\===================================================================*/
+static class DFTCoprimeFactorReal
+
+{ /* begin class DFTCoprimeFactorReal */
+
+/*....................................................................
+	DFTCoprimeFactorReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int K1,
+	final int K2
+) {
+	if (FFTSetup.taboos.contains(new Integer(K1))
+		|| FFTSetup.taboos.contains(new Integer(K2))) {
+		return(-1L);
+	}
+	final long K = (long)K1 * (long)K2;
+	final long k = K >> 1L;
+	final long k1 = (long)(K1 >> 1);
+	final long k2 = (long)(K2 >> 1);
+	return(FFTSetup.FLALLOC * 0L
+		+ FFTSetup.FLOP * ((K >> 2L) * 1L)
+		+ FFTSetup.FLASSIGN * (K * 2L + (k + 1L) * 2L)
+		+ FFTSetup.INTALLOC * 12L
+		+ FFTSetup.INTOP * (3L + K * 5L + 4L + (long)K1 * 3L + 1L
+			+ (k2 + 1L) * 3L + 2L + (k + 1L) * 6L)
+		+ FFTSetup.INTASSIGN * (5L + K * 3L + 4L + (long)K1 * 2L + 2L
+			+ (k2 + 1L) * 2L + 3L + (k + 1L) * 4L)
+		+ FFTSetup.IDX * (K * 5L + (k + 1L) * 5L)
+		+ FFTSetup.NEWOBJ * ((long)K1 * 1L + (long)K2 * 1L)
+		+ k1 * FFTSetupDuoReal.cost(K2) + (k2 + 1L) * FFTSetup.cost(K1)
+	);
+} /* end cost */
+
+/*------------------------------------------------------------------*/
+static int[] getTruncatedChineseRemainderShuffling (
+	final int K1,
+	final int K2
+) {
+	final int K1K2 = K1 * K2;
+	final int halfK1K2 = (K1K2 >> 1) + 1;
+	final int halfK2 = (K2 >> 1) + 1;
+	int[] chineseRemainderShuffling = new int[halfK1K2];
+	final int p1 = DFTCoprimeFactor.modularMultiplicativeInverse(K1, K2);
+	final int p2 = DFTCoprimeFactor.modularMultiplicativeInverse(K2, K1);
+	int n = 0;
+	for (int k2 = 0; (k2 < K2); k2++) {
+		int q = n * p1;
+		for (int k1 = 0; (k1 < K1); k1++) {
+			final int p = q - K1K2 * (q / K1K2);
+			if (p < halfK1K2) {
+				final int i0 = n / K1;
+				final int j0 = n - K1 * i0;
+				chineseRemainderShuffling[p] = (i0 < halfK2) ? (n)
+					: ((0 == j0) ? (n - K1K2) : (n - K1K2 - K1));
+			}
+			q += p2 * K2;
+			n++;
+		}
+	}
+	return(chineseRemainderShuffling);
+} /* end getTruncatedChineseRemainderShuffling */
+
+} /* end class DFTCoprimeFactorReal */
+
+/*====================================================================
+|	DFTCoprimeFactorRealDouble
+\===================================================================*/
+static class DFTCoprimeFactorRealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTCoprimeFactorRealDouble */
+
+/*....................................................................
+	DFTCoprimeFactorRealDouble private variables
+....................................................................*/
+private double[] imBuffer;
+private double[] reBuffer;
+private int[] ruritanian;
+private int[] chinese;
+private int K1;
+
+/*....................................................................
+	DFTCoprimeFactorRealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTCoprimeFactorRealDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final int[] ruritanian,
+	final int[] chinese,
+	final int K1
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.ruritanian = ruritanian;
+	this.chinese = chinese;
+	this.K1 = K1;
+} /* end DFTCoprimeFactorRealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = ruritanian.length;
+//;IJ.log("DFTCoprimeFactorRealDouble " + transformLength + " / " + K1);
+	final int K2 = transformLength / K1;
+	final int halfK2 = (K2 >> 1) + 1;
+	int p = startIndex;
+	for (int k = 0; (k < transformLength); k++) {
+		final int q = startIndex + ruritanian[k] * stride;
+		reBuffer[p] = reData[q];
+		imBuffer[p] = imData[q];
+		p += stride;
+	}
+	final int d1 = stride;
+	final int d2 = K1 * d1;
+	p = startIndex;
+	int k1 = 0;
+	if (1 == (K1 & 1)) {
+		final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2));
+		switch (fft2.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				break;
+			}
+			case LENGTH1: {
+				throw(new IllegalStateException());
+			}
+			case LENGTH2: {
+				new DFTLength2RealDouble(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealDouble(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealDouble(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealDouble(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealDouble(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealDouble(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+					fft2.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealDouble(reBuffer, imBuffer,
+					p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble,
+					fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				break;
+			}
+		}
+		p += d1;
+		k1++;
+	}
+	final FFTSetupDuoReal fft2 =
+		FFTSetupDuoReal.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			while (k1++ < K1) {
+				new DFTBruteForceRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				p += d1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (k1++ < K1) {
+				new DFTCoprimeFactorRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (k1++ < K1) {
+				new DFTDuoRealDouble(reBuffer, imBuffer,
+					reData, imData, p, p + d1, d2, K2).run();
+				p += 2 * d1;
+				k1++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (k1++ < K1) {
+				new DFTEvenRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			while (k1++ < K1) {
+				new DFTLength2RealDouble(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (k1++ < K1) {
+				new DFTLength3RealDouble(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (k1++ < K1) {
+				new DFTLength4RealDouble(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (k1++ < K1) {
+				new DFTLength5RealDouble(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (k1++ < K1) {
+				new DFTLength6RealDouble(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (k1++ < K1) {
+				new DFTLength8RealDouble(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (k1++ < K1) {
+				new DFTMixedRadixRealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+					fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (k1++ < K1) {
+				new DFTPaddedRaderRealDouble(reBuffer, imBuffer,
+					p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADER: {
+			while (k1++ < K1) {
+				new DFTRaderRealDouble(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADIX2: {
+			while (k1++ < K1) {
+				new DFTRadix2RealDouble(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble,
+					fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run();
+				p += d1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (k1++ < K1) {
+				new DFTSplitRadixRealDouble(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				p += d1;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1));
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				p += d2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength2Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength3Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength4Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength5Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength6Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength8Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble,
+					fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTPaddedRaderDouble(reBuffer, imBuffer,
+					p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTRaderDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTRadix2Double(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				p += d2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				p += d2;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	for (int k = 0, K = (transformLength >> 1) + 1; (k < K); k++) {
+		int q = chinese[k];
+		if (q < 0) {
+			q = startIndex - q * stride;
+			reData[p] = reBuffer[q];
+			imData[p] = -imBuffer[q];
+		}
+		else {
+			q = startIndex + q * stride;
+			reData[p] = reBuffer[q];
+			imData[p] = imBuffer[q];
+		}
+		p += stride;
+	}
+} /* end run */
+
+} /* end class DFTCoprimeFactorRealDouble */
+
+/*====================================================================
+|	DFTCoprimeFactorRealFloat
+\===================================================================*/
+static class DFTCoprimeFactorRealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTCoprimeFactorRealFloat */
+
+/*....................................................................
+	DFTCoprimeFactorRealFloat private variables
+....................................................................*/
+private float[] imBuffer;
+private float[] reBuffer;
+private int[] ruritanian;
+private int[] chinese;
+private int K1;
+
+/*....................................................................
+	DFTCoprimeFactorRealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTCoprimeFactorRealFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final int[] ruritanian,
+	final int[] chinese,
+	final int K1
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.ruritanian = ruritanian;
+	this.chinese = chinese;
+	this.K1 = K1;
+} /* end DFTCoprimeFactorRealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = ruritanian.length;
+//;IJ.log("DFTCoprimeFactorRealFloat " + transformLength + " / " + K1);
+	final int K2 = transformLength / K1;
+	final int halfK2 = (K2 >> 1) + 1;
+	int p = startIndex;
+	for (int k = 0; (k < transformLength); k++) {
+		final int q = startIndex + ruritanian[k] * stride;
+		reBuffer[p] = reData[q];
+		imBuffer[p] = imData[q];
+		p += stride;
+	}
+	final int d1 = stride;
+	final int d2 = K1 * d1;
+	p = startIndex;
+	int k1 = 0;
+	if (1 == (K1 & 1)) {
+		final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2));
+		switch (fft2.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				break;
+			}
+			case LENGTH1: {
+				throw(new IllegalStateException());
+			}
+			case LENGTH2: {
+				new DFTLength2RealFloat(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealFloat(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealFloat(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealFloat(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealFloat(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealFloat(reBuffer, imBuffer, p, d2).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+					fft2.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealFloat(reBuffer, imBuffer,
+					p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat,
+					fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				break;
+			}
+		}
+		p += d1;
+		k1++;
+	}
+	final FFTSetupDuoReal fft2 =
+		FFTSetupDuoReal.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			while (k1++ < K1) {
+				new DFTBruteForceRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				p += d1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (k1++ < K1) {
+				new DFTCoprimeFactorRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (k1++ < K1) {
+				new DFTDuoRealFloat(reBuffer, imBuffer,
+					reData, imData, p, p + d1, d2, K2).run();
+				p += 2 * d1;
+				k1++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (k1++ < K1) {
+				new DFTEvenRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			while (k1++ < K1) {
+				new DFTLength2RealFloat(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (k1++ < K1) {
+				new DFTLength3RealFloat(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (k1++ < K1) {
+				new DFTLength4RealFloat(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (k1++ < K1) {
+				new DFTLength5RealFloat(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (k1++ < K1) {
+				new DFTLength6RealFloat(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (k1++ < K1) {
+				new DFTLength8RealFloat(reBuffer, imBuffer, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (k1++ < K1) {
+				new DFTMixedRadixRealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+					fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (k1++ < K1) {
+				new DFTPaddedRaderRealFloat(reBuffer, imBuffer,
+					p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADER: {
+			while (k1++ < K1) {
+				new DFTRaderRealFloat(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADIX2: {
+			while (k1++ < K1) {
+				new DFTRadix2RealFloat(reBuffer, imBuffer,
+					reData, imData, p, d2,
+					fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat,
+					fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run();
+				p += d1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (k1++ < K1) {
+				new DFTSplitRadixRealFloat(reBuffer, imBuffer, reData, imData,
+					p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				p += d1;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1));
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				p += d2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength2Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength3Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength4Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength5Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength6Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength8Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat,
+					fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTPaddedRaderFloat(reBuffer, imBuffer,
+					p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTRaderFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTRadix2Float(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				p += d2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				p += d2;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	for (int k = 0, K = (transformLength >> 1) + 1; (k < K); k++) {
+		int q = chinese[k];
+		if (q < 0) {
+			q = startIndex - q * stride;
+			reData[p] = reBuffer[q];
+			imData[p] = -imBuffer[q];
+		}
+		else {
+			q = startIndex + q * stride;
+			reData[p] = reBuffer[q];
+			imData[p] = imBuffer[q];
+		}
+		p += stride;
+	}
+} /* end run */
+
+} /* end class DFTCoprimeFactorRealFloat */
+
+/*====================================================================
+|	DFTDuoReal
+\===================================================================*/
+static class DFTDuoReal
+
+{ /* begin class DFTDuoReal */
+
+/*....................................................................
+	DFTDuoReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(transformLength / 2))) {
+		return(-1L);
+	}
+	final long K = (long)transformLength;
+	final long k = K >> 1L;
+	return(FFTSetup.FLALLOC * 0L
+		+ FFTSetup.FLOP * (k * 8L)
+		+ FFTSetup.FLASSIGN * (K * 2L + 4L + k * 4L)
+		+ FFTSetup.INTALLOC * 6L
+		+ FFTSetup.INTOP * (K * 4L + 9L + k * 5L)
+		+ FFTSetup.INTASSIGN * (3L + K * 3L + 7L + k * 4L)
+		+ FFTSetup.IDX * (K * 4L + 6L + k * 8L)
+		+ FFTSetup.NEWOBJ * 1L
+		+ FFTSetup.cost(transformLength)
+	);
+} /* end cost */
+
+} /* end class DFTDuoReal */
+
+/*====================================================================
+|	DFTDuoRealDouble
+\===================================================================*/
+static class DFTDuoRealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTDuoRealDouble */
+
+/*....................................................................
+	DFTDuoRealDouble static variables
+....................................................................*/
+private double[] imBuffer;
+private double[] reBuffer;
+private int duoStartIndex;
+private int transformLength;
+
+/*....................................................................
+	DFTDuoRealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTDuoRealDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int duoStartIndex,
+	final int stride,
+	final int transformLength
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.duoStartIndex = duoStartIndex;
+	this.transformLength = transformLength;
+} /* end DFTDuoRealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTDuoRealDouble " + transformLength);
+	int i0 = startIndex;
+	int i1 = duoStartIndex;
+	for (int m = 0; (m < transformLength); m++) {
+		reBuffer[i0] = reData[i0];
+		imBuffer[i0] = reData[i1];
+		i0 += stride;
+		i1 += stride;
+	}
+	final FFTSetup fft = FFTSetup.transforms.get(new Integer(transformLength));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderDouble(reBuffer, imBuffer,
+				startIndex, stride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Double(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+	}
+	i0 = startIndex;
+	i1 = duoStartIndex;
+	reData[i0] = reBuffer[i0];
+	imData[i0] = 0.0;
+	reData[i1] = imBuffer[i0];
+	imData[i1] = 0.0;
+	i0 += stride;
+	i1 += stride;
+	int j0 = startIndex + (transformLength - 1) * stride;
+	if (0 == (transformLength & 1)) {
+		for (int m = 1, M = transformLength >> 1; (m < M); m++) {
+			reData[i0] = 0.5 * (reBuffer[i0] + reBuffer[j0]);
+			imData[i0] = 0.5 * (imBuffer[i0] - imBuffer[j0]);
+			reData[i1] = 0.5 * (imBuffer[i0] + imBuffer[j0]);
+			imData[i1] = -0.5 * (reBuffer[i0] - reBuffer[j0]);
+			i0 += stride;
+			i1 += stride;
+			j0 -= stride;
+		}
+		reData[i0] = reBuffer[i0];
+		imData[i0] = 0.0;
+		reData[i1] = imBuffer[i0];
+		imData[i1] = 0.0;
+	}
+	else {
+		for (int m = 1, M = transformLength >> 1; (m <= M); m++) {
+			reData[i0] = 0.5 * (reBuffer[i0] + reBuffer[j0]);
+			imData[i0] = 0.5 * (imBuffer[i0] - imBuffer[j0]);
+			reData[i1] = 0.5 * (imBuffer[i0] + imBuffer[j0]);
+			imData[i1] = -0.5 * (reBuffer[i0] - reBuffer[j0]);
+			i0 += stride;
+			i1 += stride;
+			j0 -= stride;
+		}
+	}
+} /* end run */
+
+} /* end class DFTDuoRealDouble */
+
+/*====================================================================
+|	DFTDuoRealFloat
+\===================================================================*/
+static class DFTDuoRealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTDuoRealFloat */
+
+/*....................................................................
+	DFTDuoRealFloat static variables
+....................................................................*/
+private float[] imBuffer;
+private float[] reBuffer;
+private int duoStartIndex;
+private int transformLength;
+
+/*....................................................................
+	DFTDuoRealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTDuoRealFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int duoStartIndex,
+	final int stride,
+	final int transformLength
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.duoStartIndex = duoStartIndex;
+	this.transformLength = transformLength;
+} /* end DFTDuoRealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTDuoRealFloat " + transformLength);
+	int i0 = startIndex;
+	int i1 = duoStartIndex;
+	for (int m = 0; (m < transformLength); m++) {
+		reBuffer[i0] = reData[i0];
+		imBuffer[i0] = reData[i1];
+		i0 += stride;
+		i1 += stride;
+	}
+	final FFTSetup fft = FFTSetup.transforms.get(new Integer(transformLength));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderFloat(reBuffer, imBuffer,
+				startIndex, stride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Float(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+	}
+	i0 = startIndex;
+	i1 = duoStartIndex;
+	reData[i0] = reBuffer[i0];
+	imData[i0] = 0.0F;
+	reData[i1] = imBuffer[i0];
+	imData[i1] = 0.0F;
+	i0 += stride;
+	i1 += stride;
+	int j0 = startIndex + (transformLength - 1) * stride;
+	if (0 == (transformLength & 1)) {
+		for (int m = 1, M = transformLength >> 1; (m < M); m++) {
+			reData[i0] = 0.5F * (reBuffer[i0] + reBuffer[j0]);
+			imData[i0] = 0.5F * (imBuffer[i0] - imBuffer[j0]);
+			reData[i1] = 0.5F * (imBuffer[i0] + imBuffer[j0]);
+			imData[i1] = -0.5F * (reBuffer[i0] - reBuffer[j0]);
+			i0 += stride;
+			i1 += stride;
+			j0 -= stride;
+		}
+		reData[i0] = reBuffer[i0];
+		imData[i0] = 0.0F;
+		reData[i1] = imBuffer[i0];
+		imData[i1] = 0.0F;
+	}
+	else {
+		for (int m = 1, M = transformLength >> 1; (m <= M); m++) {
+			reData[i0] = 0.5F * (reBuffer[i0] + reBuffer[j0]);
+			imData[i0] = 0.5F * (imBuffer[i0] - imBuffer[j0]);
+			reData[i1] = 0.5F * (imBuffer[i0] + imBuffer[j0]);
+			imData[i1] = -0.5F * (reBuffer[i0] - reBuffer[j0]);
+			i0 += stride;
+			i1 += stride;
+			j0 -= stride;
+		}
+	}
+} /* end run */
+
+} /* end class DFTDuoRealFloat */
+
+/*====================================================================
+|	DFTEvenReal
+\===================================================================*/
+static class DFTEvenReal
+
+{ /* begin class DFTEvenReal */
+
+/*....................................................................
+	DFTEvenReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(transformLength / 2))) {
+		return(-1L);
+	}
+	final long k = (long)(transformLength >> 1);
+	return(FFTSetup.FLALLOC * 4L
+		+ FFTSetup.FLOP * (1L + (k - 1L) * 14L + 1L)
+		+ FFTSetup.FLASSIGN * (k * 2L + 2L + (k - 1L) * 6L + 2L)
+		+ FFTSetup.INTALLOC * 7L
+		+ FFTSetup.INTOP * (1L + k * 5L + 5L + (k - 1L) * 4L)
+		+ FFTSetup.INTASSIGN * (6L + k * 4L + 4L + (k - 1L) * 3L)
+		+ FFTSetup.IDX * (k * 4L + 4L + (k - 1L) * 12L + 4L)
+		+ FFTSetup.NEWOBJ * 1L
+		+ FFTSetup.cost(transformLength >> 1)
+	);
+} /* end cost */
+
+} /* end class DFTEvenReal */
+
+/*====================================================================
+|	DFTEvenRealDouble
+\===================================================================*/
+static class DFTEvenRealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTEvenRealDouble */
+
+/*....................................................................
+	DFTEvenRealDouble static variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imUnitRoot;
+private double[] reBuffer;
+private double[] reUnitRoot;
+
+/*....................................................................
+	DFTEvenRealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTEvenRealDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reUnitRoot,
+	final double[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTEvenRealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int halfTransformLength = reUnitRoot.length;
+//;IJ.log("DFTEvenRealDouble " + (2 * halfTransformLength));
+	final int doubleStride = stride << 1;
+	int m0 = startIndex;
+	int i0 = startIndex;
+	int i1 = startIndex + stride;
+	for (int m = 0; (m < halfTransformLength); m++) {
+		reBuffer[m0] = reData[i0];
+		imBuffer[m0] = reData[i1];
+		m0 += stride;
+		i0 += doubleStride;
+		i1 += doubleStride;
+	}
+	final FFTSetup fft =
+		FFTSetup.transforms.get(new Integer(halfTransformLength));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Double(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderDouble(reBuffer, imBuffer,
+				startIndex, stride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Double(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+	}
+	i0 = startIndex;
+	reData[i0] = reBuffer[i0] + imBuffer[i0];
+	imData[i0] = 0.0;
+	i0 += stride;
+	i1 = startIndex + (halfTransformLength - 1) * stride;
+	for (int m = 1; (m < halfTransformLength); m++) {
+		final double re = reBuffer[i0] - reBuffer[i1];
+		final double im = imBuffer[i0] + imBuffer[i1];
+		final double reRoot = reUnitRoot[m];
+		final double imRoot = imUnitRoot[m];
+		reData[i0] = 0.5 * (reBuffer[i0] + reBuffer[i1]
+			+ re * imRoot + im * reRoot);
+		imData[i0] = 0.5 * (imBuffer[i0] - imBuffer[i1]
+			- re * reRoot + im * imRoot);
+		i0 += stride;
+		i1 -= stride;
+	}
+	reData[i0] = reBuffer[startIndex] - imBuffer[startIndex];
+	imData[i0] = 0.0;
+} /* end run */
+
+/*....................................................................
+	DFTEvenRealDouble static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static double[] getImUnitRoot (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final double[] imUnitRoot = new double[halfTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		imUnitRoot[k] = sin((double)k * angularStep);
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static double[] getReUnitRoot (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final double[] reUnitRoot = new double[halfTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		reUnitRoot[k] = cos((double)k * angularStep);
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTEvenRealDouble */
+
+/*====================================================================
+|	DFTEvenRealFloat
+\===================================================================*/
+static class DFTEvenRealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTEvenRealFloat */
+
+/*....................................................................
+	DFTEvenRealFloat static variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imUnitRoot;
+private float[] reBuffer;
+private float[] reUnitRoot;
+
+/*....................................................................
+	DFTEvenRealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTEvenRealFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reUnitRoot,
+	final float[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTEvenRealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int halfTransformLength = reUnitRoot.length;
+//;IJ.log("DFTEvenRealFloat " + (2 * halfTransformLength));
+	final int doubleStride = stride << 1;
+	int m0 = startIndex;
+	int i0 = startIndex;
+	int i1 = startIndex + stride;
+	for (int m = 0; (m < halfTransformLength); m++) {
+		reBuffer[m0] = reData[i0];
+		imBuffer[m0] = reData[i1];
+		m0 += stride;
+		i0 += doubleStride;
+		i1 += doubleStride;
+	}
+	final FFTSetup fft =
+		FFTSetup.transforms.get(new Integer(halfTransformLength));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Float(reBuffer, imBuffer,
+				startIndex, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderFloat(reBuffer, imBuffer,
+				startIndex, stride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Float(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData,
+				startIndex, stride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+	}
+	i0 = startIndex;
+	reData[i0] = reBuffer[i0] + imBuffer[i0];
+	imData[i0] = 0.0F;
+	i0 += stride;
+	i1 = startIndex + (halfTransformLength - 1) * stride;
+	for (int m = 1; (m < halfTransformLength); m++) {
+		final float re = reBuffer[i0] - reBuffer[i1];
+		final float im = imBuffer[i0] + imBuffer[i1];
+		final float reRoot = reUnitRoot[m];
+		final float imRoot = imUnitRoot[m];
+		reData[i0] = 0.5F * (reBuffer[i0] + reBuffer[i1]
+			+ re * imRoot + im * reRoot);
+		imData[i0] = 0.5F * (imBuffer[i0] - imBuffer[i1]
+			- re * reRoot + im * imRoot);
+		i0 += stride;
+		i1 -= stride;
+	}
+	reData[i0] = reBuffer[startIndex] - imBuffer[startIndex];
+	imData[i0] = 0.0F;
+} /* end run */
+
+/*....................................................................
+	DFTEvenRealFloat static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static float[] getImUnitRoot (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final float[] imUnitRoot = new float[halfTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		imUnitRoot[k] = (float)sin((double)((float)k * angularStep));
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static float[] getReUnitRoot (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final float[] reUnitRoot = new float[halfTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		reUnitRoot[k] = (float)cos((double)((float)k * angularStep));
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTEvenRealFloat */
+
+/*====================================================================
+|	DFTLength2
+\===================================================================*/
+static class DFTLength2
+
+{ /* begin class DFTLength2 */
+
+/*....................................................................
+	DFTLength2 static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 3L
+		+ FFTSetup.FLOP * 4L
+		+ FFTSetup.FLASSIGN * 8L
+		+ FFTSetup.INTALLOC * 1L
+		+ FFTSetup.INTOP * 1L
+		+ FFTSetup.INTASSIGN * 1L
+		+ FFTSetup.IDX * 8L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength2 */
+
+/*====================================================================
+|	DFTLength2Double
+\===================================================================*/
+static class DFTLength2Double
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength2Double */
+
+/*....................................................................
+	DFTLength2Double constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength2Double (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength2Double */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength2Double");
+	final int i1 = startIndex + stride;
+	final double re1 = reData[i1];
+	final double im1 = imData[i1];
+	double butterfly = reData[startIndex] - re1;
+	reData[startIndex] += re1;
+	reData[i1] = butterfly;
+	butterfly = imData[startIndex] - im1;
+	imData[startIndex] += im1;
+	imData[i1] = butterfly;
+} /* end run */
+
+} /* end class DFTLength2Double */
+
+/*====================================================================
+|	DFTLength2Float
+\===================================================================*/
+static class DFTLength2Float
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength2Float */
+
+/*....................................................................
+	DFTLength2Float constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength2Float (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength2Float */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength2Float");
+	final int i1 = startIndex + stride;
+	final float re1 = reData[i1];
+	final float im1 = imData[i1];
+	float butterfly = reData[startIndex] - re1;
+	reData[startIndex] += re1;
+	reData[i1] = butterfly;
+	butterfly = imData[startIndex] - im1;
+	imData[startIndex] += im1;
+	imData[i1] = butterfly;
+} /* end run */
+
+} /* end class DFTLength2Float */
+
+/*====================================================================
+|	DFTLength2Real
+\===================================================================*/
+static class DFTLength2Real
+
+{ /* begin class DFTLength2Real */
+
+/*....................................................................
+	DFTLength2Real static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 2L
+		+ FFTSetup.FLOP * 2L
+		+ FFTSetup.FLASSIGN * 6L
+		+ FFTSetup.INTALLOC * 1L
+		+ FFTSetup.INTOP * 1L
+		+ FFTSetup.INTASSIGN * 1L
+		+ FFTSetup.IDX * 6L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength2Real */
+
+/*====================================================================
+|	DFTLength2RealDouble
+\===================================================================*/
+static class DFTLength2RealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength2RealDouble */
+
+/*....................................................................
+	DFTLength2RealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength2RealDouble (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength2RealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength2RealDouble");
+	final int i1 = startIndex + stride;
+	final double re1 = reData[i1];
+	double butterfly = reData[startIndex] - re1;
+	reData[startIndex] += re1;
+	reData[i1] = butterfly;
+	imData[startIndex] = 0.0;
+	imData[i1] = 0.0;
+} /* end run */
+
+} /* end class DFTLength2RealDouble */
+
+/*====================================================================
+|	DFTLength2RealFloat
+\===================================================================*/
+static class DFTLength2RealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength2RealFloat */
+
+/*....................................................................
+	DFTLength2RealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength2RealFloat (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength2RealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength2RealFloat");
+	final int i1 = startIndex + stride;
+	final float re1 = reData[i1];
+	float butterfly = reData[startIndex] - re1;
+	reData[startIndex] += re1;
+	reData[i1] = butterfly;
+	imData[startIndex] = 0.0F;
+	imData[i1] = 0.0F;
+} /* end run */
+
+} /* end class DFTLength2RealFloat */
+
+/*====================================================================
+|	DFTLength3
+\===================================================================*/
+static class DFTLength3
+
+{ /* begin class DFTLength3 */
+
+/*....................................................................
+	DFTLength3 static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 6L
+		+ FFTSetup.FLOP * 20L
+		+ FFTSetup.FLASSIGN * 16L
+		+ FFTSetup.INTALLOC * 2L
+		+ FFTSetup.INTOP * 2L
+		+ FFTSetup.INTASSIGN * 2L
+		+ FFTSetup.IDX * 12L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength3 */
+
+/*====================================================================
+|	DFTLength3Double
+\===================================================================*/
+static class DFTLength3Double
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength3Double */
+
+/*....................................................................
+	DFTLength3Double static variables
+....................................................................*/
+private static final double SQRT3 = sqrt(3.0);
+
+/*....................................................................
+	DFTLength3Double constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength3Double (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength3Double */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength3Double");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	double reButterfly = reData[i1];
+	final double imButterfly = imData[i1];
+	double reDragonfly = reData[i2];
+	final double imDragonfly = imData[i2];
+	final double reLadybug = SQRT3 * (imDragonfly - imButterfly);
+	final double imLadybug = SQRT3 * (reButterfly - reDragonfly);
+	reButterfly += reDragonfly;
+	reDragonfly = reData[startIndex];
+	reData[i1] = reDragonfly - 0.5 * (reLadybug + reButterfly);
+	reData[i2] = reDragonfly + 0.5 * (reLadybug - reButterfly);
+	reData[startIndex] += reButterfly;
+	reDragonfly = imData[startIndex];
+	reButterfly = imButterfly + imDragonfly;
+	imData[i1] = reDragonfly - 0.5 * (imLadybug + reButterfly);
+	imData[i2] = reDragonfly + 0.5 * (imLadybug - reButterfly);
+	imData[startIndex] += reButterfly;
+} /* end run */
+
+} /* end class DFTLength3Double */
+
+/*====================================================================
+|	DFTLength3Float
+\===================================================================*/
+static class DFTLength3Float
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength3Float */
+
+/*....................................................................
+	DFTLength3Float static variables
+....................................................................*/
+private static final float SQRT3 = (float)sqrt(3.0);
+
+/*....................................................................
+	DFTLength3Float constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength3Float (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength3Float */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength3Float");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	float reButterfly = reData[i1];
+	final float imButterfly = imData[i1];
+	float reDragonfly = reData[i2];
+	final float imDragonfly = imData[i2];
+	final float reLadybug = SQRT3 * (imDragonfly - imButterfly);
+	final float imLadybug = SQRT3 * (reButterfly - reDragonfly);
+	reButterfly += reDragonfly;
+	reDragonfly = reData[startIndex];
+	reData[i1] = reDragonfly - 0.5F * (reLadybug + reButterfly);
+	reData[i2] = reDragonfly + 0.5F * (reLadybug - reButterfly);
+	reData[startIndex] += reButterfly;
+	reDragonfly = imData[startIndex];
+	reButterfly = imButterfly + imDragonfly;
+	imData[i1] = reDragonfly - 0.5F * (imLadybug + reButterfly);
+	imData[i2] = reDragonfly + 0.5F * (imLadybug - reButterfly);
+	imData[startIndex] += reButterfly;
+} /* end run */
+
+} /* end class DFTLength3Float */
+
+/*====================================================================
+|	DFTLength3Real
+\===================================================================*/
+static class DFTLength3Real
+
+{ /* begin class DFTLength3Real */
+
+/*....................................................................
+	DFTLength3Real static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 2L
+		+ FFTSetup.FLOP * 6L
+		+ FFTSetup.FLASSIGN * 6L
+		+ FFTSetup.INTALLOC * 2L
+		+ FFTSetup.INTOP * 2L
+		+ FFTSetup.INTASSIGN * 2L
+		+ FFTSetup.IDX * 9L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength3Real */
+
+/*====================================================================
+|	DFTLength3RealDouble
+\===================================================================*/
+static class DFTLength3RealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength3RealDouble */
+
+/*....................................................................
+	DFTLength3RealDouble static variables
+....................................................................*/
+private static final double HALFSQRT3 = 0.5 * sqrt(3.0);
+
+/*....................................................................
+	DFTLength3RealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength3RealDouble (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength3RealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength3RealDouble");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final double reButterfly = reData[i2] + reData[i1];
+	final double imButterfly = HALFSQRT3 * (reData[i2] - reData[i1]);
+	reData[i1] = reData[startIndex] - 0.5 * reButterfly;
+	reData[startIndex] += reButterfly;
+	imData[startIndex] = 0.0;
+	imData[i1] = imButterfly;
+} /* end run */
+
+} /* end class DFTLength3RealDouble */
+
+/*====================================================================
+|	DFTLength3RealFloat
+\===================================================================*/
+static class DFTLength3RealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength3RealFloat */
+
+/*....................................................................
+	DFTLength3RealFloat static variables
+....................................................................*/
+private static final float HALFSQRT3 = 0.5F * (float)sqrt(3.0);
+
+/*....................................................................
+	DFTLength3RealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength3RealFloat (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength3RealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength3RealFloat");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final float reButterfly = reData[i2] + reData[i1];
+	final float imButterfly = HALFSQRT3 * (reData[i2] - reData[i1]);
+	reData[i1] = reData[startIndex] - 0.5F * reButterfly;
+	reData[startIndex] += reButterfly;
+	imData[startIndex] = 0.0F;
+	imData[i1] = imButterfly;
+} /* end run */
+
+} /* end class DFTLength3RealFloat */
+
+/*====================================================================
+|	DFTLength4
+\===================================================================*/
+static class DFTLength4
+
+{ /* begin class DFTLength4 */
+
+/*....................................................................
+	DFTLength4 static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 8L
+		+ FFTSetup.FLOP * 16L
+		+ FFTSetup.FLASSIGN * 24L
+		+ FFTSetup.INTALLOC * 1L
+		+ FFTSetup.INTOP * 11L
+		+ FFTSetup.INTASSIGN * 10L
+		+ FFTSetup.IDX * 16L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength4 */
+
+/*====================================================================
+|	DFTLength4Double
+\===================================================================*/
+static class DFTLength4Double
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength4Double */
+
+/*....................................................................
+	DFTLength4Double constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength4Double (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength4Double */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength4Double");
+	double data0 = reData[startIndex];
+	int k = startIndex + stride;
+	double data1 = reData[k];
+	k += stride;
+	double data2 = reData[k];
+	k += stride;
+	double data3 = reData[k];
+	double butterfly = data0 + data2;
+	double dragonfly = data1 + data3;
+	double ladybug = data0 - data2;
+	double moth = data1 - data3;
+	data3 = imData[k];
+	k -= stride;
+	data2 = imData[k];
+	k -= stride;
+	data1 = imData[k];
+	k += stride;
+	data0 = imData[startIndex];
+	reData[startIndex] = butterfly + dragonfly;
+	reData[k] = butterfly - dragonfly;
+	k -= stride;
+	butterfly = data0 - data2;
+	dragonfly = data1 - data3;
+	reData[k] = ladybug + dragonfly;
+	k += stride + stride;
+	reData[k] = ladybug - dragonfly;
+	dragonfly = data0 + data2;
+	ladybug = data1 + data3;
+	imData[k] = butterfly + moth;
+	k -= stride;
+	imData[k] = dragonfly - ladybug;
+	k -= stride;
+	imData[k] = butterfly - moth;
+	imData[startIndex] = dragonfly + ladybug;
+} /* end run */
+
+} /* end class DFTLength4Double */
+
+/*====================================================================
+|	DFTLength4Float
+\===================================================================*/
+static class DFTLength4Float
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength4Float */
+
+/*....................................................................
+	DFTLength4Float constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength4Float (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength4Float */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength4Float");
+	float data0 = reData[startIndex];
+	int k = startIndex + stride;
+	float data1 = reData[k];
+	k += stride;
+	float data2 = reData[k];
+	k += stride;
+	float data3 = reData[k];
+	float butterfly = data0 + data2;
+	float dragonfly = data1 + data3;
+	float ladybug = data0 - data2;
+	float moth = data1 - data3;
+	data3 = imData[k];
+	k -= stride;
+	data2 = imData[k];
+	k -= stride;
+	data1 = imData[k];
+	k += stride;
+	data0 = imData[startIndex];
+	reData[startIndex] = butterfly + dragonfly;
+	reData[k] = butterfly - dragonfly;
+	k -= stride;
+	butterfly = data0 - data2;
+	dragonfly = data1 - data3;
+	reData[k] = ladybug + dragonfly;
+	k += stride + stride;
+	reData[k] = ladybug - dragonfly;
+	dragonfly = data0 + data2;
+	ladybug = data1 + data3;
+	imData[k] = butterfly + moth;
+	k -= stride;
+	imData[k] = dragonfly - ladybug;
+	k -= stride;
+	imData[k] = butterfly - moth;
+	imData[startIndex] = dragonfly + ladybug;
+} /* end run */
+
+} /* end class DFTLength4Float */
+
+/*====================================================================
+|	DFTLength4Real
+\===================================================================*/
+static class DFTLength4Real
+
+{ /* begin class DFTLength4Real */
+
+/*....................................................................
+	DFTLength4Real static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 2L
+		+ FFTSetup.FLOP * 6L
+		+ FFTSetup.FLASSIGN * 8L
+		+ FFTSetup.INTALLOC * 3L
+		+ FFTSetup.INTOP * 3L
+		+ FFTSetup.INTASSIGN * 3L
+		+ FFTSetup.IDX * 14L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength4Real */
+
+/*====================================================================
+|	DFTLength4RealDouble
+\===================================================================*/
+static class DFTLength4RealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength4RealDouble */
+
+/*....................................................................
+	DFTLength4RealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength4RealDouble (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength4RealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength4RealDouble");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final int i3 = i2 + stride;
+	final double butterfly = reData[startIndex] + reData[i2];
+	final double dragonfly = reData[i1] + reData[i3];
+	imData[startIndex] = 0.0;
+	imData[i1] = reData[i3] - reData[i1];
+	imData[i2] = 0.0;
+	reData[i1] = reData[startIndex] - reData[i2];
+	reData[i2] = butterfly - dragonfly;
+	reData[startIndex] = butterfly + dragonfly;
+} /* end run */
+
+} /* end class DFTLength4RealDouble */
+
+/*====================================================================
+|	DFTLength4RealFloat
+\===================================================================*/
+static class DFTLength4RealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength4RealFloat */
+
+/*....................................................................
+	DFTLength4RealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength4RealFloat (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength4RealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength4RealFloat");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final int i3 = i2 + stride;
+	final float butterfly = reData[startIndex] + reData[i2];
+	final float dragonfly = reData[i1] + reData[i3];
+	imData[startIndex] = 0.0F;
+	imData[i1] = reData[i3] - reData[i1];
+	imData[i2] = 0.0F;
+	reData[i1] = reData[startIndex] - reData[i2];
+	reData[i2] = butterfly - dragonfly;
+	reData[startIndex] = butterfly + dragonfly;
+} /* end run */
+
+} /* end class DFTLength4RealFloat */
+
+/*====================================================================
+|	DFTLength5
+\===================================================================*/
+static class DFTLength5
+
+{ /* begin class DFTLength5 */
+
+/*....................................................................
+	DFTLength5 static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 18L
+		+ FFTSetup.FLOP * 52L
+		+ FFTSetup.FLASSIGN * 50L
+		+ FFTSetup.INTALLOC * 1L
+		+ FFTSetup.INTOP * 7L
+		+ FFTSetup.INTASSIGN * 7L
+		+ FFTSetup.IDX * 20L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength5 */
+
+/*====================================================================
+|	DFTLength5Double
+\===================================================================*/
+static class DFTLength5Double
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength5Double */
+
+/*....................................................................
+	DFTLength5Double static variables
+....................................................................*/
+private static final double IM5 = sqrt((5.0 - sqrt(5.0)) / 32.0);
+private static final double RE5 = sqrt((5.0 + sqrt(5.0)) / 32.0);
+private static final double S5 = sqrt(5.0 / 16.0);
+
+/*....................................................................
+	DFTLength5Double constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength5Double (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength5Double */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength5Double");
+	int k = startIndex + stride;
+	double re1 = reData[k];
+	double im1 = imData[k];
+	k += stride;
+	double re2 = reData[k];
+	double im2 = imData[k];
+	k += stride;
+	double re3 = reData[k];
+	double im3 = imData[k];
+	k += stride;
+	double re4 = reData[k];
+	double im4 = imData[k];
+	double reDragonfly = re1 + re4;
+	double imDragonfly = im1 + im4;
+	double reLadybug = re1 - re4;
+	double imLadybug = im1 - im4;
+	double reMoth = re2 + re3;
+	double imMoth = im2 + im3;
+	double reBeetle = re2 - re3;
+	double imBeetle = im2 - im3;
+	final double reButterfly = reDragonfly + reMoth;
+	final double imButterfly = imDragonfly + imMoth;
+	re1 = imLadybug + reBeetle;
+	im1 = reLadybug - imBeetle;
+	re2 = IM5 * im1 - RE5 * re1;
+	im2 = IM5 * re1 + RE5 * im1;
+	re1 = -0.25 * reButterfly;
+	im1 = -0.25 * imButterfly;
+	re3 = reBeetle - imLadybug;
+	im3 = imBeetle + reLadybug;
+	re4 = RE5 * re3 - IM5 * im3;
+	im4 = RE5 * im3 + IM5 * re3;
+	re3 = S5 * (reDragonfly - reMoth);
+	im3 = S5 * (imDragonfly - imMoth);
+	reDragonfly = reData[startIndex] + re1;
+	imDragonfly = imData[startIndex] + im1;
+	reLadybug = reDragonfly - re3;
+	imLadybug = imDragonfly - im3;
+	reDragonfly += re3;
+	imDragonfly += im3;
+	reMoth = re2 + re4;
+	imMoth = im2 + im4;
+	reBeetle = re2 - re4;
+	imBeetle = im2 - im4;
+	reData[k] = reDragonfly + reMoth;
+	imData[k] = imDragonfly + imMoth;
+	k -= stride;
+	reData[k] = reLadybug - imBeetle;
+	imData[k] = imLadybug + reBeetle;
+	k -= stride;
+	reData[k] = reLadybug + imBeetle;
+	imData[k] = imLadybug - reBeetle;
+	k -= stride;
+	reData[k] = reDragonfly - reMoth;
+	imData[k] = imDragonfly - imMoth;
+	reData[startIndex] += reButterfly;
+	imData[startIndex] += imButterfly;
+} /* end run */
+
+} /* end class DFTLength5Double */
+
+/*====================================================================
+|	DFTLength5Float
+\===================================================================*/
+static class DFTLength5Float
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength5Float */
+
+/*....................................................................
+	DFTLength5Float static variables
+....................................................................*/
+private static final float IM5 = (float)sqrt((5.0 - sqrt(5.0)) / 32.0);
+private static final float RE5 = (float)sqrt((5.0 + sqrt(5.0)) / 32.0);
+private static final float S5 = (float)sqrt(5.0 / 16.0);
+
+/*....................................................................
+	DFTLength5Float constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength5Float (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength5Float */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength5Float");
+	int k = startIndex + stride;
+	float re1 = reData[k];
+	float im1 = imData[k];
+	k += stride;
+	float re2 = reData[k];
+	float im2 = imData[k];
+	k += stride;
+	float re3 = reData[k];
+	float im3 = imData[k];
+	k += stride;
+	float re4 = reData[k];
+	float im4 = imData[k];
+	float reDragonfly = re1 + re4;
+	float imDragonfly = im1 + im4;
+	float reLadybug = re1 - re4;
+	float imLadybug = im1 - im4;
+	float reMoth = re2 + re3;
+	float imMoth = im2 + im3;
+	float reBeetle = re2 - re3;
+	float imBeetle = im2 - im3;
+	final float reButterfly = reDragonfly + reMoth;
+	final float imButterfly = imDragonfly + imMoth;
+	re1 = imLadybug + reBeetle;
+	im1 = reLadybug - imBeetle;
+	re2 = IM5 * im1 - RE5 * re1;
+	im2 = IM5 * re1 + RE5 * im1;
+	re1 = -0.25F * reButterfly;
+	im1 = -0.25F * imButterfly;
+	re3 = reBeetle - imLadybug;
+	im3 = imBeetle + reLadybug;
+	re4 = RE5 * re3 - IM5 * im3;
+	im4 = RE5 * im3 + IM5 * re3;
+	re3 = S5 * (reDragonfly - reMoth);
+	im3 = S5 * (imDragonfly - imMoth);
+	reDragonfly = reData[startIndex] + re1;
+	imDragonfly = imData[startIndex] + im1;
+	reLadybug = reDragonfly - re3;
+	imLadybug = imDragonfly - im3;
+	reDragonfly += re3;
+	imDragonfly += im3;
+	reMoth = re2 + re4;
+	imMoth = im2 + im4;
+	reBeetle = re2 - re4;
+	imBeetle = im2 - im4;
+	reData[k] = reDragonfly + reMoth;
+	imData[k] = imDragonfly + imMoth;
+	k -= stride;
+	reData[k] = reLadybug - imBeetle;
+	imData[k] = imLadybug + reBeetle;
+	k -= stride;
+	reData[k] = reLadybug + imBeetle;
+	imData[k] = imLadybug - reBeetle;
+	k -= stride;
+	reData[k] = reDragonfly - reMoth;
+	imData[k] = imDragonfly - imMoth;
+	reData[startIndex] += reButterfly;
+	imData[startIndex] += imButterfly;
+} /* end run */
+
+} /* end class DFTLength5Float */
+
+/*====================================================================
+|	DFTLength5Real
+\===================================================================*/
+static class DFTLength5Real
+
+{ /* begin class DFTLength5Real */
+
+/*....................................................................
+	DFTLength5Real static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 4L
+		+ FFTSetup.FLOP * 18L
+		+ FFTSetup.FLASSIGN * 13L
+		+ FFTSetup.INTALLOC * 4L
+		+ FFTSetup.INTOP * 4L
+		+ FFTSetup.INTASSIGN * 4L
+		+ FFTSetup.IDX * 15L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength5Real */
+
+/*====================================================================
+|	DFTLength5RealDouble
+\===================================================================*/
+static class DFTLength5RealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength5RealDouble */
+
+/*....................................................................
+	DFTLength5RealDouble static variables
+....................................................................*/
+private static final double IM5 = -sqrt((5.0 - sqrt(5.0)) / 8.0);
+private static final double RE5 = -sqrt((5.0 + sqrt(5.0)) / 8.0);
+private static final double S5 = sqrt(5.0 / 16.0);
+
+/*....................................................................
+	DFTLength5RealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength5RealDouble (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength5RealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength5RealDouble");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final int i3 = i2 + stride;
+	final int i4 = i3 + stride;
+	double butterfly = reData[i1] - reData[i4];
+	double dragonfly = reData[i2] - reData[i3];
+	imData[startIndex] = 0.0;
+	imData[i1] = RE5 * butterfly + IM5 * dragonfly;
+	imData[i2] = IM5 * butterfly - RE5 * dragonfly;
+	butterfly = reData[i1] + reData[i4];
+	dragonfly = reData[i2] + reData[i3];
+	final double ladybug = S5 * (butterfly - dragonfly);
+	final double moth = butterfly + dragonfly;
+	butterfly = reData[startIndex] - 0.25 * moth;
+	reData[startIndex] += moth;
+	reData[i1] = butterfly + ladybug;
+	reData[i2] = butterfly - ladybug;
+} /* end run */
+
+} /* end class DFTLength5RealDouble */
+
+/*====================================================================
+|	DFTLength5RealFloat
+\===================================================================*/
+static class DFTLength5RealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength5RealFloat */
+
+/*....................................................................
+	DFTLength5RealFloat static variables
+....................................................................*/
+private static final float IM5 = -(float)sqrt((5.0 - sqrt(5.0)) / 8.0);
+private static final float RE5 = -(float)sqrt((5.0 + sqrt(5.0)) / 8.0);
+private static final float S5 = (float)sqrt(5.0 / 16.0);
+
+/*....................................................................
+	DFTLength5RealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength5RealFloat (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength5RealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength5RealFloat");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final int i3 = i2 + stride;
+	final int i4 = i3 + stride;
+	float butterfly = reData[i1] - reData[i4];
+	float dragonfly = reData[i2] - reData[i3];
+	imData[startIndex] = 0.0F;
+	imData[i1] = RE5 * butterfly + IM5 * dragonfly;
+	imData[i2] = IM5 * butterfly - RE5 * dragonfly;
+	butterfly = reData[i1] + reData[i4];
+	dragonfly = reData[i2] + reData[i3];
+	final float ladybug = S5 * (butterfly - dragonfly);
+	final float moth = butterfly + dragonfly;
+	butterfly = reData[startIndex] - 0.25F * moth;
+	reData[startIndex] += moth;
+	reData[i1] = butterfly + ladybug;
+	reData[i2] = butterfly - ladybug;
+} /* end run */
+
+} /* end class DFTLength5RealFloat */
+
+/*====================================================================
+|	DFTLength6
+\===================================================================*/
+static class DFTLength6
+
+{ /* begin class DFTLength6 */
+
+/*....................................................................
+	DFTLength6 static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 24L
+		+ FFTSetup.FLOP * 52L
+		+ FFTSetup.FLASSIGN * 44L
+		+ FFTSetup.INTALLOC * 1L
+		+ FFTSetup.INTOP * 12L
+		+ FFTSetup.INTASSIGN * 9L
+		+ FFTSetup.IDX * 24L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength6 */
+
+/*====================================================================
+|	DFTLength6Double
+\===================================================================*/
+static class DFTLength6Double
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength6Double */
+
+/*....................................................................
+	DFTLength6Double static variables
+....................................................................*/
+private static final double SQRT3 = sqrt(3.0);
+
+/*....................................................................
+	DFTLength6Double constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength6Double (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength6Double */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength6Double");
+	double reLadybug = reData[startIndex];
+	int k = startIndex + 3 * stride;
+	double reMoth = reData[k];
+	final double reButterfly = reLadybug + reMoth;
+	final double reDragonfly = reLadybug - reMoth;
+	double imLadybug = imData[startIndex];
+	double imMoth = imData[k];
+	k -= stride << 1;
+	final double imButterfly = imLadybug + imMoth;
+	final double imDragonfly = imLadybug - imMoth;
+	double re1 = reData[k];
+	double im1 = imData[k];
+	k += stride;
+	double re2 = reData[k];
+	double im2 = imData[k];
+	k += stride << 1;
+	double re4 = reData[k];
+	double im4 = imData[k];
+	k += stride;
+	double re5 = reData[k];
+	double im5 = imData[k];
+	reLadybug = re1 + re2;
+	imLadybug = im1 + im2;
+	reMoth = re4 + re5;
+	imMoth = im4 + im5;
+	final double reAnt = reLadybug + reMoth;
+	final double imAnt = imLadybug + imMoth;
+	final double reBee = SQRT3 * (imMoth - imLadybug);
+	final double imBee = SQRT3 * (reLadybug - reMoth);
+	reLadybug = re1 - re2;
+	imLadybug = im1 - im2;
+	reMoth = re4 - re5;
+	imMoth = im4 - im5;
+	final double reGrasshopper = SQRT3 * (imMoth + imLadybug);
+	final double imGrasshopper = SQRT3 * (reMoth + reLadybug);
+	final double reBeetle = reMoth - reLadybug;
+	final double imBeetle = imMoth - imLadybug;
+	reData[k] = reDragonfly + 0.5 * (reBee - reBeetle);
+	imData[k] = imDragonfly + 0.5 * (imBee - imBeetle);
+	k -= stride;
+	reData[k] = reButterfly - 0.5 * (reAnt + reGrasshopper);
+	imData[k] = imButterfly - 0.5 * (imAnt - imGrasshopper);
+	k -= stride;
+	reData[k] = reDragonfly + reBeetle;
+	imData[k] = imDragonfly + imBeetle;
+	k -= stride;
+	reData[k] = reButterfly + 0.5 * (reGrasshopper - reAnt);
+	imData[k] = imButterfly - 0.5 * (imGrasshopper + imAnt);
+	k -= stride;
+	reData[k] = reDragonfly - 0.5 * (reBee + reBeetle);
+	imData[k] = imDragonfly - 0.5 * (imBee + imBeetle);
+	reData[startIndex] = reButterfly + reAnt;
+	imData[startIndex] = imButterfly + imAnt;
+} /* end run */
+
+} /* end class DFTLength6Double */
+
+/*====================================================================
+|	DFTLength6Float
+\===================================================================*/
+static class DFTLength6Float
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength6Float */
+
+/*....................................................................
+	DFTLength6Float static variables
+....................................................................*/
+private static final float SQRT3 = (float)sqrt(3.0);
+
+/*....................................................................
+	DFTLength6Float constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength6Float (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength6Float */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength6Float");
+	float reLadybug = reData[startIndex];
+	int k = startIndex + 3 * stride;
+	float reMoth = reData[k];
+	final float reButterfly = reLadybug + reMoth;
+	final float reDragonfly = reLadybug - reMoth;
+	float imLadybug = imData[startIndex];
+	float imMoth = imData[k];
+	k -= stride << 1;
+	final float imButterfly = imLadybug + imMoth;
+	final float imDragonfly = imLadybug - imMoth;
+	float re1 = reData[k];
+	float im1 = imData[k];
+	k += stride;
+	float re2 = reData[k];
+	float im2 = imData[k];
+	k += stride << 1;
+	float re4 = reData[k];
+	float im4 = imData[k];
+	k += stride;
+	float re5 = reData[k];
+	float im5 = imData[k];
+	reLadybug = re1 + re2;
+	imLadybug = im1 + im2;
+	reMoth = re4 + re5;
+	imMoth = im4 + im5;
+	final float reAnt = reLadybug + reMoth;
+	final float imAnt = imLadybug + imMoth;
+	final float reBee = SQRT3 * (imMoth - imLadybug);
+	final float imBee = SQRT3 * (reLadybug - reMoth);
+	reLadybug = re1 - re2;
+	imLadybug = im1 - im2;
+	reMoth = re4 - re5;
+	imMoth = im4 - im5;
+	final float reGrasshopper = SQRT3 * (imMoth + imLadybug);
+	final float imGrasshopper = SQRT3 * (reMoth + reLadybug);
+	final float reBeetle = reMoth - reLadybug;
+	final float imBeetle = imMoth - imLadybug;
+	reData[k] = reDragonfly + 0.5F * (reBee - reBeetle);
+	imData[k] = imDragonfly + 0.5F * (imBee - imBeetle);
+	k -= stride;
+	reData[k] = reButterfly - 0.5F * (reAnt + reGrasshopper);
+	imData[k] = imButterfly - 0.5F * (imAnt - imGrasshopper);
+	k -= stride;
+	reData[k] = reDragonfly + reBeetle;
+	imData[k] = imDragonfly + imBeetle;
+	k -= stride;
+	reData[k] = reButterfly + 0.5F * (reGrasshopper - reAnt);
+	imData[k] = imButterfly - 0.5F * (imGrasshopper + imAnt);
+	k -= stride;
+	reData[k] = reDragonfly - 0.5F * (reBee + reBeetle);
+	imData[k] = imDragonfly - 0.5F * (imBee + imBeetle);
+	reData[startIndex] = reButterfly + reAnt;
+	imData[startIndex] = imButterfly + imAnt;
+} /* end run */
+
+} /* end class DFTLength6Float */
+
+/*====================================================================
+|	DFTLength6Real
+\===================================================================*/
+static class DFTLength6Real
+
+{ /* begin class DFTLength6Real */
+
+/*....................................................................
+	DFTLength6Real static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 6L
+		+ FFTSetup.FLOP * 18L
+		+ FFTSetup.FLASSIGN * 16L
+		+ FFTSetup.INTALLOC * 5L
+		+ FFTSetup.INTOP * 5L
+		+ FFTSetup.INTASSIGN * 5L
+		+ FFTSetup.IDX * 20L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength6Real */
+
+/*====================================================================
+|	DFTLength6RealDouble
+\===================================================================*/
+static class DFTLength6RealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength6RealDouble */
+
+/*....................................................................
+	DFTLength6RealDouble static variables
+....................................................................*/
+private static final double NEGHALFSQRT3 = -0.5 * sqrt(3.0);
+
+/*....................................................................
+	DFTLength6RealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength6RealDouble (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength6RealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength6RealDouble");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final int i3 = i2 + stride;
+	final int i4 = i3 + stride;
+	final int i5 = i4 + stride;
+	double butterfly = reData[i1] + reData[i2];
+	double dragonfly = reData[i1] - reData[i2];
+	final double ladybug = reData[i4] + reData[i5];
+	final double grasshopper = reData[i4] - reData[i5];
+	imData[startIndex] = 0.0;
+	imData[i1] = NEGHALFSQRT3 * (butterfly - ladybug);
+	imData[i2] = NEGHALFSQRT3 * (dragonfly + grasshopper);
+	imData[i3] = 0.0;
+	final double beetle = butterfly + ladybug;
+	final double ant = dragonfly - grasshopper;
+	butterfly = reData[startIndex] + reData[i3];
+	dragonfly = reData[startIndex] - reData[i3];
+	reData[startIndex] = butterfly + beetle;
+	reData[i1] = dragonfly + 0.5 * ant;
+	reData[i2] = butterfly - 0.5 * beetle;
+	reData[i3] = dragonfly - ant;
+} /* end run */
+
+} /* end class DFTLength6RealDouble */
+
+/*====================================================================
+|	DFTLength6RealFloat
+\===================================================================*/
+static class DFTLength6RealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength6RealFloat */
+
+/*....................................................................
+	DFTLength6RealFloat static variables
+....................................................................*/
+private static final float NEGHALFSQRT3 = -0.5F * (float)sqrt(3.0);
+
+/*....................................................................
+	DFTLength6RealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength6RealFloat (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength6RealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength6RealFloat");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final int i3 = i2 + stride;
+	final int i4 = i3 + stride;
+	final int i5 = i4 + stride;
+	float butterfly = reData[i1] + reData[i2];
+	float dragonfly = reData[i1] - reData[i2];
+	final float ladybug = reData[i4] + reData[i5];
+	final float grasshopper = reData[i4] - reData[i5];
+	imData[startIndex] = 0.0F;
+	imData[i1] = NEGHALFSQRT3 * (butterfly - ladybug);
+	imData[i2] = NEGHALFSQRT3 * (dragonfly + grasshopper);
+	imData[i3] = 0.0F;
+	final float beetle = butterfly + ladybug;
+	final float ant = dragonfly - grasshopper;
+	butterfly = reData[startIndex] + reData[i3];
+	dragonfly = reData[startIndex] - reData[i3];
+	reData[startIndex] = butterfly + beetle;
+	reData[i1] = dragonfly + 0.5F * ant;
+	reData[i2] = butterfly - 0.5F * beetle;
+	reData[i3] = dragonfly - ant;
+} /* end run */
+
+} /* end class DFTLength6RealFloat */
+
+/*====================================================================
+|	DFTLength8
+\===================================================================*/
+static class DFTLength8
+
+{ /* begin class DFTLength8 */
+
+/*....................................................................
+	DFTLength8 static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 24L
+		+ FFTSetup.FLOP * 56L
+		+ FFTSetup.FLASSIGN * 68L
+		+ FFTSetup.INTALLOC * 1L
+		+ FFTSetup.INTOP * 15L
+		+ FFTSetup.INTASSIGN * 14L
+		+ FFTSetup.IDX * 32L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength8 */
+
+/*====================================================================
+|	DFTLength8Double
+\===================================================================*/
+static class DFTLength8Double
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength8Double */
+
+/*....................................................................
+	DFTLength8Double static variables
+....................................................................*/
+private static final double SQRTHALF = 1.0 / sqrt(2.0);
+
+/*....................................................................
+	DFTLength8Double constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength8Double (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength8Double */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength8Double");
+	int k = startIndex;
+	double reData0 = reData[k];
+	double imData0 = imData[k];
+	k += stride;
+	double reData1 = reData[k];
+	double imData1 = imData[k];
+	k += stride;
+	double reData2 = reData[k];
+	double imData2 = imData[k];
+	k += stride;
+	double reData3 = reData[k];
+	double imData3 = imData[k];
+	k += stride;
+	double reData4 = reData[k];
+	double imData4 = imData[k];
+	k += stride;
+	double reData5 = reData[k];
+	double imData5 = imData[k];
+	k += stride;
+	double reData6 = reData[k];
+	double imData6 = imData[k];
+	k += stride;
+	double reData7 = reData[k];
+	double imData7 = imData[k];
+	double reButterfly = reData0 + reData4;
+	double imButterfly = imData0 + imData4;
+	double reDragonfly = reData2 + reData6;
+	double imDragonfly = imData2 + imData6;
+	final double re0 = reButterfly + reDragonfly;
+	final double im0 = imButterfly + imDragonfly;
+	final double re1 = reButterfly - reDragonfly;
+	final double im1 = imButterfly - imDragonfly;
+	reButterfly = reData0 - reData4;
+	imButterfly = imData0 - imData4;
+	reDragonfly = imData6 - imData2;
+	imDragonfly = reData2 - reData6;
+	reData0 = reButterfly + reDragonfly;
+	imData0 = imButterfly + imDragonfly;
+	reData2 = reButterfly - reDragonfly;
+	imData2 = imButterfly - imDragonfly;
+	reButterfly = reData1 + reData5;
+	imButterfly = imData1 + imData5;
+	reDragonfly = reData3 + reData7;
+	imDragonfly = imData3 + imData7;
+	reData4 = reButterfly + reDragonfly;
+	imData4 = imButterfly + imDragonfly;
+	reData6 = imDragonfly - imButterfly;
+	imData6 = reButterfly - reDragonfly;
+	reData5 -= reData1;
+	imData5 -= imData1;
+	reButterfly = SQRTHALF * (reData5 + imData5);
+	imButterfly = SQRTHALF * (reData5 - imData5);
+	reData7 -= reData3;
+	imData7 -= imData3;
+	reDragonfly = SQRTHALF * (imData7 - reData7);
+	imDragonfly = SQRTHALF * (imData7 + reData7);
+	reData1 = imButterfly - imDragonfly;
+	imData1 = reDragonfly - reButterfly;
+	reData3 = reDragonfly + reButterfly;
+	imData3 = imButterfly + imDragonfly;
+	reData[k] = reData0 - reData1;
+	imData[k] = imData0 + imData1;
+	k -= stride;
+	reData[k] = re1 + reData6;
+	imData[k] = im1 + imData6;
+	k -= stride;
+	reData[k] = reData2 + reData3;
+	imData[k] = imData2 - imData3;
+	k -= stride;
+	reData[k] = re0 - reData4;
+	imData[k] = im0 - imData4;
+	k -= stride;
+	reData[k] = reData0 + reData1;
+	imData[k] = imData0 - imData1;
+	k -= stride;
+	reData[k] = re1 - reData6;
+	imData[k] = im1 - imData6;
+	k -= stride;
+	reData[k] = reData2 - reData3;
+	imData[k] = imData2 + imData3;
+	k -= stride;
+	reData[k] = re0 + reData4;
+	imData[k] = im0 + imData4;
+} /* end run */
+
+} /* end class DFTLength8Double */
+
+/*====================================================================
+|	DFTLength8Float
+\===================================================================*/
+static class DFTLength8Float
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength8Float */
+
+/*....................................................................
+	DFTLength8Float static variables
+....................................................................*/
+private static final float SQRTHALF = 1.0F / (float)sqrt(2.0);
+
+/*....................................................................
+	DFTLength8Float constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength8Float (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength8Float */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength8Float");
+	int k = startIndex;
+	float reData0 = reData[k];
+	float imData0 = imData[k];
+	k += stride;
+	float reData1 = reData[k];
+	float imData1 = imData[k];
+	k += stride;
+	float reData2 = reData[k];
+	float imData2 = imData[k];
+	k += stride;
+	float reData3 = reData[k];
+	float imData3 = imData[k];
+	k += stride;
+	float reData4 = reData[k];
+	float imData4 = imData[k];
+	k += stride;
+	float reData5 = reData[k];
+	float imData5 = imData[k];
+	k += stride;
+	float reData6 = reData[k];
+	float imData6 = imData[k];
+	k += stride;
+	float reData7 = reData[k];
+	float imData7 = imData[k];
+	float reButterfly = reData0 + reData4;
+	float imButterfly = imData0 + imData4;
+	float reDragonfly = reData2 + reData6;
+	float imDragonfly = imData2 + imData6;
+	final float re0 = reButterfly + reDragonfly;
+	final float im0 = imButterfly + imDragonfly;
+	final float re1 = reButterfly - reDragonfly;
+	final float im1 = imButterfly - imDragonfly;
+	reButterfly = reData0 - reData4;
+	imButterfly = imData0 - imData4;
+	reDragonfly = imData6 - imData2;
+	imDragonfly = reData2 - reData6;
+	reData0 = reButterfly + reDragonfly;
+	imData0 = imButterfly + imDragonfly;
+	reData2 = reButterfly - reDragonfly;
+	imData2 = imButterfly - imDragonfly;
+	reButterfly = reData1 + reData5;
+	imButterfly = imData1 + imData5;
+	reDragonfly = reData3 + reData7;
+	imDragonfly = imData3 + imData7;
+	reData4 = reButterfly + reDragonfly;
+	imData4 = imButterfly + imDragonfly;
+	reData6 = imDragonfly - imButterfly;
+	imData6 = reButterfly - reDragonfly;
+	reData5 -= reData1;
+	imData5 -= imData1;
+	reButterfly = SQRTHALF * (reData5 + imData5);
+	imButterfly = SQRTHALF * (reData5 - imData5);
+	reData7 -= reData3;
+	imData7 -= imData3;
+	reDragonfly = SQRTHALF * (imData7 - reData7);
+	imDragonfly = SQRTHALF * (imData7 + reData7);
+	reData1 = imButterfly - imDragonfly;
+	imData1 = reDragonfly - reButterfly;
+	reData3 = reDragonfly + reButterfly;
+	imData3 = imButterfly + imDragonfly;
+	reData[k] = reData0 - reData1;
+	imData[k] = imData0 + imData1;
+	k -= stride;
+	reData[k] = re1 + reData6;
+	imData[k] = im1 + imData6;
+	k -= stride;
+	reData[k] = reData2 + reData3;
+	imData[k] = imData2 - imData3;
+	k -= stride;
+	reData[k] = re0 - reData4;
+	imData[k] = im0 - imData4;
+	k -= stride;
+	reData[k] = reData0 + reData1;
+	imData[k] = imData0 - imData1;
+	k -= stride;
+	reData[k] = re1 - reData6;
+	imData[k] = im1 - imData6;
+	k -= stride;
+	reData[k] = reData2 - reData3;
+	imData[k] = imData2 + imData3;
+	k -= stride;
+	reData[k] = re0 + reData4;
+	imData[k] = im0 + imData4;
+} /* end run */
+
+} /* end class DFTLength8Float */
+
+/*====================================================================
+|	DFTLength8Real
+\===================================================================*/
+static class DFTLength8Real
+
+{ /* begin class DFTLength8Real */
+
+/*....................................................................
+	DFTLength8Real static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+) {
+	return(FFTSetup.FLALLOC * 8L
+		+ FFTSetup.FLOP * 22L
+		+ FFTSetup.FLASSIGN * 26L
+		+ FFTSetup.INTALLOC * 7L
+		+ FFTSetup.INTOP * 7L
+		+ FFTSetup.INTASSIGN * 7L
+		+ FFTSetup.IDX * 22L
+		+ FFTSetup.NEWOBJ * 0L
+	);
+} /* end cost */
+
+} /* end class DFTLength8Real */
+
+/*====================================================================
+|	DFTLength8RealDouble
+\===================================================================*/
+static class DFTLength8RealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTLength8RealDouble */
+
+/*....................................................................
+	DFTLength8RealDouble static variables
+....................................................................*/
+private static final double NEGSQRTHALF = -1.0 / sqrt(2.0);
+
+/*....................................................................
+	DFTLength8RealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength8RealDouble (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength8RealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength8RealDouble");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final int i3 = i2 + stride;
+	final int i4 = i3 + stride;
+	final int i5 = i4 + stride;
+	final int i6 = i5 + stride;
+	final int i7 = i6 + stride;
+	double re0 = reData[startIndex];
+	double re2 = reData[i2];
+	final double re4 = reData[i4];
+	final double re6 = reData[i6];
+	double butterfly = re0 + re4;
+	double dragonfly = re2 + re6;
+	double ladybug = butterfly + dragonfly;
+	reData[i2] = butterfly - dragonfly;
+	butterfly = reData[i1] + reData[i5];
+	dragonfly = reData[i3] + reData[i7];
+	double moth = dragonfly + butterfly;
+	imData[i2] = dragonfly - butterfly;
+	reData[startIndex] = ladybug + moth;
+	imData[startIndex] = 0.0;
+	reData[i4] = ladybug - moth;
+	imData[i4] = 0.0;
+	butterfly = re0 - re4;
+	dragonfly = re2 - re6;
+	re0 = reData[i1] - reData[i5];
+	re2 = reData[i3] - reData[i7];
+	ladybug = NEGSQRTHALF * (re0 - re2);
+	moth = NEGSQRTHALF * (re0 + re2);
+	reData[i1] = butterfly - ladybug;
+	imData[i1] = moth - dragonfly;
+	reData[i3] = butterfly + ladybug;
+	imData[i3] = moth + dragonfly;
+} /* end run */
+
+} /* end class DFTLength8RealDouble */
+
+/*====================================================================
+|	DFTLength8RealFloat
+\===================================================================*/
+static class DFTLength8RealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTLength8RealFloat */
+
+/*....................................................................
+	DFTLength8RealFloat static variables
+....................................................................*/
+private static final float NEGSQRTHALF = -1.0F / (float)sqrt(2.0);
+
+/*....................................................................
+	DFTLength8RealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTLength8RealFloat (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride
+) {
+	super(reData, imData, startIndex, stride);
+} /* end DFTLength8RealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+//;IJ.log("DFTLength8RealFloat");
+	final int i1 = startIndex + stride;
+	final int i2 = i1 + stride;
+	final int i3 = i2 + stride;
+	final int i4 = i3 + stride;
+	final int i5 = i4 + stride;
+	final int i6 = i5 + stride;
+	final int i7 = i6 + stride;
+	float re0 = reData[startIndex];
+	float re2 = reData[i2];
+	final float re4 = reData[i4];
+	final float re6 = reData[i6];
+	float butterfly = re0 + re4;
+	float dragonfly = re2 + re6;
+	float ladybug = butterfly + dragonfly;
+	reData[i2] = butterfly - dragonfly;
+	butterfly = reData[i1] + reData[i5];
+	dragonfly = reData[i3] + reData[i7];
+	float moth = dragonfly + butterfly;
+	imData[i2] = dragonfly - butterfly;
+	reData[startIndex] = ladybug + moth;
+	imData[startIndex] = 0.0F;
+	reData[i4] = ladybug - moth;
+	imData[i4] = 0.0F;
+	butterfly = re0 - re4;
+	dragonfly = re2 - re6;
+	re0 = reData[i1] - reData[i5];
+	re2 = reData[i3] - reData[i7];
+	ladybug = NEGSQRTHALF * (re0 - re2);
+	moth = NEGSQRTHALF * (re0 + re2);
+	reData[i1] = butterfly - ladybug;
+	imData[i1] = moth - dragonfly;
+	reData[i3] = butterfly + ladybug;
+	imData[i3] = moth + dragonfly;
+} /* end run */
+
+} /* end class DFTLength8RealFloat */
+
+/*====================================================================
+|	DFTMixedRadix
+\===================================================================*/
+static class DFTMixedRadix
+
+{ /* begin class DFTMixedRadix */
+
+/*....................................................................
+	DFTMixedRadix static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int K1,
+	final int K2
+) {
+	if (FFTSetup.taboos.contains(new Integer(K1))
+		|| FFTSetup.taboos.contains(new Integer(K2))) {
+		return(-1L);
+	}
+	final long K = (long)K1 * (long)K2;
+	return(FFTSetup.FLALLOC * 4L
+		+ FFTSetup.FLOP * ((long)K2 * ((long)K1 * 6L))
+		+ FFTSetup.FLASSIGN * ((long)K2 * ((long)K1 * 6L)
+			+ (long)K2 * ((long)K1 * 2L) + K * 2L)
+		+ FFTSetup.INTALLOC * 16L
+		+ FFTSetup.INTOP * (3L + (long)K2 * 3L + (long)K2 * (3L
+			+ (long)K1 * 7L) + 1L + (long)K1 * 3L + (long)K2 * (3L
+			+ (long)K1 * 4L) + K * 3L)
+		+ FFTSetup.INTASSIGN * (6L + (long)K2 * 2L + 2L + (long)K2 * (3L
+			+ (long)K1 * 4L) + 2L + (long)K1 * 2L + 3L + (long)K2 * (4L
+			+ (long)K1 * 3L) + 2L + K * 2L)
+		+ FFTSetup.IDX * ((long)K2 * ((long)K1 * 6L)
+			+ (long)K2 * ((long)K1 * 4L) + K * 4L)
+		+ FFTSetup.NEWOBJ * ((long)K2 * 1L + (long)K1 * 1L)
+		+ (long)K2 * FFTSetup.cost(K1) + (long)K1 * FFTSetup.cost(K2)
+	);
+} /* end cost */
+
+} /* end class DFTMixedRadix */
+
+/*====================================================================
+|	DFTMixedRadixDouble
+\===================================================================*/
+static class DFTMixedRadixDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTMixedRadixDouble */
+
+/*....................................................................
+	DFTMixedRadixDouble private variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imUnitRoot;
+private double[] reBuffer;
+private double[] reUnitRoot;
+private int K1;
+
+/*....................................................................
+	DFTMixedRadixDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTMixedRadixDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reUnitRoot,
+	final double[] imUnitRoot,
+	final int K1
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+	this.K1 = K1;
+} /* end DFTMixedRadixDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = reUnitRoot.length;
+//;IJ.log("DFTMixedRadixDouble " + transformLength + " / " + K1);
+	final int K2 = transformLength / K1;
+	final int d1 = stride;
+	final int d2 = K2 * d1;
+	int k2 = startIndex;
+	final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1));
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.ruritanian, fft1.chinese, fft1.K1).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength2Double(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength3Double(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength4Double(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength5Double(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength6Double(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength8Double(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reUnitRootDouble, fft1.imUnitRootDouble,
+					fft1.K1).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTPaddedRaderDouble(reData, imData,
+					k2, d2, fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case RADER: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTRaderDouble(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTRadix2Double(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				k2 += d1;
+			}
+			break;
+		}
+	}
+	k2 = startIndex;
+	for (int n = 0; (n < K2); n++) {
+		int m2 = 0;
+		int m1 = k2;
+		k2 += d1;
+		for (int m = 0; (m < K1); m++) {
+			final double re = reData[m1];
+			final double im = imData[m1];
+			final double reRoot = reUnitRoot[m2];
+			final double imRoot = imUnitRoot[m2];
+			reData[m1] = re * reRoot - im * imRoot;
+			imData[m1] = re * imRoot + im * reRoot;
+			m1 += d2;
+			m2 += n;
+			m2 -= transformLength * (m2 / transformLength);
+		}
+	}
+	int k1 = startIndex;
+	final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength2Double(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength3Double(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength4Double(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength5Double(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength6Double(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength8Double(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+					fft2.K1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTPaddedRaderDouble(reData, imData,
+					k1, d1, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTRaderDouble(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTRadix2Double(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				k1 += d2;
+			}
+			break;
+		}
+	}
+	k1 = startIndex;
+	k2 = startIndex;
+	for (int m2 = 0; (m2 < K2); m2++) {
+		int n2 = k1;
+		for (int m1 = 0; (m1 < K1); m1++) {
+			reBuffer[k2] = reData[n2];
+			imBuffer[k2] = imData[n2];
+			k2 += d1;
+			n2 += d2;
+		}
+		k1 += d1;
+	}
+	k1 = startIndex;
+	for (int m = 0; (m < transformLength); m++) {
+		reData[k1] = reBuffer[k1];
+		imData[k1] = imBuffer[k1];
+		k1 += d1;
+	}
+} /* end run */
+
+/*....................................................................
+	DFTMixedRadixDouble static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static double[] getImUnitRoot (
+	final int transformLength
+) {
+	final double[] imUnitRoot = new double[transformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < transformLength); k++) {
+		imUnitRoot[k] = sin((double)k * angularStep);
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static double[] getReUnitRoot (
+	final int transformLength
+) {
+	final double[] reUnitRoot = new double[transformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < transformLength); k++) {
+		reUnitRoot[k] = cos((double)k * angularStep);
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTMixedRadixDouble */
+
+/*====================================================================
+|	DFTMixedRadixFloat
+\===================================================================*/
+static class DFTMixedRadixFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTMixedRadixFloat */
+
+/*....................................................................
+	DFTMixedRadixFloat private variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imUnitRoot;
+private float[] reBuffer;
+private float[] reUnitRoot;
+private int K1;
+
+/*....................................................................
+	DFTMixedRadixFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTMixedRadixFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reUnitRoot,
+	final float[] imUnitRoot,
+	final int K1
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+	this.K1 = K1;
+} /* end DFTMixedRadixFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = reUnitRoot.length;
+//;IJ.log("DFTMixedRadixFloat " + transformLength + " / " + K1);
+	final int K2 = transformLength / K1;
+	final int d1 = stride;
+	final int d2 = K2 * d1;
+	int k2 = startIndex;
+	final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1));
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.ruritanian, fft1.chinese, fft1.K1).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength2Float(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength3Float(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength4Float(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength5Float(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength6Float(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTLength8Float(reData, imData, k2, d2).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reUnitRootFloat, fft1.imUnitRootFloat,
+					fft1.K1).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTPaddedRaderFloat(reData, imData,
+					k2, d2, fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case RADER: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTRaderFloat(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTRadix2Float(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				k2 += d1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int n = 0; (n < K2); n++) {
+				new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer,
+					k2, d2, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				k2 += d1;
+			}
+			break;
+		}
+	}
+	k2 = startIndex;
+	for (int n = 0; (n < K2); n++) {
+		int m2 = 0;
+		int m1 = k2;
+		k2 += d1;
+		for (int m = 0; (m < K1); m++) {
+			final float re = reData[m1];
+			final float im = imData[m1];
+			final float reRoot = reUnitRoot[m2];
+			final float imRoot = imUnitRoot[m2];
+			reData[m1] = re * reRoot - im * imRoot;
+			imData[m1] = re * imRoot + im * reRoot;
+			m1 += d2;
+			m2 += n;
+			m2 -= transformLength * (m2 / transformLength);
+		}
+	}
+	int k1 = startIndex;
+	final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength2Float(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength3Float(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength4Float(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength5Float(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength6Float(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTLength8Float(reData, imData, k1, d1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+					fft2.K1).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTPaddedRaderFloat(reData, imData,
+					k1, d1, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTRaderFloat(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTRadix2Float(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				k1 += d2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int n = 0; (n < K1); n++) {
+				new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer,
+					k1, d1, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				k1 += d2;
+			}
+			break;
+		}
+	}
+	k1 = startIndex;
+	k2 = startIndex;
+	for (int m2 = 0; (m2 < K2); m2++) {
+		int n2 = k1;
+		for (int m1 = 0; (m1 < K1); m1++) {
+			reBuffer[k2] = reData[n2];
+			imBuffer[k2] = imData[n2];
+			k2 += d1;
+			n2 += d2;
+		}
+		k1 += d1;
+	}
+	k1 = startIndex;
+	for (int m = 0; (m < transformLength); m++) {
+		reData[k1] = reBuffer[k1];
+		imData[k1] = imBuffer[k1];
+		k1 += d1;
+	}
+} /* end run */
+
+/*....................................................................
+	DFTMixedRadixFloat static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static float[] getImUnitRoot (
+	final int transformLength
+) {
+	final float[] imUnitRoot = new float[transformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < transformLength); k++) {
+		imUnitRoot[k] = (float)sin((double)((float)k * angularStep));
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static float[] getReUnitRoot (
+	final int transformLength
+) {
+	final float[] reUnitRoot = new float[transformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < transformLength); k++) {
+		reUnitRoot[k] = (float)cos((double)((float)k * angularStep));
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTMixedRadixFloat */
+
+/*====================================================================
+|	DFTMixedRadixReal
+\===================================================================*/
+static class DFTMixedRadixReal
+
+{ /* begin class DFTMixedRadixReal */
+
+/*....................................................................
+	DFTMixedRadixReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int K1,
+	final int K2
+) {
+	if (FFTSetup.taboos.contains(new Integer(K1))
+		|| FFTSetup.taboos.contains(new Integer(K2))) {
+		return(-1L);
+	}
+	final long K = (long)K1 * (long)K2;
+	final long k = K >> 1L;
+	final long k1 = (long)(K1 >> 1);
+	final long k2 = (long)(K2 >> 1);
+	return(FFTSetup.FLALLOC * 4L
+		+ FFTSetup.FLOP * ((k2 + 1L) * ((long)K1 * 6L) + k1 * k2 * 1L)
+		+ FFTSetup.FLASSIGN * ((k2 + 1L) * ((long)K1 * 6L) + 2L + K * 1L)
+		+ FFTSetup.INTALLOC * 14L
+		+ FFTSetup.INTOP * (7L + (long)K1 * 3L + (k2 + 1L) * (2L
+			+ (long)K1 * 4L) + 1L + (k2 + 1L) * 3L + 4L + (K * 2L + K1 * 5L)
+			+ 2L)
+		+ FFTSetup.INTASSIGN * (7L + (long)K1 * 2L + 1L + (k2 + 1L) * (3L
+			+ (long)K1 * 3L) + 2L + (k2 + 1L) * 2L + 5L + (k * 3L + K1 * 3L))
+		+ FFTSetup.IDX * ((k2 + 1L) * ((long)K1 * 6L) + 3L + (K * 2L))
+		+ FFTSetup.NEWOBJ * ((long)K1 * 1L + (k2 + 1L) * 1L)
+		+ k1 * FFTSetupDuoReal.cost(K2) + (k2 + 1L) * FFTSetup.cost(K1)
+	);
+} /* end cost */
+
+} /* end class DFTMixedRadixReal */
+
+/*====================================================================
+|	DFTMixedRadixRealDouble
+\===================================================================*/
+static class DFTMixedRadixRealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTMixedRadixRealDouble */
+
+/*....................................................................
+	DFTMixedRadixRealDouble private variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imUnitRoot;
+private double[] reBuffer;
+private double[] reUnitRoot;
+private int K1;
+
+/*....................................................................
+	DFTMixedRadixRealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTMixedRadixRealDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reUnitRoot,
+	final double[] imUnitRoot,
+	final int K1
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+	this.K1 = K1;
+} /* end DFTMixedRadixDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = reUnitRoot.length;
+//;IJ.log("DFTMixedRadixRealDouble " + transformLength + " / " + K1);
+	final int K2 = transformLength / K1;
+	final int halfK2 = (K2 >> 1) + 1;
+	final int d1 = stride;
+	final int d2 = K1 * d1;
+	int p = startIndex;
+	int k1 = 0;
+	if (1 == (K1 & 1)) {
+		final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2));
+		switch (fft2.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				break;
+			}
+			case LENGTH1: {
+				throw(new IllegalStateException());
+			}
+			case LENGTH2: {
+				new DFTLength2RealDouble(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealDouble(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealDouble(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealDouble(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealDouble(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealDouble(reData, imData, p, d2).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+					fft2.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealDouble(reData, imData,
+					p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble,
+					fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				break;
+			}
+		}
+		p += d1;
+		k1++;
+	}
+	final FFTSetupDuoReal fft2 =
+		FFTSetupDuoReal.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			while (k1++ < K1) {
+				new DFTBruteForceRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				p += d1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (k1++ < K1) {
+				new DFTCoprimeFactorRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (k1++ < K1) {
+				new DFTDuoRealDouble(reData, imData,
+					reBuffer, imBuffer, p, p + d1, d2, K2).run();
+				p += 2 * d1;
+				k1++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (k1++ < K1) {
+				new DFTEvenRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			while (k1++ < K1) {
+				new DFTLength2RealDouble(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (k1++ < K1) {
+				new DFTLength3RealDouble(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (k1++ < K1) {
+				new DFTLength4RealDouble(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (k1++ < K1) {
+				new DFTLength5RealDouble(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (k1++ < K1) {
+				new DFTLength6RealDouble(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (k1++ < K1) {
+				new DFTLength8RealDouble(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (k1++ < K1) {
+				new DFTMixedRadixRealDouble(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+					fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (k1++ < K1) {
+				new DFTPaddedRaderRealDouble(reData, imData,
+					p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADER: {
+			while (k1++ < K1) {
+				new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer,
+					p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADIX2: {
+			while (k1++ < K1) {
+				new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer,
+					p, d2, fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble,
+					fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run();
+				p += d1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (k1++ < K1) {
+				new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer,
+					p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+				p += d1;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	for (int n2 = 0; (n2 < halfK2); n2++) {
+		int n = 0;
+		for (k1 = 0; (k1 < K1); k1++) {
+			final double re = reData[p];
+			final double im = imData[p];
+			final double reRoot = reUnitRoot[n];
+			final double imRoot = imUnitRoot[n];
+			reBuffer[p] = re * reRoot - im * imRoot;
+			imBuffer[p] = re * imRoot + im * reRoot;
+			p += d1;
+			n += n2;
+		}
+	}
+	p = startIndex;
+	final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1));
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				p += d2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength2Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength3Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength4Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength5Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength6Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength8Double(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble,
+					fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTPaddedRaderDouble(reBuffer, imBuffer,
+					p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTRaderDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTRadix2Double(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				p += d2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run();
+				p += d2;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	reData[p] = reBuffer[p];
+	imData[p] = 0.0;
+	p += d1;
+	int progressive = startIndex;
+	int regressive = startIndex + (K1 - 1) * d1;
+	int k2 = 1;
+	while (progressive < regressive) {
+		int q = progressive + d2;
+		while (k2 < halfK2) {
+			reData[p] = reBuffer[q];
+			imData[p] = imBuffer[q];
+			k2++;
+			p += d1;
+			q += d2;
+		}
+		k2 -= 2 - (K2 & 1);
+		progressive += d1;
+		q = regressive + k2 * d2;
+		while (0 <= k2) {
+			reData[p] = reBuffer[q];
+			imData[p] = -imBuffer[q];
+			k2--;
+			p += d1;
+			q -= d2;
+		}
+		k2 += 2;
+		regressive -= d1;
+	}
+	if (1 == (K1 & 1)) {
+		int q = progressive + k2 * d2;
+		while (k2 < halfK2) {
+			reData[p] = reBuffer[q];
+			imData[p] = imBuffer[q];
+			k2++;
+			p += d1;
+			q += d2;
+		}
+	}
+} /* end run */
+
+} /* end class DFTMixedRadixRealDouble */
+
+/*====================================================================
+|	DFTMixedRadixRealFloat
+\===================================================================*/
+static class DFTMixedRadixRealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTMixedRadixRealFloat */
+
+/*....................................................................
+	DFTMixedRadixRealFloat private variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imUnitRoot;
+private float[] reBuffer;
+private float[] reUnitRoot;
+private int K1;
+
+/*....................................................................
+	DFTMixedRadixRealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTMixedRadixRealFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reUnitRoot,
+	final float[] imUnitRoot,
+	final int K1
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+	this.K1 = K1;
+} /* end DFTMixedRadixRealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int transformLength = reUnitRoot.length;
+//;IJ.log("DFTMixedRadixRealFloat " + transformLength + " / " + K1);
+	final int K2 = transformLength / K1;
+	final int halfK2 = (K2 >> 1) + 1;
+	final int d1 = stride;
+	final int d2 = K1 * d1;
+	int p = startIndex;
+	int k1 = 0;
+	if (1 == (K1 & 1)) {
+		final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2));
+		switch (fft2.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				break;
+			}
+			case LENGTH1: {
+				throw(new IllegalStateException());
+			}
+			case LENGTH2: {
+				new DFTLength2RealFloat(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealFloat(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealFloat(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealFloat(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealFloat(reData, imData, p, d2).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealFloat(reData, imData, p, d2).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+					fft2.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealFloat(reData, imData,
+					p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat,
+					fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				break;
+			}
+		}
+		p += d1;
+		k1++;
+	}
+	final FFTSetupDuoReal fft2 =
+		FFTSetupDuoReal.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			while (k1++ < K1) {
+				new DFTBruteForceRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				p += d1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (k1++ < K1) {
+				new DFTCoprimeFactorRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.ruritanian, fft2.chinese, fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (k1++ < K1) {
+				new DFTDuoRealFloat(reData, imData,
+					reBuffer, imBuffer, p, p + d1, d2, K2).run();
+				p += 2 * d1;
+				k1++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (k1++ < K1) {
+				new DFTEvenRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			while (k1++ < K1) {
+				new DFTLength2RealFloat(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (k1++ < K1) {
+				new DFTLength3RealFloat(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (k1++ < K1) {
+				new DFTLength4RealFloat(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (k1++ < K1) {
+				new DFTLength5RealFloat(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (k1++ < K1) {
+				new DFTLength6RealFloat(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (k1++ < K1) {
+				new DFTLength8RealFloat(reData, imData, p, d2).run();
+				p += d1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (k1++ < K1) {
+				new DFTMixedRadixRealFloat(reData, imData,
+					reBuffer, imBuffer, p, d2,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+					fft2.K1).run();
+				p += d1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (k1++ < K1) {
+				new DFTPaddedRaderRealFloat(reData, imData,
+					p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADER: {
+			while (k1++ < K1) {
+				new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer,
+					p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular).run();
+				p += d1;
+			}
+			break;
+		}
+		case RADIX2: {
+			while (k1++ < K1) {
+				new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer,
+					p, d2, fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat,
+					fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run();
+				p += d1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (k1++ < K1) {
+				new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer,
+					p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+				p += d1;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	for (int n2 = 0; (n2 < halfK2); n2++) {
+		int n = 0;
+		for (k1 = 0; (k1 < K1); k1++) {
+			final float re = reData[p];
+			final float im = imData[p];
+			final float reRoot = reUnitRoot[n];
+			final float imRoot = imUnitRoot[n];
+			reBuffer[p] = re * reRoot - im * imRoot;
+			imBuffer[p] = re * imRoot + im * reRoot;
+			p += d1;
+			n += n2;
+		}
+	}
+	p = startIndex;
+	final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1));
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				p += d2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength2Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength3Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength4Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength5Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength6Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTLength8Float(reBuffer, imBuffer, p, d1).run();
+				p += d2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat,
+					fft1.K1).run();
+				p += d2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTPaddedRaderFloat(reBuffer, imBuffer,
+					p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTRaderFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular).run();
+				p += d2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTRadix2Float(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				p += d2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData,
+					p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run();
+				p += d2;
+			}
+			break;
+		}
+	}
+	p = startIndex;
+	reData[p] = reBuffer[p];
+	imData[p] = 0.0F;
+	p += d1;
+	int progressive = startIndex;
+	int regressive = startIndex + (K1 - 1) * d1;
+	int k2 = 1;
+	while (progressive < regressive) {
+		int q = progressive + d2;
+		while (k2 < halfK2) {
+			reData[p] = reBuffer[q];
+			imData[p] = imBuffer[q];
+			k2++;
+			p += d1;
+			q += d2;
+		}
+		k2 -= 2 - (K2 & 1);
+		progressive += d1;
+		q = regressive + k2 * d2;
+		while (0 <= k2) {
+			reData[p] = reBuffer[q];
+			imData[p] = -imBuffer[q];
+			k2--;
+			p += d1;
+			q -= d2;
+		}
+		k2 += 2;
+		regressive -= d1;
+	}
+	if (1 == (K1 & 1)) {
+		int q = progressive + k2 * d2;
+		while (k2 < halfK2) {
+			reData[p] = reBuffer[q];
+			imData[p] = imBuffer[q];
+			k2++;
+			p += d1;
+			q += d2;
+		}
+	}
+} /* end run */
+
+} /* end class DFTMixedRadixRealFloat */
+
+/*====================================================================
+|	DFTPaddedRader
+\===================================================================*/
+static class DFTPaddedRader
+
+{ /* begin class DFTPaddedRader */
+
+/*....................................................................
+	DFTPaddedRader static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int prime,
+	final int paddedLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(paddedLength))) {
+		return(-1L);
+	}
+	final long K = (long)prime;
+	final long P = (long)paddedLength;
+	return(FFTSetup.FLALLOC * (P * 4L + 8L)
+		+ FFTSetup.FLOP * (P * 6L + (K - 1L) * 2L + 2L)
+		+ FFTSetup.FLASSIGN * (P * 4L + 2L + (K - 1L) * 2L + 2L + P * 6L
+			+ (K - 1L) * 2L + 2L)
+		+ FFTSetup.INTALLOC * 9L
+		+ FFTSetup.INTOP * (2L + (K - 1L) * 5L + P * 2L + 1L + (K - 1L) * 3L)
+		+ FFTSetup.INTASSIGN * (4L + (K - 1L) * 3L + 1L + P * 1L + 2L
+			+ (K - 1L) * 3L)
+		+ FFTSetup.IDX * (2L + (K - 1L) * 5L + 2L + P * 6L + (K - 1L) * 5L + 2L)
+		+ 1L * (FFTSetup.NEWOBJ
+			+ FFTSetup.FLALLOC * 0L
+			+ FFTSetup.FLOP * 0L
+			+ FFTSetup.FLASSIGN * 0L
+			+ FFTSetup.INTALLOC * 0L
+			+ FFTSetup.INTOP * 1L
+			+ FFTSetup.INTASSIGN * 6L
+			+ FFTSetup.IDX * 0L
+			+ FFTSetup.NEWOBJ * 6L)
+		+ 2L * (FFTSetup.cost(paddedLength)
+			+ FFTSetup.FLALLOC * 0L
+			+ FFTSetup.FLOP * 0L
+			+ FFTSetup.FLASSIGN * 0L
+			+ FFTSetup.INTALLOC * 0L
+			+ FFTSetup.INTOP * (10L + 2L)
+			+ FFTSetup.INTASSIGN * 0L
+			+ FFTSetup.IDX * 0L
+			+ FFTSetup.NEWOBJ * 1L)
+	);
+} /* end cost */
+
+/*------------------------------------------------------------------*/
+static int[] getInverseModularPowerShuffling (
+	final int[] modularShuffling,
+	final int paddedLength
+) {
+	final int prime = modularShuffling.length;
+	int[] inverseShuffling = new int[prime];
+	inverseShuffling[modularShuffling[prime - 3]] = 0;
+	inverseShuffling[modularShuffling[prime - 2]] = 1;
+	inverseShuffling[modularShuffling[prime - 1]] = paddedLength - prime + 3;
+	for (int k = 4; (k < prime); k++) {
+		inverseShuffling[modularShuffling[k - 3]] = paddedLength - prime + k;
+	}
+	return(inverseShuffling);
+} /* end getInverseModularPowerShuffling */
+
+} /* end class DFTPaddedRader */
+
+/*====================================================================
+|	DFTPaddedRaderDouble
+\===================================================================*/
+static class DFTPaddedRaderDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTPaddedRaderDouble */
+
+/*....................................................................
+	DFTPaddedRaderDouble private variables
+....................................................................*/
+private double[] imConvolver;
+private double[] reConvolver;
+private int[] inverseModular;
+private int[] modular;
+
+/*....................................................................
+	DFTPaddedRaderDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTPaddedRaderDouble (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride,
+	final double[] reConvolver,
+	final double[] imConvolver,
+	final int[] modular,
+	final int[] inverseModular
+) {
+	super(reData, imData, startIndex, stride);
+	this.reConvolver = reConvolver;
+	this.imConvolver = imConvolver;
+	this.modular = modular;
+	this.inverseModular = inverseModular;
+} /* end DFTPaddedRaderDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int prime = modular.length;
+	final int paddedLength = reConvolver.length;
+//;IJ.log("DFTPaddedRaderDouble " + prime + " (" + paddedLength +")");
+	final double[] rePadded = new double[paddedLength];
+	final double[] imPadded = new double[paddedLength];
+	final double[] reBuffer = new double[paddedLength];
+	final double[] imBuffer = new double[paddedLength];
+	final double reBaseline = reData[startIndex];
+	final double imBaseline = imData[startIndex];
+	for (int k = paddedLength - prime + 1, m = 1; (k < paddedLength); k++) {
+		final int q = startIndex + modular[m++] * stride;
+		rePadded[k] = reData[q];
+		imPadded[k] = imData[q];
+	}
+	final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0);
+	fft.directTransform(rePadded, imPadded, reBuffer, imBuffer,
+		InputDataType.COMPLEXINPUT);
+	final double reSum = rePadded[0];
+	final double imSum = imPadded[0];
+	for (int k = 0; (k < paddedLength); k++) {
+		final double re = rePadded[k];
+		final double im = imPadded[k];
+		final double reWeight = reConvolver[k];
+		final double imWeight = imConvolver[k];
+		rePadded[k] = re * reWeight - im * imWeight;
+		imPadded[k] = re * imWeight + im * reWeight;
+	}
+	fft.directTransform(rePadded, imPadded, reBuffer, imBuffer,
+		InputDataType.COMPLEXINPUT);
+	int p = startIndex + stride;
+	for (int k = 1; (k < prime); k++) {
+		final int q = inverseModular[k];
+		reData[p] = rePadded[q] + reBaseline;
+		imData[p] = imPadded[q] + imBaseline;
+		p += stride;
+	}
+	reData[startIndex] += reSum;
+	imData[startIndex] += imSum;
+} /* end run */
+
+/*....................................................................
+	DFTPaddedRaderDouble static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static double[][] getConvolverReAndIm (
+	final int[] modular,
+	final int paddedLength
+) {
+	final int prime = modular.length;
+	final double[] reConvolver = new double[paddedLength];
+	final double[] imConvolver = new double[paddedLength];
+	final double angularStep = -2.0 * PI / (double)prime;
+	final double norm = 1.0 / (double)paddedLength;
+	int n = 0;
+	for (int k = prime - 2; (1 <= k); k--) {
+		final double shuffled = (double)modular[k];
+		reConvolver[n] = norm * cos(shuffled * angularStep);
+		imConvolver[n] = norm * sin(shuffled * angularStep);
+		n++;
+	}
+	reConvolver[n] = norm * cos(angularStep);
+	imConvolver[n] = norm * sin(angularStep);
+	while (++n < paddedLength) {
+		reConvolver[n] = reConvolver[n - prime + 1];
+		imConvolver[n] = imConvolver[n - prime + 1];
+	}
+	final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0);
+	fft.directTransform(reConvolver, imConvolver, null, null,
+		InputDataType.COMPLEXINPUT);
+	return(new double[][] {
+		reConvolver,
+		imConvolver
+	});
+} /* end getConvolverReAndIm */
+
+} /* end class DFTPaddedRaderDouble */
+
+/*====================================================================
+|	DFTPaddedRaderFloat
+\===================================================================*/
+static class DFTPaddedRaderFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTPaddedRaderFloat */
+
+/*....................................................................
+	DFTPaddedRaderFloat private variables
+....................................................................*/
+private float[] imConvolver;
+private float[] reConvolver;
+private int[] inverseModular;
+private int[] modular;
+
+/*....................................................................
+	DFTPaddedRaderFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTPaddedRaderFloat (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride,
+	final float[] reConvolver,
+	final float[] imConvolver,
+	final int[] modular,
+	final int[] inverseModular
+) {
+	super(reData, imData, startIndex, stride);
+	this.reConvolver = reConvolver;
+	this.imConvolver = imConvolver;
+	this.modular = modular;
+	this.inverseModular = inverseModular;
+} /* end DFTPaddedRaderFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int prime = modular.length;
+	final int paddedLength = reConvolver.length;
+//;IJ.log("DFTPaddedRaderFloat " + prime + " (" + paddedLength +")");
+	final float[] rePadded = new float[paddedLength];
+	final float[] imPadded = new float[paddedLength];
+	final float[] reBuffer = new float[paddedLength];
+	final float[] imBuffer = new float[paddedLength];
+	final float reBaseline = reData[startIndex];
+	final float imBaseline = imData[startIndex];
+	for (int k = paddedLength - prime + 1, m = 1; (k < paddedLength); k++) {
+		final int q = startIndex + modular[m++] * stride;
+		rePadded[k] = reData[q];
+		imPadded[k] = imData[q];
+	}
+	final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0);
+	fft.directTransform(rePadded, imPadded, reBuffer, imBuffer,
+		InputDataType.COMPLEXINPUT);
+	final float reSum = rePadded[0];
+	final float imSum = imPadded[0];
+	for (int k = 0; (k < paddedLength); k++) {
+		final float re = rePadded[k];
+		final float im = imPadded[k];
+		final float reWeight = reConvolver[k];
+		final float imWeight = imConvolver[k];
+		rePadded[k] = re * reWeight - im * imWeight;
+		imPadded[k] = re * imWeight + im * reWeight;
+	}
+	fft.directTransform(rePadded, imPadded, reBuffer, imBuffer,
+		InputDataType.COMPLEXINPUT);
+	int p = startIndex + stride;
+	for (int k = 1; (k < prime); k++) {
+		final int q = inverseModular[k];
+		reData[p] = rePadded[q] + reBaseline;
+		imData[p] = imPadded[q] + imBaseline;
+		p += stride;
+	}
+	reData[startIndex] += reSum;
+	imData[startIndex] += imSum;
+} /* end run */
+
+/*....................................................................
+	DFTPaddedRaderFloat static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static float[][] getConvolverReAndIm (
+	final int[] modular,
+	final int paddedLength
+) {
+	final int prime = modular.length;
+	final float[] reConvolver = new float[paddedLength];
+	final float[] imConvolver = new float[paddedLength];
+	final float angularStep = -2.0F * (float)PI / (float)prime;
+	final float norm = 1.0F / (float)paddedLength;
+	int n = 0;
+	for (int k = prime - 2; (1 <= k); k--) {
+		final float shuffled = (float)modular[k];
+		reConvolver[n] = norm * (float)cos((double)(shuffled * angularStep));
+		imConvolver[n] = norm * (float)sin((double)(shuffled * angularStep));
+		n++;
+	}
+	reConvolver[n] = norm * (float)cos((double)angularStep);
+	imConvolver[n] = norm * (float)sin((double)angularStep);
+	while (++n < paddedLength) {
+		reConvolver[n] = reConvolver[n - prime + 1];
+		imConvolver[n] = imConvolver[n - prime + 1];
+	}
+	final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0);
+	fft.directTransform(reConvolver, imConvolver, null, null,
+		InputDataType.COMPLEXINPUT);
+	return(new float[][] {
+		reConvolver,
+		imConvolver
+	});
+} /* end getConvolverReAndIm */
+
+} /* end class DFTPaddedRaderFloat */
+
+/*====================================================================
+|	DFTPaddedRaderReal
+\===================================================================*/
+static class DFTPaddedRaderReal
+
+{ /* begin class DFTPaddedRaderReal */
+
+/*....................................................................
+	DFTPaddedRaderReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int prime,
+	final int paddedLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(paddedLength))) {
+		return(-1L);
+	}
+	final long K = (long)prime;
+	final long k = K >> 1L;
+	final long P = (long)paddedLength;
+	return(FFTSetup.FLALLOC * (P * 4L + 6L)
+		+ FFTSetup.FLOP * (P * 6L + k * 1L + 1L)
+		+ FFTSetup.FLASSIGN * (P * 4L + 1L + (K - 1L) * 1L + 1L + P * 6L
+			+ k * 2L + 2L)
+		+ FFTSetup.INTALLOC * 8L
+		+ FFTSetup.INTOP * (4L + (K - 1L) * 5L + P * 2L + 1L + k * 3L)
+		+ FFTSetup.INTASSIGN * (5L + (K - 1L) * 2L + 1L + P * 1L + 1L
+			+ k * 3L)
+		+ FFTSetup.IDX * (1L + (K - 1L) * 3L + 1L + P * 6L + k * 5L + 2L)
+		+ 1L * (FFTSetup.NEWOBJ
+			+ FFTSetup.FLALLOC * 0L
+			+ FFTSetup.FLOP * 0L
+			+ FFTSetup.FLASSIGN * 0L
+			+ FFTSetup.INTALLOC * 0L
+			+ FFTSetup.INTOP * 1L
+			+ FFTSetup.INTASSIGN * 6L
+			+ FFTSetup.IDX * 0L
+			+ FFTSetup.NEWOBJ * 6L)
+		+ 1L * (FFTSetupReal.cost(paddedLength)
+			+ FFTSetup.FLALLOC * 0L
+			+ FFTSetup.FLOP * (k * 1L)
+			+ FFTSetup.FLASSIGN * (k * 2L)
+			+ FFTSetup.INTALLOC * 2L
+			+ FFTSetup.INTOP * (10L + 2L + 1L + k * 3L)
+			+ FFTSetup.INTASSIGN * (2L + k * 2L)
+			+ FFTSetup.IDX * (k * 4L)
+			+ FFTSetup.NEWOBJ * 1L)
+		+ 1L * (FFTSetup.cost(paddedLength)
+			+ FFTSetup.FLALLOC * 0L
+			+ FFTSetup.FLOP * 0L
+			+ FFTSetup.FLASSIGN * 0L
+			+ FFTSetup.INTALLOC * 0L
+			+ FFTSetup.INTOP * (10L + 2L)
+			+ FFTSetup.INTASSIGN * 0L
+			+ FFTSetup.IDX * 0L
+			+ FFTSetup.NEWOBJ * 1L)
+	);
+} /* end cost */
+
+} /* end class DFTPaddedRaderReal */
+
+/*====================================================================
+|	DFTPaddedRaderRealDouble
+\===================================================================*/
+static class DFTPaddedRaderRealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTPaddedRaderRealDouble */
+
+/*....................................................................
+	DFTPaddedRaderRealDouble private variables
+....................................................................*/
+private double[] imConvolver;
+private double[] reConvolver;
+private int[] inverseModular;
+private int[] modular;
+
+/*....................................................................
+	DFTPaddedRaderRealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTPaddedRaderRealDouble (
+	final double[] reData,
+	final double[] imData,
+	final int startIndex,
+	final int stride,
+	final double[] reConvolver,
+	final double[] imConvolver,
+	final int[] modular,
+	final int[] inverseModular
+) {
+	super(reData, imData, startIndex, stride);
+	this.reConvolver = reConvolver;
+	this.imConvolver = imConvolver;
+	this.modular = modular;
+	this.inverseModular = inverseModular;
+} /* end DFTPaddedRaderRealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int prime = modular.length;
+	final int halfPrime = (prime >> 1) + 1;
+	final int paddedLength = reConvolver.length;
+//;IJ.log("DFTPaddedRaderRealDouble " + prime + " (" + paddedLength +")");
+	final double[] rePadded = new double[paddedLength];
+	final double[] imPadded = new double[paddedLength];
+	final double[] reBuffer = new double[paddedLength];
+	final double[] imBuffer = new double[paddedLength];
+	final double reBaseline = reData[startIndex];
+	for (int k = paddedLength - prime + 1, m = 1; (k < paddedLength); k++) {
+		rePadded[k] = reData[startIndex + modular[m++] * stride];
+	}
+	final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0);
+	fft.directTransform(rePadded, imPadded, reBuffer, imBuffer,
+		InputDataType.REALINPUT);
+	final double reSum = rePadded[0];
+	for (int k = 0; (k < paddedLength); k++) {
+		final double re = rePadded[k];
+		final double im = imPadded[k];
+		final double reWeight = reConvolver[k];
+		final double imWeight = imConvolver[k];
+		rePadded[k] = re * reWeight - im * imWeight;
+		imPadded[k] = re * imWeight + im * reWeight;
+	}
+	fft.directTransform(rePadded, imPadded, reBuffer, imBuffer,
+		InputDataType.COMPLEXINPUT);
+	int p = startIndex + stride;
+	for (int k = 1; (k < halfPrime); k++) {
+		final int q = inverseModular[k];
+		reData[p] = rePadded[q] + reBaseline;
+		imData[p] = imPadded[q];
+		p += stride;
+	}
+	reData[startIndex] += reSum;
+	imData[startIndex] = 0.0;
+} /* end run */
+
+} /* end class DFTPaddedRaderRealDouble */
+
+/*====================================================================
+|	DFTPaddedRaderRealFloat
+\===================================================================*/
+static class DFTPaddedRaderRealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTPaddedRaderRealFloat */
+
+/*....................................................................
+	DFTPaddedRaderRealFloat private variables
+....................................................................*/
+private float[] imConvolver;
+private float[] reConvolver;
+private int[] inverseModular;
+private int[] modular;
+
+/*....................................................................
+	DFTPaddedRaderRealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTPaddedRaderRealFloat (
+	final float[] reData,
+	final float[] imData,
+	final int startIndex,
+	final int stride,
+	final float[] reConvolver,
+	final float[] imConvolver,
+	final int[] modular,
+	final int[] inverseModular
+) {
+	super(reData, imData, startIndex, stride);
+	this.reConvolver = reConvolver;
+	this.imConvolver = imConvolver;
+	this.modular = modular;
+	this.inverseModular = inverseModular;
+} /* end DFTPaddedRaderRealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int prime = modular.length;
+	final int halfPrime = (prime >> 1) + 1;
+	final int paddedLength = reConvolver.length;
+//;IJ.log("DFTPaddedRaderRealFloat " + prime + " (" + paddedLength +")");
+	final float[] rePadded = new float[paddedLength];
+	final float[] imPadded = new float[paddedLength];
+	final float[] reBuffer = new float[paddedLength];
+	final float[] imBuffer = new float[paddedLength];
+	final float reBaseline = reData[startIndex];
+	for (int k = paddedLength - prime + 1, m = 1; (k < paddedLength); k++) {
+		rePadded[k] = reData[startIndex + modular[m++] * stride];
+	}
+	final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0);
+	fft.directTransform(rePadded, imPadded, reBuffer, imBuffer,
+		InputDataType.REALINPUT);
+	final float reSum = rePadded[0];
+	for (int k = 0; (k < paddedLength); k++) {
+		final float re = rePadded[k];
+		final float im = imPadded[k];
+		final float reWeight = reConvolver[k];
+		final float imWeight = imConvolver[k];
+		rePadded[k] = re * reWeight - im * imWeight;
+		imPadded[k] = re * imWeight + im * reWeight;
+	}
+	fft.directTransform(rePadded, imPadded, reBuffer, imBuffer,
+		InputDataType.COMPLEXINPUT);
+	int p = startIndex + stride;
+	for (int k = 1; (k < halfPrime); k++) {
+		final int q = inverseModular[k];
+		reData[p] = rePadded[q] + reBaseline;
+		imData[p] = imPadded[q];
+		p += stride;
+	}
+	reData[startIndex] += reSum;
+	imData[startIndex] = 0.0F;
+} /* end run */
+
+} /* end class DFTPaddedRaderRealFloat */
+
+/*====================================================================
+|	DFTRader
+\===================================================================*/
+static class DFTRader
+
+{ /* begin class DFTRader */
+
+/*....................................................................
+	DFTRader static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int prime
+) {
+	if (FFTSetup.taboos.contains(new Integer(prime))
+		|| FFTSetup.taboos.contains(new Integer(prime - 1))) {
+		return(-1L);
+	}
+	final long K = (long)prime;
+	return(FFTSetup.FLALLOC * 6L
+		+ FFTSetup.FLOP * ((K - 1L) * 6L + (K - 1L) * 2L + 2L)
+		+ FFTSetup.FLASSIGN * (2L + (K - 1L) * 2L + 2L + (K - 1L) * 6L
+			+ (K - 1L) * 2L + 2L)
+		+ FFTSetup.INTALLOC * 8L
+		+ FFTSetup.INTOP * (1L + (K - 1L) * 5L + 4L + (K- 1L) * 3L + 2L
+			+ (K - 1L) * 5L)
+		+ FFTSetup.INTASSIGN * (3L + (K - 1L) * 3L + 3L + (K - 1L) * 2L + 2L
+			+ (K - 1L) * 3L)
+		+ FFTSetup.IDX * (2L + (K - 1L) * 5L + 4L + (K - 1L) * 6L
+			+ (K - 1L) * 5L + 4L)
+		+ FFTSetup.NEWOBJ * 2L
+		+ 2L * FFTSetup.cost(prime - 1)
+	);
+} /* end cost */
+
+/*------------------------------------------------------------------*/
+static int[] getInverseModularPowerShuffling (
+	final int[] modularShuffling
+) {
+	final int prime = modularShuffling.length;
+	int[] inverseShuffling = new int[prime];
+	inverseShuffling[0] = 0;
+	inverseShuffling[modularShuffling[prime - 3]] = 1;
+	inverseShuffling[modularShuffling[prime - 2]] = 2;
+	inverseShuffling[modularShuffling[prime - 1]] = 3;
+	for (int k = 4; (k < prime); k++) {
+		inverseShuffling[modularShuffling[k - 3]] = k;
+	}
+	return(inverseShuffling);
+} /* end getInverseModularPowerShuffling */
+
+/*------------------------------------------------------------------*/
+static int[] getModularPowerShuffling (
+	final int prime
+) {
+	final int g = smallestPrimitiveRootOfPrime(prime);
+	int[] modularShuffling = new int[prime];
+	modularShuffling[0] = 0;
+	for (int k = 1; (k < prime); k++) {
+		modularShuffling[k] = modularPositivePower(g, k, prime);
+	}
+	return(modularShuffling);
+} /* end getModularPowerShuffling */
+
+/*....................................................................
+	DFTRader private methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static private TreeMap<Integer, Integer> factorize (
+	int number
+) {
+	final TreeMap<Integer, Integer> factors =
+		new TreeMap<Integer, Integer>();
+	for (int divisor = 2; (divisor <= number); divisor++) {
+		if ((divisor * (number / divisor)) == number) {
+			number /= divisor;
+			int multiplicity = 1;
+			while ((divisor * (number / divisor)) == number) {
+				number /= divisor;
+				multiplicity++;
+			}
+			factors.put(new Integer(divisor), new Integer(multiplicity));
+		}
+	}
+	return(factors);
+} /* end factorize */
+
+/*------------------------------------------------------------------*/
+static private int modularPositivePower (
+	int number,
+	int exponent,
+	final int modulo
+) {
+	int result = 1;
+	number -= modulo * (number / modulo);
+	while (0 < exponent) {
+		if (1 == (exponent & 1)) {
+			result *= number;
+			result -= modulo * (result / modulo);
+		}
+		exponent >>= 1;
+		number *= number;
+		number -= modulo * (number / modulo);
+	}
+	return(result);
+} /* end modularPositivePower */
+
+/*------------------------------------------------------------------*/
+static private int smallestPrimitiveRootOfPrime (
+	final int prime
+) {
+	if (2 == prime) {
+		return(1);
+	}
+	final TreeMap<Integer, Integer> factors = factorize(prime - 1);
+	int result = -1;
+	for (int candidate = 1; (candidate < prime); candidate++) {
+		result = candidate;
+		for (Integer primeDivisor: factors.navigableKeySet()) {
+			if (1 == modularPositivePower(candidate, (prime - 1)
+				/ primeDivisor.intValue(), prime)) {
+				result = -1;
+				break;
+			}
+		}
+		if (0 < result) {
+			break;
+		}
+	}
+	return(result);
+} /* end smallestPrimitiveRootOfPrime */
+
+} /* end class DFTRader */
+
+/*====================================================================
+|	DFTRaderDouble
+\===================================================================*/
+static class DFTRaderDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTRaderDouble */
+
+/*....................................................................
+	DFTRaderDouble private variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imConvolver;
+private double[] reBuffer;
+private double[] reConvolver;
+private int[] inverseModular;
+private int[] modular;
+
+/*....................................................................
+	DFTRaderDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTRaderDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reConvolver,
+	final double[] imConvolver,
+	final int[] modular,
+	final int[] inverseModular
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reConvolver = reConvolver;
+	this.imConvolver = imConvolver;
+	this.modular = modular;
+	this.inverseModular = inverseModular;
+} /* end DFTRaderDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int prime = modular.length;
+//;IJ.log("DFTRaderDouble " + prime);
+	final double reBaseline = reData[startIndex];
+	final double imBaseline = imData[startIndex];
+	int p = startIndex + stride;
+	for (int k = 1; (k < prime); k++) {
+		final int q = startIndex + modular[k] * stride;
+		reBuffer[p] = reData[q];
+		imBuffer[p] = imData[q];
+		p += stride;
+	}
+	final FFTSetup fft = FFTSetup.transforms.get(new Integer(prime - 1));
+	p = startIndex + stride;
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			throw(new IllegalStateException());
+		}
+		case RADER: {
+			throw(new IllegalStateException());
+		}
+		case RADIX2: {
+			new DFTRadix2Double(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+	}
+	reBuffer[startIndex] = reBuffer[p];
+	imBuffer[startIndex] = imBuffer[p];
+	for (int k = 0, K = prime - 1; (k < K); k++) {
+		final double re = reBuffer[p];
+		final double im = imBuffer[p];
+		final double reWeight = reConvolver[k];
+		final double imWeight = imConvolver[k];
+		reBuffer[p] = re * reWeight - im * imWeight;
+		imBuffer[p] = re * imWeight + im * reWeight;
+		p += stride;
+	}
+	p = startIndex + stride;
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			throw(new IllegalStateException());
+		}
+		case RADER: {
+			throw(new IllegalStateException());
+		}
+		case RADIX2: {
+			new DFTRadix2Double(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+	}
+	for (int k = 1; (k < prime); k++) {
+		final int q = startIndex + inverseModular[k] * stride;
+		reData[p] = reBuffer[q] + reBaseline;
+		imData[p] = imBuffer[q] + imBaseline;
+		p += stride;
+	}
+	reData[startIndex] += reBuffer[startIndex];
+	imData[startIndex] += imBuffer[startIndex];
+} /* end run */
+
+/*....................................................................
+	DFTRaderDouble static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static double[][] getConvolverReAndIm (
+	final int[] modular
+) {
+	final int prime = modular.length;
+	final double[] reConvolver = new double[prime - 1];
+	final double[] imConvolver = new double[prime - 1];
+	final double angularStep = -2.0 * PI / (double)prime;
+	final double norm = 1.0 / (double)(prime - 1);
+	int n = 0;
+	for (int k = prime - 2; (1 <= k); k--) {
+		final double shuffled = (double)modular[k];
+		reConvolver[n] = norm * cos(shuffled * angularStep);
+		imConvolver[n] = norm * sin(shuffled * angularStep);
+		n++;
+	}
+	reConvolver[n] = norm * cos(angularStep);
+	imConvolver[n] = norm * sin(angularStep);
+	final AcademicFourierTransform fft = new AcademicFourierTransform(prime - 1, 0);
+	fft.directTransform(reConvolver, imConvolver, null, null,
+		InputDataType.COMPLEXINPUT);
+	return(new double[][] {
+		reConvolver,
+		imConvolver
+	});
+} /* end getConvolverReAndIm */
+
+} /* end class DFTRaderDouble */
+
+/*====================================================================
+|	DFTRaderFloat
+\===================================================================*/
+static class DFTRaderFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTRaderFloat */
+
+/*....................................................................
+	DFTRaderFloat private variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imConvolver;
+private float[] reBuffer;
+private float[] reConvolver;
+private int[] inverseModular;
+private int[] modular;
+
+/*....................................................................
+	DFTRaderFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTRaderFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reConvolver,
+	final float[] imConvolver,
+	final int[] modular,
+	final int[] inverseModular
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reConvolver = reConvolver;
+	this.imConvolver = imConvolver;
+	this.modular = modular;
+	this.inverseModular = inverseModular;
+} /* end DFTRaderFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int prime = modular.length;
+//;IJ.log("DFTRaderFloat " + prime);
+	final float reBaseline = reData[startIndex];
+	final float imBaseline = imData[startIndex];
+	int p = startIndex + stride;
+	for (int k = 1; (k < prime); k++) {
+		final int q = startIndex + modular[k] * stride;
+		reBuffer[p] = reData[q];
+		imBuffer[p] = imData[q];
+		p += stride;
+	}
+	final FFTSetup fft = FFTSetup.transforms.get(new Integer(prime - 1));
+	p = startIndex + stride;
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			throw(new IllegalStateException());
+		}
+		case RADER: {
+			throw(new IllegalStateException());
+		}
+		case RADIX2: {
+			new DFTRadix2Float(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+	}
+	reBuffer[startIndex] = reBuffer[p];
+	imBuffer[startIndex] = imBuffer[p];
+	for (int k = 0, K = prime - 1; (k < K); k++) {
+		final float re = reBuffer[p];
+		final float im = imBuffer[p];
+		final float reWeight = reConvolver[k];
+		final float imWeight = imConvolver[k];
+		reBuffer[p] = re * reWeight - im * imWeight;
+		imBuffer[p] = re * imWeight + im * reWeight;
+		p += stride;
+	}
+	p = startIndex + stride;
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			throw(new IllegalStateException());
+		}
+		case RADER: {
+			throw(new IllegalStateException());
+		}
+		case RADIX2: {
+			new DFTRadix2Float(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+	}
+	for (int k = 1; (k < prime); k++) {
+		final int q = startIndex + inverseModular[k] * stride;
+		reData[p] = reBuffer[q] + reBaseline;
+		imData[p] = imBuffer[q] + imBaseline;
+		p += stride;
+	}
+	reData[startIndex] += reBuffer[startIndex];
+	imData[startIndex] += imBuffer[startIndex];
+} /* end run */
+
+/*....................................................................
+	DFTRaderFloat static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static float[][] getConvolverReAndIm (
+	final int[] modular
+) {
+	final int prime = modular.length;
+	final float[] reConvolver = new float[prime - 1];
+	final float[] imConvolver = new float[prime - 1];
+	final float angularStep = -2.0F * (float)PI / (float)prime;
+	final float norm = 1.0F / (float)(prime - 1);
+	int n = 0;
+	for (int k = prime - 2; (1 <= k); k--) {
+		final float shuffled = (float)modular[k];
+		reConvolver[n] = norm * (float)cos((double)(shuffled * angularStep));
+		imConvolver[n] = norm * (float)sin((double)(shuffled * angularStep));
+		n++;
+	}
+	reConvolver[n] = norm * (float)cos((double)angularStep);
+	imConvolver[n] = norm * (float)sin((double)angularStep);
+	final AcademicFourierTransform fft = new AcademicFourierTransform(prime - 1, 0);
+	fft.directTransform(reConvolver, imConvolver, null, null,
+		InputDataType.COMPLEXINPUT);
+	return(new float[][] {
+		reConvolver,
+		imConvolver
+	});
+} /* end getConvolverReAndIm */
+
+} /* end class DFTRaderFloat */
+
+/*====================================================================
+|	DFTRaderReal
+\===================================================================*/
+static class DFTRaderReal
+
+{ /* begin class DFTRaderReal */
+
+/*....................................................................
+	DFTRaderReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int prime
+) {
+	if (FFTSetup.taboos.contains(new Integer(prime))
+		|| FFTSetup.taboos.contains(new Integer(prime - 1))) {
+		return(-1L);
+	}
+	final long K = (long)prime;
+	final long k = K >> 1L;
+	return(FFTSetup.FLALLOC * 5L
+		+ FFTSetup.FLOP * (k * 1L + (K - 1L) * 6L + k * 1L + 1L)
+		+ FFTSetup.FLASSIGN * (1L + (K - 1L) * 1L + k * 2L + 1L + (K - 1L) * 6L
+			+ k * 2L + 2L)
+		+ FFTSetup.INTALLOC * 11L
+		+ FFTSetup.INTOP * (3L + (K - 1L) * 5L + 7L + k * 3L + 1L
+			+ (K- 1L) * 3L + 3L + k * 5L)
+		+ FFTSetup.INTASSIGN * (4L + (K - 1L) * 3L + 3L + k * 2L + 2L
+			+ (K - 1L) * 2L + 2L + k * 3L)
+		+ FFTSetup.IDX * (1L + (K - 1L) * 3L + k * 4L + 2L + (K - 1L) * 6L
+			+ k * 5L + 3L)
+		+ FFTSetup.NEWOBJ * 2L
+		+ FFTSetupReal.cost(prime - 1) + FFTSetup.cost(prime - 1)
+	);
+} /* end cost */
+
+} /* end class DFTRaderReal */
+
+/*====================================================================
+|	DFTRaderRealDouble
+\===================================================================*/
+static class DFTRaderRealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTRaderRealDouble */
+
+/*....................................................................
+	DFTRaderRealDouble private variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imConvolver;
+private double[] reBuffer;
+private double[] reConvolver;
+private int[] inverseModular;
+private int[] modular;
+
+/*....................................................................
+	DFTRaderRealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTRaderRealDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reConvolver,
+	final double[] imConvolver,
+	final int[] modular,
+	final int[] inverseModular
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reConvolver = reConvolver;
+	this.imConvolver = imConvolver;
+	this.modular = modular;
+	this.inverseModular = inverseModular;
+} /* end DFTRaderRealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int prime = modular.length;
+	final int halfPrime = (prime >> 1) + 1;
+//;IJ.log("DFTRaderRealDouble " + prime);
+	final double reBaseline = reData[startIndex];
+	int p = startIndex + stride;
+	for (int k = 1; (k < prime); k++) {
+		final int q = startIndex + modular[k] * stride;
+		reBuffer[p] = reData[q];
+		p += stride;
+	}
+	final FFTSetupReal fftReal =
+		FFTSetupReal.transforms.get(new Integer(prime - 1));
+	p = startIndex + stride;
+	switch (fftReal.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealDouble(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootDouble, fftReal.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealDouble(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.ruritanian, fftReal.chinese, fftReal.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			new DFTEvenRealDouble(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootDouble, fftReal.imUnitRootDouble).run();
+			break;
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2RealDouble(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4RealDouble(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6RealDouble(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealDouble(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealDouble(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootDouble, fftReal.imUnitRootDouble,
+				fftReal.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			throw(new IllegalStateException());
+		}
+		case RADER: {
+			throw(new IllegalStateException());
+		}
+		case RADIX2: {
+			new DFTRadix2RealDouble(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootEvenDouble, fftReal.imUnitRootEvenDouble,
+				fftReal.reUnitRootOddDouble, fftReal.imUnitRootOddDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealDouble(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootDouble, fftReal.imUnitRootDouble).run();
+			break;
+		}
+	}
+	int progressive = p + stride;
+	int regressive = p + (prime - 2) * stride;
+	while (progressive < regressive) {
+		reBuffer[regressive] = reBuffer[progressive];
+		imBuffer[regressive] = -imBuffer[progressive];
+		progressive += stride;
+		regressive -= stride;
+	}
+	reBuffer[startIndex] = reBuffer[p];
+	for (int k = 0, K = prime - 1; (k < K); k++) {
+		final double re = reBuffer[p];
+		final double im = imBuffer[p];
+		final double reWeight = reConvolver[k];
+		final double imWeight = imConvolver[k];
+		reBuffer[p] = re * reWeight - im * imWeight;
+		imBuffer[p] = re * imWeight + im * reWeight;
+		p += stride;
+	}
+	p = startIndex + stride;
+	final FFTSetup fft = FFTSetup.transforms.get(new Integer(prime - 1));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Double(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			throw(new IllegalStateException());
+		}
+		case RADER: {
+			throw(new IllegalStateException());
+		}
+		case RADIX2: {
+			new DFTRadix2Double(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+	}
+	for (int k = 1; (k < halfPrime); k++) {
+		final int q = startIndex + inverseModular[k] * stride;
+		reData[p] = reBuffer[q] + reBaseline;
+		imData[p] = imBuffer[q];
+		p += stride;
+	}
+	reData[startIndex] += reBuffer[startIndex];
+	imData[startIndex] = 0.0;
+} /* end run */
+
+} /* end class DFTRaderRealDouble */
+
+/*====================================================================
+|	DFTRaderRealFloat
+\===================================================================*/
+static class DFTRaderRealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTRaderRealFloat */
+
+/*....................................................................
+	DFTRaderRealFloat private variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imConvolver;
+private float[] reBuffer;
+private float[] reConvolver;
+private int[] inverseModular;
+private int[] modular;
+
+/*....................................................................
+	DFTRaderRealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTRaderRealFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reConvolver,
+	final float[] imConvolver,
+	final int[] modular,
+	final int[] inverseModular
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reConvolver = reConvolver;
+	this.imConvolver = imConvolver;
+	this.modular = modular;
+	this.inverseModular = inverseModular;
+} /* end DFTRaderRealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int prime = modular.length;
+	final int halfPrime = (prime >> 1) + 1;
+//;IJ.log("DFTRaderRealFloat " + prime);
+	final float reBaseline = reData[startIndex];
+	int p = startIndex + stride;
+	for (int k = 1; (k < prime); k++) {
+		final int q = startIndex + modular[k] * stride;
+		reBuffer[p] = reData[q];
+		p += stride;
+	}
+	final FFTSetupReal fftReal =
+		FFTSetupReal.transforms.get(new Integer(prime - 1));
+	p = startIndex + stride;
+	switch (fftReal.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealFloat(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootFloat, fftReal.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealFloat(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.ruritanian, fftReal.chinese, fftReal.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			new DFTEvenRealFloat(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootFloat, fftReal.imUnitRootFloat).run();
+			break;
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2RealFloat(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4RealFloat(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6RealFloat(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealFloat(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealFloat(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootFloat, fftReal.imUnitRootFloat,
+				fftReal.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			throw(new IllegalStateException());
+		}
+		case RADER: {
+			throw(new IllegalStateException());
+		}
+		case RADIX2: {
+			new DFTRadix2RealFloat(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootEvenFloat, fftReal.imUnitRootEvenFloat,
+				fftReal.reUnitRootOddFloat, fftReal.imUnitRootOddFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealFloat(reBuffer, imBuffer, reData, imData,
+				p, stride,
+				fftReal.reUnitRootFloat, fftReal.imUnitRootFloat).run();
+			break;
+		}
+	}
+	int progressive = p + stride;
+	int regressive = p + (prime - 2) * stride;
+	while (progressive < regressive) {
+		reBuffer[regressive] = reBuffer[progressive];
+		imBuffer[regressive] = -imBuffer[progressive];
+		progressive += stride;
+		regressive -= stride;
+	}
+	reBuffer[startIndex] = reBuffer[p];
+	for (int k = 0, K = prime - 1; (k < K); k++) {
+		final float re = reBuffer[p];
+		final float im = imBuffer[p];
+		final float reWeight = reConvolver[k];
+		final float imWeight = imConvolver[k];
+		reBuffer[p] = re * reWeight - im * imWeight;
+		imBuffer[p] = re * imWeight + im * reWeight;
+		p += stride;
+	}
+	p = startIndex + stride;
+	final FFTSetup fft = FFTSetup.transforms.get(new Integer(prime - 1));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Float(reBuffer, imBuffer, p, stride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			throw(new IllegalStateException());
+		}
+		case RADER: {
+			throw(new IllegalStateException());
+		}
+		case RADIX2: {
+			new DFTRadix2Float(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData,
+				p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+	}
+	for (int k = 1; (k < halfPrime); k++) {
+		final int q = startIndex + inverseModular[k] * stride;
+		reData[p] = reBuffer[q] + reBaseline;
+		imData[p] = imBuffer[q];
+		p += stride;
+	}
+	reData[startIndex] += reBuffer[startIndex];
+	imData[startIndex] = 0.0F;
+} /* end run */
+
+} /* end class DFTRaderRealFloat */
+
+/*====================================================================
+|	DFTRadix2
+\===================================================================*/
+static class DFTRadix2
+
+{ /* begin class DFTRadix2 */
+
+/*....................................................................
+	DFTRadix2 static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(transformLength / 2))) {
+		return(-1L);
+	}
+	final long k = (long)(transformLength >> 1);
+	return(FFTSetup.FLALLOC * 4L
+		+ FFTSetup.FLOP * (k * 6L + k * 4L)
+		+ FFTSetup.FLASSIGN * (k * 4L + k * 8L + k * 4L)
+		+ FFTSetup.INTALLOC * 8L
+		+ FFTSetup.INTOP * (5L + k * 6L + k * 5L + k * 4L)
+		+ FFTSetup.INTASSIGN * (6L + k * 5L + 2L + k * 4L + 1L + k * 3L)
+		+ FFTSetup.IDX * (k * 8L + k * 10L + k * 4L)
+		+ FFTSetup.NEWOBJ * 2L
+		+ 2L * FFTSetup.cost(transformLength / 2)
+	);
+} /* end cost */
+
+} /* end class DFTRadix2 */
+
+/*====================================================================
+|	DFTRadix2Double
+\===================================================================*/
+static class DFTRadix2Double
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTRadix2Double */
+
+/*....................................................................
+	DFTRadix2Double static variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imUnitRoot;
+private double[] reBuffer;
+private double[] reUnitRoot;
+
+/*....................................................................
+	DFTRadix2Double constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTRadix2Double (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reUnitRoot,
+	final double[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTRadix2Double */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int K2 = reUnitRoot.length;
+//;IJ.log("DFTRadix2Double " + (2 * K2));
+	final int doubleStride = stride << 1;
+	final FFTSetup fft = FFTSetup.transforms.get(new Integer(K2));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Double(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength2Double(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Double(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength3Double(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Double(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength4Double(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Double(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength5Double(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Double(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength6Double(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Double(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength8Double(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble,
+				fft.K1).run();
+			new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderDouble(reData, imData,
+				startIndex, doubleStride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			new DFTPaddedRaderDouble(reData, imData,
+				startIndex + stride, doubleStride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			new DFTRaderDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Double(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			new DFTRadix2Double(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+	}
+	int m0 = startIndex;
+	int i0 = startIndex;
+	int i1 = startIndex + K2 * stride;
+	for (int m = 0; (m < K2); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += stride;
+		reBuffer[i1] = reData[m0];
+		imBuffer[i1] = imData[m0];
+		m0 += stride;
+		i0 += stride;
+		i1 += stride;
+	}
+	m0 = K2;
+	for (int m = 0; (m < K2); m++) {
+		m0--;
+		i1 -= stride;
+		i0 -= stride;
+		reData[i0] = reBuffer[i0];
+		imData[i0] = imBuffer[i0];
+		final double re = reBuffer[i1];
+		final double im = imBuffer[i1];
+		final double reRoot = reUnitRoot[m0];
+		final double imRoot = imUnitRoot[m0];
+		reData[i1] = re * reRoot - im * imRoot;
+		imData[i1] = re * imRoot + im * reRoot;
+	}
+	for (int m = 0; (m < K2); m++) {
+		reData[i0] -= reData[i1];
+		imData[i0] -= imData[i1];
+		reData[i1] += reBuffer[i0];
+		imData[i1] += imBuffer[i0];
+		i0 += stride;
+		i1 += stride;
+	}
+} /* end run */
+
+/*....................................................................
+	DFTRadix2Double static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static double[] getImUnitRoot (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final double[] imUnitRoot = new double[halfTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		imUnitRoot[k] = sin((double)(halfTransformLength + k) * angularStep);
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static double[] getReUnitRoot (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final double[] reUnitRoot = new double[halfTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		reUnitRoot[k] = cos((double)(halfTransformLength + k) * angularStep);
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTRadix2Double */
+
+/*====================================================================
+|	DFTRadix2Float
+\===================================================================*/
+static class DFTRadix2Float
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTRadix2Float */
+
+/*....................................................................
+	DFTRadix2Float static variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imUnitRoot;
+private float[] reBuffer;
+private float[] reUnitRoot;
+
+/*....................................................................
+	DFTRadix2Float constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTRadix2Float (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reUnitRoot,
+	final float[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTRadix2Float */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int K2 = reUnitRoot.length;
+//;IJ.log("DFTRadix2Float " + (2 * K2));
+	final int doubleStride = stride << 1;
+	final FFTSetup fft = FFTSetup.transforms.get(new Integer(K2));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.ruritanian, fft.chinese,
+				fft.K1).run();
+			new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.ruritanian, fft.chinese,
+				fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Float(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength2Float(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Float(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength3Float(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Float(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength4Float(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Float(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength5Float(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Float(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength6Float(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Float(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength8Float(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat,
+				fft.K1).run();
+			new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderFloat(reData, imData,
+				startIndex, doubleStride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			new DFTPaddedRaderFloat(reData, imData,
+				startIndex + stride, doubleStride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			new DFTRaderFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Float(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			new DFTRadix2Float(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+	}
+	int m0 = startIndex;
+	int i0 = startIndex;
+	int i1 = startIndex + K2 * stride;
+	for (int m = 0; (m < K2); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += stride;
+		reBuffer[i1] = reData[m0];
+		imBuffer[i1] = imData[m0];
+		m0 += stride;
+		i0 += stride;
+		i1 += stride;
+	}
+	m0 = K2;
+	for (int m = 0; (m < K2); m++) {
+		m0--;
+		i1 -= stride;
+		i0 -= stride;
+		reData[i0] = reBuffer[i0];
+		imData[i0] = imBuffer[i0];
+		final float re = reBuffer[i1];
+		final float im = imBuffer[i1];
+		final float reRoot = reUnitRoot[m0];
+		final float imRoot = imUnitRoot[m0];
+		reData[i1] = re * reRoot - im * imRoot;
+		imData[i1] = re * imRoot + im * reRoot;
+	}
+	for (int m = 0; (m < K2); m++) {
+		reData[i0] -= reData[i1];
+		imData[i0] -= imData[i1];
+		reData[i1] += reBuffer[i0];
+		imData[i1] += imBuffer[i0];
+		i0 += stride;
+		i1 += stride;
+	}
+} /* end run */
+
+/*....................................................................
+	DFTRadix2Float static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static float[] getImUnitRoot (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final float[] imUnitRoot = new float[halfTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		imUnitRoot[k] = (float)sin((double)((float)(halfTransformLength + k)
+			* angularStep));
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static float[] getReUnitRoot (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final float[] reUnitRoot = new float[halfTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		reUnitRoot[k] = (float)cos((double)((float)(halfTransformLength + k)
+			* angularStep));
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTRadix2Float */
+
+/*====================================================================
+|	DFTRadix2Real
+\===================================================================*/
+static class DFTRadix2Real
+
+{ /* begin class DFTRadix2Real */
+
+/*....................................................................
+	DFTRadix2Real static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(transformLength / 2))) {
+		return(-1L);
+	}
+	final long k2 = (long)(transformLength >> 2);
+	return(FFTSetup.FLALLOC * 8L
+		+ FFTSetup.FLOP * (1L + k2 * 8L + k2 * 9L + 1L)
+		+ FFTSetup.FLASSIGN * ((k2 + 1L) * 4L + 2L + k2 * 6L + k2 * 6L + 2L)
+		+ FFTSetup.INTALLOC * 9L
+		+ FFTSetup.INTOP * (6L + (k2 + 1L) * 6L + 6L + k2 * 4L + 2L + k2 * 5L)
+		+ FFTSetup.INTASSIGN * (7L + (k2 + 1L) * 5L + 4L + k2 * 3L + 4L
+			+ k2 * 4L)
+		+ FFTSetup.IDX * ((k2 + 1L) * 8L + 4L + k2 * 8L + k2 * 8L + 4L)
+		+ FFTSetup.NEWOBJ * 2L
+		+ FFTSetupDuoReal.cost(transformLength / 2)
+	);
+} /* end cost */
+
+} /* end class DFTRadix2Real */
+
+/*====================================================================
+|	DFTRadix2RealDouble
+\===================================================================*/
+static class DFTRadix2RealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTRadix2RealDouble */
+
+/*....................................................................
+	DFTRadix2RealDouble static variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imUnitRootEven;
+private double[] imUnitRootOdd;
+private double[] reBuffer;
+private double[] reUnitRootEven;
+private double[] reUnitRootOdd;
+
+/*....................................................................
+	DFTRadix2RealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTRadix2RealDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reUnitRootEven,
+	final double[] imUnitRootEven,
+	final double[] reUnitRootOdd,
+	final double[] imUnitRootOdd
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRootEven = reUnitRootEven;
+	this.imUnitRootEven = imUnitRootEven;
+	this.reUnitRootOdd = reUnitRootOdd;
+	this.imUnitRootOdd = imUnitRootOdd;
+} /* end DFTRadix2RealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int K2 = reUnitRootEven.length;
+//;IJ.log("DFTRadix2RealDouble " + (2 * K2));
+	final int K4 = K2 >> 1;
+	final int doubleStride = stride << 1;
+	final FFTSetupDuoReal fft =
+		FFTSetupDuoReal.transforms.get(new Integer(K2));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			new DFTDuoRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, startIndex + stride, doubleStride, K2).run();
+			break;
+		}
+		case EVENREAL: {
+			new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case LENGTH1: {
+			imData[startIndex] = 0.0;
+			imData[startIndex + stride] = 0.0;
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength2RealDouble(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength3RealDouble(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength4RealDouble(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength5RealDouble(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength6RealDouble(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength8RealDouble(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble,
+				fft.K1).run();
+			new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderRealDouble(reData, imData,
+				startIndex, doubleStride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			new DFTPaddedRaderRealDouble(reData, imData,
+				startIndex + stride, doubleStride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble,
+				fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run();
+			new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble,
+				fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+	}
+	int m0 = startIndex;
+	int i0 = startIndex;
+	int i1 = startIndex + K2 * stride;
+	for (int m = 0; (m <= K4); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += stride;
+		reBuffer[i1] = reData[m0];
+		imBuffer[i1] = imData[m0];
+		m0 += stride;
+		i0 += stride;
+		i1 += stride;
+	}
+	i1 = startIndex + K2 * stride;
+	reData[startIndex] = reBuffer[startIndex] + reBuffer[i1];
+	imData[startIndex] = 0.0;
+	i0 = startIndex + stride;
+	i1 += stride;
+	if (0 == (K2 & 1)) {
+		for (int m = 1; (m < K4); m++) {
+			final double re = reBuffer[i1];
+			final double im = imBuffer[i1];
+			final double reRoot = reUnitRootEven[m];
+			final double imRoot = imUnitRootEven[m];
+			reData[i0] = reBuffer[i0] + re * reRoot - im * imRoot;
+			imData[i0] = imBuffer[i0] + re * imRoot + im * reRoot;
+			i0 += stride;
+			i1 += stride;
+		}
+		reData[i0] = reBuffer[i0];
+		imData[i0] = -reBuffer[i1];
+		m0 = i0 + stride;
+		i0 -= stride;
+		i1 -= stride;
+		for (int m = 1; (m < K4); m++) {
+			final double re = imBuffer[i1];
+			final double im = reBuffer[i1];
+			final double reRoot = reUnitRootEven[m];
+			final double imRoot = imUnitRootEven[m];
+			reData[m0] = reBuffer[i0] - re * reRoot + im * imRoot;
+			imData[m0] = -imBuffer[i0] - re * imRoot - im * reRoot;
+			m0 += stride;
+			i0 -= stride;
+			i1 -= stride;
+		}
+	}
+	else {
+		for (int m = 1; (m <= K4); m++) {
+			final double re = reBuffer[i1];
+			final double im = imBuffer[i1];
+			final double reRoot = reUnitRootEven[m];
+			final double imRoot = imUnitRootEven[m];
+			reData[i0] = reBuffer[i0] + re * reRoot - im * imRoot;
+			imData[i0] = imBuffer[i0] + re * imRoot + im * reRoot;
+			i0 += stride;
+			i1 += stride;
+		}
+		m0 = i0;
+		i0 -= stride;
+		i1 -= stride;
+		for (int m = 0; (m < K4); m++) {
+			final double re = imBuffer[i1];
+			final double im = reBuffer[i1];
+			final double reRoot = reUnitRootOdd[m];
+			final double imRoot = imUnitRootOdd[m];
+			reData[m0] = reBuffer[i0] - re * reRoot + im * imRoot;
+			imData[m0] = -imBuffer[i0] - re * imRoot - im * reRoot;
+			i0 -= stride;
+			i1 -= stride;
+			m0 += stride;
+		}
+	}
+	reData[m0] = reBuffer[i0] - reBuffer[i1];
+	imData[m0] = 0.0;
+} /* end run */
+
+/*....................................................................
+	DFTRadix2RealDouble static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static double[] getImUnitRootEven (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final double[] imUnitRoot = new double[halfTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		imUnitRoot[k] = sin((double)k * angularStep);
+	}
+	return(imUnitRoot);
+} /* end getImUnitRootEven */
+
+/*------------------------------------------------------------------*/
+static double[] getReUnitRootEven (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final double[] reUnitRoot = new double[halfTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		reUnitRoot[k] = cos((double)k * angularStep);
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+/*------------------------------------------------------------------*/
+static double[] getImUnitRootOdd (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final double[] imUnitRoot = new double[halfTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		imUnitRoot[k] = sin(((double)k + 0.5) * angularStep);
+	}
+	return(imUnitRoot);
+} /* end getImUnitRootOdd */
+
+/*------------------------------------------------------------------*/
+static double[] getReUnitRootOdd (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final double[] reUnitRoot = new double[halfTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		reUnitRoot[k] = cos(((double)k + 0.5) * angularStep);
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class getReUnitRootOdd */
+
+/*====================================================================
+|	DFTRadix2RealFloat
+\===================================================================*/
+static class DFTRadix2RealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTRadix2RealFloat */
+
+/*....................................................................
+	DFTRadix2RealFloat static variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imUnitRootEven;
+private float[] imUnitRootOdd;
+private float[] reBuffer;
+private float[] reUnitRootEven;
+private float[] reUnitRootOdd;
+
+/*....................................................................
+	DFTRadix2RealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTRadix2RealFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reUnitRootEven,
+	final float[] imUnitRootEven,
+	final float[] reUnitRootOdd,
+	final float[] imUnitRootOdd
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRootEven = reUnitRootEven;
+	this.imUnitRootEven = imUnitRootEven;
+	this.reUnitRootOdd = reUnitRootOdd;
+	this.imUnitRootOdd = imUnitRootOdd;
+} /* end DFTRadix2RealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int K2 = reUnitRootEven.length;
+//;IJ.log("DFTRadix2RealFloat " + (2 * K2));
+	final int K4 = K2 >> 1;
+	final int doubleStride = stride << 1;
+	final FFTSetupDuoReal fft =
+		FFTSetupDuoReal.transforms.get(new Integer(K2));
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			new DFTDuoRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, startIndex + stride, doubleStride, K2).run();
+			break;
+		}
+		case EVENREAL: {
+			new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case LENGTH1: {
+			imData[startIndex] = 0.0F;
+			imData[startIndex + stride] = 0.0F;
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength2RealFloat(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength3RealFloat(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength4RealFloat(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength5RealFloat(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength6RealFloat(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			new DFTLength8RealFloat(reData, imData,
+				startIndex + stride, doubleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat,
+				fft.K1).run();
+			new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat,
+				fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderRealFloat(reData, imData,
+				startIndex, doubleStride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			new DFTPaddedRaderRealFloat(reData, imData,
+				startIndex + stride, doubleStride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat,
+				fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run();
+			new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat,
+				fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, doubleStride,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+	}
+	int m0 = startIndex;
+	int i0 = startIndex;
+	int i1 = startIndex + K2 * stride;
+	for (int m = 0; (m <= K4); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += stride;
+		reBuffer[i1] = reData[m0];
+		imBuffer[i1] = imData[m0];
+		m0 += stride;
+		i0 += stride;
+		i1 += stride;
+	}
+	i1 = startIndex + K2 * stride;
+	reData[startIndex] = reBuffer[startIndex] + reBuffer[i1];
+	imData[startIndex] = 0.0F;
+	i0 = startIndex + stride;
+	i1 += stride;
+	if (0 == (K2 & 1)) {
+		for (int m = 1; (m < K4); m++) {
+			final float re = reBuffer[i1];
+			final float im = imBuffer[i1];
+			final float reRoot = reUnitRootEven[m];
+			final float imRoot = imUnitRootEven[m];
+			reData[i0] = reBuffer[i0] + re * reRoot - im * imRoot;
+			imData[i0] = imBuffer[i0] + re * imRoot + im * reRoot;
+			i0 += stride;
+			i1 += stride;
+		}
+		reData[i0] = reBuffer[i0];
+		imData[i0] = -reBuffer[i1];
+		m0 = i0 + stride;
+		i0 -= stride;
+		i1 -= stride;
+		for (int m = 1; (m < K4); m++) {
+			final float re = imBuffer[i1];
+			final float im = reBuffer[i1];
+			final float reRoot = reUnitRootEven[m];
+			final float imRoot = imUnitRootEven[m];
+			reData[m0] = reBuffer[i0] - re * reRoot + im * imRoot;
+			imData[m0] = -imBuffer[i0] - re * imRoot - im * reRoot;
+			m0 += stride;
+			i0 -= stride;
+			i1 -= stride;
+		}
+	}
+	else {
+		for (int m = 1; (m <= K4); m++) {
+			final float re = reBuffer[i1];
+			final float im = imBuffer[i1];
+			final float reRoot = reUnitRootEven[m];
+			final float imRoot = imUnitRootEven[m];
+			reData[i0] = reBuffer[i0] + re * reRoot - im * imRoot;
+			imData[i0] = imBuffer[i0] + re * imRoot + im * reRoot;
+			i0 += stride;
+			i1 += stride;
+		}
+		m0 = i0;
+		i0 -= stride;
+		i1 -= stride;
+		for (int m = 0; (m < K4); m++) {
+			final float re = imBuffer[i1];
+			final float im = reBuffer[i1];
+			final float reRoot = reUnitRootOdd[m];
+			final float imRoot = imUnitRootOdd[m];
+			reData[m0] = reBuffer[i0] - re * reRoot + im * imRoot;
+			imData[m0] = -imBuffer[i0] - re * imRoot - im * reRoot;
+			i0 -= stride;
+			i1 -= stride;
+			m0 += stride;
+		}
+	}
+	reData[m0] = reBuffer[i0] - reBuffer[i1];
+	imData[m0] = 0.0F;
+} /* end run */
+
+/*....................................................................
+	DFTRadix2RealFloat static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static float[] getImUnitRootEven (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final float[] imUnitRoot = new float[halfTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		imUnitRoot[k] = (float)sin((double)((float)k * angularStep));
+	}
+	return(imUnitRoot);
+} /* end getImUnitRootEven */
+
+/*------------------------------------------------------------------*/
+static float[] getReUnitRootEven (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final float[] reUnitRoot = new float[halfTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		reUnitRoot[k] = (float)cos((double)((float)k * angularStep));
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+/*------------------------------------------------------------------*/
+static float[] getImUnitRootOdd (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final float[] imUnitRoot = new float[halfTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		imUnitRoot[k] = (float)sin((double)(((float)k + 0.5F)
+			* angularStep));
+	}
+	return(imUnitRoot);
+} /* end getImUnitRootOdd */
+
+/*------------------------------------------------------------------*/
+static float[] getReUnitRootOdd (
+	final int transformLength
+) {
+	final int halfTransformLength = transformLength >> 1;
+	final float[] reUnitRoot = new float[halfTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < halfTransformLength); k++) {
+		reUnitRoot[k] = (float)cos((double)(((float)k + 0.5F)
+			* angularStep));
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class getReUnitRootOdd */
+
+/*====================================================================
+|	DFTSplitRadix
+\===================================================================*/
+static class DFTSplitRadix
+
+{ /* begin class DFTSplitRadix */
+
+/*....................................................................
+	DFTSplitRadix static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(transformLength / 2))
+		|| FFTSetup.taboos.contains(new Integer(transformLength / 4))) {
+		return(-1L);
+	}
+	final long k = (long)(transformLength >> 1);
+	final long k2 = k >> 1L;
+	return(FFTSetup.FLALLOC * 12L
+		+ FFTSetup.FLOP * (k2 * 24L)
+		+ FFTSetup.FLASSIGN * (k * 2L + k2 * 2L + k2 * 2L + k2 * 28L)
+		+ FFTSetup.INTALLOC * 15L
+		+ FFTSetup.INTOP * (9L + k * 4L + 1L + k2 * 4L + 1L + k2 * 4L + 6L
+			+ k2 * 7L)
+		+ FFTSetup.INTASSIGN * (8L + k * 3L + 2L + k2 * 3L + 2L + k2 * 3L + 6L
+			+ k2 * 6L)
+		+ FFTSetup.IDX * (k * 4L + k2 * 4L + k2 * 4L + k2 * 20L)
+		+ FFTSetup.NEWOBJ * 3L
+		+ FFTSetup.cost(transformLength >> 1)
+		+ 2L * FFTSetup.cost(transformLength >> 2)
+	);
+} /* end cost */
+
+} /* end class DFTSplitRadix */
+
+/*====================================================================
+|	DFTSplitRadixDouble
+\===================================================================*/
+static class DFTSplitRadixDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTSplitRadixDouble */
+
+/*....................................................................
+	DFTSplitRadixDouble static variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imUnitRoot;
+private double[] reBuffer;
+private double[] reUnitRoot;
+
+/*....................................................................
+	DFTSplitRadixDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTSplitRadixDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reUnitRoot,
+	final double[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTSplitRadixDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int K4 = reUnitRoot.length / 3;
+//;IJ.log("DFTSplitRadixDouble " + (4 * K4));
+	final int K2 = K4 << 1;
+	final int doubleStride = stride << 1;
+	final int tripleStride = doubleStride + stride;
+	final int quadrupleStride = tripleStride + stride;
+	final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.ruritanian, fft2.chinese, fft2.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2Double(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4Double(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6Double(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Double(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+				fft2.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderDouble(reData, imData,
+				startIndex, doubleStride,
+				fft2.reConvolverDouble, fft2.imConvolverDouble,
+				fft2.modular, fft2.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reConvolverDouble, fft2.imConvolverDouble,
+				fft2.modular, fft2.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Double(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+			break;
+		}
+	}
+	final FFTSetup fft4 = FFTSetup.transforms.get(new Integer(K4));
+	switch (fft4.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.ruritanian, fft4.chinese,
+				fft4.K1).run();
+			new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.ruritanian, fft4.chinese,
+				fft4.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Double(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength2Double(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Double(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength3Double(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Double(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength4Double(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Double(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength5Double(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Double(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength6Double(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Double(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength8Double(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble,
+				fft4.K1).run();
+			new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble,
+				fft4.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderDouble(reData, imData,
+				startIndex + stride, quadrupleStride,
+				fft4.reConvolverDouble, fft4.imConvolverDouble,
+				fft4.modular, fft4.inverseModular).run();
+			new DFTPaddedRaderDouble(reData, imData,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reConvolverDouble, fft4.imConvolverDouble,
+				fft4.modular, fft4.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reConvolverDouble, fft4.imConvolverDouble,
+				fft4.modular, fft4.inverseModular).run();
+			new DFTRaderDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reConvolverDouble, fft4.imConvolverDouble,
+				fft4.modular, fft4.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Double(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			new DFTRadix2Double(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			break;
+		}
+	}
+	int m0 = startIndex;
+	int i0 = startIndex;
+	for (int m = 0; (m < K2); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += doubleStride;
+		i0 += stride;
+	}
+	m0 = startIndex + stride;
+	for (int m = 0; (m < K4); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += quadrupleStride;
+		i0 += stride;
+	}
+	m0 = startIndex + tripleStride;
+	for (int m = 0; (m < K4); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += quadrupleStride;
+		i0 += stride;
+	}
+	i0 = startIndex;
+	int i1 = i0 + K4 * stride;
+	int i2 = i1 + K4 * stride;
+	int i3 = i2 + K4 * stride;
+	for (int m1 = 0, m3 = 0; (m1 < K4); m1++, m3 += 3) {
+		double re = reBuffer[i2];
+		double im = imBuffer[i2];
+		double reRoot = reUnitRoot[m1];
+		double imRoot = imUnitRoot[m1];
+		double reButterfly = re * reRoot - im * imRoot;
+		double imButterfly = re * imRoot + im * reRoot;
+		re = reBuffer[i3];
+		im = imBuffer[i3];
+		reRoot = reUnitRoot[m3];
+		imRoot = imUnitRoot[m3];
+		double reDragonfly = re * reRoot - im * imRoot;
+		double imDragonfly = re * imRoot + im * reRoot;
+		final double reLadybug = reButterfly + reDragonfly;
+		final double imLadybug = imButterfly + imDragonfly;
+		final double reMoth = reButterfly - reDragonfly;
+		final double imMoth = imButterfly - imDragonfly;
+		reButterfly = reBuffer[i0];
+		imButterfly = imBuffer[i0];
+		reDragonfly = reBuffer[i1];
+		imDragonfly = imBuffer[i1];
+		reData[i0] = reButterfly + reLadybug;
+		imData[i0] = imButterfly + imLadybug;
+		reData[i1] = reDragonfly + imMoth;
+		imData[i1] = imDragonfly - reMoth;
+		reData[i2] = reButterfly - reLadybug;
+		imData[i2] = imButterfly - imLadybug;
+		reData[i3] = reDragonfly - imMoth;
+		imData[i3] = imDragonfly + reMoth;
+		i0 += stride;
+		i1 += stride;
+		i2 += stride;
+		i3 += stride;
+	}
+} /* end run */
+
+/*....................................................................
+	DFTSplitRadixDouble static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static double[] getImUnitRoot (
+	final int transformLength
+) {
+	final int fourthTransformLength = transformLength >> 2;
+	final int threeFourthTransformLength = 3 * fourthTransformLength;
+	final double[] imUnitRoot = new double[threeFourthTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < threeFourthTransformLength); k++) {
+		imUnitRoot[k] = sin((double)k * angularStep);
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static double[] getReUnitRoot (
+	final int transformLength
+) {
+	final int fourthTransformLength = transformLength >> 2;
+	final int threeFourthTransformLength = 3 * fourthTransformLength;
+	final double[] reUnitRoot = new double[threeFourthTransformLength];
+	final double angularStep = -2.0 * PI / (double)transformLength;
+	for (int k = 0; (k < threeFourthTransformLength); k++) {
+		reUnitRoot[k] = cos((double)k * angularStep);
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTSplitRadixDouble */
+
+/*====================================================================
+|	DFTSplitRadixFloat
+\===================================================================*/
+static class DFTSplitRadixFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTSplitRadixFloat */
+
+/*....................................................................
+	DFTSplitRadixFloat static variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imUnitRoot;
+private float[] reBuffer;
+private float[] reUnitRoot;
+
+/*....................................................................
+	DFTSplitRadixFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTSplitRadixFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reUnitRoot,
+	final float[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTSplitRadixFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int K4 = reUnitRoot.length / 3;
+//;IJ.log("DFTSplitRadixFloat " + (4 * K4));
+	final int K2 = K4 << 1;
+	final int doubleStride = stride << 1;
+	final int tripleStride = doubleStride + stride;
+	final int quadrupleStride = tripleStride + stride;
+	final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.ruritanian, fft2.chinese,
+				fft2.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2Float(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4Float(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6Float(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Float(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+				fft2.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderFloat(reData, imData,
+				startIndex, doubleStride,
+				fft2.reConvolverFloat, fft2.imConvolverFloat,
+				fft2.modular, fft2.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reConvolverFloat, fft2.imConvolverFloat,
+				fft2.modular, fft2.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Float(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+			break;
+		}
+	}
+	final FFTSetup fft4 = FFTSetup.transforms.get(new Integer(K4));
+	switch (fft4.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.ruritanian, fft4.chinese,
+				fft4.K1).run();
+			new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.ruritanian, fft4.chinese,
+				fft4.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Float(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength2Float(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Float(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength3Float(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Float(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength4Float(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Float(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength5Float(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Float(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength6Float(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Float(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength8Float(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat,
+				fft4.K1).run();
+			new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat,
+				fft4.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderFloat(reData, imData,
+				startIndex + stride, quadrupleStride,
+				fft4.reConvolverFloat, fft4.imConvolverFloat,
+				fft4.modular, fft4.inverseModular).run();
+			new DFTPaddedRaderFloat(reData, imData,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reConvolverFloat, fft4.imConvolverFloat,
+				fft4.modular, fft4.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reConvolverFloat, fft4.imConvolverFloat,
+				fft4.modular, fft4.inverseModular).run();
+			new DFTRaderFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reConvolverFloat, fft4.imConvolverFloat,
+				fft4.modular, fft4.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Float(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			new DFTRadix2Float(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			break;
+		}
+	}
+	int m0 = startIndex;
+	int i0 = startIndex;
+	for (int m = 0; (m < K2); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += doubleStride;
+		i0 += stride;
+	}
+	m0 = startIndex + stride;
+	for (int m = 0; (m < K4); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += quadrupleStride;
+		i0 += stride;
+	}
+	m0 = startIndex + tripleStride;
+	for (int m = 0; (m < K4); m++) {
+		reBuffer[i0] = reData[m0];
+		imBuffer[i0] = imData[m0];
+		m0 += quadrupleStride;
+		i0 += stride;
+	}
+	i0 = startIndex;
+	int i1 = i0 + K4 * stride;
+	int i2 = i1 + K4 * stride;
+	int i3 = i2 + K4 * stride;
+	for (int m1 = 0, m3 = 0; (m1 < K4); m1++, m3 += 3) {
+		float re = reBuffer[i2];
+		float im = imBuffer[i2];
+		float reRoot = reUnitRoot[m1];
+		float imRoot = imUnitRoot[m1];
+		float reButterfly = re * reRoot - im * imRoot;
+		float imButterfly = re * imRoot + im * reRoot;
+		re = reBuffer[i3];
+		im = imBuffer[i3];
+		reRoot = reUnitRoot[m3];
+		imRoot = imUnitRoot[m3];
+		float reDragonfly = re * reRoot - im * imRoot;
+		float imDragonfly = re * imRoot + im * reRoot;
+		final float reLadybug = reButterfly + reDragonfly;
+		final float imLadybug = imButterfly + imDragonfly;
+		final float reMoth = reButterfly - reDragonfly;
+		final float imMoth = imButterfly - imDragonfly;
+		reButterfly = reBuffer[i0];
+		imButterfly = imBuffer[i0];
+		reDragonfly = reBuffer[i1];
+		imDragonfly = imBuffer[i1];
+		reData[i0] = reButterfly + reLadybug;
+		imData[i0] = imButterfly + imLadybug;
+		reData[i1] = reDragonfly + imMoth;
+		imData[i1] = imDragonfly - reMoth;
+		reData[i2] = reButterfly - reLadybug;
+		imData[i2] = imButterfly - imLadybug;
+		reData[i3] = reDragonfly - imMoth;
+		imData[i3] = imDragonfly + reMoth;
+		i0 += stride;
+		i1 += stride;
+		i2 += stride;
+		i3 += stride;
+	}
+} /* end run */
+
+/*....................................................................
+	DFTSplitRadixFloat static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static float[] getImUnitRoot (
+	final int transformLength
+) {
+	final int fourthTransformLength = transformLength >> 2;
+	final int threeFourthTransformLength = 3 * fourthTransformLength;
+	final float[] imUnitRoot = new float[threeFourthTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < threeFourthTransformLength); k++) {
+		imUnitRoot[k] = (float)sin((double)((float)k * angularStep));
+	}
+	return(imUnitRoot);
+} /* end getImUnitRoot */
+
+/*------------------------------------------------------------------*/
+static float[] getReUnitRoot (
+	final int transformLength
+) {
+	final int fourthTransformLength = transformLength >> 2;
+	final int threeFourthTransformLength = 3 * fourthTransformLength;
+	final float[] reUnitRoot = new float[threeFourthTransformLength];
+	final float angularStep = -2.0F * (float)PI / (float)transformLength;
+	for (int k = 0; (k < threeFourthTransformLength); k++) {
+		reUnitRoot[k] = (float)cos((double)((float)k * angularStep));
+	}
+	return(reUnitRoot);
+} /* end getReUnitRoot */
+
+} /* end class DFTSplitRadixFloat */
+
+/*====================================================================
+|	DFTSplitRadixReal
+\===================================================================*/
+static class DFTSplitRadixReal
+
+{ /* begin class DFTSplitRadixReal */
+
+/*....................................................................
+	DFTSplitRadixReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (FFTSetup.taboos.contains(new Integer(transformLength / 2))
+		|| FFTSetup.taboos.contains(new Integer(transformLength / 4))) {
+		return(-1L);
+	}
+	final long k = (long)(transformLength >> 1);
+	final long k2 = k >> 1L;
+	final long k4 = k2 >> 1L;
+	return(FFTSetup.FLALLOC * 8L
+		+ FFTSetup.FLOP * ((k4 + 1L) * 16L + 1L + k4 * 2L + (k2 - k4) * 2L + 1L
+			+ k4 * 3L + (k2 - k4) * 2L + 1L)
+		+ FFTSetup.FLASSIGN * ((k2 + 1L) * 2L + (k4 + 1L) * 16L + 2L + k4 * 2L
+			+ (k2 - k4) * 2L + 2L + k4 * 2L + (k2 - k4) * 2L + 2L)
+		+ FFTSetup.INTALLOC * 18L
+		+ FFTSetup.INTOP * (12L + (k2 + 1L) * 4L + 6L + (k4 + 1L) * 7L + 4L
+			+ k4 * 4L + 4L + (k2 - k4) * 4L + 5L + k4 * 5L + 4L
+			+ (k2 - k4) * 5L)
+		+ FFTSetup.INTASSIGN * (10L + (k2 + 1L) * 3L + 6L + (k4 + 1L) * 6L + 5L
+			+ k4 * 3L + 2L + (k2 - k4) * 3L + 4L + k4 * 4L + 2L
+			+ (k2 - k4) * 4L)
+		+ FFTSetup.IDX * ((k2 + 1L) * 4L + (k4 + 1L) * 12L + 4L + k4 * 6L
+			+ (k2 - k4) * 6L + 4L + k4 * 6L + (k2 - k4) * 6L + 4L)
+		+ FFTSetup.NEWOBJ * 3L
+		+ FFTSetupReal.cost(transformLength >> 1)
+		+ FFTSetupDuoReal.cost(transformLength >> 2)
+	);
+} /* end cost */
+
+} /* end class DFTSplitRadixReal */
+
+/*====================================================================
+|	DFTSplitRadixRealDouble
+\===================================================================*/
+static class DFTSplitRadixRealDouble
+	extends
+		DFTDouble
+	implements
+		Runnable
+
+{ /* begin class DFTSplitRadixRealDouble */
+
+/*....................................................................
+	DFTSplitRadixRealDouble static variables
+....................................................................*/
+private double[] imBuffer;
+private double[] imUnitRoot;
+private double[] reBuffer;
+private double[] reUnitRoot;
+
+/*....................................................................
+	DFTSplitRadixRealDouble constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTSplitRadixRealDouble (
+	final double[] reData,
+	final double[] imData,
+	final double[] reBuffer,
+	final double[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final double[] reUnitRoot,
+	final double[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTSplitRadixRealDouble */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int K4 = reUnitRoot.length / 3;
+//;IJ.log("DFTSplitRadixRealDouble " + (4 * K4));
+	final int halfK4 = (K4 >> 1) + 1;
+	final int K2 = K4 << 1;
+	final int halfK2 = K4 + 1;
+	final int doubleStride = stride << 1;
+	final int tripleStride = doubleStride + stride;
+	final int quadrupleStride = tripleStride + stride;
+	final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.ruritanian, fft2.chinese, fft2.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+			break;
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealDouble(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+				fft2.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderRealDouble(reData, imData,
+				startIndex, doubleStride,
+				fft2.reConvolverDouble, fft2.imConvolverDouble,
+				fft2.modular, fft2.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reConvolverDouble, fft2.imConvolverDouble,
+				fft2.modular, fft2.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble,
+				fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootDouble, fft2.imUnitRootDouble).run();
+			break;
+		}
+	}
+	final FFTSetupDuoReal fft4 =
+		FFTSetupDuoReal.transforms.get(new Integer(K4));
+	switch (fft4.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.ruritanian, fft4.chinese,
+				fft4.K1).run();
+			new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.ruritanian, fft4.chinese,
+				fft4.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			new DFTDuoRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, startIndex + tripleStride,
+				quadrupleStride, K4).run();
+			break;
+		}
+		case EVENREAL: {
+			new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			break;
+		}
+		case LENGTH1: {
+			imData[startIndex + stride] = 0.0;
+			imData[startIndex + tripleStride] = 0.0;
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2RealDouble(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength2RealDouble(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3RealDouble(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength3RealDouble(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4RealDouble(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength4RealDouble(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5RealDouble(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength5RealDouble(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6RealDouble(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength6RealDouble(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealDouble(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength8RealDouble(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble,
+				fft4.K1).run();
+			new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble,
+				fft4.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderRealDouble(reData, imData,
+				startIndex + stride, quadrupleStride,
+				fft4.reConvolverDouble, fft4.imConvolverDouble,
+				fft4.modular, fft4.inverseModular).run();
+			new DFTPaddedRaderRealDouble(reData, imData,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reConvolverDouble, fft4.imConvolverDouble,
+				fft4.modular, fft4.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reConvolverDouble, fft4.imConvolverDouble,
+				fft4.modular, fft4.inverseModular).run();
+			new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reConvolverDouble, fft4.imConvolverDouble,
+				fft4.modular, fft4.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootEvenDouble, fft4.imUnitRootEvenDouble,
+				fft4.reUnitRootOddDouble, fft4.imUnitRootOddDouble).run();
+			new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootEvenDouble, fft4.imUnitRootEvenDouble,
+				fft4.reUnitRootOddDouble, fft4.imUnitRootOddDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootDouble, fft4.imUnitRootDouble).run();
+			break;
+		}
+	}
+	int p = startIndex;
+	int q = startIndex;
+	for (int m = 0; (m < halfK2); m++) {
+		reBuffer[p] = reData[q];
+		imBuffer[p] = imData[q];
+		q += doubleStride;
+		p += stride;
+	}
+	p = startIndex + K2 * stride;
+	q = p + K4 * stride;
+	int r = startIndex + stride;
+	int s = r + doubleStride;
+	for (int m = 0, n = 0; (m < halfK4); m++, n += 3) {
+		double re = reData[r];
+		double im = imData[r];
+		double reRoot = reUnitRoot[m];
+		double imRoot = imUnitRoot[m];
+		double reButterfly = re * reRoot - im * imRoot;
+		double imButterfly = re * imRoot + im * reRoot;
+		re = reData[s];
+		im = imData[s];
+		reRoot = reUnitRoot[n];
+		imRoot = imUnitRoot[n];
+		final double reDragonfly = re * reRoot - im * imRoot;
+		final double imDragonfly = re * imRoot + im * reRoot;
+		reBuffer[p] = reButterfly + reDragonfly;
+		imBuffer[p] = imButterfly + imDragonfly;
+		reBuffer[q] = reButterfly - reDragonfly;
+		imBuffer[q] = imButterfly - imDragonfly;
+		r += quadrupleStride;
+		s += quadrupleStride;
+		p += stride;
+		q += stride;
+	};
+	p = startIndex;
+	q = startIndex + K2 * stride;
+	reData[p] = reBuffer[p] + reBuffer[q];
+	imData[p] = 0.0;
+	p += stride;
+	q += stride;
+	for (int n = 1; (n < halfK4); n++) {
+		reData[p] = reBuffer[p] + reBuffer[q];
+		imData[p] = imBuffer[p] + imBuffer[q];
+		p += stride;
+		q += stride;
+	}
+	q = startIndex + (2 * K2 - halfK4) * stride;
+	for (int n = halfK4; (n < K4); n++) {
+		reData[p] = reBuffer[p] - imBuffer[q];
+		imData[p] = imBuffer[p] - reBuffer[q];
+		p += stride;
+		q -= stride;
+	}
+	reData[p] = reBuffer[p];
+	imData[p] = -reBuffer[q];
+	p += stride;
+	q += stride;
+	r = startIndex + (K4 - 1) * stride;
+	for (int n = 1; (n < halfK4); n++) {
+		reData[p] = reBuffer[r] + imBuffer[q];
+		imData[p] = -imBuffer[r] - reBuffer[q];
+		p += stride;
+		q += stride;
+		r -= stride;
+	}
+	q = startIndex + (K2 + K4 - halfK4) * stride;
+	for (int n = halfK4; (n < K4); n++) {
+		reData[p] = reBuffer[r] - reBuffer[q];
+		imData[p] = imBuffer[q] - imBuffer[r];
+		p += stride;
+		q -= stride;
+		r -= stride;
+	}
+	reData[p] = reBuffer[startIndex] - reBuffer[q];
+	imData[p] = 0.0;
+} /* end run */
+
+} /* end class DFTSplitRadixRealDouble */
+
+/*====================================================================
+|	DFTSplitRadixRealFloat
+\===================================================================*/
+static class DFTSplitRadixRealFloat
+	extends
+		DFTFloat
+	implements
+		Runnable
+
+{ /* begin class DFTSplitRadixRealFloat */
+
+/*....................................................................
+	DFTSplitRadixRealFloat static variables
+....................................................................*/
+private float[] imBuffer;
+private float[] imUnitRoot;
+private float[] reBuffer;
+private float[] reUnitRoot;
+
+/*....................................................................
+	DFTSplitRadixRealFloat constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+DFTSplitRadixRealFloat (
+	final float[] reData,
+	final float[] imData,
+	final float[] reBuffer,
+	final float[] imBuffer,
+	final int startIndex,
+	final int stride,
+	final float[] reUnitRoot,
+	final float[] imUnitRoot
+) {
+	super(reData, imData, startIndex, stride);
+	this.reBuffer = reBuffer;
+	this.imBuffer = imBuffer;
+	this.reUnitRoot = reUnitRoot;
+	this.imUnitRoot = imUnitRoot;
+} /* end DFTSplitRadixRealFloat */
+
+/*....................................................................
+	Runnable methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public void run (
+) {
+	final int K4 = reUnitRoot.length / 3;
+//;IJ.log("DFTSplitRadixRealFloat " + (4 * K4));
+	final int halfK4 = (K4 >> 1) + 1;
+	final int K2 = K4 << 1;
+	final int halfK2 = K4 + 1;
+	final int doubleStride = stride << 1;
+	final int tripleStride = doubleStride + stride;
+	final int quadrupleStride = tripleStride + stride;
+	final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2));
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.ruritanian, fft2.chinese, fft2.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+			break;
+		}
+		case LENGTH1: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH2: {
+			new DFTLength2RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH4: {
+			new DFTLength4RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH6: {
+			new DFTLength6RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealFloat(reData, imData,
+				startIndex, doubleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+				fft2.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderRealFloat(reData, imData,
+				startIndex, doubleStride,
+				fft2.reConvolverFloat, fft2.imConvolverFloat,
+				fft2.modular, fft2.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reConvolverFloat, fft2.imConvolverFloat,
+				fft2.modular, fft2.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat,
+				fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex, doubleStride,
+				fft2.reUnitRootFloat, fft2.imUnitRootFloat).run();
+			break;
+		}
+	}
+	final FFTSetupDuoReal fft4 =
+		FFTSetupDuoReal.transforms.get(new Integer(K4));
+	switch (fft4.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.ruritanian, fft4.chinese,
+				fft4.K1).run();
+			new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.ruritanian, fft4.chinese,
+				fft4.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			new DFTDuoRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, startIndex + tripleStride,
+				quadrupleStride, K4).run();
+			break;
+		}
+		case EVENREAL: {
+			new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			break;
+		}
+		case LENGTH1: {
+			imData[startIndex + stride] = 0.0F;
+			imData[startIndex + tripleStride] = 0.0F;
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2RealFloat(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength2RealFloat(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3RealFloat(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength3RealFloat(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4RealFloat(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength4RealFloat(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5RealFloat(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength5RealFloat(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6RealFloat(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength6RealFloat(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealFloat(reData, imData,
+				startIndex + stride, quadrupleStride).run();
+			new DFTLength8RealFloat(reData, imData,
+				startIndex + tripleStride, quadrupleStride).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat,
+				fft4.K1).run();
+			new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat,
+				fft4.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderRealFloat(reData, imData,
+				startIndex + stride, quadrupleStride,
+				fft4.reConvolverFloat, fft4.imConvolverFloat,
+				fft4.modular, fft4.inverseModular).run();
+			new DFTPaddedRaderRealFloat(reData, imData,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reConvolverFloat, fft4.imConvolverFloat,
+				fft4.modular, fft4.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reConvolverFloat, fft4.imConvolverFloat,
+				fft4.modular, fft4.inverseModular).run();
+			new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reConvolverFloat, fft4.imConvolverFloat,
+				fft4.modular, fft4.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootEvenFloat, fft4.imUnitRootEvenFloat,
+				fft4.reUnitRootOddFloat, fft4.imUnitRootOddFloat).run();
+			new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootEvenFloat, fft4.imUnitRootEvenFloat,
+				fft4.reUnitRootOddFloat, fft4.imUnitRootOddFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + stride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer,
+				startIndex + tripleStride, quadrupleStride,
+				fft4.reUnitRootFloat, fft4.imUnitRootFloat).run();
+			break;
+		}
+	}
+	int p = startIndex;
+	int q = startIndex;
+	for (int m = 0; (m < halfK2); m++) {
+		reBuffer[p] = reData[q];
+		imBuffer[p] = imData[q];
+		q += doubleStride;
+		p += stride;
+	}
+	p = startIndex + K2 * stride;
+	q = p + K4 * stride;
+	int r = startIndex + stride;
+	int s = r + doubleStride;
+	for (int m = 0, n = 0; (m < halfK4); m++, n += 3) {
+		float re = reData[r];
+		float im = imData[r];
+		float reRoot = reUnitRoot[m];
+		float imRoot = imUnitRoot[m];
+		float reButterfly = re * reRoot - im * imRoot;
+		float imButterfly = re * imRoot + im * reRoot;
+		re = reData[s];
+		im = imData[s];
+		reRoot = reUnitRoot[n];
+		imRoot = imUnitRoot[n];
+		final float reDragonfly = re * reRoot - im * imRoot;
+		final float imDragonfly = re * imRoot + im * reRoot;
+		reBuffer[p] = reButterfly + reDragonfly;
+		imBuffer[p] = imButterfly + imDragonfly;
+		reBuffer[q] = reButterfly - reDragonfly;
+		imBuffer[q] = imButterfly - imDragonfly;
+		r += quadrupleStride;
+		s += quadrupleStride;
+		p += stride;
+		q += stride;
+	};
+	p = startIndex;
+	q = startIndex + K2 * stride;
+	reData[p] = reBuffer[p] + reBuffer[q];
+	imData[p] = 0.0F;
+	p += stride;
+	q += stride;
+	for (int n = 1; (n < halfK4); n++) {
+		reData[p] = reBuffer[p] + reBuffer[q];
+		imData[p] = imBuffer[p] + imBuffer[q];
+		p += stride;
+		q += stride;
+	}
+	q = startIndex + (2 * K2 - halfK4) * stride;
+	for (int n = halfK4; (n < K4); n++) {
+		reData[p] = reBuffer[p] - imBuffer[q];
+		imData[p] = imBuffer[p] - reBuffer[q];
+		p += stride;
+		q -= stride;
+	}
+	reData[p] = reBuffer[p];
+	imData[p] = -reBuffer[q];
+	p += stride;
+	q += stride;
+	r = startIndex + (K4 - 1) * stride;
+	for (int n = 1; (n < halfK4); n++) {
+		reData[p] = reBuffer[r] + imBuffer[q];
+		imData[p] = -imBuffer[r] - reBuffer[q];
+		p += stride;
+		q += stride;
+		r -= stride;
+	}
+	q = startIndex + (K2 + K4 - halfK4) * stride;
+	for (int n = halfK4; (n < K4); n++) {
+		reData[p] = reBuffer[r] - reBuffer[q];
+		imData[p] = imBuffer[q] - imBuffer[r];
+		p += stride;
+		q -= stride;
+		r -= stride;
+	}
+	reData[p] = reBuffer[startIndex] - reBuffer[q];
+	imData[p] = 0.0F;
+} /* end run */
+
+} /* end class DFTSplitRadixRealFloat */
+
+/*====================================================================
+|	FFTSetup
+\===================================================================*/
+static class FFTSetup
+
+{ /* begin class FFTSetup */
+
+/*....................................................................
+	FFTSetup static private variables
+....................................................................*/
+private static final HashSet<Integer> composites = new HashSet<Integer>();
+private static final HashSet<Integer> primes = new HashSet<Integer>();
+
+/*....................................................................
+	FFTSetup static protected variables
+....................................................................*/
+protected static final HashMap<Integer, Algorithm> algorithms =
+	new HashMap<Integer, Algorithm>();
+protected static final HashMap<Integer, Integer> lengths =
+	new HashMap<Integer, Integer>();
+protected static final HashMap<Integer, FFTSetup> transforms =
+	new HashMap<Integer, FFTSetup>();
+protected static final HashMap<Integer, Long> costs =
+	new HashMap<Integer, Long>();
+protected static final HashSet<Integer> taboos =
+	new HashSet<Integer>();
+protected static final long FLASSIGN = 2L;
+protected static final long FLOP = 4L;
+protected static final long IDX = 1L;
+protected static final long INTASSIGN = 1L;
+protected static final long INTOP = 2L;
+protected static final long NEWOBJ = 50L;
+
+protected static final long FLALLOC = FLOP + FLASSIGN;
+protected static final long INTALLOC = INTOP + INTASSIGN;
+
+/*....................................................................
+	FFTSetup static private variables
+....................................................................*/
+private static int futurePrime = 7;
+
+/*....................................................................
+	FFTSetup protected variables
+....................................................................*/
+protected Algorithm algorithm;
+protected double[] imConvolverDouble;
+protected double[] imUnitRootDouble;
+protected double[] reConvolverDouble;
+protected double[] reUnitRootDouble;
+protected float[] imConvolverFloat;
+protected float[] imUnitRootFloat;
+protected float[] reConvolverFloat;
+protected float[] reUnitRootFloat;
+protected int[] chinese;
+protected int[] inverseModular;
+protected int[] modular;
+protected int[] ruritanian;
+protected int K1;
+
+/*....................................................................
+	FFTSetup inner classes
+....................................................................*/
+/*====================================================================
+|	FFTCostPrediction
+\===================================================================*/
+static class FFTCostPrediction
+
+{ /* begin class FFTCostPrediction */
+
+/*....................................................................
+	FFTCostPrediction variables
+....................................................................*/
+protected Algorithm algorithm;
+protected int length;
+protected long cost;
+
+/*....................................................................
+	FFTCostPrediction constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+FFTCostPrediction (
+	final Algorithm algorithm,
+	final int length,
+	final long cost
+) {
+	this.algorithm = algorithm;
+	this.length = length;
+	this.cost = cost;
+} /* end FFTCostPrediction */
+
+/*....................................................................
+	Object methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+@Override
+public boolean equals (
+	final Object o
+) {
+	return((((FFTCostPrediction)o).algorithm == algorithm)
+		&& (((FFTCostPrediction)o).length == length));
+} /* end equals */
+
+} /* end class FFTCostPrediction */
+
+/*....................................................................
+	FFTSetup static initialization block
+....................................................................*/
+static {
+	initialize();
+}
+
+/*....................................................................
+	FFTSetup constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+FFTSetup (
+	final int transformLength
+) {
+	if (transforms.containsKey(new Integer(transformLength))) {
+		return;
+	}
+	transforms.put(new Integer(transformLength), this);
+	cost(transformLength);
+	algorithm = algorithms.get(new Integer(transformLength));
+	switch (algorithm) {
+		case BRUTEFORCE: {
+			reUnitRootDouble =
+				DFTBruteForceDouble.getReUnitRoot(transformLength);
+			imUnitRootDouble =
+				DFTBruteForceDouble.getImUnitRoot(transformLength);
+			reUnitRootFloat =
+				DFTBruteForceFloat.getReUnitRoot(transformLength);
+			imUnitRootFloat =
+				DFTBruteForceFloat.getImUnitRoot(transformLength);
+			break;
+		}
+		case COPRIMEFACTOR: {
+			K1 = lengths.get(new Integer(transformLength)).intValue();
+			final int K2 = transformLength / K1;
+			ruritanian = DFTCoprimeFactor.getRuritanianShuffling(K1, K2);
+			chinese = DFTCoprimeFactor.getChineseRemainderShuffling(K1, K2);
+			new FFTSetup(K1);
+			new FFTSetup(K2);
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			break;
+		}
+		case LENGTH3: {
+			break;
+		}
+		case LENGTH4: {
+			break;
+		}
+		case LENGTH5: {
+			break;
+		}
+		case LENGTH6: {
+			break;
+		}
+		case LENGTH8: {
+			break;
+		}
+		case MIXEDRADIX: {
+			K1 = lengths.get(new Integer(transformLength)).intValue();
+			final int K2 = transformLength / K1;
+			reUnitRootDouble =
+				DFTMixedRadixDouble.getReUnitRoot(transformLength);
+			imUnitRootDouble =
+				DFTMixedRadixDouble.getImUnitRoot(transformLength);
+			reUnitRootFloat =
+				DFTMixedRadixFloat.getReUnitRoot(transformLength);
+			imUnitRootFloat =
+				DFTMixedRadixFloat.getImUnitRoot(transformLength);
+			new FFTSetup(K1);
+			new FFTSetup(K2);
+			break;
+		}
+		case PADDEDRADER: {
+			final int paddedLength =
+				lengths.get(new Integer(transformLength)).intValue();
+			modular = DFTRader.getModularPowerShuffling(transformLength);
+			inverseModular =
+				DFTPaddedRader.getInverseModularPowerShuffling(modular,
+				paddedLength);
+			new FFTSetup(paddedLength);
+			final double[][] convolverD =
+				DFTPaddedRaderDouble.getConvolverReAndIm(modular,
+				paddedLength);
+			reConvolverDouble = convolverD[0];
+			imConvolverDouble = convolverD[1];
+			final float[][] convolverF =
+				DFTPaddedRaderFloat.getConvolverReAndIm(modular,
+				paddedLength);
+			reConvolverFloat = convolverF[0];
+			imConvolverFloat = convolverF[1];
+			break;
+		}
+		case RADER: {
+			modular = DFTRader.getModularPowerShuffling(transformLength);
+			inverseModular = DFTRader.getInverseModularPowerShuffling(modular);
+			new FFTSetup(transformLength - 1);
+			final double[][] convolverD =
+				DFTRaderDouble.getConvolverReAndIm(modular);
+			reConvolverDouble = convolverD[0];
+			imConvolverDouble = convolverD[1];
+			final float[][] convolverF =
+				DFTRaderFloat.getConvolverReAndIm(modular);
+			reConvolverFloat = convolverF[0];
+			imConvolverFloat = convolverF[1];
+			break;
+		}
+		case RADIX2: {
+			reUnitRootDouble =
+				DFTRadix2Double.getReUnitRoot(transformLength);
+			imUnitRootDouble =
+				DFTRadix2Double.getImUnitRoot(transformLength);
+			reUnitRootFloat =
+				DFTRadix2Float.getReUnitRoot(transformLength);
+			imUnitRootFloat =
+				DFTRadix2Float.getImUnitRoot(transformLength);
+			new FFTSetup(transformLength >> 1);
+			break;
+		}
+		case SPLITRADIX: {
+			reUnitRootDouble =
+				DFTSplitRadixDouble.getReUnitRoot(transformLength);
+			imUnitRootDouble =
+				DFTSplitRadixDouble.getImUnitRoot(transformLength);
+			reUnitRootFloat =
+				DFTSplitRadixFloat.getReUnitRoot(transformLength);
+			imUnitRootFloat =
+				DFTSplitRadixFloat.getImUnitRoot(transformLength);
+			new FFTSetup(transformLength >> 1);
+			new FFTSetup(transformLength >> 2);
+			break;
+		}
+	}
+} /* end FFTSetup */
+
+/*....................................................................
+	FFTSetup static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (costs.containsKey(new Integer(transformLength))) {
+		return(costs.get(new Integer(transformLength)).longValue());
+	}
+	final Vector<FFTCostPrediction> costPredictions =
+		new Vector<FFTCostPrediction>();
+	switch (transformLength) {
+		case 1: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH1, -1, 0L));
+			break;
+		}
+		case 2: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH2, -1, DFTLength2.cost()));
+			break;
+		}
+		case 3: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH3, -1, DFTLength3.cost()));
+			break;
+		}
+		case 4: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH4, -1, DFTLength4.cost()));
+			break;
+		}
+		case 5: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH5, -1, DFTLength5.cost()));
+			break;
+		}
+		case 6: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH6, -1, DFTLength6.cost()));
+			break;
+		}
+		case 8: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH8, -1, DFTLength8.cost()));
+			break;
+		}
+		default: {
+			final HashSet<Integer> divisors = divisors(transformLength);
+			if (divisors.isEmpty()) {
+				costPredictions.add(new FFTCostPrediction(
+					Algorithm.RADER, -1, DFTRader.cost(transformLength)));
+				while (futurePrime < transformLength) {
+					cost(futurePrime);
+				}
+				do {
+					futurePrime +=2;
+				} while (!isPrime(futurePrime));
+				int minPaddedLength = 2 * transformLength - 3;
+				int maxPaddedLength = 1;
+				int n = minPaddedLength;
+				while (0 < n) {
+					n >>= 1;
+					maxPaddedLength <<= 1;
+				}
+				for (n = transformLength; (n < minPaddedLength); n += 2) {
+					if (costs.containsKey(new Integer(n))) {
+						continue;
+					}
+					if (isPrime(n)) {
+						taboos.add(new Integer(n));
+						taboos.add(new Integer(2 * n));
+						taboos.add(new Integer(3 * n));
+					}
+				}
+				for (n = minPaddedLength; (n <= maxPaddedLength); n += 2) {
+					if (costs.containsKey(new Integer(n))) {
+						continue;
+					}
+					if (isPrime(n)) {
+						taboos.add(new Integer(n));
+						taboos.add(new Integer(2 * n));
+					}
+				}
+				for (n = minPaddedLength; (n <= maxPaddedLength); n++) {
+					costPredictions.add(new FFTCostPrediction(
+						Algorithm.PADDEDRADER, n,
+						DFTPaddedRader.cost(transformLength, n)));
+				}
+				taboos.clear();
+			}
+			else {
+				for (Integer d: divisors) {
+					final int K1 = d.intValue();
+					final int K2 = transformLength / K1;
+					costPredictions.add(new FFTCostPrediction(
+						Algorithm.MIXEDRADIX, K1,
+						DFTMixedRadix.cost(K1, K2)));
+					if (1 == greatestCommonDivisor(K1, K2)) {
+						costPredictions.add(new FFTCostPrediction(
+							Algorithm.COPRIMEFACTOR, K1,
+							DFTCoprimeFactor.cost(K1, K2)));
+					}
+					if (2 == K1) {
+						costPredictions.add(new FFTCostPrediction(
+							Algorithm.RADIX2, -1,
+							DFTRadix2.cost(transformLength)));
+					}
+					if (4 == K1) {
+						costPredictions.add(new FFTCostPrediction(
+							Algorithm.SPLITRADIX, -1,
+							DFTSplitRadix.cost(transformLength)));
+					}
+				}
+			}
+		}
+	}
+	FFTCostPrediction best = new FFTCostPrediction(
+		Algorithm.BRUTEFORCE, -1, DFTBruteForce.cost(transformLength));
+	long cheapest = best.cost;
+	for (FFTCostPrediction predicted: costPredictions) {
+		final long current = predicted.cost;
+		if (0L <= current) {
+			if (current < cheapest) {
+				cheapest = current;
+				best = predicted;
+			}
+		}
+	}
+	algorithms.put(new Integer(transformLength), best.algorithm);
+	lengths.put(new Integer(transformLength), new Integer(best.length));
+	costs.put(new Integer(transformLength), new Long(cheapest));
+	return(cheapest);
+} /* end cost */
+
+/*------------------------------------------------------------------*/
+static void reset (
+) {
+	composites.clear();
+	primes.clear();
+	algorithms.clear();
+	lengths.clear();
+	transforms.clear();
+	costs.clear();
+	taboos.clear();
+	initialize();
+} /* end reset */
+
+/*....................................................................
+	FFTSetup private methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+private static HashSet<Integer> divisors (
+	int number
+) {
+	final HashSet<Integer> divisors = new HashSet<Integer>();
+	for (int k = (int)floor(sqrt(0.5 + (double)number)); (2 <= k); k--) {
+		if (number == (k * (number / k))) {
+			divisors.add(new Integer(k));
+			divisors.add(new Integer(number / k));
+		}
+	}
+	return(divisors);
+} /* end divisors */
+
+/*------------------------------------------------------------------*/
+private static int greatestCommonDivisor (
+	int m,
+	int n
+) {
+	m = (m < 0) ? (-m) : (m);
+	n = (n < 0) ? (-n) : (n);
+	while (0 != n) {
+		final int p = n;
+		n = m - n * (m / n);
+		m = p;
+	}
+	return(m);
+} /* end greatestCommonDivisor */
+
+/*------------------------------------------------------------------*/
+static private void initialize (
+) {
+	primes.add(new Integer(2));
+	primes.add(new Integer(3));
+	primes.add(new Integer(5));
+	new FFTSetup(2);
+	new FFTSetup(3);
+	new FFTSetup(5);
+	futurePrime = 7;
+} /* end initialize */
+
+/*------------------------------------------------------------------*/
+static private boolean isPrime (
+	final int number
+) {
+	if (composites.contains(new Integer(number))) {
+		return(false);
+	}
+	if (primes.contains(new Integer(number))) {
+		return(true);
+	}
+	boolean isComposite = false;
+	for (int k = (int)floor(sqrt(0.5 + (double)number)); (1 < k); k--) {
+		if (number == (k * (number / k))) {
+			isComposite = true;
+			break;
+		}
+	}
+	if (isComposite) {
+		composites.add(new Integer(number));
+		return(false);
+	}
+	primes.add(new Integer(number));
+	return(true);
+} /* end isPrime */
+
+} /* end class FFTSetup */
+
+/*====================================================================
+|	FFTSetupDuoReal
+\===================================================================*/
+static class FFTSetupDuoReal
+	extends FFTSetupReal
+
+{ /* begin class FFTSetupDuoReal */
+
+/*....................................................................
+	FFTSetupDuoReal static protected variables
+....................................................................*/
+protected static final HashMap<Integer, Algorithm> algorithms =
+	new HashMap<Integer, Algorithm>();
+protected static final HashMap<Integer, Integer> lengths =
+	new HashMap<Integer, Integer>();
+protected static final HashMap<Integer, FFTSetupDuoReal> transforms =
+	new HashMap<Integer, FFTSetupDuoReal>();
+protected static final HashMap<Integer, Long> costs =
+	new HashMap<Integer, Long>();
+
+/*....................................................................
+	FFTSetupDuoReal protected variables
+....................................................................*/
+protected Algorithm algorithm;
+
+/*....................................................................
+	FFTSetupDuoReal static initialization block
+....................................................................*/
+static {
+	initialize();
+}
+
+/*....................................................................
+	FFTSetupDuoReal constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+FFTSetupDuoReal (
+	final int transformLength
+) {
+	super(transformLength);
+	if (transforms.containsKey(new Integer(transformLength))) {
+		return;
+	}
+	new FFTSetupReal(transformLength);
+	algorithm = cheapest(transformLength);
+	FFTSetupReal fft =
+		FFTSetupReal.transforms.get(new Integer(transformLength));
+	imConvolverDouble = fft.imConvolverDouble;
+	imUnitRootDouble = fft.imUnitRootDouble;
+	imUnitRootEvenDouble = fft.imUnitRootEvenDouble;
+	imUnitRootOddDouble = fft.imUnitRootOddDouble;
+	reConvolverDouble = fft.reConvolverDouble;
+	reUnitRootDouble = fft.reUnitRootDouble;
+	reUnitRootEvenDouble = fft.reUnitRootEvenDouble;
+	reUnitRootOddDouble = fft.reUnitRootOddDouble;
+	imConvolverFloat = fft.imConvolverFloat;
+	imUnitRootEvenFloat = fft.imUnitRootEvenFloat;
+	imUnitRootFloat = fft.imUnitRootFloat;
+	imUnitRootOddFloat = fft.imUnitRootOddFloat;
+	reConvolverFloat = fft.reConvolverFloat;
+	reUnitRootEvenFloat = fft.reUnitRootEvenFloat;
+	reUnitRootFloat = fft.reUnitRootFloat;
+	reUnitRootOddFloat = fft.reUnitRootOddFloat;
+	chinese = fft.chinese;
+	inverseModular = fft.inverseModular;
+	modular = fft.modular;
+	ruritanian = fft.ruritanian;
+	K1 = fft.K1;
+	transforms.put(new Integer(transformLength), this);
+} /* end FFTSetupDuoReal */
+
+/*....................................................................
+	FFTSetupDuoReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static Algorithm cheapest (
+	final int transformLength
+) {
+	if (transforms.containsKey(new Integer(transformLength))) {
+		return(transforms.get(new Integer(transformLength)).algorithm);
+	}
+	final Vector<FFTCostPrediction> costPredictions =
+		new Vector<FFTCostPrediction>();
+	costPredictions.add(new FFTCostPrediction(
+		FFTSetupReal.algorithms.get(new Integer(transformLength)),
+		FFTSetupReal.lengths.get(new Integer(transformLength)).intValue(),
+		2L * FFTSetupReal.costs.get(new Integer(transformLength)).longValue()));
+	FFTCostPrediction best = new FFTCostPrediction(
+		Algorithm.DUOREAL, -1, DFTDuoReal.cost(transformLength));
+	long cheapest = best.cost;
+	for (FFTCostPrediction predicted: costPredictions) {
+		final long current = predicted.cost;
+		if (0L <= current) {
+			if (current < cheapest) {
+				cheapest = current;
+				best = predicted;
+			}
+		}
+	}
+	algorithms.put(new Integer(transformLength), best.algorithm);
+	lengths.put(new Integer(transformLength), new Integer(best.length));
+	costs.put(new Integer(transformLength), new Long(cheapest));
+	return(best.algorithm);
+} /* end cheapest */
+
+/*------------------------------------------------------------------*/
+static void reset (
+) {
+	transforms.clear();
+	initialize();
+} /* end reset */
+
+/*------------------------------------------------------------------*/
+static private void initialize (
+) {
+	new FFTSetupDuoReal(2);
+	new FFTSetupDuoReal(3);
+	new FFTSetupDuoReal(5);
+} /* end initialize */
+
+} /* end class FFTSetupDuoReal */
+
+/*====================================================================
+|	FFTSetupReal
+\===================================================================*/
+static class FFTSetupReal
+	extends FFTSetup
+
+{ /* begin class FFTSetupReal */
+
+/*....................................................................
+	FFTSetupReal static protected variables
+....................................................................*/
+protected static final HashMap<Integer, Algorithm> algorithms =
+	new HashMap<Integer, Algorithm>();
+protected static final HashMap<Integer, Integer> lengths =
+	new HashMap<Integer, Integer>();
+protected static final HashMap<Integer, FFTSetupReal> transforms =
+	new HashMap<Integer, FFTSetupReal>();
+protected static final HashMap<Integer, Long> costs =
+	new HashMap<Integer, Long>();
+
+/*....................................................................
+	FFTSetupReal static private variables
+....................................................................*/
+private static int futurePrime = 7;
+
+/*....................................................................
+	FFTSetupReal protected variables
+....................................................................*/
+protected Algorithm algorithm;
+protected double[] imConvolverDouble;
+protected double[] imUnitRootDouble;
+protected double[] imUnitRootEvenDouble;
+protected double[] imUnitRootOddDouble;
+protected double[] reConvolverDouble;
+protected double[] reUnitRootDouble;
+protected double[] reUnitRootEvenDouble;
+protected double[] reUnitRootOddDouble;
+protected float[] imConvolverFloat;
+protected float[] imUnitRootEvenFloat;
+protected float[] imUnitRootFloat;
+protected float[] imUnitRootOddFloat;
+protected float[] reConvolverFloat;
+protected float[] reUnitRootEvenFloat;
+protected float[] reUnitRootFloat;
+protected float[] reUnitRootOddFloat;
+protected int[] chinese;
+protected int[] inverseModular;
+protected int[] modular;
+protected int[] ruritanian;
+protected int K1;
+
+/*....................................................................
+	FFTSetupReal static initialization block
+....................................................................*/
+static {
+	initialize();
+}
+
+/*....................................................................
+	FFTSetupReal constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+FFTSetupReal (
+	final int transformLength
+) {
+	super(transformLength);
+	if (transforms.containsKey(new Integer(transformLength))) {
+		return;
+	}
+	transforms.put(new Integer(transformLength), this);
+	cost(transformLength);
+	algorithm = algorithms.get(new Integer(transformLength));
+	switch (algorithm) {
+		case BRUTEFORCE: {
+			reUnitRootDouble =
+				DFTBruteForceDouble.getReUnitRoot(transformLength);
+			imUnitRootDouble =
+				DFTBruteForceDouble.getImUnitRoot(transformLength);
+			reUnitRootFloat =
+				DFTBruteForceFloat.getReUnitRoot(transformLength);
+			imUnitRootFloat =
+				DFTBruteForceFloat.getImUnitRoot(transformLength);
+			break;
+		}
+		case COPRIMEFACTOR: {
+			K1 = lengths.get(new Integer(transformLength)).intValue();
+			final int K2 = transformLength / K1;
+			ruritanian = DFTCoprimeFactor.getRuritanianShuffling(K1, K2);
+			chinese =
+				DFTCoprimeFactorReal.getTruncatedChineseRemainderShuffling(
+				K1, K2);
+			new FFTSetup(K1);
+			new FFTSetupReal(K2);
+			new FFTSetupDuoReal(K2);
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			reUnitRootDouble =
+				DFTEvenRealDouble.getReUnitRoot(transformLength);
+			imUnitRootDouble =
+				DFTEvenRealDouble.getImUnitRoot(transformLength);
+			reUnitRootFloat =
+				DFTEvenRealFloat.getReUnitRoot(transformLength);
+			imUnitRootFloat =
+				DFTEvenRealFloat.getImUnitRoot(transformLength);
+			new FFTSetup(transformLength >> 1);
+			break;
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			break;
+		}
+		case LENGTH3: {
+			break;
+		}
+		case LENGTH4: {
+			break;
+		}
+		case LENGTH5: {
+			break;
+		}
+		case LENGTH6: {
+			break;
+		}
+		case LENGTH8: {
+			break;
+		}
+		case MIXEDRADIX: {
+			K1 = lengths.get(new Integer(transformLength)).intValue();
+			final int K2 = transformLength / K1;
+			reUnitRootDouble =
+				DFTMixedRadixDouble.getReUnitRoot(transformLength);
+			imUnitRootDouble =
+				DFTMixedRadixDouble.getImUnitRoot(transformLength);
+			reUnitRootFloat =
+				DFTMixedRadixFloat.getReUnitRoot(transformLength);
+			imUnitRootFloat =
+				DFTMixedRadixFloat.getImUnitRoot(transformLength);
+			new FFTSetup(K1);
+			new FFTSetupReal(K2);
+			new FFTSetupDuoReal(K2);
+			break;
+		}
+		case PADDEDRADER: {
+			final int paddedLength =
+				lengths.get(new Integer(transformLength)).intValue();
+			modular = DFTRader.getModularPowerShuffling(transformLength);
+			inverseModular =
+				DFTPaddedRader.getInverseModularPowerShuffling(modular,
+				paddedLength);
+			new FFTSetup(paddedLength);
+			new FFTSetupReal(paddedLength);
+			final double[][] convolverD =
+				DFTPaddedRaderDouble.getConvolverReAndIm(modular,
+				paddedLength);
+			reConvolverDouble = convolverD[0];
+			imConvolverDouble = convolverD[1];
+			final float[][] convolverF =
+				DFTPaddedRaderFloat.getConvolverReAndIm(modular,
+				paddedLength);
+			reConvolverFloat = convolverF[0];
+			imConvolverFloat = convolverF[1];
+			break;
+		}
+		case RADER: {
+			modular = DFTRader.getModularPowerShuffling(transformLength);
+			inverseModular = DFTRader.getInverseModularPowerShuffling(modular);
+			new FFTSetup(transformLength - 1);
+			new FFTSetupReal(transformLength - 1);
+			final double[][] convolverD =
+				DFTRaderDouble.getConvolverReAndIm(modular);
+			reConvolverDouble = convolverD[0];
+			imConvolverDouble = convolverD[1];
+			final float[][] convolverF =
+				DFTRaderFloat.getConvolverReAndIm(modular);
+			reConvolverFloat = convolverF[0];
+			imConvolverFloat = convolverF[1];
+			break;
+		}
+		case RADIX2: {
+			reUnitRootEvenDouble =
+				DFTRadix2RealDouble.getReUnitRootEven(transformLength);
+			imUnitRootEvenDouble =
+				DFTRadix2RealDouble.getImUnitRootEven(transformLength);
+			reUnitRootOddDouble =
+				DFTRadix2RealDouble.getReUnitRootOdd(transformLength);
+			imUnitRootOddDouble =
+				DFTRadix2RealDouble.getImUnitRootOdd(transformLength);
+			reUnitRootEvenFloat =
+				DFTRadix2RealFloat.getReUnitRootEven(transformLength);
+			imUnitRootEvenFloat =
+				DFTRadix2RealFloat.getImUnitRootEven(transformLength);
+			reUnitRootOddFloat =
+				DFTRadix2RealFloat.getReUnitRootOdd(transformLength);
+			imUnitRootOddFloat =
+				DFTRadix2RealFloat.getImUnitRootOdd(transformLength);
+			new FFTSetupDuoReal(transformLength >> 1);
+			break;
+		}
+		case SPLITRADIX: {
+			reUnitRootDouble =
+				DFTSplitRadixDouble.getReUnitRoot(transformLength);
+			imUnitRootDouble =
+				DFTSplitRadixDouble.getImUnitRoot(transformLength);
+			reUnitRootFloat =
+				DFTSplitRadixFloat.getReUnitRoot(transformLength);
+			imUnitRootFloat =
+				DFTSplitRadixFloat.getImUnitRoot(transformLength);
+			new FFTSetupReal(transformLength >> 1);
+			new FFTSetupDuoReal(transformLength >> 2);
+			break;
+		}
+	}
+} /* end FFTSetupReal */
+
+/*....................................................................
+	FFTSetupReal static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+static long cost (
+	final int transformLength
+) {
+	if (costs.containsKey(new Integer(transformLength))) {
+		return(costs.get(new Integer(transformLength)).longValue());
+	}
+	final Vector<FFTCostPrediction> costPredictions =
+		new Vector<FFTCostPrediction>();
+	switch (transformLength) {
+		case 1: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH1, -1, 0L));
+			break;
+		}
+		case 2: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH2, -1, DFTLength2Real.cost()));
+			break;
+		}
+		case 3: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH3, -1, DFTLength3Real.cost()));
+			break;
+		}
+		case 4: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH4, -1, DFTLength4Real.cost()));
+			break;
+		}
+		case 5: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH5, -1, DFTLength5Real.cost()));
+			break;
+		}
+		case 6: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH6, -1, DFTLength6Real.cost()));
+			break;
+		}
+		case 8: {
+			costPredictions.add(new FFTCostPrediction(
+				Algorithm.LENGTH8, -1, DFTLength8Real.cost()));
+			break;
+		}
+		default: {
+			final HashSet<Integer> divisors =
+				FFTSetup.divisors(transformLength);
+			if (divisors.isEmpty()) {
+				costPredictions.add(new FFTCostPrediction(
+					Algorithm.RADER, -1, DFTRaderReal.cost(transformLength)));
+				while (futurePrime < transformLength) {
+					cost(futurePrime);
+				}
+				do {
+					futurePrime +=2;
+				} while (!FFTSetup.isPrime(futurePrime));
+				int minPaddedLength = 2 * transformLength - 3;
+				int maxPaddedLength = 1;
+				int n = minPaddedLength;
+				while (0 < n) {
+					n >>= 1;
+					maxPaddedLength <<= 1;
+				}
+				for (n = transformLength; (n < minPaddedLength); n += 2) {
+					if (costs.containsKey(new Integer(n))) {
+						continue;
+					}
+					if (FFTSetup.isPrime(n)) {
+						taboos.add(new Integer(n));
+						taboos.add(new Integer(2 * n));
+						taboos.add(new Integer(3 * n));
+					}
+				}
+				for (n = minPaddedLength; (n <= maxPaddedLength); n += 2) {
+					if (costs.containsKey(new Integer(n))) {
+						continue;
+					}
+					if (FFTSetup.isPrime(n)) {
+						taboos.add(new Integer(n));
+						taboos.add(new Integer(2 * n));
+					}
+				}
+				for (n = minPaddedLength; (n <= maxPaddedLength); n++) {
+					costPredictions.add(new FFTCostPrediction(
+						Algorithm.PADDEDRADER, n,
+						DFTPaddedRaderReal.cost(transformLength, n)));
+				}
+				taboos.clear();
+			}
+			else {
+				for (Integer d: divisors) {
+					final int K1 = d.intValue();
+					final int K2 = transformLength / K1;
+					costPredictions.add(new FFTCostPrediction(
+						Algorithm.MIXEDRADIX, K1,
+						DFTMixedRadixReal.cost(K1, K2)));
+					if (1 == FFTSetup.greatestCommonDivisor(K1, K2)) {
+						costPredictions.add(new FFTCostPrediction(
+							Algorithm.COPRIMEFACTOR, K1,
+							DFTCoprimeFactorReal.cost(K1, K2)));
+					}
+					if (2 == K1) {
+						costPredictions.add(new FFTCostPrediction(
+							Algorithm.EVENREAL, -1,
+							DFTEvenReal.cost(transformLength)));
+						costPredictions.add(new FFTCostPrediction(
+							Algorithm.RADIX2, -1,
+							DFTRadix2Real.cost(transformLength)));
+					}
+					if (4 == K1) {
+						costPredictions.add(new FFTCostPrediction(
+							Algorithm.SPLITRADIX, -1,
+							DFTSplitRadixReal.cost(transformLength)));
+					}
+				}
+			}
+		}
+	}
+	FFTCostPrediction best = new FFTCostPrediction(
+		Algorithm.BRUTEFORCE, -1, DFTBruteForceReal.cost(transformLength));
+	long cheapest = best.cost;
+	for (FFTCostPrediction predicted: costPredictions) {
+		final long current = predicted.cost;
+		if (0L <= current) {
+			if (current < cheapest) {
+				cheapest = current;
+				best = predicted;
+			}
+		}
+	}
+	algorithms.put(new Integer(transformLength), best.algorithm);
+	lengths.put(new Integer(transformLength), new Integer(best.length));
+	costs.put(new Integer(transformLength), new Long(cheapest));
+	return(cheapest);
+} /* end cost */
+
+/*------------------------------------------------------------------*/
+static void reset (
+) {
+	algorithms.clear();
+	lengths.clear();
+	transforms.clear();
+	costs.clear();
+	initialize();
+} /* end reset */
+
+/*------------------------------------------------------------------*/
+static private void initialize (
+) {
+	new FFTSetupReal(2);
+	new FFTSetupReal(3);
+	new FFTSetupReal(5);
+	futurePrime = 7;
+} /* end initialize */
+
+} /* end class FFTSetupReal */
+
+/*....................................................................
+	SlowFourierTransform constructors
+....................................................................*/
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This constructor prepares a one-dimensional Fourier transform. It
+ depends on the length of the sequence but not on the actual data
+ being transformed, so that it can be reused for different data.
+ It specifies the convention to adopt for locating the origin of the
+ Fourier transform.
+ </p>
+ @param width The length of the sequence.
+ @param fourierOrigin1 The origin of the Fourier transform in the
+ Fourier domain.
+ ********************************************************************/
+public AcademicFourierTransform (
+	final int width,
+	final int fourierOrigin1
+) {
+	if (width <= 0) {
+		throw(new IllegalArgumentException());
+	}
+	this.width = new Integer(width);
+	this.height = new Integer(1);
+	this.depth = new Integer(1);
+	this.fourierOrigin1 = fourierOrigin1;
+	this.fourierOrigin2 = 0;
+	this.fourierOrigin3 = 0;
+	dimensions = 1;
+	dataLength = width;
+	new FFTSetup(width);
+	new FFTSetupReal(width);
+	new FFTSetupDuoReal(width);
+	firstDimension = 1;
+} /* end SlowFourierTransform */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This constructor prepares a two-dimensional Fourier transform. It
+ depends on the width and height of the image but not on the actual
+ data being transformed, so that it can be reused for different data.
+ It specifies the convention to adopt for locating the origin of the
+ Fourier transform.
+ </p>
+ @param width The width of the image.
+ @param height The height of the image.
+ @param fourierOrigin1 The horizontal component of the origin of the
+ Fourier transform in the Fourier domain.
+ @param fourierOrigin2 The vertical component of the origin of the
+ Fourier transform in the Fourier domain.
+ ********************************************************************/
+public AcademicFourierTransform (
+	final int width,
+	final int height,
+	final int fourierOrigin1,
+	final int fourierOrigin2
+) {
+	if ((width <= 0) || (height <= 0)) {
+		throw(new IllegalArgumentException());
+	}
+	this.width = new Integer(width);
+	this.height = new Integer(height);
+	this.depth = new Integer(1);
+	this.fourierOrigin1 = fourierOrigin1;
+	this.fourierOrigin2 = fourierOrigin2;
+	this.fourierOrigin3 = 0;
+	dimensions = 2;
+	dataLength = width * height;
+	new FFTSetup(width);
+	new FFTSetupReal(width);
+	new FFTSetupDuoReal(width);
+	new FFTSetup(height);
+	new FFTSetupReal(height);
+	new FFTSetupDuoReal(height);
+	final long K1 = (long)width;
+	final long K2 = (long)height;
+	final long k1 = K1 >> 1L;
+	final long k2 = K2 >> 1L;
+	final long costColumnFirst = FFTSetup.FLALLOC * 0L
+		+ FFTSetup.FLOP * (k2 * 1L + k2 * ((K1 - 1L) * 1L))
+		+ FFTSetup.FLASSIGN * (k2 * 2L + k2 * ((K1 - 1L) * 2L))
+		+ FFTSetup.INTALLOC * 10L
+		+ FFTSetup.INTOP * (5L + K1 * 2L + 1L + (k2 + 1L) * 3L + 2L
+			+ k2 * 3L + 3L + k2 * (4L + (K1 - 1L) * 4L))
+		+ FFTSetup.INTASSIGN * (4L + K1 * 1L + 2L + (k2 + 1L) * 2L + 2L
+			+ k2 * 2L + 3L + k2 * (6L + (K1 - 1L) * 3L))
+		+ FFTSetup.IDX * (k2 * 4L + k2 * ((K1 - 1L) * 4L))
+		+ FFTSetup.NEWOBJ * (K1 + (k2 + 1L))
+		+ k1 * FFTSetupDuoReal.cost(height)
+		+ (k2 + 1L) * FFTSetup.cost(width);
+	final long costRowFirst = FFTSetup.FLALLOC * 0L
+		+ FFTSetup.FLOP * (k1 * 1L + (K2 - 1L) * (k1 * 1L))
+		+ FFTSetup.FLASSIGN * (k1 * 2L + (K2 - 1L) * (k1 * 2L))
+		+ FFTSetup.INTALLOC * 9L
+		+ FFTSetup.INTOP * (5L + K2 * 3L + 1L + (k1 + 1L) * 2L
+			+ k1 * 3L + 3L + (K2 - 1L) * (4L + k1 * 4L))
+		+ FFTSetup.INTASSIGN * (5L + K2 * 2L + 1L + (k1 + 1L) * 1L + 2L
+			+ k1 * 2L + 3L + (K2 - 1L) * (6L + k1 * 3L))
+		+ FFTSetup.IDX * (k1 * 4L + (K2 - 1L) * (k1 * 4L))
+		+ FFTSetup.NEWOBJ * (K2 + (k1 + 1L))
+		+ k2 * FFTSetupDuoReal.cost(width)
+		+ (k1 + 1L) * FFTSetup.cost(height);
+	firstDimension = (costRowFirst < costColumnFirst) ? (1) : (2);
+} /* end SlowFourierTransform */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This constructor prepares a three-dimensional Fourier transform. It
+ depends on the width, height, and depth of the volume but not on the
+ actual data being transformed, so that it can be reused for different
+ data. It specifies the convention to adopt for locating the origin of
+ the Fourier transform.
+ </p>
+ @param width The width of the volume.
+ @param height The height of the volume.
+ @param depth The depth of the volume.
+ @param fourierOrigin1 The horizontal component of the origin of the
+ Fourier transform in the Fourier domain.
+ @param fourierOrigin2 The vertical component of the origin of the
+ Fourier transform in the Fourier domain.
+ @param fourierOrigin3 The depth component of the origin of the
+ Fourier transform in the Fourier domain.
+ ********************************************************************/
+public AcademicFourierTransform (
+	final int width,
+	final int height,
+	final int depth,
+	final int fourierOrigin1,
+	final int fourierOrigin2,
+	final int fourierOrigin3
+) {
+	if ((width <= 0) || (height <= 0) || (depth <= 0)) {
+		throw(new IllegalArgumentException());
+	}
+	this.width = new Integer(width);
+	this.height = new Integer(height);
+	this.depth = new Integer(depth);
+	this.fourierOrigin1 = fourierOrigin1;
+	this.fourierOrigin2 = fourierOrigin2;
+	this.fourierOrigin3 = fourierOrigin3;
+	dimensions = 3;
+	dataLength = width * height* depth;
+	new FFTSetup(width);
+	new FFTSetupReal(width);
+	new FFTSetupDuoReal(width);
+	new FFTSetup(height);
+	new FFTSetupReal(height);
+	new FFTSetupDuoReal(height);
+	new FFTSetup(depth);
+	new FFTSetupReal(depth);
+	new FFTSetupDuoReal(depth);
+	final long K1 = (long)width;
+	final long K2 = (long)height;
+	final long K3 = (long)depth;
+	final long k1 = K1 >> 1L;
+	final long k2 = K2 >> 1L;
+	final long k3 = K3 >> 1L;
+	final long costAcrossFirst = FFTSetup.FLALLOC * 0L
+		+ FFTSetup.FLOP * (k3 * 1L + k3  * ((K2 - 1L) * 1L)
+			+ k3 * ((K1 - 1L) * 1L) + k3 * ((K2 - 1L) * ((K1 - 1L) * 1L)))
+		+ FFTSetup.FLASSIGN * (k3 * 2L + k3  * ((K2 - 1L) * 2L)
+			+ k3 * ((K1 - 1L) * 2L) + k3 * ((K2 - 1L) * ((K1 - 1L) * 2L)))
+		+ FFTSetup.INTALLOC * 23L
+		+ FFTSetup.INTOP * (6L + K2 * K1 * 2L + 1L + (k3 + 1L) * (2L + K2 * 3L)
+			+ 1L + (k3 + 1L) * (3L + K1 * 3L) + 1L + k3 * 3L + 2L
+			+ k3 * (4L + (K2 - 1L) * 4L) + 5L + k3 * (4L + (K1 - 1L) * 4L) + 3L
+			+ k3 * (4L + (K2 - 1L) * (4L + (K1 - 1L) * 4L)))
+		+ FFTSetup.INTASSIGN * (6L + K2 * K1 * 1L + 2L
+			+ (k3 + 1L) * (2L + K2 * 2L) + 2L + (k3 + 1L) * (3L + K1 * 1L) + 2L
+			+ k3 * 2L + 3L + k3 * (6L + (K2 - 1L) * 3L) + 3L
+			+ k3 * (6L + (K1 - 1L) * 3L) + 3L
+			+ k3 * (4L + (K2 - 1L) * (4L + (K1 - 1L) * 3L)))
+		+ FFTSetup.IDX * (k3 * 4L + k3 * ((K2 - 1L) * 4L)
+			+ k3 * ((K1 - 1L) * 4L) + k3 * ((K2 - 1L) * ((K1 - 1L) * 4L)))
+		+ FFTSetup.NEWOBJ * (K2 * K1 + (k3 + 1L) * K2+ (k3 + 1L) * K1)
+		+ ((K2 * K1) >> 1) * FFTSetupDuoReal.cost(depth)
+		+ (k3 + 1L) * K2 * FFTSetup.cost(width)
+		+ (k3 + 1L) * K1 * FFTSetup.cost(height);
+	final long costColumnFirst = FFTSetup.FLALLOC * 0L
+		+ FFTSetup.FLOP * (k2 * 1L + k2  * ((K2 - 1L) * 1L)
+			+ (K3 - 1L) * (k2 * 1L) + (K3 - 1L) * (k2 * ((K1 - 1L) * 1L)))
+		+ FFTSetup.FLASSIGN * (k2 * 2L + k2 * ((K1 - 1L) * 2L)
+			+ (K3 - 1L) * (k2 * 2L) + (K3 - 1L) * (k2 * ((K1 - 1L) * 2L)))
+		+ FFTSetup.INTALLOC * 26L
+		+ FFTSetup.INTOP * (8L + K3 * K1 * 5L + 3L + K3 * (3L + (k2 + 1L) * 3L)
+			+ 1L + (k2 + 1L) * (2L + K1 * 3L) + 1L + k2 * 3L + 2L
+			+ k2 * (4L + (K1 - 1L) * 4L) + 3L + (K3 - 1L) * (4L + k2 * 4L) + 3L
+			+ (K3 - 1L) * (6L + k2 * (4L + (K1 - 1L) * 4L)))
+		+ FFTSetup.INTASSIGN * (8L + K3 * K1 * 1L + 3L
+			+ K3 * (3L + (k2 + 1L) * 2L) + 2L + (k2 + 1L) * (2L + K1 * 2L) + 2L
+			+ k2 * 2L + 3L + k2 * (6L + (K1 - 1L) * 3L) + 3L
+			+ (K3 - 1L) * (6L + k2 * 3L) + 3L
+			+ (K3 - 1L) * (4L + k2 * (4L + (K1 - 1L) * 3L)))
+		+ FFTSetup.IDX * (k2 * 4L + k2 * ((K1 - 1L) * 4L)
+			+ (K3 - 1L) * (k2 * 4L) + (K3 - 1L) * (k2 * ((K1 - 1L) * 4L)))
+		+ FFTSetup.NEWOBJ * (K3 * K1 + K3 * (k2 + 1L) + (k2 + 1L) * K1)
+		+ ((K3 * K1) >> 1) * FFTSetupDuoReal.cost(height)
+		+ K3 * (k2 + 1L) * FFTSetup.cost(width)
+		+ (k2 + 1L) * K1 * FFTSetup.cost(depth);
+	final long costRowFirst = FFTSetup.FLALLOC * 0L
+		+ FFTSetup.FLOP * (k1 * 1L + (K2 - 1L) * (k1 * 1L)
+			+ (K3 - 1L) * (k1 * 1L) + (K3 - 1L) * ((K2 - 1L) * (k1 * 1L)))
+		+ FFTSetup.FLASSIGN * (k1 * 2L + (K2 - 1L) * (k1 * 2L)
+			+ (K3 - 1L) * (k1 * 2L) + (K3 - 1L) * ((K2 - 1L) * (k1 * 2L)))
+		+ FFTSetup.INTALLOC * 22L
+		+ FFTSetup.INTOP * (7L + K3 * K2 * 3L + 1L + K3 * (3L + (k1 + 1L) * 3L)
+			+ 1L + K2 * (3L + (k1 + 1L) * 3L) + k1 * 3L + 2L
+			+ (K2 - 1L) * (4L + k1 * 4L) + 5L + (K3 - 1L) * (4L + k1 * 4L) + 3L
+			+ (K3 - 1L) * (4L + (K2 - 1L) * (4L + k1 * 4L)))
+		+ FFTSetup.INTASSIGN * (8L + K3 * K2 * 2L + 2L
+			+ K3 * (3L + (k1 + 1L) * 1L) + 2L + K2 * (3L + (k1 + 1L) * 1L) + 2L
+			+ k1 * 2L + 3L + (K2 - 1L) * (6L + k1 * 3L) + 3L
+			+ (K3 - 1L) * (6L + k1 * 3L) + 3L
+			+ (K3 - 1L) * (4L + (K2 - 1L) * (4L + k1 * 3L)))
+		+ FFTSetup.IDX * (k1 * 4L + (K2 - 1L) * (k1 * 4L)
+			+ (K3 - 1L) * (k1 * 4L) + (K3 - 1L) * ((K2 - 1L) * (k1 * 4L)))
+		+ FFTSetup.NEWOBJ * (K3 * K2 + K3 * (k1 + 1L) + K2 * (k1 + 1L))
+		+ ((K3 * K2) >> 1) * FFTSetupDuoReal.cost(width)
+		+ K3 * (k1 + 1L) * FFTSetup.cost(height)
+		+ K2 * (k1 + 1L) * FFTSetup.cost(depth);
+	firstDimension = (costRowFirst < costColumnFirst)
+		? ((costRowFirst < costAcrossFirst) ? (1) : (3))
+		: ((costColumnFirst < costAcrossFirst) ? (2) : (3));
+} /* end SlowFourierTransform */
+
+/*....................................................................
+	SlowFourierTransform static methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method converts complex data from an amplitude-phase
+ representation to a real-imaginary representation. The phase is
+ assumed to be provided in radian units.
+ </p>
+ @param amToRe At input, the amplitude of the data; at output, the
+ real part of the data. The processing is in-place.
+ @param phToIm At input, the phase of the data; at output, the
+ imaginary part of the data. The processing is in-place.
+ ********************************************************************/
+static public void amplitudePhaseToRealImaginary (
+	final double[] amToRe,
+	final double[] phToIm
+) {
+	if (amToRe.length != phToIm.length) {
+		throw(new IllegalArgumentException());
+	}
+	for (int k = 0, K = amToRe.length; (k < K); k++) {
+		final double am = amToRe[k];
+		final double ph = phToIm[k];
+		amToRe[k] = am * cos(ph);
+		phToIm[k] = am * sin(ph);
+	}
+} /* end amplitudePhaseToRealImaginary */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method converts complex data from an amplitude-phase
+ representation to a real-imaginary representation. The phase is
+ assumed to be provided in radian units.
+ </p>
+ @param amToRe At input, the amplitude of the data; at output, the
+ real part of the data. The processing is in-place.
+ @param phToIm At input, the phase of the data; at output, the
+ imaginary part of the data. The processing is in-place.
+ ********************************************************************/
+static public void amplitudePhaseToRealImaginary (
+	final float[] amToRe,
+	final float[] phToIm
+) {
+	if (amToRe.length != phToIm.length) {
+		throw(new IllegalArgumentException());
+	}
+	for (int k = 0, K = amToRe.length; (k < K); k++) {
+		final float am = amToRe[k];
+		final float ph = phToIm[k];
+		amToRe[k] = am * (float)cos((double)ph);
+		phToIm[k] = am * (float)sin((double)ph);
+	}
+} /* end amplitudePhaseToRealImaginary */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method converts complex data from a real-imaginary
+ representation to an amplitude-phase representation. The resulting
+ phase is provided in radian units in the range [&#8722;&#960;,
+ &#960;].
+ </p>
+ @param reToAm At input, the real part of the data; at output, the
+ amplitude of the data. The processing is in-place.
+ @param imToPh At input, the imaginary part of the data; at output,
+ the phase of the data. The processing is in-place.
+ ********************************************************************/
+static public void realImaginaryToAmplitudePhase (
+	final double[] reToAm,
+	final double[] imToPh
+) {
+	if (reToAm.length != imToPh.length) {
+		throw(new IllegalArgumentException());
+	}
+	for (int k = 0, K = reToAm.length; (k < K); k++) {
+		final double am = sqrt(reToAm[k] * reToAm[k] + imToPh[k] * imToPh[k]);
+		final double ph = atan2(imToPh[k], reToAm[k]);
+		reToAm[k] = am;
+		imToPh[k] = ph;
+	}
+} /* end realImaginaryToAmplitudePhase */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method converts complex data from a real-imaginary
+ representation to an amplitude-phase representation. The resulting
+ phase is provided in radian units in the range [&#8722;&#960;,
+ &#960;].
+ </p>
+ @param reToAm At input, the real part of the data; at output, the
+ amplitude of the data. The processing is in-place.
+ @param imToPh At input, the imaginary part of the data; at output,
+ the phase of the data. The processing is in-place.
+ ********************************************************************/
+static public void realImaginaryToAmplitudePhase (
+	final float[] reToAm,
+	final float[] imToPh
+) {
+	if (reToAm.length != imToPh.length) {
+		throw(new IllegalArgumentException());
+	}
+	for (int k = 0, K = reToAm.length; (k < K); k++) {
+		final float am = (float)sqrt((double)(reToAm[k] * reToAm[k]
+			+ imToPh[k] * imToPh[k]));
+		final float ph = (float)atan2((double)imToPh[k], (double)reToAm[k]);
+		reToAm[k] = am;
+		imToPh[k] = ph;
+	}
+} /* end realImaginaryToAmplitudePhase */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ Several <code>static</code> auxiliary data are stored by this class,
+ as needs arise, to be reused by subsequent instances of this class;
+ this method frees the associated memory.
+ </p>
+ ********************************************************************/
+static public void reset (
+) {
+	FFTSetup.reset();
+	FFTSetupReal.reset();
+	FFTSetupDuoReal.reset();
+} /* end reset */
+
+/*....................................................................
+	SlowFourierTransform public methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method computes the circular convolution of data provided in a
+ real-imaginary representation. The number of dimensions of the data
+ is determined by the constructor of this object. Likewise, the
+ dimensions themselves must match those provided to the constructor of
+ this object.
+ </p>
+ @param reData1 At input, the real part of the first operand; at
+ output, the real part of the convolution result. The processing is
+ in-place.
+ @param imData1 At input, the imaginary part of the first operand; at
+ output, the imaginary part of the convolution result. The processing
+ is in-place.
+ @param reData2 At input, the real part of the second operand; at
+ output, the real part of the Fourier transform of the second operand.
+ The origin of the Fourier transform of the second operand follows the
+ conventions for this object. The processing is in-place.
+ @param imData2 At input, the imaginary part of the second operand; at
+ output, the imaginary part of the Fourier transform of the second
+ operand. The origin of the Fourier transform of the second operand
+ follows the conventions for this object. The processing is in-place.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData1.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData1.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ ********************************************************************/
+public void circularConvolution (
+	final double[] reData1,
+	final double[] imData1,
+	final double[] reData2,
+	final double[] imData2,
+	double[] reBuffer,
+	double[] imBuffer
+) {
+	if (null == reBuffer) {
+		reBuffer = new double[reData1.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new double[imData1.length];
+	}
+	if ((reData1.length != dataLength)
+		|| (imData1.length != dataLength)
+		|| (reData2.length != dataLength)
+		|| (imData2.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reBufferDouble = reBuffer;
+	this.imBufferDouble = imBuffer;
+	this.reDataDouble = reData2;
+	this.imDataDouble = imData2;
+	transformDouble(InputDataType.COMPLEXINPUT);
+	this.reDataDouble = reData1;
+	this.imDataDouble = imData1;
+	transformDouble(InputDataType.COMPLEXINPUT);
+	final double norm = 1.0 / (double)dataLength;
+	for (int k = 0; (k < dataLength); k++) {
+		final double re1 = norm * reData1[k];
+		final double im1 = norm * imData1[k];
+		reData1[k] = re1 * reData2[k] - im1 * imData2[k];
+		imData1[k] = re1 * imData2[k] + im1 * reData2[k];
+	}
+	reverseDouble();
+	transformDouble(InputDataType.COMPLEXINPUT);
+	this.reDataDouble = reData2;
+	this.imDataDouble = imData2;
+	this.reBufferDouble = reBuffer;
+	this.imBufferDouble = imBuffer;
+	switch (dimensions) {
+		case 1: {
+			shiftDouble(fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftDouble(fourierOrigin1, fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftDouble(fourierOrigin1, fourierOrigin2, fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+} /* end circularConvolution */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method computes the circular convolution of data provided in a
+ real-imaginary representation. The number of dimensions of the data
+ is determined by the constructor of this object. Likewise, the
+ dimensions themselves must match those provided to the constructor of
+ this object.
+ </p>
+ @param reData1 At input, the real part of the first operand; at
+ output, the real part of the convolution result. The processing is
+ in-place.
+ @param imData1 At input, the imaginary part of the first operand; at
+ output, the imaginary part of the convolution result. The processing
+ is in-place.
+ @param reData2 At input, the real part of the second operand; at
+ output, the real part of the Fourier transform of the second operand.
+ The origin of the Fourier transform of the second operand follows the
+ conventions for this object. The processing is in-place.
+ @param imData2 At input, the imaginary part of the second operand; at
+ output, the imaginary part of the Fourier transform of the second
+ operand. The origin of the Fourier transform of the second operand
+ follows the conventions for this object. The processing is in-place.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData1.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData1.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ ********************************************************************/
+public void circularConvolution (
+	final float[] reData1,
+	final float[] imData1,
+	final float[] reData2,
+	final float[] imData2,
+	float[] reBuffer,
+	float[] imBuffer
+) {
+	if (null == reBuffer) {
+		reBuffer = new float[reData1.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new float[imData1.length];
+	}
+	if ((reData1.length != dataLength)
+		|| (imData1.length != dataLength)
+		|| (reData2.length != dataLength)
+		|| (imData2.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reBufferFloat = reBuffer;
+	this.imBufferFloat = imBuffer;
+	this.reDataFloat = reData2;
+	this.imDataFloat = imData2;
+	transformFloat(InputDataType.COMPLEXINPUT);
+	this.reDataFloat = reData1;
+	this.imDataFloat = imData1;
+	transformFloat(InputDataType.COMPLEXINPUT);
+	final float norm = 1.0F / (float)dataLength;
+	for (int k = 0; (k < dataLength); k++) {
+		final float re1 = norm * reData1[k];
+		final float im1 = norm * imData1[k];
+		reData1[k] = re1 * reData2[k] - im1 * imData2[k];
+		imData1[k] = re1 * imData2[k] + im1 * reData2[k];
+	}
+	reverseFloat();
+	transformFloat(InputDataType.COMPLEXINPUT);
+	this.reDataFloat = reData2;
+	this.imDataFloat = imData2;
+	this.reBufferFloat = reBuffer;
+	this.imBufferFloat = imBuffer;
+	switch (dimensions) {
+		case 1: {
+			shiftFloat(fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftFloat(fourierOrigin1, fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftFloat(fourierOrigin1, fourierOrigin2, fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+} /* end circularConvolution */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method computes the circular convolution of data; the first
+ operand is provided in a real-imaginary representation while the
+ second operand is provided in a real-imaginary Fourier
+ representation. The number of dimensions of the data is determined by
+ the constructor of this object. Likewise, the dimensions themselves
+ must match those provided to the constructor of this object.
+ </p>
+ @param reData1 At input, the real part of the first operand; at
+ output, the real part of the convolution result. The processing is
+ in-place.
+ @param imData1 At input, the imaginary part of the first operand; at
+ output, the imaginary part of the convolution result. The processing
+ is in-place.
+ @param reFourierData2 Real part of the Fourier transform of the
+ second operand. The origin of the Fourier transform of the second
+ operand follows the conventions for this object.
+ @param imFourierData2 Imaginary part of the Fourier transform of the
+ second operand. The origin of the Fourier transform of the second
+ operand follows the conventions for this object.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData1.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData1.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ ********************************************************************/
+public void circularFourierConvolution (
+	final double[] reData1,
+	final double[] imData1,
+	final double[] reFourierData2,
+	final double[] imFourierData2,
+	double[] reBuffer,
+	double[] imBuffer
+) {
+	if (null == reBuffer) {
+		reBuffer = new double[reData1.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new double[imData1.length];
+	}
+	if ((reData1.length != dataLength)
+		|| (imData1.length != dataLength)
+		|| (reFourierData2.length != dataLength)
+		|| (imFourierData2.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reBufferDouble = reBuffer;
+	this.imBufferDouble = imBuffer;
+	this.reDataDouble = reData1;
+	this.imDataDouble = imData1;
+	transformDouble(InputDataType.COMPLEXINPUT);
+	switch (dimensions) {
+		case 1: {
+			shiftDouble(fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftDouble(fourierOrigin1, fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftDouble(fourierOrigin1, fourierOrigin2, fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+	final double norm = 1.0 / (double)dataLength;
+	for (int k = 0; (k < dataLength); k++) {
+		final double re1 = norm * reData1[k];
+		final double im1 = norm * imData1[k];
+		reData1[k] = re1 * reFourierData2[k] - im1 * imFourierData2[k];
+		imData1[k] = re1 * imFourierData2[k] + im1 * reFourierData2[k];
+	}
+	switch (dimensions) {
+		case 1: {
+			shiftDouble(-fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftDouble(-fourierOrigin1, -fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftDouble(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+	reverseDouble();
+	transformDouble(InputDataType.COMPLEXINPUT);
+} /* circularFourierConvolution */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method computes the circular convolution of data; the first
+ operand is provided in a real-imaginary representation while the
+ second operand is provided in a real-imaginary Fourier
+ representation. The number of dimensions of the data is determined by
+ the constructor of this object. Likewise, the dimensions themselves
+ must match those provided to the constructor of this object.
+ </p>
+ @param reData1 At input, the real part of the first operand; at
+ output, the real part of the convolution result. The processing is
+ in-place.
+ @param imData1 At input, the imaginary part of the first operand; at
+ output, the imaginary part of the convolution result. The processing
+ is in-place.
+ @param reFourierData2 Real part of the Fourier transform of the
+ second operand. The origin of the Fourier transform of the second
+ operand follows the conventions for this object.
+ @param imFourierData2 Imaginary part of the Fourier transform of the
+ second operand. The origin of the Fourier transform of the second
+ operand follows the conventions for this object.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData1.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData1.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ ********************************************************************/
+public void circularFourierConvolution (
+	final float[] reData1,
+	final float[] imData1,
+	final float[] reFourierData2,
+	final float[] imFourierData2,
+	float[] reBuffer,
+	float[] imBuffer
+) {
+	if (null == reBuffer) {
+		reBuffer = new float[reData1.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new float[imData1.length];
+	}
+	if ((reData1.length != dataLength)
+		|| (imData1.length != dataLength)
+		|| (reFourierData2.length != dataLength)
+		|| (imFourierData2.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reBufferFloat = reBuffer;
+	this.imBufferFloat = imBuffer;
+	this.reDataFloat = reData1;
+	this.imDataFloat = imData1;
+	transformFloat(InputDataType.COMPLEXINPUT);
+	switch (dimensions) {
+		case 1: {
+			shiftFloat(fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftFloat(fourierOrigin1, fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftFloat(fourierOrigin1, fourierOrigin2, fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+	final float norm = 1.0F / (float)dataLength;
+	for (int k = 0; (k < dataLength); k++) {
+		final float re1 = norm * reData1[k];
+		final float im1 = norm * imData1[k];
+		reData1[k] = re1 * reFourierData2[k] - im1 * imFourierData2[k];
+		imData1[k] = re1 * imFourierData2[k] + im1 * reFourierData2[k];
+	}
+	switch (dimensions) {
+		case 1: {
+			shiftFloat(-fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftFloat(-fourierOrigin1, -fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftFloat(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+	reverseFloat();
+	transformFloat(InputDataType.COMPLEXINPUT);
+} /* circularFourierConvolution */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method computes the Fourier transform of data provided in a
+ real-imaginary representation. The number of dimensions of the data
+ is determined by the constructor of this object. Likewise, the
+ dimensions themselves must match those provided to the constructor of
+ this object.
+ </p>
+ @param reData At input, the real part of the data; at output, the
+ real part of the Fourier transform of the data. The processing is
+ in-place.
+ @param imData At input, the imaginary part of the data; at output,
+ the imaginary part of the Fourier transform of the data. The
+ processing is in-place.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ @param inputDataType When set to
+ <code>SlowFourierTransform.InputDataType.REALINPUT</code>, disregards
+ the values provided in <code>imData</code> and assumes that every
+ element of <code>imData</code> is <code>0.0</code>; when set to
+ <code>SlowFourierTransform.InputDataType.COMPLEXINPUT</code>, honors
+ the values provided in <code>imData</code>.
+ ********************************************************************/
+public void directTransform (
+	final double[] reData,
+	final double[] imData,
+	double[] reBuffer,
+	double[] imBuffer,
+	final InputDataType inputDataType
+) {
+	if (null == reBuffer) {
+		reBuffer = new double[reData.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new double[imData.length];
+	}
+	if ((reData.length != dataLength)
+		|| (imData.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reDataDouble = reData;
+	this.imDataDouble = imData;
+	this.reBufferDouble = reBuffer;
+	this.imBufferDouble = imBuffer;
+	transformDouble(inputDataType);
+	switch (dimensions) {
+		case 1: {
+			shiftDouble(fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftDouble(fourierOrigin1, fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftDouble(fourierOrigin1, fourierOrigin2, fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+} /* end directTransform */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method computes the Fourier transform of data provided in a
+ real-imaginary representation. The number of dimensions of the data
+ is determined by the constructor of this object. Likewise, the
+ dimensions themselves must match those provided to the constructor of
+ this object.
+ </p>
+ @param reData At input, the real part of the data; at output, the
+ real part of the Fourier transform of the data. The processing is
+ in-place.
+ @param imData At input, the imaginary part of the data; at output,
+ the imaginary part of the Fourier transform of the data. The
+ processing is in-place.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ @param inputDataType When set to
+ <code>SlowFourierTransform.InputDataType.REALINPUT</code>, disregards
+ the values provided in <code>imData</code> and assumes that every
+ element of <code>imData</code> is <code>0.0</code>; when set to
+ <code>SlowFourierTransform.InputDataType.COMPLEXINPUT</code>, honors
+ the values provided in <code>imData</code>.
+ ********************************************************************/
+public void directTransform (
+	final float[] reData,
+	final float[] imData,
+	float[] reBuffer,
+	float[] imBuffer,
+	final InputDataType inputDataType
+) {
+	if (null == reBuffer) {
+		reBuffer = new float[reData.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new float[imData.length];
+	}
+	if ((reData.length != dataLength)
+		|| (imData.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reDataFloat = reData;
+	this.imDataFloat = imData;
+	this.reBufferFloat = reBuffer;
+	this.imBufferFloat = imBuffer;
+	transformFloat(inputDataType);
+	switch (dimensions) {
+		case 1: {
+			shiftFloat(fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftFloat(fourierOrigin1, fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftFloat(fourierOrigin1, fourierOrigin2, fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+} /* end directTransform */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method computes the inverse Fourier transform of data provided
+ in a real-imaginary representation. The number of dimensions of the
+ data is determined by the constructor of this object. Likewise, the
+ dimensions themselves must match those provided to the constructor of
+ this object.
+ </p>
+ @param reData At input, the real part of the data; at output, the
+ real part of the Fourier transform of the data. The processing is
+ in-place.
+ @param imData At input, the imaginary part of the data; at output,
+ the imaginary part of the Fourier transform of the data. The
+ processing is in-place.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ ********************************************************************/
+public void inverseTransform (
+	final double[] reData,
+	final double[] imData,
+	double[] reBuffer,
+	double[] imBuffer
+) {
+	if (null == reBuffer) {
+		reBuffer = new double[reData.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new double[imData.length];
+	}
+	if ((reData.length != dataLength)
+		|| (imData.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reDataDouble = reData;
+	this.imDataDouble = imData;
+	this.reBufferDouble = reBuffer;
+	this.imBufferDouble = imBuffer;
+	final double norm = 1.0 / (double)dataLength;
+	for (int k = 0; (k < dataLength); k++) {
+		reData[k] *= norm;
+		imData[k] *= norm;
+	}
+	switch (dimensions) {
+		case 1: {
+			shiftDouble(-fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftDouble(-fourierOrigin1, -fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftDouble(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+	reverseDouble();
+	transformDouble(InputDataType.COMPLEXINPUT);
+} /* end inverseTransform */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method computes the inverse Fourier transform of data provided
+ in a real-imaginary representation. The number of dimensions of the
+ data is determined by the constructor of this object. Likewise, the
+ dimensions themselves must match those provided to the constructor of
+ this object.
+ </p>
+ @param reData At input, the real part of the data; at output, the
+ real part of the Fourier transform of the data. The processing is
+ in-place.
+ @param imData At input, the imaginary part of the data; at output,
+ the imaginary part of the Fourier transform of the data. The
+ processing is in-place.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ ********************************************************************/
+public void inverseTransform (
+	final float[] reData,
+	final float[] imData,
+	float[] reBuffer,
+	float[] imBuffer
+) {
+	if (null == reBuffer) {
+		reBuffer = new float[reData.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new float[imData.length];
+	}
+	if ((reData.length != dataLength)
+		|| (imData.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reDataFloat = reData;
+	this.imDataFloat = imData;
+	this.reBufferFloat = reBuffer;
+	this.imBufferFloat = imBuffer;
+	final float norm = 1.0F / (float)dataLength;
+	for (int k = 0; (k < dataLength); k++) {
+		reData[k] *= norm;
+		imData[k] *= norm;
+	}
+	switch (dimensions) {
+		case 1: {
+			shiftFloat(-fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftFloat(-fourierOrigin1, -fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftFloat(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+	reverseFloat();
+	transformFloat(InputDataType.COMPLEXINPUT);
+} /* end inverseTransform */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method enforces Hermitian symmetry to Fourier data provided in a
+ real-imaginary representation. The number of dimensions of the data
+ is determined by the constructor of this object. Likewise, the
+ dimensions themselves must match those provided to the constructor of
+ this object. The output Fourier data <i>Y</i> satisfies
+ &#8476;(<i>Y</i>[0]) = &#8476;(<i>X</i>[0]), &#8465;(<i>Y</i>[0]) = 0,
+ <i>Y</i>[<i>n</i>] = &#8476;(<i>X</i>[<i>n</i>] + <i>X</i>[<i>K</i>
+ &#8722; <i>n</i>]) &#8725; 2 + j &#8465;(<i>X</i>[<i>n</i>] &#8722;
+ <i>X</i>[<i>K</i> &#8722; <i>n</i>]) &#8725; 2 for <i>n</i> &#8712;
+ [1&#8230;<i>K</i> &#8722; 1], where <i>X</i> is the input Fourier
+ data.
+ </p>
+ @param reData At input, the real part of the data; at output, the
+ real part is symmetric with respect to the origin. The processing is
+ in-place.
+ @param imData At input, the imaginary part of the data; at output,
+ the imaginary part is antisymmetric with respect to the origin. The
+ processing is in-place.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ ********************************************************************/
+public void makeHermitian (
+	final double[] reData,
+	final double[] imData,
+	double[] reBuffer,
+	double[] imBuffer
+) {
+	if (null == reBuffer) {
+		reBuffer = new double[reData.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new double[imData.length];
+	}
+	if ((reData.length != dataLength)
+		|| (imData.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reDataDouble = reData;
+	this.imDataDouble = imData;
+	this.reBufferDouble = reBuffer;
+	this.imBufferDouble = imBuffer;
+	switch (dimensions) {
+		case 1: {
+			shiftDouble(-fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftDouble(-fourierOrigin1, -fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftDouble(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+	System.arraycopy(reData, 0, reBuffer, 0, dataLength);
+	System.arraycopy(imData, 0, imBuffer, 0, dataLength);
+	reverseDouble();
+	for (int k = 0; (k < dataLength); k++) {
+		reData[k] = 0.5 * (reBuffer[k] + reData[k]);
+		imData[k] = 0.5 * (imBuffer[k] - imData[k]);
+	}
+	switch (dimensions) {
+		case 1: {
+			shiftDouble(fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftDouble(fourierOrigin1, fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftDouble(fourierOrigin1, fourierOrigin2, fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+} /* end makeHermitian */
+
+/*------------------------------------------------------------------*/
+/*********************************************************************
+ <p>
+ This method enforces Hermitian symmetry to Fourier data provided in a
+ real-imaginary representation. The number of dimensions of the data
+ is determined by the constructor of this object. Likewise, the
+ dimensions themselves must match those provided to the constructor of
+ this object. The output Fourier data <i>Y</i> satisfies
+ &#8476;(<i>Y</i>[0]) = &#8476;(<i>X</i>[0]), &#8465;(<i>Y</i>[0]) = 0,
+ <i>Y</i>[<i>n</i>] = &#8476;(<i>X</i>[<i>n</i>] + <i>X</i>[<i>K</i>
+ &#8722; <i>n</i>]) &#8725; 2 + j &#8465;(<i>X</i>[<i>n</i>] &#8722;
+ <i>X</i>[<i>K</i> &#8722; <i>n</i>]) &#8725; 2 for <i>n</i> &#8712;
+ [1&#8230;<i>K</i> &#8722; 1], where <i>X</i> is the input Fourier
+ data.
+ </p>
+ @param reData At input, the real part of the data; at output, the
+ real part is symmetric with respect to the origin. The processing is
+ in-place.
+ @param imData At input, the imaginary part of the data; at output,
+ the imaginary part is antisymmetric with respect to the origin. The
+ processing is in-place.
+ @param reBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>reData.length</code> is created internally if
+ <code>reBuffer</code> is <code>null</code>.
+ @param imBuffer Garbage in, garbage out. A temporary buffer of length
+ <code>imData.length</code> is created internally if
+ <code>imBuffer</code> is <code>null</code>.
+ ********************************************************************/
+public void makeHermitian (
+	final float[] reData,
+	final float[] imData,
+	float[] reBuffer,
+	float[] imBuffer
+) {
+	if (null == reBuffer) {
+		reBuffer = new float[reData.length];
+	}
+	if (null == imBuffer) {
+		imBuffer = new float[imData.length];
+	}
+	if ((reData.length != dataLength)
+		|| (imData.length != dataLength)
+		|| (reBuffer.length != dataLength)
+		|| (imBuffer.length != dataLength)) {
+		throw(new IllegalArgumentException());
+	}
+	this.reDataFloat = reData;
+	this.imDataFloat = imData;
+	this.reBufferFloat = reBuffer;
+	this.imBufferFloat = imBuffer;
+	switch (dimensions) {
+		case 1: {
+			shiftFloat(-fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftFloat(-fourierOrigin1, -fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftFloat(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+	System.arraycopy(reData, 0, reBuffer, 0, dataLength);
+	System.arraycopy(imData, 0, imBuffer, 0, dataLength);
+	reverseFloat();
+	for (int k = 0; (k < dataLength); k++) {
+		reData[k] = 0.5F * (reBuffer[k] + reData[k]);
+		imData[k] = 0.5F * (imBuffer[k] - imData[k]);
+	}
+	switch (dimensions) {
+		case 1: {
+			shiftFloat(fourierOrigin1);
+			break;
+		}
+		case 2: {
+			shiftFloat(fourierOrigin1, fourierOrigin2);
+			break;
+		}
+		case 3: {
+			shiftFloat(fourierOrigin1, fourierOrigin2, fourierOrigin3);
+			break;
+		}
+		default: {
+			throw(new IllegalStateException());
+		}
+	}
+} /* end makeHermitian */
+
+/*....................................................................
+	SlowFourierTransform private methods
+....................................................................*/
+/*------------------------------------------------------------------*/
+private void reverseDouble (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	if (1 < K1) {
+		int p = 0;
+		for (int k3 = 0; (k3 < K3); k3++) {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				int progressive = p;
+				int regressive = p + K1;
+				while (++progressive < --regressive) {
+					final double reSwap = reDataDouble[progressive];
+					reDataDouble[progressive] = reDataDouble[regressive];
+					reDataDouble[regressive] = reSwap;
+					final double imSwap = imDataDouble[progressive];
+					imDataDouble[progressive] = imDataDouble[regressive];
+					imDataDouble[regressive] = imSwap;
+				}
+				p += K1;
+			}
+		}
+	}
+	if (1 < K2) {
+		int p = 0;
+		for (int k3 = 0; (k3 < K3); k3++) {
+			int q = p;
+			for (int k1 = 0; (k1 < K1); k1++) {
+				int progressive = q + K1;
+				int regressive = q + K1 * (K2 - 1);
+				while (progressive < regressive) {
+					final double reSwap = reDataDouble[progressive];
+					reDataDouble[progressive] = reDataDouble[regressive];
+					reDataDouble[regressive] = reSwap;
+					final double imSwap = imDataDouble[progressive];
+					imDataDouble[progressive] = imDataDouble[regressive];
+					imDataDouble[regressive] = imSwap;
+					progressive += K1;
+					regressive -= K1;
+				}
+				q++;
+			}
+			p += K1K2;
+		}
+	}
+	if (1 < K3) {
+		int p = 0;
+		for (int k2 = 0; (k2 < K2); k2++) {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				int progressive = p + K1K2;
+				int regressive = p + K1K2 * (K3 - 1);
+				while (progressive < regressive) {
+					final double reSwap = reDataDouble[progressive];
+					reDataDouble[progressive] = reDataDouble[regressive];
+					reDataDouble[regressive] = reSwap;
+					final double imSwap = imDataDouble[progressive];
+					imDataDouble[progressive] = imDataDouble[regressive];
+					imDataDouble[regressive] = imSwap;
+					progressive += K1K2;
+					regressive -= K1K2;
+				}
+				p++;
+			}
+		}
+	}
+} /* end reverseDouble */
+
+/*------------------------------------------------------------------*/
+private void reverseFloat (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	if (1 < K1) {
+		int p = 0;
+		for (int k3 = 0; (k3 < K3); k3++) {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				int progressive = p;
+				int regressive = p + K1;
+				while (++progressive < --regressive) {
+					final float reSwap = reDataFloat[progressive];
+					reDataFloat[progressive] = reDataFloat[regressive];
+					reDataFloat[regressive] = reSwap;
+					final float imSwap = imDataFloat[progressive];
+					imDataFloat[progressive] = imDataFloat[regressive];
+					imDataFloat[regressive] = imSwap;
+				}
+				p += K1;
+			}
+		}
+	}
+	if (1 < K2) {
+		int p = 0;
+		for (int k3 = 0; (k3 < K3); k3++) {
+			int q = p;
+			for (int k1 = 0; (k1 < K1); k1++) {
+				int progressive = q + K1;
+				int regressive = q + K1 * (K2 - 1);
+				while (progressive < regressive) {
+					final float reSwap = reDataFloat[progressive];
+					reDataFloat[progressive] = reDataFloat[regressive];
+					reDataFloat[regressive] = reSwap;
+					final float imSwap = imDataFloat[progressive];
+					imDataFloat[progressive] = imDataFloat[regressive];
+					imDataFloat[regressive] = imSwap;
+					progressive += K1;
+					regressive -= K1;
+				}
+				q++;
+			}
+			p += K1K2;
+		}
+	}
+	if (1 < K3) {
+		int p = 0;
+		for (int k2 = 0; (k2 < K2); k2++) {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				int progressive = p + K1K2;
+				int regressive = p + K1K2 * (K3 - 1);
+				while (progressive < regressive) {
+					final float reSwap = reDataFloat[progressive];
+					reDataFloat[progressive] = reDataFloat[regressive];
+					reDataFloat[regressive] = reSwap;
+					final float imSwap = imDataFloat[progressive];
+					imDataFloat[progressive] = imDataFloat[regressive];
+					imDataFloat[regressive] = imSwap;
+					progressive += K1K2;
+					regressive -= K1K2;
+				}
+				p++;
+			}
+		}
+	}
+} /* end reverseFloat */
+
+/*------------------------------------------------------------------*/
+private void shiftDouble (
+	int o1
+) {
+	final int K1 = width.intValue();
+	o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1));
+	if (0 == o1) {
+		return;
+	}
+	final int range1 = K1 - o1;
+	System.arraycopy(reDataDouble, 0, reBufferDouble, o1,
+		range1);
+	System.arraycopy(imDataDouble, 0, imBufferDouble, o1,
+		range1);
+	System.arraycopy(reDataDouble, range1, reBufferDouble, 0,
+		o1);
+	System.arraycopy(imDataDouble, range1, imBufferDouble, 0,
+		o1);
+	System.arraycopy(reBufferDouble, 0, reDataDouble, 0,
+		K1);
+	System.arraycopy(imBufferDouble, 0, imDataDouble, 0,
+		K1);
+} /* end shiftDouble */
+
+/*------------------------------------------------------------------*/
+private void shiftDouble (
+	int o1,
+	int o2
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1));
+	o2 = (o2 < 0) ? (o2 + K2 * ((K2 - 1 - o2) / K2)) : (o2 - K2 * (o2 / K2));
+	if ((0 == o1) && (0 == o2)) {
+		return;
+	}
+	final int range1 = K1 - o1;
+	final int range2 = K2 - o2;
+	int p = 0;
+	for (int k2 = 0; (k2 < K2); k2++) {
+		System.arraycopy(reDataDouble, p, reBufferDouble, p + o1,
+			range1);
+		System.arraycopy(imDataDouble, p, imBufferDouble, p + o1,
+			range1);
+		System.arraycopy(reDataDouble, p + range1, reBufferDouble, p,
+			o1);
+		System.arraycopy(imDataDouble, p + range1, imBufferDouble, p,
+			o1);
+		p += K1;
+	}
+	System.arraycopy(reBufferDouble, 0, reDataDouble, o2 * K1,
+		range2 * K1);
+	System.arraycopy(imBufferDouble, 0, imDataDouble, o2 * K1,
+		range2 * K1);
+	System.arraycopy(reBufferDouble, range2 * K1, reDataDouble, 0,
+		o2 * K1);
+	System.arraycopy(imBufferDouble, range2 * K1, imDataDouble, 0,
+		o2 * K1);
+} /* end shiftDouble */
+
+/*------------------------------------------------------------------*/
+private void shiftDouble (
+	int o1,
+	int o2,
+	int o3
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1));
+	o2 = (o2 < 0) ? (o2 + K2 * ((K2 - 1 - o2) / K2)) : (o2 - K2 * (o2 / K2));
+	o3 = (o3 < 0) ? (o3 + K3 * ((K3 - 1 - o3) / K3)) : (o3 - K3 * (o3 / K3));
+	if ((0 == o1) && (0 == o2) && (0 == o3)) {
+		return;
+	}
+	final int K1K2 = K1 * K2;
+	final int range1 = K1 - o1;
+	final int range2 = K2 - o2;
+	final int range3 = K3 - o3;
+	int p = 0;
+	for (int k3 = 0; (k3 < K3); k3++) {
+		for (int k2 = 0; (k2 < K2); k2++) {
+			System.arraycopy(reDataDouble, p, reBufferDouble, p + o1,
+				range1);
+			System.arraycopy(imDataDouble, p, imBufferDouble, p + o1,
+				range1);
+			System.arraycopy(reDataDouble, p + range1, reBufferDouble, p,
+				o1);
+			System.arraycopy(imDataDouble, p + range1, imBufferDouble, p,
+				o1);
+			p += K1;
+		}
+	}
+	p = 0;
+	for (int k3 = 0; (k3 < K3); k3++) {
+		System.arraycopy(reBufferDouble, p, reDataDouble, o2 * K1,
+			range2 * K1);
+		System.arraycopy(imBufferDouble, p, imDataDouble, o2 * K1,
+			range2 * K1);
+		System.arraycopy(reBufferDouble, range2 * K1, reDataDouble, p,
+			o2 * K1);
+		System.arraycopy(imBufferDouble, range2 * K1, imDataDouble, p,
+			fourierOrigin2 * K1);
+		p += K1K2;
+	}
+	System.arraycopy(reDataDouble, 0, reBufferDouble, o3 * K1K2,
+		range3 * K1K2);
+	System.arraycopy(imDataDouble, 0, imBufferDouble, o3 * K1K2,
+		range3 * K1K2);
+	System.arraycopy(reDataDouble, range3 * K1K2, reBufferDouble, 0,
+		o3 * K1K2);
+	System.arraycopy(imDataDouble, range3 * K1K2, imBufferDouble, 0,
+		o3 * K1K2);
+	System.arraycopy(reBufferDouble, 0, reDataDouble, 0,
+		dataLength);
+	System.arraycopy(imBufferDouble, 0, imDataDouble, 0,
+		dataLength);
+} /* end shiftDouble */
+
+/*------------------------------------------------------------------*/
+private void shiftFloat (
+	int o1
+) {
+	final int K1 = width.intValue();
+	o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1));
+	if (0 == o1) {
+		return;
+	}
+	final int range1 = K1 - o1;
+	System.arraycopy(reDataFloat, 0, reBufferFloat, o1,
+		range1);
+	System.arraycopy(imDataFloat, 0, imBufferFloat, o1,
+		range1);
+	System.arraycopy(reDataFloat, range1, reBufferFloat, 0,
+		o1);
+	System.arraycopy(imDataFloat, range1, imBufferFloat, 0,
+		o1);
+	System.arraycopy(reBufferFloat, 0, reDataFloat, 0,
+		K1);
+	System.arraycopy(imBufferFloat, 0, imDataFloat, 0,
+		K1);
+} /* end shiftFloat */
+
+/*------------------------------------------------------------------*/
+private void shiftFloat (
+	int o1,
+	int o2
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1));
+	o2 = (o2 < 0) ? (o2 + K2 * ((K2 - 1 - o2) / K2)) : (o2 - K2 * (o2 / K2));
+	if ((0 == o1) && (0 == o2)) {
+		return;
+	}
+	final int range1 = K1 - o1;
+	final int range2 = K2 - o2;
+	int p = 0;
+	for (int k2 = 0; (k2 < K2); k2++) {
+		System.arraycopy(reDataFloat, p, reBufferFloat, p + o1,
+			range1);
+		System.arraycopy(imDataFloat, p, imBufferFloat, p + o1,
+			range1);
+		System.arraycopy(reDataFloat, p + range1, reBufferFloat, p,
+			o1);
+		System.arraycopy(imDataFloat, p + range1, imBufferFloat, p,
+			o1);
+		p += K1;
+	}
+	System.arraycopy(reBufferFloat, 0, reDataFloat, o2 * K1,
+		range2 * K1);
+	System.arraycopy(imBufferFloat, 0, imDataFloat, o2 * K1,
+		range2 * K1);
+	System.arraycopy(reBufferFloat, range2 * K1, reDataFloat, 0,
+		o2 * K1);
+	System.arraycopy(imBufferFloat, range2 * K1, imDataFloat, 0,
+		o2 * K1);
+} /* end shiftFloat */
+
+/*------------------------------------------------------------------*/
+private void shiftFloat (
+	int o1,
+	int o2,
+	int o3
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1));
+	o2 = (o2 < 0) ? (o2 + K2 * ((K2 - 1 - o2) / K2)) : (o2 - K2 * (o2 / K2));
+	o3 = (o3 < 0) ? (o3 + K3 * ((K3 - 1 - o3) / K3)) : (o3 - K3 * (o3 / K3));
+	if ((0 == o1) && (0 == o2) && (0 == o3)) {
+		return;
+	}
+	final int K1K2 = K1 * K2;
+	final int range1 = K1 - o1;
+	final int range2 = K2 - o2;
+	final int range3 = K3 - o3;
+	int p = 0;
+	for (int k3 = 0; (k3 < K3); k3++) {
+		for (int k2 = 0; (k2 < K2); k2++) {
+			System.arraycopy(reDataFloat, p, reBufferFloat, p + o1,
+				range1);
+			System.arraycopy(imDataFloat, p, imBufferFloat, p + o1,
+				range1);
+			System.arraycopy(reDataFloat, p + range1, reBufferFloat, p,
+				o1);
+			System.arraycopy(imDataFloat, p + range1, imBufferFloat, p,
+				o1);
+			p += K1;
+		}
+	}
+	p = 0;
+	for (int k3 = 0; (k3 < K3); k3++) {
+		System.arraycopy(reBufferFloat, p, reDataFloat, o2 * K1,
+			range2 * K1);
+		System.arraycopy(imBufferFloat, p, imDataFloat, o2 * K1,
+			range2 * K1);
+		System.arraycopy(reBufferFloat, range2 * K1, reDataFloat, p,
+			o2 * K1);
+		System.arraycopy(imBufferFloat, range2 * K1, imDataFloat, p,
+			fourierOrigin2 * K1);
+		p += K1K2;
+	}
+	System.arraycopy(reDataFloat, 0, reBufferFloat, o3 * K1K2,
+		range3 * K1K2);
+	System.arraycopy(imDataFloat, 0, imBufferFloat, o3 * K1K2,
+		range3 * K1K2);
+	System.arraycopy(reDataFloat, range3 * K1K2, reBufferFloat, 0,
+		o3 * K1K2);
+	System.arraycopy(imDataFloat, range3 * K1K2, imBufferFloat, 0,
+		o3 * K1K2);
+	System.arraycopy(reBufferFloat, 0, reDataFloat, 0,
+		dataLength);
+	System.arraycopy(imBufferFloat, 0, imDataFloat, 0,
+		dataLength);
+} /* end shiftFloat */
+
+/*------------------------------------------------------------------*/
+private void transformDouble (
+	final InputDataType inputDataType
+) {
+	switch (inputDataType) {
+		case COMPLEXINPUT: {
+			switch (dimensions) {
+				case 1: {
+					transformDouble1D();
+					break;
+				}
+				case 2: {
+					transformDouble2D();
+					break;
+				}
+				case 3: {
+					transformDouble3D();
+					break;
+				}
+				default: {
+					throw(new IllegalStateException());
+				}
+			}
+			break;
+		}
+		case REALINPUT: {
+			switch (dimensions) {
+				case 1: {
+					transformRealDouble1D();
+					break;
+				}
+				case 2: {
+					switch (firstDimension) {
+						case 1: {
+							transformRealDouble2DRowFirst();
+							break;
+						}
+						case 2: {
+							transformRealDouble2DColumnFirst();
+							break;
+						}
+						default: {
+							throw(new IllegalStateException());
+						}
+					}
+					break;
+				}
+				case 3: {
+					switch (firstDimension) {
+						case 1: {
+							transformRealDouble3DRowFirst();
+							break;
+						}
+						case 2: {
+							transformRealDouble3DColumnFirst();
+							break;
+						}
+						case 3: {
+							transformRealDouble3DAcrossFirst();
+							break;
+						}
+						default: {
+							throw(new IllegalStateException());
+						}
+					}
+					break;
+				}
+				default: {
+					throw(new IllegalStateException());
+				}
+			}
+			break;
+		}
+	}
+} /* end transformDouble */
+
+/*------------------------------------------------------------------*/
+private void transformDouble1D (
+) {
+	final FFTSetup fft = FFTSetup.transforms.get(width);
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Double(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Double(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Double(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Double(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Double(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Double(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderDouble(reDataDouble, imDataDouble, 0, 1,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Double(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+	}
+} /* end transformDouble1D */
+
+/*------------------------------------------------------------------*/
+private void transformDouble2D (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	// Rows
+	final FFTSetup fft1 = FFTSetup.transforms.get(width);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTBruteForceDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTCoprimeFactorDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength2Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength3Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength4Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength5Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength6Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength8Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTMixedRadixDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTPaddedRaderDouble(
+					reDataDouble, imDataDouble, p, 1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTRaderDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTRadix2Double(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTSplitRadixDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft2 = FFTSetup.transforms.get(height);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTBruteForceDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTCoprimeFactorDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.ruritanian, fft2.chinese, fft2.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength2Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength3Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength4Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength5Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength6Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength8Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTMixedRadixDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTPaddedRaderDouble(
+					reDataDouble, imDataDouble, k1, K1,
+					fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTRaderDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTRadix2Double(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTSplitRadixDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+} /* end transformDouble2D */
+
+/*------------------------------------------------------------------*/
+private void transformDouble3D (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	// Rows
+	final FFTSetup fft1 = FFTSetup.transforms.get(width);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTBruteForceDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTCoprimeFactorDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft1.ruritanian, fft1.chinese, fft1.K1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength2Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength3Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength4Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength5Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength6Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength8Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTMixedRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTPaddedRaderDouble(
+						reDataDouble, imDataDouble, p, 1,
+						fft1.reConvolverDouble, fft1.imConvolverDouble,
+						fft1.modular, fft1.inverseModular));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTRaderDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft1.reConvolverDouble, fft1.imConvolverDouble,
+						fft1.modular, fft1.inverseModular));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTRadix2Double(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTSplitRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+					p += K1;
+				}
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft2 = FFTSetup.transforms.get(height);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTBruteForceDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTCoprimeFactorDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.ruritanian, fft2.chinese, fft2.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength2Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength3Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength4Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength5Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength6Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength8Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTMixedRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTPaddedRaderDouble(
+						reDataDouble, imDataDouble, p + k1, K1,
+						fft2.reConvolverDouble, fft2.imConvolverDouble,
+						fft2.modular, fft2.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTRaderDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reConvolverDouble, fft2.imConvolverDouble,
+						fft2.modular, fft2.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTRadix2Double(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTSplitRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Across
+	final FFTSetup fft3 = FFTSetup.transforms.get(depth);
+	final ExecutorService executor3 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft3.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTBruteForceDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+					p++;
+				}
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTCoprimeFactorDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.ruritanian, fft3.chinese, fft3.K1));
+					p++;
+				}
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength2Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength3Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength4Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength5Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength6Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength8Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTMixedRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble, fft3.K1));
+					p++;
+				}
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTPaddedRaderDouble(
+						reDataDouble, imDataDouble, p, K1K2,
+						fft3.reConvolverDouble, fft3.imConvolverDouble,
+						fft3.modular, fft3.inverseModular));
+					p++;
+				}
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRaderDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reConvolverDouble, fft3.imConvolverDouble,
+						fft3.modular, fft3.inverseModular));
+					p++;
+				}
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRadix2Double(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+					p++;
+				}
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTSplitRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+					p++;
+				}
+			}
+			break;
+		}
+	}
+	try {
+		executor3.shutdown();
+		executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+} /* end transformDouble3D */
+
+/*------------------------------------------------------------------*/
+private void transformFloat (
+	final InputDataType inputDataType
+) {
+	switch (inputDataType) {
+		case COMPLEXINPUT: {
+			switch (dimensions) {
+				case 1: {
+					transformFloat1D();
+					break;
+				}
+				case 2: {
+					transformFloat2D();
+					break;
+				}
+				case 3: {
+					transformFloat3D();
+					break;
+				}
+				default: {
+					throw(new IllegalStateException());
+				}
+			}
+			break;
+		}
+		case REALINPUT: {
+			switch (dimensions) {
+				case 1: {
+					transformRealFloat1D();
+					break;
+				}
+				case 2: {
+					switch (firstDimension) {
+						case 1: {
+							transformRealFloat2DRowFirst();
+							break;
+						}
+						case 2: {
+							transformRealFloat2DColumnFirst();
+							break;
+						}
+						default: {
+							throw(new IllegalStateException());
+						}
+					}
+					break;
+				}
+				case 3: {
+					switch (firstDimension) {
+						case 1: {
+							transformRealFloat3DRowFirst();
+							break;
+						}
+						case 2: {
+							transformRealFloat3DColumnFirst();
+							break;
+						}
+						case 3: {
+							transformRealFloat3DAcrossFirst();
+							break;
+						}
+						default: {
+							throw(new IllegalStateException());
+						}
+					}
+					break;
+				}
+				default: {
+					throw(new IllegalStateException());
+				}
+			}
+			break;
+		}
+	}
+} /* end transformFloat */
+
+/*------------------------------------------------------------------*/
+private void transformFloat1D (
+) {
+	final FFTSetup fft = FFTSetup.transforms.get(width);
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2Float(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3Float(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4Float(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5Float(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6Float(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8Float(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderFloat(reDataFloat, imDataFloat, 0, 1,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2Float(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+	}
+} /* end transformFloat1D */
+
+/*------------------------------------------------------------------*/
+private void transformFloat2D (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	// Rows
+	final FFTSetup fft1 = FFTSetup.transforms.get(width);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTBruteForceFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTCoprimeFactorFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength2Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength3Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength4Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength5Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength6Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTLength8Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTMixedRadixFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTPaddedRaderFloat(
+					reDataFloat, imDataFloat, p, 1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTRaderFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTRadix2Float(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				executor1.execute(new DFTSplitRadixFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft2 = FFTSetup.transforms.get(height);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTBruteForceFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTCoprimeFactorFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.ruritanian, fft2.chinese, fft2.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength2Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength3Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength4Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength5Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength6Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTLength8Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTMixedRadixFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTPaddedRaderFloat(
+					reDataFloat, imDataFloat, k1, K1,
+					fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTRaderFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTRadix2Float(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k1 = 0; (k1 < K1); k1++) {
+				executor2.execute(new DFTSplitRadixFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+} /* end transformFloat2D */
+
+/*------------------------------------------------------------------*/
+private void transformFloat3D (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	// Rows
+	final FFTSetup fft1 = FFTSetup.transforms.get(width);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTBruteForceFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTCoprimeFactorFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft1.ruritanian, fft1.chinese, fft1.K1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength2Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength3Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength4Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength5Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength6Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTLength8Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTMixedRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft1.reUnitRootFloat, fft1.imUnitRootFloat,
+						fft1.K1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTPaddedRaderFloat(
+						reDataFloat, imDataFloat, p, 1,
+						fft1.reConvolverFloat, fft1.imConvolverFloat,
+						fft1.modular, fft1.inverseModular));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTRaderFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft1.reConvolverFloat, fft1.imConvolverFloat,
+						fft1.modular, fft1.inverseModular));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTRadix2Float(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor1.execute(new DFTSplitRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+					p += K1;
+				}
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft2 = FFTSetup.transforms.get(height);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTBruteForceFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTCoprimeFactorFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.ruritanian, fft2.chinese, fft2.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength2Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength3Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength4Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength5Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength6Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTLength8Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTMixedRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTPaddedRaderFloat(
+						reDataFloat, imDataFloat, p + k1, K1,
+						fft2.reConvolverFloat, fft2.imConvolverFloat,
+						fft2.modular, fft2.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTRaderFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reConvolverFloat, fft2.imConvolverFloat,
+						fft2.modular, fft2.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTRadix2Float(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor2.execute(new DFTSplitRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Across
+	final FFTSetup fft3 = FFTSetup.transforms.get(depth);
+	final ExecutorService executor3 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft3.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTBruteForceFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+					p++;
+				}
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTCoprimeFactorFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.ruritanian, fft3.chinese, fft3.K1));
+					p++;
+				}
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength2Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength3Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength4Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength5Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength6Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength8Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTMixedRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat, fft3.K1));
+					p++;
+				}
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTPaddedRaderFloat(
+						reDataFloat, imDataFloat, p, K1K2,
+						fft3.reConvolverFloat, fft3.imConvolverFloat,
+						fft3.modular, fft3.inverseModular));
+					p++;
+				}
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRaderFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reConvolverFloat, fft3.imConvolverFloat,
+						fft3.modular, fft3.inverseModular));
+					p++;
+				}
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRadix2Float(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+					p++;
+				}
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTSplitRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+					p++;
+				}
+			}
+			break;
+		}
+	}
+	try {
+		executor3.shutdown();
+		executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+} /* end transformFloat3D */
+
+/*------------------------------------------------------------------*/
+private void transformRealDouble1D (
+) {
+	final FFTSetupReal fft = FFTSetupReal.transforms.get(width);
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			new DFTEvenRealDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+		}
+		case LENGTH1: {
+			imDataDouble[0] = 0.0;
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, 1,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderRealDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reConvolverDouble, fft.imConvolverDouble,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2RealDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble,
+				fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealDouble(reDataDouble, imDataDouble,
+				reBufferDouble, imBufferDouble, 0, 1,
+				fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+			break;
+		}
+	}
+	int progressive = 0;
+	int regressive = width.intValue();
+	while (++progressive < --regressive) {
+		reDataDouble[regressive] = reDataDouble[progressive];
+		imDataDouble[regressive] = -imDataDouble[progressive];
+	}
+} /* end transformRealDouble1D */
+
+/*------------------------------------------------------------------*/
+private void transformRealDouble2DColumnFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int halfK2 = (K2 >> 1) + 1;
+	int k1 = -1;
+	// First column for an odd width
+	if (1 == (K1 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(height);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataDouble[0] = 0.0;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealDouble(reDataDouble, imDataDouble,
+					0, K1).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealDouble(reDataDouble, imDataDouble,
+					0, K1).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealDouble(reDataDouble, imDataDouble,
+					0, K1).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealDouble(reDataDouble, imDataDouble,
+					0, K1).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealDouble(reDataDouble, imDataDouble,
+					0, K1).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealDouble(reDataDouble, imDataDouble,
+					0, K1).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, K1,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble,
+					fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+		}
+		k1 = 0;
+	}
+	// Remaining columns
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(height);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTBruteForceRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTCoprimeFactorRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTDuoRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, k1 + 1, K1, K2));
+				++k1;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTEvenRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (++k1 < K1) {
+				imDataDouble[k1] = 0.0;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength2RealDouble(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength3RealDouble(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength4RealDouble(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength5RealDouble(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength6RealDouble(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength8RealDouble(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTMixedRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTPaddedRaderRealDouble(
+					reDataDouble, imDataDouble, k1, K1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTRaderRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTRadix2RealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble,
+					fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTSplitRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Rows
+	final FFTSetup fft2 = FFTSetup.transforms.get(width);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	int p = 0;
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTBruteForceDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTCoprimeFactorDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft2.ruritanian, fft2.chinese, fft2.K1));
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength2Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength3Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength4Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength5Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength6Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength8Double(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTMixedRadixDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1));
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTPaddedRaderDouble(
+					reDataDouble, imDataDouble, p, 1,
+					fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTRaderDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTRadix2Double(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTSplitRadixDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = K1;
+	int regressive = K1 * (K2 - 1);
+	while (progressive < regressive) {
+		reDataDouble[regressive] = reDataDouble[progressive];
+		imDataDouble[regressive] = -imDataDouble[progressive];
+		progressive += K1;
+		regressive -= K1;
+	}
+	p = K1 + 1;
+	int q = K1 * K2 - 1;
+	for (int k2 = halfK2; (k2 < K2); k2++) {
+		progressive = p;
+		regressive = q;
+		for (k1 = 1; (k1 < K1); k1++) {
+			reDataDouble[regressive] = reDataDouble[progressive];
+			imDataDouble[regressive] = -imDataDouble[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1;
+		q -= K1;
+	}
+} /* end transformRealDouble2DColumnFirst */
+
+/*------------------------------------------------------------------*/
+private void transformRealDouble2DRowFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int halfK1 = (K1 >> 1) + 1;
+	int p = 0;
+	int k2 = 0;
+	// First row for an odd height
+	if (1 == (K2 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(width);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataDouble[0] = 0.0;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealDouble(reDataDouble, imDataDouble,
+					0, 1).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealDouble(reDataDouble, imDataDouble,
+					0, 1).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealDouble(reDataDouble, imDataDouble,
+					0, 1).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealDouble(reDataDouble, imDataDouble,
+					0, 1).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealDouble(reDataDouble, imDataDouble,
+					0, 1).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealDouble(reDataDouble, imDataDouble,
+					0, 1).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, 1,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble,
+					fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+		}
+		p = K1;
+		k2 = 1;
+	}
+	// Remaining rows
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(width);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTBruteForceRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTCoprimeFactorRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTDuoRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, p + K1, 1, K1));
+				p += 2 * K1;
+				k2++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTEvenRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (k2++ < K2) {
+				imDataDouble[p] = 0.0;
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength2RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength3RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength4RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength5RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength6RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength8RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTMixedRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTPaddedRaderRealDouble(
+					reDataDouble, imDataDouble, p, 1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTRaderRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTRadix2RealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble,
+					fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble));
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTSplitRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft2 = FFTSetup.transforms.get(height);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTBruteForceDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTCoprimeFactorDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.ruritanian, fft2.chinese, fft2.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength2Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength3Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength4Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength5Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength6Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength8Double(
+					reDataDouble, imDataDouble, k1, K1));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTMixedRadixDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTPaddedRaderDouble(
+					reDataDouble, imDataDouble, k1, K1,
+					fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTRaderDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reConvolverDouble, fft2.imConvolverDouble,
+					fft2.modular, fft2.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTRadix2Double(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTSplitRadixDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1, K1,
+					fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = 0;
+	int regressive = K1;
+	while (++progressive < --regressive) {
+		reDataDouble[regressive] = reDataDouble[progressive];
+		imDataDouble[regressive] = -imDataDouble[progressive];
+	}
+	p = K1 + 1;
+	int q = K1 * K2 - 1;
+	for (k2 = 1; (k2 < K2); k2++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = halfK1; (k1 < K1); k1++) {
+			reDataDouble[regressive] = reDataDouble[progressive];
+			imDataDouble[regressive] = -imDataDouble[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1;
+		q -= K1;
+	}
+} /* end transformRealDouble2DRowFirst */
+
+/*------------------------------------------------------------------*/
+private void transformRealDouble3DAcrossFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	final int halfK3 = (K3 >> 1) + 1;
+	int k = -1;
+	// First across for an odd (width * height)
+	if (1 == (K1K2 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(depth);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1K2,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1K2,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1K2,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataDouble[0] = 0.0;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealDouble(reDataDouble, imDataDouble,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealDouble(reDataDouble, imDataDouble,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealDouble(reDataDouble, imDataDouble,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealDouble(reDataDouble, imDataDouble,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealDouble(reDataDouble, imDataDouble,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealDouble(reDataDouble, imDataDouble,
+					0, K1K2).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1K2,
+					fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealDouble(
+					reDataDouble, imDataDouble, 0, K1K2,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1K2,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1K2,
+					fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble,
+					fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1K2,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+		}
+		k = 0;
+	}
+	// Remaining across
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(depth);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTBruteForceRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k, K1K2,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTCoprimeFactorRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k, K1K2,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTDuoRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k, k + 1, K1K2, K3));
+				k++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTEvenRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k, K1K2,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (++k < K1K2) {
+				imDataDouble[k] = 0.0;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength2RealDouble(
+					reDataDouble, imDataDouble, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength3RealDouble(
+					reDataDouble, imDataDouble, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength4RealDouble(
+					reDataDouble, imDataDouble, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength5RealDouble(
+					reDataDouble, imDataDouble, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength6RealDouble(
+					reDataDouble, imDataDouble, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength8RealDouble(
+					reDataDouble, imDataDouble, k, K1K2));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTMixedRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k, K1K2,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTPaddedRaderRealDouble(
+					reDataDouble, imDataDouble, k, K1K2,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTRaderRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k, K1K2,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTRadix2RealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k, K1K2,
+					fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble,
+					fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTSplitRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k, K1K2,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Rows
+	final FFTSetup fft2 = FFTSetup.transforms.get(width);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTBruteForceDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTCoprimeFactorDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.ruritanian, fft2.chinese, fft2.K1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength2Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength3Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength4Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength5Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength6Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength8Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTMixedRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+						fft2.K1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTPaddedRaderDouble(
+						reDataDouble, imDataDouble, p, 1,
+						fft2.reConvolverDouble, fft2.imConvolverDouble,
+						fft2.modular, fft2.inverseModular));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTRaderDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reConvolverDouble, fft2.imConvolverDouble,
+						fft2.modular, fft2.inverseModular));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTRadix2Double(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTSplitRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+					p += K1;
+				}
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft3 = FFTSetup.transforms.get(height);
+	final ExecutorService executor3 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft3.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTBruteForceDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTCoprimeFactorDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft3.ruritanian, fft3.chinese, fft3.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength2Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength3Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength4Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength5Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength6Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength8Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTMixedRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble, fft3.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTPaddedRaderDouble(
+						reDataDouble, imDataDouble, p + k1, K1,
+						fft3.reConvolverDouble, fft3.imConvolverDouble,
+						fft3.modular, fft3.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRaderDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft3.reConvolverDouble, fft3.imConvolverDouble,
+						fft3.modular, fft3.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRadix2Double(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTSplitRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+	}
+	try {
+		executor3.shutdown();
+		executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = K1K2;
+	int regressive = dataLength - K1K2;
+	while (progressive < regressive) {
+		reDataDouble[regressive] = reDataDouble[progressive];
+		imDataDouble[regressive] = -imDataDouble[progressive];
+		progressive += K1K2;
+		regressive -= K1K2;
+	}
+	int p = K1K2 + K1;
+	int q = dataLength - K1;
+	for (int k3 = halfK3; (k3 < K3); k3++) {
+		progressive = p;
+		regressive = q;
+		for (int k2 = 1; (k2 < K2); k2++) {
+			reDataDouble[regressive] = reDataDouble[progressive];
+			imDataDouble[regressive] = -imDataDouble[progressive];
+			progressive += K1;
+			regressive -= K1;
+		}
+		p += K1K2;
+		q -= K1K2;
+	}
+	p = K1K2 + 1;
+	q = K1K2 * (K3 - 1) + K1 - 1;
+	for (int k3 = halfK3; (k3 < K3); k3++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = 1; (k1 < K1); k1++) {
+			reDataDouble[regressive] = reDataDouble[progressive];
+			imDataDouble[regressive] = -imDataDouble[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1K2;
+		q -= K1K2;
+	}
+	progressive = K1K2 + K1 + 1;
+	regressive = dataLength - 1;
+	for (int k3 = halfK3; (k3 < K3); k3++) {
+		for (int k2 = 1; (k2 < K2); k2++) {
+			for (int k1 = 1; (k1 < K1); k1++) {
+				reDataDouble[regressive] = reDataDouble[progressive];
+				imDataDouble[regressive] = -imDataDouble[progressive];
+				progressive++;
+				regressive--;
+			}
+			progressive++;
+			regressive--;
+		}
+		progressive += K1;
+		regressive -= K1;
+	}
+} /* end transformRealDouble3DAcrossFirst */
+
+/*------------------------------------------------------------------*/
+private void transformRealDouble3DColumnFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	final int k1K2 = K1K2 - K1;
+	final int K1K3 = K1 * K3;
+	final int halfK2 = (K2 >> 1) + 1;
+	int k = -1;
+	// First column for an odd (width * depth)
+	if (1 == (K1K3 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(height);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataDouble[0] = 0.0;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealDouble(
+					reDataDouble, imDataDouble, 0, K1).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealDouble(
+					reDataDouble, imDataDouble, 0, K1).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealDouble(
+					reDataDouble, imDataDouble, 0, K1).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealDouble(
+					reDataDouble, imDataDouble, 0, K1).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealDouble(
+					reDataDouble, imDataDouble, 0, K1).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealDouble(
+					reDataDouble, imDataDouble, 0, K1).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, K1,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble,
+					fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, K1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+		}
+		k = 0;
+	}
+	// Remaining columns
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(height);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTBruteForceRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTCoprimeFactorRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (++k < K1K3) {
+				final int p = k1K2 * (k / K1) + k;
+				k++;
+				final int q = k1K2 * (k / K1) + k;
+				executor1.execute(new DFTDuoRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, q, K1, K2));
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTEvenRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (++k < K1K3) {
+				imDataDouble[k] = 0.0;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength2RealDouble(
+					reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength3RealDouble(
+					reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength4RealDouble(
+					reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength5RealDouble(
+					reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength6RealDouble(
+					reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength8RealDouble(
+					reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTMixedRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTPaddedRaderRealDouble(
+					reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTRaderRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTRadix2RealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble,
+					fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTSplitRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Rows
+	final FFTSetup fft2 = FFTSetup.transforms.get(width);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	final int skippedRows = (K2 - halfK2) * K1;
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTBruteForceDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTCoprimeFactorDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.ruritanian, fft2.chinese, fft2.K1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength2Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength3Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength4Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength5Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength6Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength8Double(
+						reDataDouble, imDataDouble, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTMixedRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble,
+						fft2.K1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTPaddedRaderDouble(
+						reDataDouble, imDataDouble, p, 1,
+						fft2.reConvolverDouble, fft2.imConvolverDouble,
+						fft2.modular, fft2.inverseModular));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTRaderDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reConvolverDouble, fft2.imConvolverDouble,
+						fft2.modular, fft2.inverseModular));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTRadix2Double(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTSplitRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, 1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Across
+	final FFTSetup fft3 = FFTSetup.transforms.get(depth);
+	final ExecutorService executor3 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft3.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTBruteForceDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+					p++;
+				}
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTCoprimeFactorDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.ruritanian, fft3.chinese, fft3.K1));
+					p++;
+				}
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength2Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength3Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength4Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength5Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength6Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength8Double(
+						reDataDouble, imDataDouble, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTMixedRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble, fft3.K1));
+					p++;
+				}
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTPaddedRaderDouble(
+						reDataDouble, imDataDouble, p, K1K2,
+						fft3.reConvolverDouble, fft3.imConvolverDouble,
+						fft3.modular, fft3.inverseModular));
+					p++;
+				}
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRaderDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reConvolverDouble, fft3.imConvolverDouble,
+						fft3.modular, fft3.inverseModular));
+					p++;
+				}
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRadix2Double(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+					p++;
+				}
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTSplitRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+					p++;
+				}
+			}
+			break;
+		}
+	}
+	try {
+		executor3.shutdown();
+		executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = K1;
+	int regressive = K1K2 - K1;
+	while (progressive < regressive) {
+		reDataDouble[regressive] = reDataDouble[progressive];
+		imDataDouble[regressive] = -imDataDouble[progressive];
+		progressive += K1;
+		regressive -= K1;
+	}
+	int p = K1 + 1;
+	int q = K1K2 - 1;
+	for (int k2 = halfK2; (k2 < K2); k2++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = 1; (k1 < K1); k1++) {
+			reDataDouble[regressive] = reDataDouble[progressive];
+			imDataDouble[regressive] = -imDataDouble[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1;
+		q -= K1;
+	}
+	p = K1K2 + K1;
+	q = dataLength - K1;
+	for (int k3 = 1; (k3 < K3); k3++) {
+		progressive = p;
+		regressive = q;
+		for (int k2 = halfK2; (k2 < K2); k2++) {
+			reDataDouble[regressive] = reDataDouble[progressive];
+			imDataDouble[regressive] = -imDataDouble[progressive];
+			progressive += K1;
+			regressive -= K1;
+		}
+		p += K1K2;
+		q -= K1K2;
+	}
+	progressive = K1K2 + K1 + 1;
+	regressive = dataLength - 1;
+	for (int k3 = 1; (k3 < K3); k3++) {
+		for (int k2 = halfK2; (k2 < K2); k2++) {
+			for (int k1 = 1; (k1 < K1); k1++) {
+				reDataDouble[regressive] = reDataDouble[progressive];
+				imDataDouble[regressive] = -imDataDouble[progressive];
+				progressive++;
+				regressive--;
+			}
+			progressive++;
+			regressive--;
+		}
+		progressive += halfK2 * K1;
+		regressive -= halfK2 * K1;
+	}
+} /* end transformRealDouble3DColumnFirst */
+
+/*------------------------------------------------------------------*/
+private void transformRealDouble3DRowFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	final int K2K3 = K2 * K3;
+	final int halfK1 = (K1 >> 1) + 1;
+	int k = 0;
+	int p = 0;
+	// First row for an odd (height * depth)
+	if (1 == (K2K3 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(width);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataDouble[0] = 0.0;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealDouble(reDataDouble, imDataDouble, 0, 1).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, 1,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reConvolverDouble, fft.imConvolverDouble,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble,
+					fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealDouble(reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, 0, 1,
+					fft.reUnitRootDouble, fft.imUnitRootDouble).run();
+				break;
+			}
+		}
+		k = 1;
+		p = K1;
+	}
+	// Remaining rows
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(width);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTBruteForceRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTCoprimeFactorRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTDuoRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, p + K1, 1, K1));
+				p += 2 * K1;
+				k++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTEvenRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (k++ < K2K3) {
+				imDataDouble[p] = 0.0;
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength2RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength3RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength4RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength5RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength6RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength8RealDouble(
+					reDataDouble, imDataDouble, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTMixedRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTPaddedRaderRealDouble(
+					reDataDouble, imDataDouble, p, 1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTRaderRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reConvolverDouble, fft1.imConvolverDouble,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTRadix2RealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble,
+					fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble));
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTSplitRadixRealDouble(
+					reDataDouble, imDataDouble,
+					reBufferDouble, imBufferDouble, p, 1,
+					fft1.reUnitRootDouble, fft1.imUnitRootDouble));
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft2 = FFTSetup.transforms.get(height);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	p = 0;
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTBruteForceDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTCoprimeFactorDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.ruritanian, fft2.chinese, fft2.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength2Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength3Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength4Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength5Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength6Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength8Double(
+						reDataDouble, imDataDouble, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTMixedRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTPaddedRaderDouble(
+						reDataDouble, imDataDouble, p + k1, K1,
+						fft2.reConvolverDouble, fft2.imConvolverDouble,
+						fft2.modular, fft2.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTRaderDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reConvolverDouble, fft2.imConvolverDouble,
+						fft2.modular, fft2.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTRadix2Double(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTSplitRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1,
+						fft2.reUnitRootDouble, fft2.imUnitRootDouble));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Across
+	final FFTSetup fft3 = FFTSetup.transforms.get(depth);
+	final ExecutorService executor3 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	p = 0;
+	switch (fft3.algorithm) {
+		case BRUTEFORCE: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTBruteForceDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTCoprimeFactorDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1K2,
+						fft3.ruritanian, fft3.chinese, fft3.K1));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength2Double(
+						reDataDouble, imDataDouble, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength3Double(
+						reDataDouble, imDataDouble, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength4Double(
+						reDataDouble, imDataDouble, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength5Double(
+						reDataDouble, imDataDouble, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength6Double(
+						reDataDouble, imDataDouble, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength8Double(
+						reDataDouble, imDataDouble, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTMixedRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble, fft3.K1));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTPaddedRaderDouble(
+						reDataDouble, imDataDouble, p + k1, K1K2,
+						fft3.reConvolverDouble, fft3.imConvolverDouble,
+						fft3.modular, fft3.inverseModular));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTRaderDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1K2,
+						fft3.reConvolverDouble, fft3.imConvolverDouble,
+						fft3.modular, fft3.inverseModular));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTRadix2Double(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTSplitRadixDouble(
+						reDataDouble, imDataDouble,
+						reBufferDouble, imBufferDouble, p + k1, K1K2,
+						fft3.reUnitRootDouble, fft3.imUnitRootDouble));
+				}
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor3.shutdown();
+		executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = 0;
+	int regressive = K1;
+	while (++progressive < --regressive) {
+		reDataDouble[regressive] = reDataDouble[progressive];
+		imDataDouble[regressive] = -imDataDouble[progressive];
+	}
+	p = K1 + 1;
+	int q = K1K2 - 1;
+	for (int k2 = 1; (k2 < K2); k2++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = halfK1; (k1 < K1); k1++) {
+			reDataDouble[regressive] = reDataDouble[progressive];
+			imDataDouble[regressive] = -imDataDouble[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1;
+		q -= K1;
+	}
+	p = K1K2 + 1;
+	q = K1K2 * (K3 - 1) + K1 - 1;
+	for (int k3 = 1; (k3 < K3); k3++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = halfK1; (k1 < K1); k1++) {
+			reDataDouble[regressive] = reDataDouble[progressive];
+			imDataDouble[regressive] = -imDataDouble[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1K2;
+		q -= K1K2;
+	}
+	progressive = K1K2 + K1 + 1;
+	regressive = dataLength - 1;
+	for (int k3 = 1; (k3 < K3); k3++) {
+		for (int k2 = 1; (k2 < K2); k2++) {
+			for (int k1 = halfK1; (k1 < K1); k1++) {
+				reDataDouble[regressive] = reDataDouble[progressive];
+				imDataDouble[regressive] = -imDataDouble[progressive];
+				progressive++;
+				regressive--;
+			}
+			progressive += halfK1;
+			regressive -= halfK1;
+		}
+		progressive += K1;
+		regressive -= K1;
+	}
+} /* end transformRealDouble3DRowFirst */
+
+/*------------------------------------------------------------------*/
+private void transformRealFloat1D (
+) {
+	final FFTSetupReal fft = FFTSetupReal.transforms.get(width);
+	switch (fft.algorithm) {
+		case BRUTEFORCE: {
+			new DFTBruteForceRealFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+		case COPRIMEFACTOR: {
+			new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.ruritanian, fft.chinese, fft.K1).run();
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			new DFTEvenRealFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+		}
+		case LENGTH1: {
+			imDataFloat[0] = 0.0F;
+			break;
+		}
+		case LENGTH2: {
+			new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH3: {
+			new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH4: {
+			new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH5: {
+			new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH6: {
+			new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case LENGTH8: {
+			new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+			break;
+		}
+		case MIXEDRADIX: {
+			new DFTMixedRadixRealFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run();
+			break;
+		}
+		case PADDEDRADER: {
+			new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, 1,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADER: {
+			new DFTRaderRealFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reConvolverFloat, fft.imConvolverFloat,
+				fft.modular, fft.inverseModular).run();
+			break;
+		}
+		case RADIX2: {
+			new DFTRadix2RealFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat,
+				fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run();
+			break;
+		}
+		case SPLITRADIX: {
+			new DFTSplitRadixRealFloat(reDataFloat, imDataFloat,
+				reBufferFloat, imBufferFloat, 0, 1,
+				fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+			break;
+		}
+	}
+	int progressive = 0;
+	int regressive = width.intValue();
+	while (++progressive < --regressive) {
+		reDataFloat[regressive] = reDataFloat[progressive];
+		imDataFloat[regressive] = -imDataFloat[progressive];
+	}
+} /* end transformRealFloat1D */
+
+/*------------------------------------------------------------------*/
+private void transformRealFloat2DColumnFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int halfK2 = (K2 >> 1) + 1;
+	int k1 = -1;
+	// First column for an odd width
+	if (1 == (K1 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(height);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataFloat[0] = 0.0F;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, K1,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat,
+					fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+		}
+		k1 = 0;
+	}
+	// Remaining columns
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(height);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTBruteForceRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTCoprimeFactorRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTDuoRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, k1 + 1, K1, K2));
+				++k1;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTEvenRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (++k1 < K1) {
+				imDataFloat[k1] = 0.0F;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength2RealFloat(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength3RealFloat(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength4RealFloat(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength5RealFloat(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength6RealFloat(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTLength8RealFloat(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTMixedRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTPaddedRaderRealFloat(
+					reDataFloat, imDataFloat, k1, K1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTRaderRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTRadix2RealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat,
+					fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (++k1 < K1) {
+				executor1.execute(new DFTSplitRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Rows
+	final FFTSetup fft2 = FFTSetup.transforms.get(width);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	int p = 0;
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTBruteForceFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTCoprimeFactorFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft2.ruritanian, fft2.chinese, fft2.K1));
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength2Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength3Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength4Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength5Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength6Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTLength8Float(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTMixedRadixFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1));
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTPaddedRaderFloat(
+					reDataFloat, imDataFloat, p, 1,
+					fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTRaderFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTRadix2Float(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				executor2.execute(new DFTSplitRadixFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = K1;
+	int regressive = K1 * (K2 - 1);
+	while (progressive < regressive) {
+		reDataFloat[regressive] = reDataFloat[progressive];
+		imDataFloat[regressive] = -imDataFloat[progressive];
+		progressive += K1;
+		regressive -= K1;
+	}
+	p = K1 + 1;
+	int q = K1 * K2 - 1;
+	for (int k2 = halfK2; (k2 < K2); k2++) {
+		progressive = p;
+		regressive = q;
+		for (k1 = 1; (k1 < K1); k1++) {
+			reDataFloat[regressive] = reDataFloat[progressive];
+			imDataFloat[regressive] = -imDataFloat[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1;
+		q -= K1;
+	}
+} /* transformRealFloat2DColumnFirst */
+
+/*------------------------------------------------------------------*/
+private void transformRealFloat2DRowFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int halfK1 = (K1 >> 1) + 1;
+	int p = 0;
+	int k2 = 0;
+	// First row for an odd height
+	if (1 == (K2 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(width);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataFloat[0] = 0.0F;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, 1,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat,
+					fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+		}
+		p = K1;
+		k2 = 1;
+	}
+	// Remaining rows
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(width);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTBruteForceRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTCoprimeFactorRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTDuoRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, p + K1, 1, K1));
+				p += 2 * K1;
+				k2++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTEvenRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (k2++ < K2) {
+				imDataFloat[p] = 0.0F;
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength2RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength3RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength4RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength5RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength6RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTLength8RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTMixedRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTPaddedRaderRealFloat(
+					reDataFloat, imDataFloat, p, 1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTRaderRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTRadix2RealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat,
+					fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat));
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (k2++ < K2) {
+				executor1.execute(new DFTSplitRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft2 = FFTSetup.transforms.get(height);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTBruteForceFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTCoprimeFactorFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.ruritanian, fft2.chinese, fft2.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength2Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength3Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength4Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength5Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength6Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTLength8Float(
+					reDataFloat, imDataFloat, k1, K1));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTMixedRadixFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTPaddedRaderFloat(
+					reDataFloat, imDataFloat, k1, K1,
+					fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTRaderFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reConvolverFloat, fft2.imConvolverFloat,
+					fft2.modular, fft2.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTRadix2Float(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k1 = 0; (k1 < halfK1); k1++) {
+				executor2.execute(new DFTSplitRadixFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1, K1,
+					fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = 0;
+	int regressive = K1;
+	while (++progressive < --regressive) {
+		reDataFloat[regressive] = reDataFloat[progressive];
+		imDataFloat[regressive] = -imDataFloat[progressive];
+	}
+	p = K1 + 1;
+	int q = K1 * K2 - 1;
+	for (k2 = 1; (k2 < K2); k2++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = halfK1; (k1 < K1); k1++) {
+			reDataFloat[regressive] = reDataFloat[progressive];
+			imDataFloat[regressive] = -imDataFloat[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1;
+		q -= K1;
+	}
+} /* end transformRealFloat2DRowFirst */
+
+/*------------------------------------------------------------------*/
+private void transformRealFloat3DAcrossFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	final int halfK3 = (K3 >> 1) + 1;
+	int k = -1;
+	// First across for an odd (width * height)
+	if (1 == (K1K2 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(depth);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1K2,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1K2,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1K2,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataFloat[0] = 0.0F;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealFloat(reDataFloat, imDataFloat,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealFloat(reDataFloat, imDataFloat,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealFloat(reDataFloat, imDataFloat,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealFloat(reDataFloat, imDataFloat,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealFloat(reDataFloat, imDataFloat,
+					0, K1K2).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealFloat(reDataFloat, imDataFloat,
+					0, K1K2).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1K2,
+					fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, K1K2,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1K2,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1K2,
+					fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat,
+					fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1K2,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+		}
+		k = 0;
+	}
+	// Remaining across
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(depth);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTBruteForceRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k, K1K2,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTCoprimeFactorRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k, K1K2,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTDuoRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k, k + 1, K1K2, K3));
+				k++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTEvenRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k, K1K2,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (++k < K1K2) {
+				imDataFloat[k] = 0.0F;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength2RealFloat(
+					reDataFloat, imDataFloat, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength3RealFloat(
+					reDataFloat, imDataFloat, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength4RealFloat(
+					reDataFloat, imDataFloat, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength5RealFloat(
+					reDataFloat, imDataFloat, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength6RealFloat(
+					reDataFloat, imDataFloat, k, K1K2));
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTLength8RealFloat(
+					reDataFloat, imDataFloat, k, K1K2));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTMixedRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k, K1K2,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTPaddedRaderRealFloat(
+					reDataFloat, imDataFloat, k, K1K2,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTRaderRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k, K1K2,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTRadix2RealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k, K1K2,
+					fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat,
+					fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (++k < K1K2) {
+				executor1.execute(new DFTSplitRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k, K1K2,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Rows
+	final FFTSetup fft2 = FFTSetup.transforms.get(width);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTBruteForceFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTCoprimeFactorFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.ruritanian, fft2.chinese, fft2.K1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength2Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength3Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength4Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength5Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength6Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTLength8Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTMixedRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+						fft2.K1));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTPaddedRaderFloat(
+						reDataFloat, imDataFloat, p, 1,
+						fft2.reConvolverFloat, fft2.imConvolverFloat,
+						fft2.modular, fft2.inverseModular));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTRaderFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reConvolverFloat, fft2.imConvolverFloat,
+						fft2.modular, fft2.inverseModular));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTRadix2Float(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+					p += K1;
+				}
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k2 = 0; (k2 < K2); k2++) {
+					executor2.execute(new DFTSplitRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+					p += K1;
+				}
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft3 = FFTSetup.transforms.get(height);
+	final ExecutorService executor3 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft3.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTBruteForceFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTCoprimeFactorFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft3.ruritanian, fft3.chinese, fft3.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength2Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength3Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength4Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength5Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength6Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength8Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTMixedRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat, fft3.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTPaddedRaderFloat(
+						reDataFloat, imDataFloat, p + k1, K1,
+						fft3.reConvolverFloat, fft3.imConvolverFloat,
+						fft3.modular, fft3.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRaderFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft3.reConvolverFloat, fft3.imConvolverFloat,
+						fft3.modular, fft3.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRadix2Float(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < halfK3); k3++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTSplitRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+	}
+	try {
+		executor3.shutdown();
+		executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = K1K2;
+	int regressive = dataLength - K1K2;
+	while (progressive < regressive) {
+		reDataFloat[regressive] = reDataFloat[progressive];
+		imDataFloat[regressive] = -imDataFloat[progressive];
+		progressive += K1K2;
+		regressive -= K1K2;
+	}
+	int p = K1K2 + K1;
+	int q = dataLength - K1;
+	for (int k3 = halfK3; (k3 < K3); k3++) {
+		progressive = p;
+		regressive = q;
+		for (int k2 = 1; (k2 < K2); k2++) {
+			reDataFloat[regressive] = reDataFloat[progressive];
+			imDataFloat[regressive] = -imDataFloat[progressive];
+			progressive += K1;
+			regressive -= K1;
+		}
+		p += K1K2;
+		q -= K1K2;
+	}
+	p = K1K2 + 1;
+	q = K1K2 * (K3 - 1) + K1 - 1;
+	for (int k3 = halfK3; (k3 < K3); k3++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = 1; (k1 < K1); k1++) {
+			reDataFloat[regressive] = reDataFloat[progressive];
+			imDataFloat[regressive] = -imDataFloat[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1K2;
+		q -= K1K2;
+	}
+	progressive = K1K2 + K1 + 1;
+	regressive = dataLength - 1;
+	for (int k3 = halfK3; (k3 < K3); k3++) {
+		for (int k2 = 1; (k2 < K2); k2++) {
+			for (int k1 = 1; (k1 < K1); k1++) {
+				reDataFloat[regressive] = reDataFloat[progressive];
+				imDataFloat[regressive] = -imDataFloat[progressive];
+				progressive++;
+				regressive--;
+			}
+			progressive++;
+			regressive--;
+		}
+		progressive += K1;
+		regressive -= K1;
+	}
+} /* end transformRealFloat3DAcrossFirst */
+
+/*------------------------------------------------------------------*/
+private void transformRealFloat3DColumnFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	final int k1K2 = K1K2 - K1;
+	final int K1K3 = K1 * K3;
+	final int halfK2 = (K2 >> 1) + 1;
+	int k = -1;
+	// First column for an odd (width * depth)
+	if (1 == (K1K3 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(height);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataFloat[0] = 0.0F;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, K1).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, K1,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat,
+					fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, K1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+		}
+		k = 0;
+	}
+	// Remaining columns
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(height);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTBruteForceRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTCoprimeFactorRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (++k < K1K3) {
+				final int p = k1K2 * (k / K1) + k;
+				k++;
+				final int q = k1K2 * (k / K1) + k;
+				executor1.execute(new DFTDuoRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, q, K1, K2));
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTEvenRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (++k < K1K3) {
+				imDataFloat[k] = 0.0F;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength2RealFloat(
+					reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength3RealFloat(
+					reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength4RealFloat(
+					reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength5RealFloat(
+					reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength6RealFloat(
+					reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTLength8RealFloat(
+					reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1));
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTMixedRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1));
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTPaddedRaderRealFloat(
+					reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADER: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTRaderRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+			}
+			break;
+		}
+		case RADIX2: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTRadix2RealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat,
+					fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat));
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (++k < K1K3) {
+				executor1.execute(new DFTSplitRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Rows
+	final FFTSetup fft2 = FFTSetup.transforms.get(width);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	final int skippedRows = (K2 - halfK2) * K1;
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTBruteForceFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTCoprimeFactorFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.ruritanian, fft2.chinese, fft2.K1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength2Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength3Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength4Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength5Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength6Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTLength8Float(
+						reDataFloat, imDataFloat, p, 1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTMixedRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat,
+						fft2.K1));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTPaddedRaderFloat(
+						reDataFloat, imDataFloat, p, 1,
+						fft2.reConvolverFloat, fft2.imConvolverFloat,
+						fft2.modular, fft2.inverseModular));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTRaderFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reConvolverFloat, fft2.imConvolverFloat,
+						fft2.modular, fft2.inverseModular));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTRadix2Float(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k2 = 0; (k2 < halfK2); k2++) {
+					executor2.execute(new DFTSplitRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, 1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+					p += K1;
+				}
+				p += skippedRows;
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Across
+	final FFTSetup fft3 = FFTSetup.transforms.get(depth);
+	final ExecutorService executor3 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft3.algorithm) {
+		case BRUTEFORCE: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTBruteForceFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+					p++;
+				}
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTCoprimeFactorFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.ruritanian, fft3.chinese, fft3.K1));
+					p++;
+				}
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength2Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH3: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength3Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH4: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength4Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH5: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength5Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH6: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength6Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case LENGTH8: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTLength8Float(
+						reDataFloat, imDataFloat, p, K1K2));
+					p++;
+				}
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTMixedRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat, fft3.K1));
+					p++;
+				}
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTPaddedRaderFloat(
+						reDataFloat, imDataFloat, p, K1K2,
+						fft3.reConvolverFloat, fft3.imConvolverFloat,
+						fft3.modular, fft3.inverseModular));
+					p++;
+				}
+			}
+			break;
+		}
+		case RADER: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRaderFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reConvolverFloat, fft3.imConvolverFloat,
+						fft3.modular, fft3.inverseModular));
+					p++;
+				}
+			}
+			break;
+		}
+		case RADIX2: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTRadix2Float(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+					p++;
+				}
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			int p = 0;
+			for (int k2 = 0; (k2 < halfK2); k2++) {
+				for (int k1 = 0; (k1 < K1); k1++) {
+					executor3.execute(new DFTSplitRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+					p++;
+				}
+			}
+			break;
+		}
+	}
+	try {
+		executor3.shutdown();
+		executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = K1;
+	int regressive = K1K2 - K1;
+	while (progressive < regressive) {
+		reDataFloat[regressive] = reDataFloat[progressive];
+		imDataFloat[regressive] = -imDataFloat[progressive];
+		progressive += K1;
+		regressive -= K1;
+	}
+	int p = K1 + 1;
+	int q = K1K2 - 1;
+	for (int k2 = halfK2; (k2 < K2); k2++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = 1; (k1 < K1); k1++) {
+			reDataFloat[regressive] = reDataFloat[progressive];
+			imDataFloat[regressive] = -imDataFloat[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1;
+		q -= K1;
+	}
+	p = K1K2 + K1;
+	q = dataLength - K1;
+	for (int k3 = 1; (k3 < K3); k3++) {
+		progressive = p;
+		regressive = q;
+		for (int k2 = halfK2; (k2 < K2); k2++) {
+			reDataFloat[regressive] = reDataFloat[progressive];
+			imDataFloat[regressive] = -imDataFloat[progressive];
+			progressive += K1;
+			regressive -= K1;
+		}
+		p += K1K2;
+		q -= K1K2;
+	}
+	progressive = K1K2 + K1 + 1;
+	regressive = dataLength - 1;
+	for (int k3 = 1; (k3 < K3); k3++) {
+		for (int k2 = halfK2; (k2 < K2); k2++) {
+			for (int k1 = 1; (k1 < K1); k1++) {
+				reDataFloat[regressive] = reDataFloat[progressive];
+				imDataFloat[regressive] = -imDataFloat[progressive];
+				progressive++;
+				regressive--;
+			}
+			progressive++;
+			regressive--;
+		}
+		progressive += halfK2 * K1;
+		regressive -= halfK2 * K1;
+	}
+} /* end transformRealFloat3DColumnFirst */
+
+/*------------------------------------------------------------------*/
+private void transformRealFloat3DRowFirst (
+) {
+	final int K1 = width.intValue();
+	final int K2 = height.intValue();
+	final int K3 = depth.intValue();
+	final int K1K2 = K1 * K2;
+	final int K2K3 = K2 * K3;
+	final int halfK1 = (K1 >> 1) + 1;
+	int k = 0;
+	int p = 0;
+	// First row for an odd (height * depth)
+	if (1 == (K2K3 & 1)) {
+		final FFTSetupReal fft = FFTSetupReal.transforms.get(width);
+		switch (fft.algorithm) {
+			case BRUTEFORCE: {
+				new DFTBruteForceRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case COPRIMEFACTOR: {
+				new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.ruritanian, fft.chinese, fft.K1).run();
+				break;
+			}
+			case DUOREAL: {
+				throw(new IllegalStateException());
+			}
+			case EVENREAL: {
+				new DFTEvenRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+			case LENGTH1: {
+				imDataFloat[0] = 0.0F;
+				break;
+			}
+			case LENGTH2: {
+				new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH3: {
+				new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH4: {
+				new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH5: {
+				new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH6: {
+				new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case LENGTH8: {
+				new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, 1).run();
+				break;
+			}
+			case MIXEDRADIX: {
+				new DFTMixedRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run();
+				break;
+			}
+			case PADDEDRADER: {
+				new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, 1,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADER: {
+				new DFTRaderRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reConvolverFloat, fft.imConvolverFloat,
+					fft.modular, fft.inverseModular).run();
+				break;
+			}
+			case RADIX2: {
+				new DFTRadix2RealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat,
+					fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run();
+				break;
+			}
+			case SPLITRADIX: {
+				new DFTSplitRadixRealFloat(reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, 0, 1,
+					fft.reUnitRootFloat, fft.imUnitRootFloat).run();
+				break;
+			}
+		}
+		k = 1;
+		p = K1;
+	}
+	// Remaining rows
+	final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(width);
+	final ExecutorService executor1 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	switch (fft1.algorithm) {
+		case BRUTEFORCE: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTBruteForceRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTCoprimeFactorRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.ruritanian, fft1.chinese, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTDuoRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, p + K1, 1, K1));
+				p += 2 * K1;
+				k++;
+			}
+			break;
+		}
+		case EVENREAL: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTEvenRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH1: {
+			while (k++ < K2K3) {
+				imDataFloat[p] = 0.0F;
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH2: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength2RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength3RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength4RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength5RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength6RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTLength8RealFloat(
+					reDataFloat, imDataFloat, p, 1));
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTMixedRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1));
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTPaddedRaderRealFloat(
+					reDataFloat, imDataFloat, p, 1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTRaderRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reConvolverFloat, fft1.imConvolverFloat,
+					fft1.modular, fft1.inverseModular));
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTRadix2RealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat,
+					fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat));
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			while (k++ < K2K3) {
+				executor1.execute(new DFTSplitRadixRealFloat(
+					reDataFloat, imDataFloat,
+					reBufferFloat, imBufferFloat, p, 1,
+					fft1.reUnitRootFloat, fft1.imUnitRootFloat));
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor1.shutdown();
+		executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Columns
+	final FFTSetup fft2 = FFTSetup.transforms.get(height);
+	final ExecutorService executor2 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	p = 0;
+	switch (fft2.algorithm) {
+		case BRUTEFORCE: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTBruteForceFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTCoprimeFactorFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.ruritanian, fft2.chinese, fft2.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength2Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength3Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength4Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength5Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength6Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTLength8Float(
+						reDataFloat, imDataFloat, p + k1, K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTMixedRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTPaddedRaderFloat(
+						reDataFloat, imDataFloat, p + k1, K1,
+						fft2.reConvolverFloat, fft2.imConvolverFloat,
+						fft2.modular, fft2.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTRaderFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reConvolverFloat, fft2.imConvolverFloat,
+						fft2.modular, fft2.inverseModular));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTRadix2Float(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k3 = 0; (k3 < K3); k3++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor2.execute(new DFTSplitRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1,
+						fft2.reUnitRootFloat, fft2.imUnitRootFloat));
+				}
+				p += K1K2;
+			}
+			break;
+		}
+	}
+	try {
+		executor2.shutdown();
+		executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	// Across
+	final FFTSetup fft3 = FFTSetup.transforms.get(depth);
+	final ExecutorService executor3 =
+		(PARALLELPROCESSING) ? (Executors.newFixedThreadPool(
+		Runtime.getRuntime().availableProcessors()))
+		: (Executors.newSingleThreadExecutor());
+	p = 0;
+	switch (fft3.algorithm) {
+		case BRUTEFORCE: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTBruteForceFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case COPRIMEFACTOR: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTCoprimeFactorFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1K2,
+						fft3.ruritanian, fft3.chinese, fft3.K1));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case DUOREAL: {
+			throw(new IllegalStateException());
+		}
+		case EVENREAL: {
+			throw(new IllegalStateException());
+		}
+		case LENGTH1: {
+			break;
+		}
+		case LENGTH2: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength2Float(
+						reDataFloat, imDataFloat, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH3: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength3Float(
+						reDataFloat, imDataFloat, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH4: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength4Float(
+						reDataFloat, imDataFloat, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH5: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength5Float(
+						reDataFloat, imDataFloat, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH6: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength6Float(
+						reDataFloat, imDataFloat, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case LENGTH8: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTLength8Float(
+						reDataFloat, imDataFloat, p + k1, K1K2));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case MIXEDRADIX: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTMixedRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat, fft3.K1));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case PADDEDRADER: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTPaddedRaderFloat(
+						reDataFloat, imDataFloat, p + k1, K1K2,
+						fft3.reConvolverFloat, fft3.imConvolverFloat,
+						fft3.modular, fft3.inverseModular));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case RADER: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTRaderFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1K2,
+						fft3.reConvolverFloat, fft3.imConvolverFloat,
+						fft3.modular, fft3.inverseModular));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case RADIX2: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTRadix2Float(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+				}
+				p += K1;
+			}
+			break;
+		}
+		case SPLITRADIX: {
+			for (int k2 = 0; (k2 < K2); k2++) {
+				for (int k1 = 0; (k1 < halfK1); k1++) {
+					executor3.execute(new DFTSplitRadixFloat(
+						reDataFloat, imDataFloat,
+						reBufferFloat, imBufferFloat, p + k1, K1K2,
+						fft3.reUnitRootFloat, fft3.imUnitRootFloat));
+				}
+				p += K1;
+			}
+			break;
+		}
+	}
+	try {
+		executor3.shutdown();
+		executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS);
+	}
+	catch (InterruptedException ignored) {
+	}
+	int progressive = 0;
+	int regressive = K1;
+	while (++progressive < --regressive) {
+		reDataFloat[regressive] = reDataFloat[progressive];
+		imDataFloat[regressive] = -imDataFloat[progressive];
+	}
+	p = K1 + 1;
+	int q = K1K2 - 1;
+	for (int k2 = 1; (k2 < K2); k2++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = halfK1; (k1 < K1); k1++) {
+			reDataFloat[regressive] = reDataFloat[progressive];
+			imDataFloat[regressive] = -imDataFloat[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1;
+		q -= K1;
+	}
+	p = K1K2 + 1;
+	q = K1K2 * (K3 - 1) + K1 - 1;
+	for (int k3 = 1; (k3 < K3); k3++) {
+		progressive = p;
+		regressive = q;
+		for (int k1 = halfK1; (k1 < K1); k1++) {
+			reDataFloat[regressive] = reDataFloat[progressive];
+			imDataFloat[regressive] = -imDataFloat[progressive];
+			progressive++;
+			regressive--;
+		}
+		p += K1K2;
+		q -= K1K2;
+	}
+	progressive = K1K2 + K1 + 1;
+	regressive = dataLength - 1;
+	for (int k3 = 1; (k3 < K3); k3++) {
+		for (int k2 = 1; (k2 < K2); k2++) {
+			for (int k1 = halfK1; (k1 < K1); k1++) {
+				reDataFloat[regressive] = reDataFloat[progressive];
+				imDataFloat[regressive] = -imDataFloat[progressive];
+				progressive++;
+				regressive--;
+			}
+			progressive += halfK1;
+			regressive -= halfK1;
+		}
+		progressive += K1;
+		regressive -= K1;
+	}
+} /* end transformRealFloat3DRowFirst */
+
+} /* end class SlowFourierTransform */
diff --git a/src/bilib/src/bilib/optimization/.DS_Store b/src/bilib/src/bilib/optimization/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..1819d85b732e46e0739ef8393b27c7199da021a0
Binary files /dev/null and b/src/bilib/src/bilib/optimization/.DS_Store differ
diff --git a/src/bilib/src/bilib/optimization/levenbergmarquardt/Cholesky.java b/src/bilib/src/bilib/optimization/levenbergmarquardt/Cholesky.java
new file mode 100644
index 0000000000000000000000000000000000000000..49fe8290d71f8a0820bda79f6c6a23fba22f8433
--- /dev/null
+++ b/src/bilib/src/bilib/optimization/levenbergmarquardt/Cholesky.java
@@ -0,0 +1,111 @@
+package bilib.optimization.levenbergmarquardt;
+
+/**
+ * 
+ * <p>
+ * Title: Cholesky Decomposition
+ * </p>
+ * <p>
+ * Description: Performs a Cholesky decomposition of a matrix and solve a linear
+ * system using this decomposition. Ported to Java from the Numerical Recipes in
+ * C. Press, Teukolsky, Vetterling,and Flannery. 2nd edition. Cambridge
+ * University Press, 1992.
+ * </p>
+ */
+
+public class Cholesky {
+
+	/**
+	 * Given a positive-definite symmetric matrix A[1..n][1..n], this method
+	 * constructs its Cholesky decomposition, A = L � L' . On input, only the
+	 * upper triangle of a need be given; it is not modified. The Cholesky
+	 * factor L is returned in the lower triangle of a, except for its diagonal
+	 * elements which are returned in p[1..n].
+	 * 
+	 * @param A
+	 *            double[][] Input matrix.
+	 * @param p
+	 *            double[]
+	 * @return boolean Returns false if the decomposition is not possible.
+	 */
+	public static boolean decomp(double[][] A, double[] p) {
+		int n = A.length;
+		int i, j, k;
+		double sum;
+		for (i = 0; i < n; i++) {
+			for (j = 0; j < n; j++) {
+				sum = A[i][j];
+				for (k = i - 1; k >= 0; k--) {
+					sum -= (A[i][k] * A[j][k]);
+				}
+				if (i == j) {
+					if (sum <= 0.) {
+						return false; // not positive definite
+					}
+					p[i] = Math.sqrt(sum);
+				}
+				else {
+					A[j][i] = sum / p[i];
+				}
+			}
+		}
+		return true;
+	} // decomp
+
+	/**
+	 * Solves a the linear system Ax=b.
+	 * 
+	 * @param A
+	 *            double[][] Is the result of decomp(A)
+	 * @param p
+	 *            double[] The resulting diagonal vector.
+	 * @param b
+	 *            double[]
+	 * @param x
+	 *            double[]
+	 */
+	private static void solve(double[][] A, double[] p, double[] b, double[] x) {
+		int n = A.length;
+		int i, k;
+		double sum;
+		// Solve L � y = b, storing y in x.
+		for (i = 0; i < n; i++) {
+			sum = b[i];
+			for (k = i - 1; k >= 0; k--) {
+				sum -= (A[i][k] * x[k]);
+			}
+			x[i] = sum / p[i];
+		}
+
+		// Solve L' � x = y.
+		for (i = n - 1; i >= 0; i--) {
+			sum = x[i];
+			for (k = i + 1; k < n; k++) {
+				sum -= (A[k][i] * x[k]);
+			}
+			x[i] = sum / p[i];
+		}
+	} // solve
+
+	/**
+	 * Solves the linear system Ax=b.
+	 * 
+	 * @param A
+	 *            double[][]
+	 * @param x
+	 *            double[]
+	 * @param b
+	 *            double[]
+	 * @return boolean returns false if the system can not be solved using
+	 *         Cholesky decomposition.
+	 */
+	public static boolean solve(double[][] A, double[] x, double[] b) {
+		double[] p = new double[A.length];
+		if (!decomp(A, p)) {
+			return false;
+		}
+		solve(A, p, b, x);
+		return true;
+	} // solve
+
+} // Cholesky
diff --git a/src/bilib/src/bilib/optimization/levenbergmarquardt/Function.java b/src/bilib/src/bilib/optimization/levenbergmarquardt/Function.java
new file mode 100644
index 0000000000000000000000000000000000000000..8adc8be5dd76f2194a8bdf5af4fa4901e3c99b81
--- /dev/null
+++ b/src/bilib/src/bilib/optimization/levenbergmarquardt/Function.java
@@ -0,0 +1,32 @@
+package bilib.optimization.levenbergmarquardt;
+
+/**
+ */
+public interface Function {
+
+	/**
+	 * Evaluates the model at point x (may be mulidimensional).
+	 * 
+	 * @param x
+	 *            double[] Point where we evaluate the model function.
+	 * @param a
+	 *            double[] Model estimators.
+	 * @return double
+	 */
+	public abstract double eval(double[] x, double[] a);
+
+	/**
+	 * Returns the kth component of the gradient df(x,a)/da_k
+	 * 
+	 * @param x
+	 *            double[]
+	 * @param a
+	 *            double[]
+	 * @param ak
+	 *            int
+	 * @return double
+	 */
+	public abstract double grad(double[] x, double[] a, int ak);
+
+	public abstract void setDebug(boolean debug);
+}
diff --git a/src/bilib/src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java b/src/bilib/src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ef29f8720ccc3472fbb766fea869e1f37f5d0cf
--- /dev/null
+++ b/src/bilib/src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java
@@ -0,0 +1,297 @@
+package bilib.optimization.levenbergmarquardt;
+
+/**
+ * 
+ * <p>
+ * Title: Levenberg-Marquardt
+ * </p>
+ * <p>
+ * Description: Perfoms data fitting to a non linear model using the
+ * Levenberg-Marquadrt method. Ported to Java from the Numerical Recipes in C.
+ * Press, Teukolsky, Vetterling,and Flannery. 2nd edition. Cambridge University
+ * Press, 1992.
+ * </p>
+ */
+
+public class LevenbergMarquardt {
+
+	private Function	f;
+	private double		lambdaInitial	= 0.0001;
+	private int			itmax			= 1000;
+	private boolean		print			= false;
+	private int			iter;
+	private double		tolerance		= 0.001;
+
+	/**
+	 * Levenberg-Marquardt constructor. Supply a Function object, f, that
+	 * evaluates the fitting function y, and its derivatives dyda[1..ma] with
+	 * respect to the fitting parameters a at x. On the first call provide an
+	 * initial guess for the parameters a, and set alamda to some small value,
+	 * e.g. alambda=0.001. If a step succeeds chisq becomes smaller and alamda
+	 * decreases by a factor of 10. If a step fails alamda grows by a factor of
+	 * 10.
+	 * 
+	 * @param f
+	 *            Function
+	 * @param lambdaInitial
+	 *            double
+	 * @param itmax
+	 *            int
+	 */
+	public LevenbergMarquardt(Function f, double lambdaInitial, int itmax, boolean print) {
+		this.f = f;
+		this.lambdaInitial = lambdaInitial;
+		this.itmax = itmax;
+		this.print = print;
+		if (print) {
+			System.out.print("CONSTRUCTOR \tlambda:" + lambdaInitial + " max iterations: " + itmax);
+		}
+	}
+
+	public LevenbergMarquardt(Function f, int itmax, double tolerance) {
+		this.f = f;
+		this.itmax = itmax;
+		this.tolerance = tolerance;
+	}
+
+	public LevenbergMarquardt(Function f, int itmax) {
+		this.f = f;
+		this.itmax = itmax;
+	}
+
+	public LevenbergMarquardt(Function f, boolean print) {
+		this.f = f;
+		this.print = print;
+	}
+
+	public LevenbergMarquardt(Function f) {
+		this.f = f;
+	}
+
+	public void setPrint(boolean print) {
+		this.print = print;
+	}
+
+	/**
+	 * Levenberg-Marquardt method, attempting to reduce the value chi2 of a fit
+	 * between a set of data points x[1..ndata], y[1..ndata] with individual
+	 * standard deviations sig[1..ndata], and a nonlinear function dependent on
+	 * ma coefficients a[1..ma]. The input array ia[1..ma] indicates by true,
+	 * entries those components of a that should be fitted for, and by false,
+	 * entries those components that should be held fixed at their input values.
+	 * The program returns current best-fit values for the parameters a[1..ma],
+	 * and chi2 = chisq.
+	 * 
+	 * @param x
+	 *            double[]
+	 * @param y
+	 *            double[]
+	 * @param sig
+	 *            double[]
+	 * @param a
+	 *            double[]
+	 * @return double
+	 * 
+	 */
+	public double minimize(double x[], double y[], double sig[], double a[]) {
+		iter = 0;
+		double lambda = lambdaInitial;
+
+		boolean ia[] = new boolean[a.length];
+		for (int i = 0; i < a.length; i++)
+			ia[i] = true;
+
+		int rep = 0;
+		boolean done = false;
+		double eps = 0;
+		int mfit = 0;
+		int j, k, l;
+		int ma = a.length;
+		double ochisq = 0, chisq;
+
+		double[][] covar = new double[ma][ma];
+		double[][] alpha = new double[ma][ma];
+		double[] beta = new double[ma];
+		double[] atry = new double[ma];
+		double[] da = new double[ma];
+
+		double[] oneda;
+
+		// initialization
+		for (mfit = 0, j = 0; j < ma; j++) {
+			if (ia[j]) {
+				mfit++;
+			}
+		}
+		oneda = new double[mfit];
+
+		chisq = mrqcof(x, y, sig, a, ia, alpha, beta);
+		ochisq = chisq;
+		for (j = 0; j < ma; j++) {
+			atry[j] = a[j];
+		}
+
+		do {
+			// Alter linearized fitting matrix, by augmenting diagonal elements.
+			for (j = 0; j < mfit; j++) {
+				for (k = 0; k < mfit; k++) {
+					covar[j][k] = alpha[j][k];
+				}
+				covar[j][j] = alpha[j][j] * (1.0 + lambda);
+				oneda[j] = beta[j];
+			}
+
+			Cholesky.solve(covar, oneda, oneda); // Matrix solution.
+
+			for (j = 0; j < mfit; j++) {
+				da[j] = oneda[j];
+			}
+
+			for (j = 0, l = 0; l < ma; l++) {
+				if (ia[l]) {
+					atry[l] = a[l] + da[j++];
+				}
+			}
+			chisq = mrqcof(x, y, sig, atry, ia, covar, da);
+			eps = Math.abs(chisq - ochisq);
+			if (print) {
+				System.out.print("#" + iter + "\t chi:" + Math.round(Math.sqrt(chisq) * 1000) / 1000.0 + " \tlambda:" + lambda + " eps:" + eps);
+				for (int i = 0; i < a.length; i++)
+					System.out.print("\t a[" + i + "]=" + atry[i]);
+				System.out.println(";");
+			}
+			if (chisq < ochisq) {
+				// Success, accept the new solution.
+				lambda *= 0.1;
+				ochisq = chisq;
+				for (j = 0; j < mfit; j++) {
+					for (k = 0; k < mfit; k++) {
+						alpha[j][k] = covar[j][k];
+					}
+					beta[j] = da[j];
+				}
+				for (l = 0; l < ma; l++) {
+					a[l] = atry[l];
+				}
+			}
+			else {
+				// Failure, increase alamda and return.
+				lambda *= 10.0;
+				chisq = ochisq;
+			}
+			iter++;
+			if (eps > tolerance) {
+				rep = 0;
+			}
+			else {
+				rep++;
+				if (rep == 4) {
+					done = true;
+				}
+			}
+
+		}
+		while (iter < itmax && !done);
+		if (print)
+			System.out.println("Final iter" + iter + "\t rep:" + rep + " \tdone:" + done + " eps:" + eps + " tolerance:" + tolerance);
+
+		return Math.sqrt(chisq);
+	}
+
+	/**
+	 * Return the number of iterations after minimization.
+	 * 
+	 * @return number of iteration
+	 */
+	public int getIteration() {
+		return iter;
+	}
+
+	/**
+	 * Used by mrqmin to evaluate the linearized fitting matrix alpha, and
+	 * vector beta as in "NR in C"(15.5.8), and calculate chi2.
+	 * 
+	 * @param x
+	 *            double[]
+	 * @param y
+	 *            double[]
+	 * @param sig
+	 *            double[]
+	 * @param a
+	 *            double[]
+	 * @param ia
+	 *            boolean[]
+	 * @param alpha
+	 *            double[][]
+	 * @param beta
+	 *            double[]
+	 * @param f
+	 *            LMfunc
+	 * @return double
+	 */
+	private double mrqcof(double x[], double y[], double sig[], double a[], boolean ia[], double alpha[][], double beta[]) {
+
+		int ndata = x.length;
+		int ma = a.length;
+		double chisq;
+		int i, j, k, l, m, mfit = 0;
+		double ymod, wt, sig2i, dy;
+		double[] dyda = new double[ma];
+
+		for (j = 0; j < ma; j++) {
+			if (ia[j]) {
+				mfit++;
+			}
+		}
+
+		// Initialize(symmetric) alpha, beta.
+		for (j = 0; j < mfit; j++) {
+			for (k = 0; k <= j; k++) {
+				alpha[j][k] = 0;
+			}
+			beta[j] = 0;
+		}
+
+		chisq = 0;
+
+		// Summation loop over all data.
+		for (i = 0; i < ndata; i++) {
+			double[] xi = new double[1];
+			xi[0] = x[i];
+			ymod = f.eval(xi, a);
+			for (k = 0; k < a.length; k++) {
+				dyda[k] = f.grad(xi, a, k);
+			}
+
+			/*
+			 * if (print) { System.out.print("D" + iter); for(int p=0;
+			 * p<dyda.length; p++) System.out.print("\t da["+p+"]=" + dyda[p]);
+			 * System.out.println(";"); }
+			 */
+			sig2i = 1.0 / (sig[i] * sig[i]);
+			dy = y[i] - ymod;
+			for (j = 0, l = 0; l < ma; l++) {
+				if (ia[l]) {
+					wt = dyda[l] * sig2i;
+					for (k = 0, m = 0; m <= l; m++) {
+						if (ia[m]) {
+							alpha[j][k++] += wt * dyda[m];
+						}
+					}
+					beta[j] += dy * wt;
+					j++;
+				}
+			}
+			chisq += dy * dy * sig2i; // And find chi2.
+		}
+
+		// Fill in the symmetric side of alpha
+		for (j = 1; j < mfit; j++) {
+			for (k = 0; k < j; k++) {
+				alpha[k][j] = alpha[j][k];
+			}
+		}
+		return chisq;
+	}
+
+}
diff --git a/src/bilib/src/ijtools/Convolver1D.java b/src/bilib/src/ijtools/Convolver1D.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8a6b34195d1d7eab08acf785570162c0e22a5ed
--- /dev/null
+++ b/src/bilib/src/ijtools/Convolver1D.java
@@ -0,0 +1,118 @@
+package ijtools;
+
+/**
+ * Fast 1-D convolution routines for symmetric kernels. All methods require only
+ * the causal half of the kernel, i.e., elements [0..n], assuming a support of
+ * [-n..n]. Only kernels with odd support are implemented. Mirror border
+ * conditions are applied.
+ * 
+ * @author Francois Aguet
+ * @version 1.0
+ */
+public class Convolver1D {
+
+	/**
+	 * Convolution with a symmetric kernel.
+	 */
+	public static double[] convolveEven(double[] input, double[] kernel) {
+
+		int nx = input.length;
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		double[] output = new double[nx];
+
+		for (int x = 0; x < k_1; x++) {
+			output[x] = kernel[0] * input[x];
+			for (int i = 1; i <= x; i++) {
+				output[x] += kernel[i] * (input[x - i] + input[x + i]);
+			}
+			for (int i = x + 1; i < k; i++) {
+				output[x] += kernel[i] * (input[i - x] + input[x + i]);
+			}
+		}
+		for (int x = k_1; x <= nx - k; x++) {
+			output[x] = kernel[0] * input[x];
+			for (int i = 1; i < k; i++) {
+				output[x] += kernel[i] * (input[x - i] + input[x + i]);
+			}
+		}
+		for (int x = nx - k_1; x < nx; x++) {
+			output[x] = kernel[0] * input[x];
+			for (int i = 1; i < nx - x; i++) {
+				output[x] += kernel[i] * (input[x - i] + input[x + i]);
+			}
+			for (int i = nx - x; i < k; i++) {
+				output[x] += kernel[i] * (input[b - i - x] + input[x - i]);
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with an anti-symmetric kernel.
+	 */
+	public static double[] convolveOdd(double[] input, double[] kernel) {
+
+		int nx = input.length;
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		double[] output = new double[nx];
+
+		for (int x = 0; x < k_1; x++) {
+			output[x] = 0.0;
+			for (int i = 1; i <= x; i++) {
+				output[x] += kernel[i] * (input[x + i] - input[x - i]);
+			}
+			for (int i = x + 1; i < k; i++) {
+				output[x] += kernel[i] * (input[x + i] - input[i - x]);
+			}
+		}
+		for (int x = k_1; x <= nx - k; x++) {
+			output[x] = 0.0;
+			for (int i = 1; i < k; i++) {
+				output[x] += kernel[i] * (input[x + i] - input[x - i]);
+			}
+		}
+		for (int x = nx - k_1; x < nx; x++) {
+			output[x] = 0.0;
+			for (int i = 1; i < nx - x; i++) {
+				output[x] += kernel[i] * (input[x + i] - input[x - i]);
+			}
+			for (int i = nx - x; i < k; i++) {
+				output[x] += kernel[i] * (input[b - i - x] - input[x - i]);
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Moving sum filter. The length of the output is extended by 'length-1'.
+	 */
+	public static double[] movingSum(double[] input, int length) {
+
+		int nx = input.length;
+		double[] output = new double[nx + length - 1];
+		output[0] = input[0];
+		for (int i = 1; i < length; i++) {
+			output[0] += input[i];
+		}
+		for (int i = 1; i < length; i++) {
+			output[i] = output[i - 1] - input[length - i] + input[i]; // 0 -
+																		// (idx-w)
+		}
+		for (int i = length; i < nx; i++) {
+			output[i] = output[i - 1] - input[i - length] + input[i];
+		}
+		for (int i = nx; i < nx + length - 1; i++) {
+			output[i] = output[i - 1] - input[i - length] + input[2 * nx - i - 2]; // nx-1
+																					// -
+																					// (i-(nx-1))
+		}
+		return output;
+	}
+
+}
diff --git a/src/bilib/src/ijtools/Convolver2D.java b/src/bilib/src/ijtools/Convolver2D.java
new file mode 100644
index 0000000000000000000000000000000000000000..43bec6247b7a1b4975ccac4cfbd0b193a59b82f2
--- /dev/null
+++ b/src/bilib/src/ijtools/Convolver2D.java
@@ -0,0 +1,1133 @@
+package ijtools;
+
+/**
+ * Fast 2-D convolution routines for symmetric kernels. All methods require only
+ * the causal half of the kernel, i.e., elements [0..n], assuming a support of
+ * [-n..n]. Only kernels with odd support are implemented. Mirror border
+ * conditions are applied.
+ * 
+ * @author Francois Aguet
+ * @version 1.0
+ */
+public class Convolver2D {
+
+	/**
+	 * Moving sum filter, applied to both dimensions. Each dimension of the
+	 * output is extended by 'length-1'.
+	 */
+	public static double[][] movingSum(double[][] input, int length) {
+
+		int nx = input.length;
+		int ny = input[0].length;
+
+		double[][] output = new double[nx + length - 1][ny + length - 1];
+
+		for (int y = 0; y < ny; y++) {
+			output[0][y] = input[0][y];
+			for (int x = 1; x < length; x++) {
+				output[0][y] += input[x][y];
+			}
+			for (int x = 1; x < length; x++) {
+				output[x][y] = output[x - 1][y] - input[length - x][y] + input[x][y];
+			}
+			for (int x = length; x < nx; x++) {
+				output[x][y] = output[x - 1][y] - input[x - length][y] + input[x][y];
+			}
+			for (int x = nx; x < nx + length; x++) {
+				output[x][y] = output[x - 1][y] - input[x - length][y] + input[2 * nx - x - 2][y];
+			}
+		}
+
+		for (int x = 0; x < nx + length - 1; x++) {
+			output[x][0] = output[x][0];
+			for (int y = 1; y < length; y++) {
+				output[x][0] += output[x][y];
+			}
+			for (int y = 1; y < length; y++) {
+				output[x][y] = output[x][y - 1] - output[x][length - y] + output[x][y];
+			}
+			for (int y = length; y < ny; y++) {
+				output[x][y] = output[x][y - 1] - output[x][y - length] + output[x][y];
+			}
+			for (int y = ny; y < ny + length - y; y++) {
+				output[x][y] = output[x][y - 1] - output[x][y - length] + output[x][2 * ny - y - 2];
+			}
+		}
+
+		return output;
+	}
+
+	/**
+	 * Moving sum filter, applied to the x-dimension. The x-dimension of the
+	 * output is extended by 'length-1'.
+	 */
+	public static double[][] movingSumX(double[][] input, int length) {
+
+		int nx = input.length;
+		int ny = input[0].length;
+
+		double[][] output = new double[nx + length - 1][ny + length - 1];
+
+		for (int y = 0; y < ny; y++) {
+			output[0][y] = input[0][y];
+			for (int x = 1; x < length; x++) {
+				output[0][y] += input[x][y];
+			}
+			for (int x = 1; x < length; x++) {
+				output[x][y] = output[x - 1][y] - input[length - x][y] + input[x][y];
+			}
+			for (int x = length; x < nx; x++) {
+				output[x][y] = output[x - 1][y] - input[x - length][y] + input[x][y];
+			}
+			for (int x = nx; x < nx + length; x++) {
+				output[x][y] = output[x - 1][y] - input[x - length][y] + input[2 * nx - x - 2][y];
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Moving sum filter, applied to the y-dimension. The y-dimension of the
+	 * output is extended by 'length-1'.
+	 */
+	public static double[][] movingSumY(double[][] input, int length) {
+
+		int nx = input.length;
+		int ny = input[0].length;
+
+		double[][] output = new double[nx + length - 1][ny + length - 1];
+
+		for (int x = 0; x < nx + length - 1; x++) {
+			output[x][0] = output[x][0];
+			for (int y = 1; y < length; y++) {
+				output[x][0] += output[x][y];
+			}
+			for (int y = 1; y < length; y++) {
+				output[x][y] = output[x][y - 1] - output[x][length - y] + output[x][y];
+			}
+			for (int y = length; y < ny; y++) {
+				output[x][y] = output[x][y - 1] - output[x][y - length] + output[x][y];
+			}
+			for (int y = ny; y < ny + length - y; y++) {
+				output[x][y] = output[x][y - 1] - output[x][y - length] + output[x][2 * ny - y - 2];
+			}
+		}
+
+		return output;
+	}
+
+	/**
+	 * Convolution with a symmetric kernel along x.
+	 */
+	public static double[][] convolveEvenX(double[][] input, double[] kernel) {
+
+		int nx = input.length;
+		int ny = input[0].length;
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		double[][] output = new double[nx][ny];
+
+		int idx = 0;
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < k_1; x++) {
+				output[x][y] = kernel[0] * input[x][y];
+				for (int i = 1; i <= x; i++) {
+					output[x][y] += kernel[i] * (input[x - i][y] + input[x + i][y]);
+				}
+				for (int i = x + 1; i < k; i++) {
+					output[x][y] += kernel[i] * (input[i - x][y] + input[x + i][y]);
+				}
+			}
+			for (int x = k_1; x <= nx - k; x++) {
+				output[x][y] = kernel[0] * input[x][y];
+				for (int i = 1; i < k; i++) {
+					output[x][y] += kernel[i] * (input[x - i][y] + input[x + i][y]);
+				}
+			}
+			for (int x = nx - k_1; x < nx; x++) {
+				output[x][y] = kernel[0] * input[x][y];
+				for (int i = 1; i < nx - x; i++) {
+					output[x][y] += kernel[i] * (input[x - i][y] + input[x + i][y]);
+				}
+				for (int i = nx - x; i < k; i++) {
+					output[x][y] += kernel[i] * (input[b - i - x][y] + input[x - i][y]);
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with a symmetric kernel along x, for an image defined as a
+	 * 1-D array.
+	 */
+	public static double[] convolveEvenX(double[] input, double[] kernel, int nx, int ny) {
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		double[] output = new double[nx * ny];
+
+		int idx = 0;
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < k_1; x++) {
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i <= x; i++) {
+					output[idx] += kernel[i] * (input[idx - i] + input[idx + i]);
+				}
+				for (int i = x + 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[i - x + y * nx] + input[idx + i]);
+				}
+				idx++;
+			}
+			for (int x = k_1; x <= nx - k; x++) {
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[idx - i] + input[idx + i]);
+				}
+				idx++;
+			}
+			for (int x = nx - k_1; x < nx; x++) {
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i < nx - x; i++) {
+					output[idx] += kernel[i] * (input[idx - i] + input[idx + i]);
+				}
+				for (int i = nx - x; i < k; i++) {
+					output[idx] += kernel[i] * (input[b - i - x + y * nx] + input[idx - i]);
+				}
+				idx++;
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with a symmetric kernel along x, for an image defined as a
+	 * 1-D array.
+	 */
+	public static float[] convolveEvenX(float[] input, float[] kernel, int nx, int ny) {
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		float[] output = new float[nx * ny];
+
+		int idx = 0;
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < k_1; x++) {
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i <= x; i++) {
+					output[idx] += kernel[i] * (input[idx - i] + input[idx + i]);
+				}
+				for (int i = x + 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[i - x + y * nx] + input[idx + i]);
+				}
+				idx++;
+			}
+			for (int x = k_1; x <= nx - k; x++) {
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[idx - i] + input[idx + i]);
+				}
+				idx++;
+			}
+			for (int x = nx - k_1; x < nx; x++) {
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i < nx - x; i++) {
+					output[idx] += kernel[i] * (input[idx - i] + input[idx + i]);
+				}
+				for (int i = nx - x; i < k; i++) {
+					output[idx] += kernel[i] * (input[b - i - x + y * nx] + input[idx - i]);
+				}
+				idx++;
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with a symmetric kernel along y.
+	 */
+	public static double[][] convolveEvenY(double[][] input, double[] kernel) {
+
+		int nx = input.length;
+		int ny = input[0].length;
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * ny - 2;
+
+		double[][] output = new double[nx][ny];
+
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < k_1; y++) {
+				output[x][y] = kernel[0] * input[x][y];
+				for (int i = 1; i <= y; i++) {
+					output[x][y] += kernel[i] * (input[x][y - i] + input[x][y + i]);
+				}
+				for (int i = y + 1; i < k; i++) {
+					output[x][y] += kernel[i] * (input[x][i - y] + input[x][y + i]);
+				}
+			}
+			for (int y = k_1; y <= ny - k; y++) {
+				output[x][y] = kernel[0] * input[x][y];
+				for (int i = 1; i < k; i++) {
+					output[x][y] += kernel[i] * (input[x][y - i] + input[x][y + i]);
+				}
+			}
+			for (int y = ny - k_1; y < ny; y++) {
+				output[x][y] = kernel[0] * input[x][y];
+				for (int i = 1; i < ny - y; i++) {
+					output[x][y] += kernel[i] * (input[x][y - i] + input[x][y + i]);
+				}
+				for (int i = ny - y; i < k; i++) {
+					output[x][y] += kernel[i] * (input[x][b - i - y] + input[x][y - i]);
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with a symmetric kernel along y, for an image defined as a
+	 * 1-D array.
+	 */
+	public static double[] convolveEvenY(double[] input, double[] kernel, int nx, int ny) {
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * ny - 2;
+
+		double[] output = new double[nx * ny];
+
+		int idx, inx;
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < k_1; y++) {
+				idx = x + y * nx;
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i <= y; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+				}
+				for (int i = y + 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[(i - y) * nx + x] + input[idx + i * nx]);
+				}
+			}
+			for (int y = k_1; y <= ny - k; y++) {
+				idx = x + y * nx;
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i < k; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+				}
+			}
+			for (int y = ny - k_1; y < ny; y++) {
+				idx = x + y * nx;
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i < ny - y; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+				}
+				for (int i = ny - y; i < k; i++) {
+					output[idx] += kernel[i] * (input[(b - i - y) * nx + x] + input[idx - i * nx]);
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with a symmetric kernel along y, for an image defined as a
+	 * 1-D array.
+	 */
+	public static float[] convolveEvenY(float[] input, float[] kernel, int nx, int ny) {
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * ny - 2;
+
+		float[] output = new float[nx * ny];
+
+		int idx, inx;
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < k_1; y++) {
+				idx = x + y * nx;
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i <= y; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+				}
+				for (int i = y + 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[(i - y) * nx + x] + input[idx + i * nx]);
+				}
+			}
+			for (int y = k_1; y <= ny - k; y++) {
+				idx = x + y * nx;
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i < k; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+				}
+			}
+			for (int y = ny - k_1; y < ny; y++) {
+				idx = x + y * nx;
+				output[idx] = kernel[0] * input[idx];
+				for (int i = 1; i < ny - y; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+				}
+				for (int i = ny - y; i < k; i++) {
+					output[idx] += kernel[i] * (input[(b - i - y) * nx + x] + input[idx - i * nx]);
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with an anti-symmetric kernel along x.
+	 */
+	public static double[][] convolveOddX(double[][] input, double[] kernel) {
+
+		int nx = input.length;
+		int ny = input[0].length;
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		double[][] output = new double[nx][ny];
+
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < k_1; x++) {
+				output[x][y] = 0.0;
+				for (int i = 1; i <= x; i++) {
+					output[x][y] += kernel[i] * (input[x + i][y] - input[x - i][y]);
+				}
+				for (int i = x + 1; i < k; i++) {
+					output[x][y] += kernel[i] * (input[x + i][y] - input[i - x][y]);
+				}
+			}
+			for (int x = k_1; x <= nx - k; x++) {
+				output[x][y] = 0.0;
+				for (int i = 1; i < k; i++) {
+					output[x][y] += kernel[i] * (input[x + i][y] - input[x - i][y]);
+				}
+			}
+			for (int x = nx - k_1; x < nx; x++) {
+				output[x][y] = 0.0;
+				for (int i = 1; i < nx - x; i++) {
+					output[x][y] += kernel[i] * (input[x + i][y] - input[x - i][y]);
+				}
+				for (int i = nx - x; i < k; i++) {
+					output[x][y] += kernel[i] * (input[b - i - x][y] - input[x - i][y]);
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with an anti-symmetric kernel along x, for an image defined
+	 * as a 1-D array.
+	 */
+	public static double[] convolveOddX(double[] input, double[] kernel, int nx, int ny) {
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		double[] output = new double[nx * ny];
+
+		int idx = 0;
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < k_1; x++) {
+				output[idx] = 0.0;
+				for (int i = 1; i <= x; i++) {
+					output[idx] += kernel[i] * (input[idx + i] - input[idx - i]);
+				}
+				for (int i = x + 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[idx + i] - input[i - x + y * nx]);
+				}
+				idx++;
+			}
+			for (int x = k_1; x <= nx - k; x++) {
+				output[idx] = 0.0;
+				for (int i = 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[idx + i] - input[idx - i]);
+				}
+				idx++;
+			}
+			for (int x = nx - k_1; x < nx; x++) {
+				output[idx] = 0.0;
+				for (int i = 1; i < nx - x; i++) {
+					output[idx] += kernel[i] * (input[idx + i] - input[idx - i]);
+				}
+				for (int i = nx - x; i < k; i++) {
+					output[idx] += kernel[i] * (input[b - i - x + y * nx] - input[idx - i]);
+				}
+				idx++;
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with an anti-symmetric kernel along x, for an image defined
+	 * as a 1-D array.
+	 */
+	public static float[] convolveOddX(float[] input, float[] kernel, int nx, int ny) {
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		float[] output = new float[nx * ny];
+
+		int idx = 0;
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < k_1; x++) {
+				output[idx] = 0.0f;
+				for (int i = 1; i <= x; i++) {
+					output[idx] += kernel[i] * (input[idx + i] - input[idx - i]);
+				}
+				for (int i = x + 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[idx + i] - input[i - x + y * nx]);
+				}
+				idx++;
+			}
+			for (int x = k_1; x <= nx - k; x++) {
+				output[idx] = 0.0f;
+				for (int i = 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[idx + i] - input[idx - i]);
+				}
+				idx++;
+			}
+			for (int x = nx - k_1; x < nx; x++) {
+				output[idx] = 0.0f;
+				for (int i = 1; i < nx - x; i++) {
+					output[idx] += kernel[i] * (input[idx + i] - input[idx - i]);
+				}
+				for (int i = nx - x; i < k; i++) {
+					output[idx] += kernel[i] * (input[b - i - x + y * nx] - input[idx - i]);
+				}
+				idx++;
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with an anti-symmetric kernel along y.
+	 */
+	public static double[][] convolveOddY(double[][] input, double[] kernel) {
+
+		int nx = input.length;
+		int ny = input.length;
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * ny - 2;
+
+		double[][] output = new double[nx][ny];
+
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < k_1; y++) {
+				output[x][y] = 0.0;
+				for (int i = 1; i <= y; i++) {
+					output[x][y] += kernel[i] * (input[x][y + i] - input[x][y - i]);
+				}
+				for (int i = y + 1; i < k; i++) {
+					output[x][y] += kernel[i] * (input[x][y + i] - input[x][i - y]);
+				}
+			}
+			for (int y = k_1; y <= ny - k; y++) {
+				output[x][y] = 0.0;
+				for (int i = 1; i < k; i++) {
+					output[x][y] += kernel[i] * (input[x][y + i] - input[x][y - i]);
+				}
+			}
+			for (int y = ny - k_1; y < ny; y++) {
+				output[x][y] = 0.0;
+				for (int i = 1; i < ny - y; i++) {
+					output[x][y] += kernel[i] * (input[x][y + i] - input[x][y - i]);
+				}
+				for (int i = ny - y; i < k; i++) {
+					output[x][y] += kernel[i] * (input[x][b - i - y] - input[x][y - i]);
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with an anti-symmetric kernel along y, for an image defined
+	 * as a 1-D array.
+	 */
+	public static double[] convolveOddY(double[] input, double[] kernel, int nx, int ny) {
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * ny - 2;
+
+		double[] output = new double[nx * ny];
+
+		int idx, inx;
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < k_1; y++) {
+				idx = x + y * nx;
+				output[idx] = 0.0;
+				for (int i = 1; i <= y; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+				}
+				for (int i = y + 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[idx + i * nx] - input[(i - y) * nx + x]);
+				}
+			}
+			for (int y = k_1; y <= ny - k; y++) {
+				idx = x + y * nx;
+				output[idx] = 0.0;
+				for (int i = 1; i < k; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+				}
+			}
+			for (int y = ny - k_1; y < ny; y++) {
+				idx = x + y * nx;
+				output[idx] = 0.0;
+				for (int i = 1; i < ny - y; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+				}
+				for (int i = ny - y; i < k; i++) {
+					output[idx] += kernel[i] * (input[(b - i - y) * nx + x] - input[idx - i * nx]);
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with an anti-symmetric kernel along y, for an image defined
+	 * as a 1-D array.
+	 */
+	public static float[] convolveOddY(float[] input, float[] kernel, int nx, int ny) {
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * ny - 2;
+
+		float[] output = new float[nx * ny];
+
+		int idx, inx;
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < k_1; y++) {
+				idx = x + y * nx;
+				output[idx] = 0.0f;
+				for (int i = 1; i <= y; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+				}
+				for (int i = y + 1; i < k; i++) {
+					output[idx] += kernel[i] * (input[idx + i * nx] - input[(i - y) * nx + x]);
+				}
+			}
+			for (int y = k_1; y <= ny - k; y++) {
+				idx = x + y * nx;
+				output[idx] = 0.0f;
+				for (int i = 1; i < k; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+				}
+			}
+			for (int y = ny - k_1; y < ny; y++) {
+				idx = x + y * nx;
+				output[idx] = 0.0f;
+				for (int i = 1; i < ny - y; i++) {
+					inx = i * nx;
+					output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+				}
+				for (int i = ny - y; i < k; i++) {
+					output[idx] += kernel[i] * (input[(b - i - y) * nx + x] - input[idx - i * nx]);
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with a symmetric kernel along x, for an image defined as a
+	 * 1-D array, in a region bounded by [x1..x2] and [y1..y2]. Usage:
+	 * convolve{Even,Odd}X followed by convolve{Even,Odd}Y is mandatory.
+	 */
+	public static double[] convolveEvenX(double[] input, double[] kernel, int[] dims, int x1, int x2, int y1, int y2) {
+
+		int nx = dims[0];
+		int ny = dims[1];
+		int nxy = nx * ny;
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		double[] output = new double[(x2 - x1 + 1) * (y2 - y1 + 1)];
+		int idx;
+		int odx = 0;
+
+		if (x1 < k_1 && x2 > nx - k) { // 2 border conditions
+			for (int y = y1; y <= y2; y++) {
+				for (int x = x1; x < k_1; x++) {
+					idx = x + y * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i <= x; i++) {
+						output[odx] += kernel[i] * (input[idx - i] + input[idx + i]);
+					}
+					for (int i = x + 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[i - x + y * nx] + input[idx + i]);
+					}
+					odx++;
+				}
+				for (int x = k_1; x < x2 - k_1; x++) {
+					idx = x + y * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx - i] + input[idx + i]);
+					}
+					odx++;
+				}
+				for (int x = x2 - k_1; x <= x2; x++) {
+					idx = x + y * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < nx - x; i++) {
+						output[odx] += kernel[i] * (input[idx - i] + input[idx + i]);
+					}
+					for (int i = nx - x; i < k; i++) {
+						output[odx] += kernel[i] * (input[b - i - x + y * nx] + input[idx - i]);
+					}
+					odx++;
+				}
+			}
+		}
+		else if (x1 < k_1 && x2 <= nx - k) { // left border conditions
+			for (int y = y1; y <= y2; y++) {
+				for (int x = x1; x < k_1; x++) {
+					idx = x + y * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i <= x; i++) {
+						output[odx] += kernel[i] * (input[idx - i] + input[idx + i]);
+					}
+					for (int i = x + 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[i - x + y * nx] + input[idx + i]);
+					}
+					odx++;
+				}
+				for (int x = k_1; x <= x2; x++) {
+					idx = x + y * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx - i] + input[idx + i]);
+					}
+					odx++;
+				}
+			}
+		}
+		else if (x1 >= k_1 && x2 > nx - k) { // right border conditions
+			for (int y = y1; y <= y2; y++) {
+				for (int x = x1; x < x2 - k_1; x++) {
+					idx = x + y * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx - i] + input[idx + i]);
+					}
+					odx++;
+				}
+				for (int x = x2 - k_1; x <= x2; x++) {
+					idx = x + y * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < nx - x; i++) {
+						output[odx] += kernel[i] * (input[idx - i] + input[idx + i]);
+					}
+					for (int i = nx - x; i < k; i++) {
+						output[odx] += kernel[i] * (input[b - i - x + y * nx] + input[idx - i]);
+					}
+					odx++;
+				}
+			}
+		}
+		else { // no border conditions
+			for (int y = y1; y <= y2; y++) {
+				for (int x = x1; x <= x2; x++) {
+					idx = x + y * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx - i] + input[idx + i]);
+					}
+					odx++;
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with an anti-symmetric kernel along x, for an image defined
+	 * as a 1-D array, in a region bounded by [x1..x2] and [y1..y2]. Usage:
+	 * convolve{Even,Odd}X followed by convolve{Even,Odd}Y is mandatory.
+	 */
+	public static double[] convolveOddX(double[] input, double[] kernel, int[] dims, int x1, int x2, int y1, int y2) {
+
+		int nx = dims[0];
+		int ny = dims[1];
+		int nxy = nx * ny;
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * nx - 2;
+
+		double[] output = new double[(x2 - x1 + 1) * (y2 - y1 + 1)];
+
+		int idx;
+		int odx = 0;
+
+		if (x1 < k_1 && x2 > nx - k) { // 2 border conditions
+			for (int y = y1; y <= y2; y++) {
+				for (int x = x1; x < k_1; x++) {
+					idx = x + y * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i <= x; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[idx - i]);
+					}
+					for (int i = x + 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[i - x + y * nx]);
+					}
+					odx++;
+				}
+				for (int x = k_1; x < x2 - k_1; x++) {
+					idx = x + y * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[idx - i]);
+					}
+					odx++;
+				}
+				for (int x = x2 - k_1; x <= x2; x++) {
+					idx = x + y * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < nx - x; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[idx - i]);
+					}
+					for (int i = nx - x; i < k; i++) {
+						output[odx] += kernel[i] * (input[b - i - x + y * nx] - input[idx - i]);
+					}
+					odx++;
+				}
+			}
+		}
+		else if (x1 < k_1 && x2 <= nx - k) { // left border conditions
+			for (int y = y1; y <= y2; y++) {
+				for (int x = x1; x < k_1; x++) {
+					idx = x + y * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i <= x; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[idx - i]);
+					}
+					for (int i = x + 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[i - x + y * nx]);
+					}
+					odx++;
+				}
+				for (int x = k_1; x <= x2; x++) {
+					idx = x + y * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[idx - i]);
+					}
+					odx++;
+				}
+			}
+		}
+		else if (x1 >= k_1 && x2 > nx - k) { // right border conditions
+			for (int y = y1; y <= y2; y++) {
+				for (int x = x1; x < x2 - k_1; x++) {
+					idx = x + y * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[idx - i]);
+					}
+					odx++;
+				}
+				for (int x = x2 - k_1; x <= x2; x++) {
+					idx = x + y * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < nx - x; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[idx - i]);
+					}
+					for (int i = nx - x; i < k; i++) {
+						output[odx] += kernel[i] * (input[b - i - x + y * nx] - input[idx - i]);
+					}
+					odx++;
+				}
+			}
+		}
+		else { // no border conditions
+			for (int y = y1; y <= y2; y++) {
+				for (int x = x1; x <= x2; x++) {
+					idx = x + y * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx + i] - input[idx - i]);
+					}
+					odx++;
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with a symmetric kernel along x, for an image defined as a
+	 * 1-D array, in a region bounded by [x1..x2] and [y1..y2]. Usage:
+	 * convolve{Even,Odd}X followed by convolve{Even,Odd}Y is mandatory.
+	 */
+	public static double[] convolveEvenY(double[] input, double[] kernel, int[] dims, int y1, int y2) {
+
+		int nx = dims[0];
+		int ny = dims[1];
+		int nxy = nx * ny;
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * ny - 2;
+		double[] output = new double[nx * (y2 - y1 + 1)];
+
+		int idx, inx;
+		int odx = 0;
+
+		if (y1 < k_1 && y2 > ny - k) { // 2 border conditions
+			for (int x = 0; x < nx; x++) {
+				for (int y = y1; y < k_1; y++) {
+					idx = x + y * nx;
+					odx = idx - y1 * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i <= y; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+					}
+					for (int i = y + 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[(i - y) * nx + x] + input[idx + i * nx]);
+					}
+				}
+				for (int y = k_1; y < y2 - k_1; y++) {
+					idx = x + y * nx;
+					odx = idx - y1 * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < k; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+					}
+				}
+				for (int y = y2 - k_1; y <= y2; y++) {
+					idx = x + y * nx;
+					odx = idx - y1 * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < ny - y; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+					}
+					for (int i = ny - y; i < k; i++) {
+						output[odx] += kernel[i] * (input[(b - i - y) * nx + x] + input[idx - i * nx]);
+					}
+				}
+			}
+		}
+		else if (y1 < k_1 && y2 <= ny - k) { // top border conditions
+			for (int x = 0; x < nx; x++) {
+				for (int y = y1; y < k_1; y++) {
+					idx = x + y * nx;
+					odx = idx - y1 * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i <= y; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+					}
+					for (int i = y + 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[(i - y) * nx + x] + input[idx + i * nx]);
+					}
+				}
+				for (int y = k_1; y <= y2; y++) {
+					idx = x + y * nx;
+					odx = idx - y1 * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < k; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+					}
+				}
+			}
+		}
+		else if (y1 >= k_1 && y2 > ny - k) { // bottom border conditions
+			for (int x = 0; x < nx; x++) {
+				for (int y = y1; y < ny - k_1; y++) {
+					idx = x + y * nx;
+					odx = idx - y1 * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < k; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+					}
+				}
+				for (int y = ny - k_1; y <= y2; y++) {
+					idx = x + y * nx;
+					odx = idx - y1 * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < ny - y; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+					}
+					for (int i = ny - y; i < k; i++) {
+						output[odx] += kernel[i] * (input[(b - i - y) * nx + x] + input[idx - i * nx]);
+					}
+				}
+			}
+		}
+		else { // no border conditions
+			for (int x = 0; x < nx; x++) {
+				for (int y = y1; y <= y2; y++) {
+					idx = x + y * nx;
+					odx = idx - y1 * nx;
+					output[odx] = kernel[0] * input[idx];
+					for (int i = 1; i < k; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]);
+					}
+				}
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Convolution with an anti-symmetric kernel along y, for an image defined
+	 * as a 1-D array, in a region bounded by [x1..x2] and [y1..y2]. Usage:
+	 * convolve{Even,Odd}X followed by convolve{Even,Odd}Y is mandatory.
+	 */
+	public static double[] convolveOddY(double[] input, double[] kernel, int[] dims, int y1, int y2) {
+
+		int nx = dims[0];
+		int ny = dims[1];
+		int nxy = nx * ny;
+
+		int k = kernel.length;
+		int k_1 = k - 1;
+		int b = 2 * ny - 2;
+
+		double[] output = new double[nx * (y2 - y1 + 1)];
+
+		int idx, inx;
+		int odx = 0;
+
+		if (y1 < k_1 && y2 > ny - k) { // 2 border conditions
+			for (int x = 0; x < nx; x++) {
+				for (int y = y1; y < k_1; y++) {
+					idx = x + y * nx;
+					odx = x + (y - y1) * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i <= y; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+					}
+					for (int i = y + 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx + i * nx] - input[(i - y) * nx + x]);
+					}
+				}
+				for (int y = k_1; y <= ny - k; y++) {
+					idx = x + y * nx;
+					odx = x + (y - y1) * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < k; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+					}
+				}
+				for (int y = ny - k_1; y <= y2; y++) {
+					idx = x + y * nx;
+					odx = x + (y - y1) * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < ny - y; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+					}
+					for (int i = ny - y; i < k; i++) {
+						output[odx] += kernel[i] * (input[(b - i - y) * nx + x] - input[idx - i * nx]);
+					}
+				}
+			}
+		}
+		else if (y1 < k_1 && y2 <= ny - k) { // left border conditions
+			for (int x = 0; x < nx; x++) {
+				for (int y = y1; y < k_1; y++) {
+					idx = x + y * nx;
+					odx = x + (y - y1) * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i <= y; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+					}
+					for (int i = y + 1; i < k; i++) {
+						output[odx] += kernel[i] * (input[idx + i * nx] - input[(i - y) * nx + x]);
+					}
+				}
+				for (int y = k_1; y <= y2; y++) {
+					idx = x + y * nx;
+					odx = x + (y - y1) * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < k; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+					}
+				}
+			}
+		}
+		else if (y1 >= k_1 && y2 > ny - k) { // right border conditions
+			for (int x = 0; x < nx; x++) {
+				for (int y = y1; y <= ny - k; y++) {
+					idx = x + y * nx;
+					odx = x + (y - y1) * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < k; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+					}
+				}
+				for (int y = ny - k_1; y < y2; y++) {
+					idx = x + y * nx;
+					odx = x + (y - y1) * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < ny - y; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+					}
+					for (int i = ny - y; i < k; i++) {
+						output[odx] += kernel[i] * (input[(b - i - y) * nx + x] - input[idx - i * nx]);
+					}
+				}
+			}
+		}
+		else { // no border conditions
+			for (int x = 0; x < nx; x++) {
+				for (int y = y1; y <= y2; y++) {
+					idx = x + y * nx;
+					odx = x + (y - y1) * nx;
+					output[odx] = 0.0;
+					for (int i = 1; i < k; i++) {
+						inx = i * nx;
+						output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]);
+					}
+				}
+			}
+		}
+		return output;
+	}
+}
\ No newline at end of file
diff --git a/src/bilib/src/ijtools/IJmath.java b/src/bilib/src/ijtools/IJmath.java
new file mode 100644
index 0000000000000000000000000000000000000000..89eebb98338e89f650e45085398c5c249b6eaa21
--- /dev/null
+++ b/src/bilib/src/ijtools/IJmath.java
@@ -0,0 +1,549 @@
+package ijtools;
+
+/**
+ * Various methods for polynomial manipulation and basic image arithmetic.
+ * Polynomial are defined by the set of coefficients a[]: f(x) = a[0] + a[1]*x +
+ * a[2]*x^2 + ...
+ * 
+ * @author Francois Aguet
+ * @version 1.0
+ */
+public class IJmath {
+
+	/**
+	 * Returns the real roots of a quadratic polynomial. The input is of the
+	 * form: x^2 + a1*x + a0. The size of the returned array corresponds to the
+	 * number of real roots.
+	 */
+	public static double[] quadraticRoots(double a1, double a0) {
+
+		double[] roots;
+		double delta = a1 * a1 - 4.0 * a0;
+		if (delta < 0.0) {
+			roots = new double[0];
+		}
+		else {
+			roots = new double[2];
+			delta = Math.sqrt(delta);
+			roots[0] = (-a1 + delta) / 2.0;
+			roots[1] = (-a1 - delta) / 2.0;
+		}
+		return roots;
+	}
+
+	/**
+	 * Returns the real roots of a cubic polynomial. The input is of the form:
+	 * x^3 + a2*x^2 + a1*x + a0. References: Numerical Recipes in C, pp.
+	 * 184-185, http://mathworld.wolfram.com/CubicFormula.html The size of the
+	 * returned array corresponds to the number of real roots.
+	 */
+	public static double[] cubicRoots(double a2, double a1, double a0) {
+
+		double theta, A, B;
+		double q = a2 * a2 - 3.0 * a1;
+		double Q = q / 9.0;
+		double R = (2.0 * a2 * a2 * a2 - 9.0 * a2 * a1 + 27.0 * a0) / 54.0;
+		double roots[];
+		double signR, delta;
+
+		if (a0 == 0.0) {
+			delta = a2 * a2 - 4.0 * a1;
+			if (delta >= 0.0) {
+				roots = new double[3];
+				roots[0] = 0.0;
+				roots[1] = (-a2 + Math.sqrt(delta)) / 2.0;
+				roots[2] = (-a2 - Math.sqrt(delta)) / 2.0;
+			}
+			else {
+				roots = new double[1];
+				roots[0] = 0.0;
+			}
+		}
+		else {
+			if (R >= 0.0) {
+				signR = 1.0;
+			}
+			else {
+				signR = -1.0;
+			}
+			if (R * R <= Q * Q * Q) { // REF: R2 < Q3
+				roots = new double[3];
+				theta = Math.acos(R / Math.sqrt(Q * Q * Q));
+				roots[0] = -2.0 * Math.sqrt(Q) * Math.cos(theta / 3.0) - (a2 / 3.0);
+				roots[1] = -2.0 * Math.sqrt(Q) * Math.cos((theta + 2.0 * Math.PI) / 3.0) - (a2 / 3.0);
+				roots[2] = -2.0 * Math.sqrt(Q) * Math.cos((theta - 2.0 * Math.PI) / 3.0) - (a2 / 3.0);
+			}
+			else {
+				A = -signR * Math.pow(Math.abs(R) + Math.sqrt(R * R - Q * Q * Q), 1.0 / 3.0);
+				if (A == 0.0) {
+					B = 0.0;
+				}
+				else {
+					B = Q / A;
+				}
+				roots = new double[1];
+				roots[0] = A + B - (a2 / 3.0);
+			}
+		}
+		return roots;
+	}
+
+	/**
+	 * Returns the real roots of a quartic polynomial. The input is of the form:
+	 * x^4 + a3*x^3 + a2*x^2 + a1*x + a0. Reference:
+	 * http://mathworld.wolfram.com/QuarticEquation.html The size of the
+	 * returned array corresponds to the number of real roots.
+	 */
+	public static double[] quarticRoots(double a3, double a2, double a1, double a0) {
+
+		double[] roots;
+
+		if (a3 == 0.0 && a1 == 0.0) { // quadratic equation in x^2
+			double[] x2roots = quadraticRoots(a2, a0);
+			if (x2roots.length != 0) {
+				if ((x2roots[0] >= 0.0) && (x2roots[1] >= 0.0)) {
+					roots = new double[4];
+					roots[0] = Math.sqrt(x2roots[0]);
+					roots[1] = -roots[0];
+					roots[2] = Math.sqrt(x2roots[1]);
+					roots[3] = roots[2];
+				}
+				else if ((x2roots[0] < 0.0) && (x2roots[1] < 0.0)) {
+					roots = new double[0];
+				}
+				else {
+					roots = new double[2];
+					if (x2roots[0] >= 0.0) {
+						roots[0] = Math.sqrt(x2roots[0]);
+						roots[1] = -roots[0];
+					}
+					else {
+						roots[0] = Math.sqrt(x2roots[1]);
+						roots[1] = -roots[0];
+					}
+				}
+			}
+			else {
+				roots = new double[0];
+			}
+		}
+		else { // solve quartic
+
+			double[] crr = cubicRoots(-a2, a1 * a3 - 4.0 * a0, 4.0 * a2 * a0 - a1 * a1 - a3 * a3 * a0);
+			double y1 = crr[0];
+
+			double deltaR = 0.25 * a3 * a3 - a2 + y1;
+			double R;
+
+			if (deltaR < 0.0) { // 4 complex roots
+				roots = new double[0];
+			}
+			else {
+				R = Math.sqrt(deltaR);
+				double D, E;
+				double deltaD, deltaE;
+				if (R == 0.0) {
+					deltaD = 0.75 * a3 * a3 - 2.0 * a2 + 2.0 * Math.sqrt(y1 * y1 - 4.0 * a0);
+					deltaE = 0.75 * a3 * a3 - 2.0 * a2 - 2.0 * Math.sqrt(y1 * y1 - 4.0 * a0);
+					if (deltaD >= 0.0) {
+						D = Math.sqrt(deltaD);
+						if (deltaE >= 0.0) {
+							E = Math.sqrt(deltaE);
+							roots = new double[4];
+							roots[0] = -0.25 * a3 + 0.5 * D;
+							roots[1] = -0.25 * a3 - 0.5 * D;
+							roots[2] = -0.25 * a3 + 0.5 * E;
+							roots[3] = -0.25 * a3 - 0.5 * E;
+						}
+						else {
+							roots = new double[2];
+							roots[0] = -0.25 * a3 + 0.5 * D;
+							roots[1] = -0.25 * a3 - 0.5 * D;
+						}
+					}
+					else {
+						if (deltaE >= 0.0) {
+							E = Math.sqrt(deltaE);
+							roots = new double[2];
+							roots[0] = -0.25 * a3 + 0.5 * E;
+							roots[1] = -0.25 * a3 - 0.5 * E;
+						}
+						else {
+							roots = new double[0];
+						}
+					}
+				}
+				else {
+					deltaD = 0.75 * a3 * a3 - R * R - 2.0 * a2 + (a3 * a2 - 2.0 * a1 - 0.25 * a3 * a3 * a3) / R;
+					deltaE = 0.75 * a3 * a3 - R * R - 2.0 * a2 - (a3 * a2 - 2.0 * a1 - 0.25 * a3 * a3 * a3) / R;
+					if (deltaD >= 0.0) {
+						D = Math.sqrt(deltaD);
+						if (deltaE >= 0.0) {
+							E = Math.sqrt(deltaE);
+							roots = new double[4];
+							roots[0] = -0.25 * a3 + 0.5 * R + 0.5 * D;
+							roots[1] = -0.25 * a3 + 0.5 * R - 0.5 * D;
+							roots[2] = -0.25 * a3 - 0.5 * R + 0.5 * E;
+							roots[3] = -0.25 * a3 - 0.5 * R - 0.5 * E;
+						}
+						else {
+							roots = new double[2];
+							roots[0] = -0.25 * a3 + 0.5 * R + 0.5 * D;
+							roots[1] = -0.25 * a3 + 0.5 * R - 0.5 * D;
+						}
+					}
+					else {
+						if (deltaE >= 0.0) {
+							E = Math.sqrt(deltaE);
+							roots = new double[2];
+							roots[0] = -0.25 * a3 - 0.5 * R + 0.5 * E;
+							roots[1] = -0.25 * a3 - 0.5 * R - 0.5 * E;
+						}
+						else {
+							roots = new double[0];
+						}
+					}
+				}
+			}
+		}
+		return roots;
+	}
+
+	/**
+	 * Divides a polynomial by a known root.
+	 */
+	public static double[] divPolyByRoot(double[] coeffs, double root) {
+		int nx = coeffs.length;
+		double[] out = new double[nx - 1];
+		double rem = coeffs[nx - 1];
+		for (int i = nx - 2; i >= 0; i--) {
+			out[i] = rem;
+			rem = coeffs[i] + rem * root;
+		}
+		return out;
+	}
+
+	// divide by (x - (a + bi))(x - (a - bi)) = x^2 - a^2 x + a^2 + b^2
+	/**
+	 * Divides a polynomial by a pair of known conjugate roots, i.e., (x - (a +
+	 * bi))(x - (a - bi)).
+	 */
+	public static double[] divPolyByConjRoots(double[] coeffs, double a, double b) {
+		double t1 = 2.0 * a;
+		double t2 = a * a + b * b;
+		int n = coeffs.length - 2;
+		double[] out = new double[n];
+		out[n - 1] = coeffs[n + 1];
+		out[n - 2] = coeffs[n] + t1 * out[n - 1];
+		for (int i = n - 1; i >= 2; i--) {
+			out[i - 2] = coeffs[i] + t1 * out[i - 1] - t2 * out[i];
+		}
+		return out;
+	}
+
+	/**
+	 * Evaluates a polynomial in x0. f(x) = a[0] + a[1]*x + a[2]*x^2 + ...
+	 */
+	public static double evalPoly(double[] a, double x0) {
+		int n = a.length;
+		double f = a[n - 1];
+		for (int i = n - 2; i >= 0; i--) {
+			f = f * x0 + a[i];
+		}
+		return f;
+	}
+
+	/**
+	 * Evaluates a polynomial and its first derivative in x0. f(x) = a[0] +
+	 * a[1]*x + a[2]*x^2 + ...
+	 * 
+	 * @return A length two array containing f(x0) and f'(x0), respectively.
+	 */
+	public static double[] evalPolyD(double[] a, double x0) {
+		int n = a.length;
+		double[] f = new double[2];
+		f[0] = a[n - 1];
+		f[1] = 0.0;
+		for (int i = n - 2; i >= 0; i--) {
+			f[1] = f[1] * x0 + f[0];
+			f[0] = f[0] * x0 + a[i];
+		}
+		return f;
+	}
+
+	// Laguerre's method, adapted from 'Numerical Recipes' for real
+	// coefficients, complex roots
+
+	/**
+	 * Laguerre's root finding method for real-valued polynomials. f(x) = a[0] +
+	 * a[1]*x + a[2]*x^2 + ... The returned root can be complex.
+	 * 
+	 * @return An array containing the real and imaginary part of the root a +
+	 *         i*b, i.e., {a,b}.
+	 */
+	public static double[] laguerre(double[] a, double x0) {
+		// public static double[] laguerre(double[] a, double x0, int[]
+		// converged) {
+
+		int N = a.length - 1; // degree of the polynomial
+		double[] x = complex(x0, 0.0); // a (complex) root
+		double[] f, df, d2f;
+		double[] G, G2, H, sq, Gplus, Gminus, dx;
+		double[] delta;
+		double absx, error, abs_plus, abs_minus;
+		final double[] fracl = { 0.0, 0.5, 0.25, 0.75, 0.125, 0.375, 0.625, 0.875, 1.0 }; // fractions
+																							// used
+																							// to
+																							// break
+																							// limit
+																							// cycles
+		int MT = 10;
+		// int maxiter = MT*fracl.length;
+		int maxiter = 30;
+		double tol = 1.0e-15;
+		int[] converged = new int[1];
+		converged[0] = -2;
+
+		for (int iter = 1; iter <= maxiter; iter++) {
+
+			f = complex(a[N], 0.0);
+			df = complex(0.0, 0.0);
+			d2f = complex(0.0, 0.0);
+			absx = cabs(x);
+			error = cabs(f);
+			for (int k = N - 1; k >= 0; k--) {
+				d2f = cadd(cmul(d2f, x), df);
+				df = cadd(cmul(df, x), f);
+				f = cadd(cmul(f, x), complex(a[k], 0.0));
+				error = cabs(f) + absx * error;
+			}
+			if (cabs(f) <= error * tol) { // x is a root
+				converged[0] = 2;
+				break;
+			}
+			G = cdiv(df, f);
+			G2 = cmul(G, G);
+			H = csub(G2, cdiv(d2f, f));
+			delta = rcmul(N - 1, csub(rcmul(N, H), G2));
+			sq = csqrt(delta);
+			Gplus = cadd(G, sq);
+			Gminus = csub(G, sq);
+			abs_plus = cabs(Gplus);
+			abs_minus = cabs(Gminus);
+			if (abs_minus > abs_plus) {
+				Gplus = Gminus;
+				abs_plus = abs_minus;
+			} // Gplus is largest absolute denominator
+			if (abs_plus > 0.0) {
+				dx = cdiv(complex(N, 0.0), Gplus);
+			}
+			else { // G = 0, H = 0, freak case
+				dx = rcmul(1 + absx, complex(Math.cos(iter), Math.sin(iter)));
+				dx = complex(1.0, 0.0);
+			}
+			/*
+			 * if (cabs(dx) < tol) { // converged //IJ.write("test"); break; }
+			 */
+			if (iter % MT != 0) {
+				x = csub(x, dx);
+			}
+			else { // fractional step to break limit cycles
+				x = csub(x, rcmul(fracl[iter / MT], dx));
+			}
+		}
+		return x;
+	}
+
+	private static double[] complex(double a, double b) {
+		double[] c = { a, b };
+		return c;
+	}
+
+	private static double[] cadd(double[] c1, double[] c2) {
+		double[] c = { c1[0] + c2[0], c1[1] + c2[1] };
+		return c;
+	}
+
+	private static double[] csub(double[] c1, double[] c2) {
+		double[] c = { c1[0] - c2[0], c1[1] - c2[1] };
+		return c;
+	}
+
+	private static double cabs(double[] c) {
+		return Math.sqrt(c[0] * c[0] + c[1] * c[1]);
+	}
+
+	private static double[] rcmul(double a, double[] c) {
+		double[] s = { a * c[0], a * c[1] };
+		return s;
+	}
+
+	private static double[] cmul(double[] c1, double[] c2) {
+		double[] c = { c1[0] * c2[0] - c1[1] * c2[1], c1[0] * c2[1] + c2[0] * c1[1] };
+		return c;
+	}
+
+	private static double[] cdiv(double[] c1, double[] c2) {
+		double d = c2[0] * c2[0] + c2[1] * c2[1];
+		double[] c = { (c1[0] * c2[0] + c1[1] * c2[1]) / d, (c2[0] * c1[1] - c1[0] * c2[1]) / d };
+		return c;
+	}
+
+	private static double[] csqrt(double[] c) {
+		double t = cabs(c);
+		double d = Math.sqrt(2);
+		double[] s = { Math.sqrt(t + c[0]) / d, csign(c[1]) * Math.sqrt(t - c[0]) / d };
+		return s;
+	}
+
+	private static double csign(double x) {
+		if (x >= 0.0) {
+			return 1.0;
+		}
+		else {
+			return -1.0;
+		}
+	}
+
+	/**
+	 * Computes the mean.
+	 */
+	public static double mean(double[] input) {
+		return sum(input) / input.length;
+	}
+
+	/**
+	 * Computes the mean.
+	 */
+	public static double mean(double[][] input) {
+		return sum(input) / (input.length * input[0].length);
+	}
+
+	/**
+	 * Computes the sum.
+	 */
+	public static double sum(double[] input) {
+		int nxy = input.length;
+		double s = 0.0;
+		for (int k = 0; k < nxy; k++) {
+			s += input[k];
+		}
+		return s;
+	}
+
+	/**
+	 * Computes the sum.
+	 */
+	public static double sum(double[][] input) {
+		int nx = input.length;
+		int ny = input[0].length;
+		double s = 0.0;
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < ny; y++) {
+				s += input[x][y];
+			}
+		}
+		return s;
+	}
+
+	/**
+	 * Adds the two input images.
+	 */
+	public static double[] add(double[] a, double[] b) {
+		int nxy = a.length;
+		double[] out = new double[nxy];
+		for (int k = 0; k < nxy; k++) {
+			out[k] = a[k] + b[k];
+		}
+		return out;
+	}
+
+	/**
+	 * Adds the two input images.
+	 */
+	public static double[][] add(double[][] a, double[][] b) {
+		int nx = a.length;
+		int ny = a[0].length;
+		double[][] out = new double[nx][ny];
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < ny; y++) {
+				out[x][y] = a[x][y] + b[x][y];
+			}
+		}
+		return out;
+	}
+
+	/**
+	 * Subtracts the two input images.
+	 */
+	public static double[] subtract(double[] a, double[] b) {
+		int nxy = a.length;
+		double[] out = new double[nxy];
+		for (int k = 0; k < nxy; k++) {
+			out[k] = a[k] - b[k];
+		}
+		return out;
+	}
+
+	/**
+	 * Subtracts the two input images.
+	 */
+	public static double[][] subtract(double[][] a, double[][] b) {
+		int nx = a.length;
+		int ny = a[0].length;
+		double[][] out = new double[nx][ny];
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < ny; y++) {
+				out[x][y] = a[x][y] - b[x][y];
+			}
+		}
+		return out;
+	}
+
+	/**
+	 * Multiplies the two input images.
+	 */
+	public static double[] mutiply(double[] a, double[] b) {
+		int nxy = a.length;
+		double[] out = new double[nxy];
+		for (int k = 0; k < nxy; k++) {
+			out[k] = a[k] * b[k];
+		}
+		return out;
+	}
+
+	/**
+	 * Multiplies the two input images.
+	 */
+	public static double[][] multiply(double[][] a, double[][] b) {
+		int nx = a.length;
+		int ny = a[0].length;
+		double[][] out = new double[nx][ny];
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < ny; y++) {
+				out[x][y] = a[x][y] * b[x][y];
+			}
+		}
+		return out;
+	}
+
+	/*
+	 * public static double newton(double[] coeffs, double x1, double x2) {
+	 * 
+	 * if (Math.signum(SteerableUtilities.evalPoly(coeffs,
+	 * x1)*SteerableUtilities.evalPoly(coeffs, x2)) != -1.0) {
+	 * //IJ.write("error newton"+(x1*x2)); }
+	 * 
+	 * double root = (x1+x2)/2.0; int maxIter = 40; double dx; double[] f;
+	 * double acc = 1e-30;
+	 * 
+	 * for (int i=0;i<maxIter;i++) { f = evalPolyD(coeffs, root); dx =
+	 * f[0]/f[1]; root -= dx;
+	 * 
+	 * //if ((x1-root)*(root-x2) < 0.0) { //IJ.write("Newton out of bounds"); //
+	 * root = 111.13; //}
+	 * 
+	 * if (Math.abs(dx) < acc) { break; } } return root; }
+	 */
+
+}
diff --git a/src/bilib/src/ijtools/IJtools.java b/src/bilib/src/ijtools/IJtools.java
new file mode 100644
index 0000000000000000000000000000000000000000..610c8c01cd66beab2039a8771f000682f7907cd6
--- /dev/null
+++ b/src/bilib/src/ijtools/IJtools.java
@@ -0,0 +1,847 @@
+package ijtools;
+
+import ij.*;
+import ij.process.*;
+
+/**
+ * Utility class for displaying, loading and performing basic arithmetic
+ * operations on images. Where applicable, arrays are in lexicographical order.
+ * 
+ * @author Francois Aguet
+ * @version 1.0
+ */
+public class IJtools {
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, double[] pixels, int nx, int ny) {
+		(new ImagePlus(name, new FloatProcessor(nx, ny, pixels))).show();
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, float[] pixels, int nx, int ny) {
+		(new ImagePlus(name, new FloatProcessor(nx, ny, pixels, null))).show();
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, int[] pixels, int nx, int ny) {
+		(new ImagePlus(name, new FloatProcessor(nx, ny, pixels))).show();
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, short[] pixels, int nx, int ny) {
+		(new ImagePlus(name, new ShortProcessor(nx, ny, pixels, null))).show();
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, byte[] pixels, int nx, int ny) {
+		(new ImagePlus(name, new ByteProcessor(nx, ny, pixels, null))).show();
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void showRGB(String name, int[] pixels, int nx, int ny) {
+		(new ImagePlus(name, new ColorProcessor(nx, ny, pixels))).show();
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, double[][] input) {
+		int nx = input.length;
+		int ny = input[0].length;
+		double[] pixels = new double[nx * ny];
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < nx; x++) {
+				pixels[x + y * nx] = input[x][y];
+			}
+		}
+		show(name, pixels, nx, ny);
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, float[][] input) {
+		int nx = input.length;
+		int ny = input[0].length;
+		float[] pixels = new float[nx * ny];
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < nx; x++) {
+				pixels[x + y * nx] = input[x][y];
+			}
+		}
+		show(name, pixels, nx, ny);
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, int[][] input) {
+		int nx = input.length;
+		int ny = input[0].length;
+		int[] pixels = new int[nx * ny];
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < nx; x++) {
+				pixels[x + y * nx] = input[x][y];
+			}
+		}
+		show(name, pixels, nx, ny);
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, short[][] input) {
+		int nx = input.length;
+		int ny = input[0].length;
+		short[] pixels = new short[nx * ny];
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < nx; x++) {
+				pixels[x + y * nx] = input[x][y];
+			}
+		}
+		show(name, pixels, nx, ny);
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void show(String name, byte[][] input) {
+		int nx = input.length;
+		int ny = input[0].length;
+		byte[] pixels = new byte[nx * ny];
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < nx; x++) {
+				pixels[x + y * nx] = input[x][y];
+			}
+		}
+		show(name, pixels, nx, ny);
+	}
+
+	/**
+	 * Displays the image in a new window in in ImageJ.
+	 */
+	public static void showRGB(String name, int[][] input) {
+		int nx = input.length;
+		int ny = input[0].length;
+		int[] pixels = new int[nx * ny];
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < nx; x++) {
+				pixels[x + y * nx] = input[x][y];
+			}
+		}
+		showRGB(name, pixels, nx, ny);
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ.
+	 */
+	public static void showStack(String name, double[] input, int nx, int ny, int nz) {
+		ImageStack istack = new ImageStack(nx, ny);
+		for (int z = 0; z < nz; z++) {
+			double[] pixels = new double[nx * ny];
+			for (int i = 0; i < nx * ny; i++) {
+				pixels[i] = input[i + z * nx * ny];
+			}
+			istack.addSlice("", new FloatProcessor(nx, ny, pixels));
+		}
+		(new ImagePlus(name, istack)).show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ.
+	 */
+	public static void showStack(String name, float[] input, int nx, int ny, int nz) {
+		ImageStack istack = new ImageStack(nx, ny);
+		for (int z = 0; z < nz; z++) {
+			float[] pixels = new float[nx * ny];
+			for (int i = 0; i < nx * ny; i++) {
+				pixels[i] = input[i + z * nx * ny];
+			}
+			istack.addSlice("", pixels);
+		}
+		(new ImagePlus(name, istack)).show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ.
+	 */
+	public static void showStack(String name, int[] input, int nx, int ny, int nz) {
+		ImageStack istack = new ImageStack(nx, ny);
+		int[] pixels = new int[nx * ny];
+		for (int z = 0; z < nz; z++) {
+			for (int i = 0; i < nx * ny; i++) {
+				pixels[i] = input[i + z * nx * ny];
+			}
+			istack.addSlice("", new FloatProcessor(nx, ny, pixels));
+		}
+		(new ImagePlus(name, istack)).show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ.
+	 */
+	public static void showStack(String name, short[] input, int nx, int ny, int nz) {
+		ImageStack istack = new ImageStack(nx, ny);
+		for (int z = 0; z < nz; z++) {
+			short[] pixels = new short[nx * ny];
+			for (int i = 0; i < nx * ny; i++) {
+				pixels[i] = input[i + z * nx * ny];
+			}
+			istack.addSlice("", pixels);
+		}
+		(new ImagePlus(name, istack)).show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ.
+	 */
+	public static void showStack(String name, byte[] input, int nx, int ny, int nz) {
+		ImageStack istack = new ImageStack(nx, ny);
+		for (int z = 0; z < nz; z++) {
+			byte[] pixels = new byte[nx * ny];
+			for (int i = 0; i < nx * ny; i++) {
+				pixels[i] = input[i + z * nx * ny];
+			}
+			istack.addSlice("", pixels);
+		}
+		(new ImagePlus(name, istack)).show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ.
+	 */
+	public static void showStackRGB(String name, int[] input, int nx, int ny, int nz) {
+		ImageStack istack = new ImageStack(nx, ny);
+		int[] pixels = new int[nx * ny];
+		for (int z = 0; z < nz; z++) {
+			for (int i = 0; i < nx * ny; i++) {
+				pixels[i] = input[i + z * nx * ny];
+			}
+			istack.addSlice("", new ColorProcessor(nx, ny, pixels));
+		}
+		(new ImagePlus(name, istack)).show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ. Input structure is
+	 * [z][xy].
+	 */
+	public static void showStack(String name, double[][] input, int nx, int ny) {
+		int nz = input.length;
+		ImageStack istack = new ImageStack(nx, ny);
+		double max = -Double.MAX_VALUE;
+		double min = Double.MAX_VALUE;
+		double t;
+		for (int z = 0; z < nz; z++) {
+			double[] pixels = new double[nx * ny];
+			for (int i = 0; i < nx * ny; i++) {
+				t = input[z][i];
+				if (t > max) {
+					max = t;
+				}
+				if (t < min) {
+					min = t;
+				}
+			}
+			istack.addSlice("", pixels);
+		}
+		ImagePlus imp = new ImagePlus(name, istack);
+		imp.getProcessor().setMinAndMax(min, max);
+		imp.show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ. Input structure is
+	 * [z][xy].
+	 */
+	public static void showStack(String name, float[][] input, int nx, int ny) {
+		int nz = input.length;
+		ImageStack istack = new ImageStack(nx, ny);
+		float max = -Float.MAX_VALUE;
+		float min = Float.MAX_VALUE;
+		float t;
+		for (int z = 0; z < nz; z++) {
+			for (int i = 0; i < nx * ny; i++) {
+				t = input[z][i];
+				if (t > max) {
+					max = t;
+				}
+				if (t < min) {
+					min = t;
+				}
+			}
+			istack.addSlice("", input[z]);
+		}
+		ImagePlus imp = new ImagePlus(name, istack);
+		imp.getProcessor().setMinAndMax(min, max);
+		imp.show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ. Input structure is
+	 * [z][xy].
+	 */
+	public static void showStack(String name, int[][] input, int nx, int ny) {
+		int nz = input.length;
+		ImageStack istack = new ImageStack(nx, ny);
+		int max = -Integer.MAX_VALUE;
+		int min = Integer.MAX_VALUE;
+		int t;
+		int[] pixels = new int[nx * ny];
+		for (int z = 0; z < nz; z++) {
+			for (int i = 0; i < nx * ny; i++) {
+				t = input[z][i];
+				if (t > max) {
+					max = t;
+				}
+				if (t < min) {
+					min = t;
+				}
+			}
+			istack.addSlice("", new FloatProcessor(nx, ny, pixels));
+		}
+		ImagePlus imp = new ImagePlus(name, istack);
+		imp.getProcessor().setMinAndMax(min, max);
+		imp.show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ. Input structure is
+	 * [z][xy].
+	 */
+	public static void showStack(String name, short[][] input, int nx, int ny) {
+		int nz = input.length;
+		ImageStack istack = new ImageStack(nx, ny);
+		short max = -Short.MAX_VALUE;
+		short min = Short.MAX_VALUE;
+		short t;
+		for (int z = 0; z < nz; z++) {
+			short[] pixels = new short[nx * ny];
+			for (int i = 0; i < nx * ny; i++) {
+				t = input[z][i];
+				if (t > max) {
+					max = t;
+				}
+				if (t < min) {
+					min = t;
+				}
+			}
+			istack.addSlice("", pixels);
+		}
+		ImagePlus imp = new ImagePlus(name, istack);
+		imp.getProcessor().setMinAndMax(min, max);
+		imp.show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ. Input structure is
+	 * [z][xy].
+	 */
+	public static void showStack(String name, byte[][] input, int nx, int ny) {
+		int nz = input.length;
+		ImageStack istack = new ImageStack(nx, ny);
+		byte max = -Byte.MAX_VALUE;
+		byte min = Byte.MAX_VALUE;
+		byte t;
+		for (int z = 0; z < nz; z++) {
+			byte[] pixels = new byte[nx * ny];
+			for (int i = 0; i < nx * ny; i++) {
+				t = input[z][i];
+				if (t > max) {
+					max = t;
+				}
+				if (t < min) {
+					min = t;
+				}
+			}
+			istack.addSlice("", pixels);
+		}
+		ImagePlus imp = new ImagePlus(name, istack);
+		imp.getProcessor().setMinAndMax(min, max);
+		imp.show();
+	}
+
+	/**
+	 * Displays the stack in a new window in in ImageJ. Input structure is
+	 * [z][xy].
+	 */
+	public static void showStackRGB(String name, int[][] input, int nx, int ny) {
+		int nz = input.length;
+		ImageStack istack = new ImageStack(nx, ny);
+		for (int z = 0; z < nz; z++) {
+			istack.addSlice("", new ColorProcessor(nx, ny, input[z]));
+		}
+		(new ImagePlus(name, istack)).show();
+	}
+
+	/**
+	 * Loads the contents of an ImageProcessor into a double[] array.
+	 */
+	public static void loadImage(ImageProcessor ip, double[] pixels) {
+		if (ip == null) {
+			throw new ArrayStoreException("ImageProcessor == null.");
+		}
+		int nk = ip.getWidth() * ip.getHeight();
+		if (nk != pixels.length) {
+			throw new IndexOutOfBoundsException("Array sizes do not match.");
+		}
+		if (ip.getPixels() instanceof byte[]) {
+			byte[] bsrc = (byte[]) ip.getPixels();
+			for (int k = 0; k < nk; k++) {
+				pixels[k] = (double) (bsrc[k] & 0xFF);
+			}
+		}
+		else if (ip.getPixels() instanceof short[]) {
+			short[] ssrc = (short[]) ip.getPixels();
+			for (int k = 0; k < nk; k++) {
+				pixels[k] = (double) (ssrc[k] & 0xFFFF);
+			}
+		}
+		else if (ip.getPixels() instanceof float[]) {
+			float[] fsrc = (float[]) ip.getPixels();
+			for (int k = 0; k < nk; k++) {
+				pixels[k] = (double) fsrc[k];
+			}
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type.");
+		}
+	}
+
+	/**
+	 * Loads the contents of an ImageProcessor into a float[] array.
+	 */
+	public static void loadImage(ImageProcessor ip, float[] pixels) {
+		if (ip == null) {
+			throw new ArrayStoreException("ImageProcessor == null.");
+		}
+		int nk = ip.getWidth() * ip.getHeight();
+		if (nk != pixels.length) {
+			throw new IndexOutOfBoundsException("Array sizes do not match");
+		}
+		if (ip.getPixels() instanceof byte[]) {
+			byte[] bsrc = (byte[]) ip.getPixels();
+			for (int k = 0; k < nk; k++) {
+				pixels[k] = (float) (bsrc[k] & 0xFF);
+			}
+		}
+		else if (ip.getPixels() instanceof short[]) {
+			short[] ssrc = (short[]) ip.getPixels();
+			for (int k = 0; k < nk; k++) {
+				pixels[k] = (float) (ssrc[k] & 0xFFFF);
+			}
+		}
+		else if (ip.getPixels() instanceof float[]) {
+			float[] fsrc = (float[]) ip.getPixels();
+			for (int k = 0; k < nk; k++) {
+				pixels[k] = fsrc[k];
+			}
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type.");
+		}
+	}
+
+	/**
+	 * Loads the contents of an ImageProcessor into a double[][] array.
+	 */
+	public static void loadImage(ImageProcessor ip, double[][] pixels) {
+		if (ip == null) {
+			throw new ArrayStoreException("ImageProcessor == null.");
+		}
+		int nx = ip.getWidth();
+		int ny = ip.getHeight();
+		if (nx != pixels.length || ny != pixels[0].length) {
+			throw new IndexOutOfBoundsException("Array sizes do not match.");
+		}
+		if (ip.getPixels() instanceof byte[]) {
+			byte[] bsrc = (byte[]) ip.getPixels();
+			for (int y = 0; y < ny; y++)
+				for (int x = 0; x < nx; x++)
+					pixels[x][y] = (double) (bsrc[x + y * nx] & 0xFF);
+		}
+		else if (ip.getPixels() instanceof short[]) {
+			short[] ssrc = (short[]) ip.getPixels();
+			for (int y = 0; y < ny; y++)
+				for (int x = 0; x < nx; x++)
+					pixels[x][y] = (double) (ssrc[x + y * nx] & 0xFFFF);
+		}
+		else if (ip.getPixels() instanceof float[]) {
+			float[] fsrc = (float[]) ip.getPixels();
+			for (int y = 0; y < ny; y++)
+				for (int x = 0; x < nx; x++)
+					pixels[x][y] = (double) fsrc[x + y * nx];
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type.");
+		}
+	}
+
+	/**
+	 * Loads the contents of an ImageProcessor into a float[][] array.
+	 */
+	public static void loadImage(ImageProcessor ip, float[][] pixels) {
+		if (ip == null) {
+			throw new ArrayStoreException("ImageProcessor == null.");
+		}
+		int nx = ip.getWidth();
+		int ny = ip.getHeight();
+		if (nx != pixels.length || ny != pixels[0].length) {
+			throw new IndexOutOfBoundsException("Array sizes do not match.");
+		}
+		if (ip.getPixels() instanceof byte[]) {
+			byte[] bsrc = (byte[]) ip.getPixels();
+			for (int y = 0; y < ny; y++)
+				for (int x = 0; x < nx; x++)
+					pixels[x][y] = (float) (bsrc[x + y * nx] & 0xFF);
+		}
+		else if (ip.getPixels() instanceof short[]) {
+			short[] ssrc = (short[]) ip.getPixels();
+			for (int y = 0; y < ny; y++)
+				for (int x = 0; x < nx; x++)
+					pixels[x][y] = (float) (ssrc[x + y * nx] & 0xFFFF);
+		}
+		else if (ip.getPixels() instanceof float[]) {
+			float[] fsrc = (float[]) ip.getPixels();
+			for (int y = 0; y < ny; y++)
+				for (int x = 0; x < nx; x++)
+					pixels[x][y] = fsrc[x + y * nx];
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type.");
+		}
+	}
+
+	/**
+	 * Loads the three RGB channels of an ImageProcessor into individual float[]
+	 * arrays.
+	 */
+	public static void loadImageRGB(ImageProcessor ip, float[] rChannel, float[] gChannel, float[] bChannel) {
+		if (ip == null) {
+			throw new ArrayStoreException("ImageProcessor == null.");
+		}
+		int nk = ip.getWidth() * ip.getHeight();
+		if (nk != rChannel.length || nk != gChannel.length || nk != bChannel.length) {
+			throw new IndexOutOfBoundsException("Array sizes do not match");
+		}
+		if (ip.getPixels() instanceof int[]) {
+			int[] isrc = (int[]) ip.getPixels();
+			for (int k = 0; k < nk; k++) {
+				rChannel[k] = (float) ((isrc[k] >> 16) & 0x0000FF);
+				gChannel[k] = (float) ((isrc[k] >> 8) & 0x0000FF);
+				bChannel[k] = (float) ((isrc[k]) & 0x0000FF);
+			}
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type");
+		}
+	}
+
+	/**
+	 * Loads the contents of an ImageStack into a double[] array.
+	 */
+	public static void loadStack(ImageStack istack, double[] pixels) {
+		if (istack == null) {
+			throw new ArrayStoreException("ImageStack == null.");
+		}
+		int nx = istack.getWidth();
+		int ny = istack.getHeight();
+		int nz = istack.getSize();
+		int offz = 0;
+		int nxy = nx * ny;
+		if (istack.getPixels(1) instanceof byte[]) {
+			byte[] bsrc;
+			for (int z = 0; z < nz; z++) {
+				bsrc = (byte[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[i + offz] = (double) (bsrc[i] & 0xFF);
+				}
+				offz += nxy;
+			}
+		}
+		else if (istack.getPixels(1) instanceof short[]) {
+			short[] ssrc;
+			for (int z = 0; z < nz; z++) {
+				ssrc = (short[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[i + offz] = (double) (ssrc[i] & 0xFFFF);
+				}
+				offz += nxy;
+			}
+		}
+		else if (istack.getPixels(1) instanceof float[]) {
+			float[] fsrc;
+			for (int z = 0; z < nz; z++) {
+				fsrc = (float[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[i + offz] = (double) fsrc[i];
+				}
+				offz += nxy;
+			}
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type.");
+		}
+	}
+
+	/**
+	 * Loads the contents of an ImageStack into a float[] array.
+	 */
+	public static void loadStack(ImageStack istack, float[] pixels) {
+		if (istack == null) {
+			throw new ArrayStoreException("ImageStack == null.");
+		}
+		int nx = istack.getWidth();
+		int ny = istack.getHeight();
+		int nz = istack.getSize();
+		int offz = 0;
+		int nxy = nx * ny;
+		if (istack.getPixels(1) instanceof byte[]) {
+			byte[] bsrc;
+			for (int z = 0; z < nz; z++) {
+				bsrc = (byte[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[i + offz] = (float) (bsrc[i] & 0xFF);
+				}
+				offz += nxy;
+			}
+		}
+		else if (istack.getPixels(1) instanceof short[]) {
+			short[] ssrc;
+			for (int z = 0; z < nz; z++) {
+				ssrc = (short[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[i + offz] = (float) (ssrc[i] & 0xFFFF);
+				}
+				offz += nxy;
+			}
+		}
+		else if (istack.getPixels(1) instanceof float[]) {
+			float[] fsrc;
+			for (int z = 0; z < nz; z++) {
+				fsrc = (float[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[i + offz] = fsrc[i];
+				}
+				offz += nxy;
+			}
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type.");
+		}
+	}
+
+	/**
+	 * Loads the contents of an ImageStack into a double[][] array. Input
+	 * structure is [z][xy].
+	 */
+	public static void loadStack(ImageStack istack, double[][] pixels) {
+		if (istack == null)
+			throw new ArrayStoreException("ImageStack == null.");
+
+		int nz = pixels.length;
+		int nxy = pixels[0].length;
+
+		if (istack.getPixels(1) instanceof float[]) {
+			float[] fsrc;
+			for (int z = 0; z < nz; z++) {
+				fsrc = (float[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[z][i] = (double) fsrc[i];
+				}
+			}
+		}
+		else if (istack.getPixels(1) instanceof short[]) {
+			short[] ssrc;
+			for (int z = 0; z < nz; z++) {
+				ssrc = (short[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[z][i] = (double) (ssrc[i] & 0xFFFF);
+				}
+			}
+		}
+		else if (istack.getPixels(1) instanceof byte[]) {
+			byte[] bsrc;
+			for (int z = 0; z < nz; z++) {
+				bsrc = (byte[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[z][i] = (double) (bsrc[i] & 0xFF);
+				}
+			}
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type.");
+		}
+	}
+
+	/**
+	 * Loads the contents of an ImageStack into a float[][] array. Input
+	 * structure is [z][xy].
+	 */
+	public static void loadStack(ImageStack istack, float[][] pixels) {
+		if (istack == null)
+			throw new ArrayStoreException("ImageStack == null.");
+
+		int nz = pixels.length;
+		int nxy = pixels[0].length;
+
+		if (istack.getPixels(1) instanceof float[]) {
+			float[] fsrc;
+			for (int z = 0; z < nz; z++) {
+				fsrc = (float[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[z][i] = fsrc[i];
+				}
+			}
+		}
+		else if (istack.getPixels(1) instanceof short[]) {
+			short[] ssrc;
+			for (int z = 0; z < nz; z++) {
+				ssrc = (short[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[z][i] = (float) (ssrc[i] & 0xFFFF);
+				}
+			}
+		}
+		else if (istack.getPixels(1) instanceof byte[]) {
+			byte[] bsrc;
+			for (int z = 0; z < nz; z++) {
+				bsrc = (byte[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[z][i] = (float) (bsrc[i] & 0xFF);
+				}
+			}
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type.");
+		}
+	}
+
+	/**
+	 * Loads the contents of a RGB ImageStack into a int[][] array. Input
+	 * structure is [z][xy].
+	 */
+	public static void loadStackRGB(ImageStack istack, int[][] pixels) {
+		if (istack == null) {
+			throw new ArrayStoreException("ImageStack == null.");
+		}
+		int nz = pixels.length;
+		int nxy = pixels[0].length;
+
+		if (istack.getPixels(1) instanceof int[]) {
+			int[] isrc;
+			for (int z = 0; z < nz; z++) {
+				isrc = (int[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[z][i] = isrc[i];
+				}
+			}
+		}
+		else {
+			throw new ArrayStoreException("Input stack must be RGB.");
+		}
+	}
+
+	/**
+	 * Loads the contents of a RGB ImageStack into a int[] array.
+	 */
+	public static void loadStackRGB(ImageStack istack, int[] pixels) {
+		if (istack == null) {
+			throw new ArrayStoreException("ImageStack == null.");
+		}
+		int nx = istack.getWidth();
+		int ny = istack.getHeight();
+		int nz = istack.getSize();
+		int offz = 0;
+		int nxy = nx * ny;
+		if (istack.getPixels(1) instanceof int[]) {
+			int[] isrc;
+			for (int z = 0; z < nz; z++) {
+				isrc = (int[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					pixels[i + offz] = isrc[i];
+				}
+				offz += nxy;
+			}
+		}
+		else {
+			throw new ArrayStoreException("Input stack must be RGB.");
+		}
+	}
+
+	/**
+	 * Converts and RGB ImageProcessor to grayscale and loads the result into a
+	 * float[][] array. Conversion: (R*R + G*G + B*B)/(R+G+B).
+	 */
+	public static void loadAndConvertRGBStack(ImageStack istack, float[][] pixels) {
+		if (istack == null) {
+			throw new ArrayStoreException("ImageStack == null.");
+		}
+		int nz = pixels.length;
+		int nxy = pixels[0].length;
+		float R, G, B;
+		if (istack.getPixels(1) instanceof int[]) {
+			int[] isrc;
+			for (int z = 0; z < nz; z++) {
+				isrc = (int[]) istack.getPixels(z + 1);
+				for (int i = 0; i < nxy; i++) {
+					R = (float) (isrc[i] >> 16 & 0x000000FF);
+					G = (float) (isrc[i] >> 8 & 0x000000FF);
+					B = (float) (isrc[i] & 0x000000FF);
+					pixels[z][i] = (R * R + G * G + B * B) / (R + G + B);
+				}
+			}
+		}
+		else {
+			throw new ArrayStoreException("Unexpected image type.");
+		}
+	}
+
+	/*
+	 * public static double[][] kMeans(double[][] input, int K) {
+	 * 
+	 * int nx = input.length; int ny = input[0].length; int N = nx*ny; double[]
+	 * r = new double[K]; double[] t = new double[K+1]; t[0] =
+	 * -Double.MAX_VALUE; t[K] = Double.MAX_VALUE; int x, y, k; int[] count =
+	 * new int[K]; for (int i=0;i<10;i++) { // change
+	 * 
+	 * for (k=0;k<K;k++) { count[k] = 0; }
+	 * 
+	 * for (k=0;k<K;k++) { for (x=0;x<nx;x++) { for (y=0;y<ny;y++) { if
+	 * (input[x][y] < t[k+1]) { count[k]++; } } } }
+	 * 
+	 * for (k=1;k<K;k++) { t[k] = (r[k]+r[k-1])/2.0; }
+	 * 
+	 * }
+	 * 
+	 * return input; }
+	 */
+
+	/*
+	 * public static double[] histogram(double[][] input, int nBins) {
+	 * 
+	 * int nx = input.length; int ny = input[0].length; double[] hist = new
+	 * double[nBins]; for (int i=0;i<nBins;i++) { hist[i] = 0; } int index;
+	 * double min = Double.MAX_VALUE; double max = -Double.MAX_VALUE; double p;
+	 * 
+	 * for (int x=0;x<nx;x++) { for (int y=0;y<ny;y++) { p = input[x][y]; if (p
+	 * < min) { min = p; } if (p > max) { max = p; } } } for (int x=0;x<nx;x++)
+	 * { for (int y=0;y<ny;y++) { p = input[x][y] - min; index =
+	 * (int)Math.round((p-p%nBins)/nBins); hist[index]++; } } return hist; }
+	 */
+
+	// public static double region
+
+}
diff --git a/src/bilib/src/ijtools/Interpolator.java b/src/bilib/src/ijtools/Interpolator.java
new file mode 100644
index 0000000000000000000000000000000000000000..284786eec55e27b2703b9eba3a4344a0615bfde9
--- /dev/null
+++ b/src/bilib/src/ijtools/Interpolator.java
@@ -0,0 +1,324 @@
+package ijtools;
+
+import java.lang.*;
+import ij.*;
+
+/**
+ * Class for image interpolation with B-splines. Currently supported methods:
+ * linear, quadratic, cubic. Where applicable, mirror boundary conditions are
+ * used.
+ * 
+ * @author Francois Aguet
+ * @version 1.0
+ */
+public class Interpolator {
+
+	private double[]	input;
+	private double[]	c;
+	private int			nx, ny;
+	private String		mode;
+	private double		a, c0;
+
+	/**
+	 * @param input
+	 *            1-D array storing the image.
+	 * @param nx
+	 *            Width of the image.
+	 * @param ny
+	 *            Height of the image.
+	 * @param mode
+	 *            Interpolation mode. Options: "linear", "quadratic", "cubic".
+	 */
+	public Interpolator(double[] input, int nx, int ny, String mode) {
+		this.input = input;
+		this.nx = nx;
+		this.ny = ny;
+		this.mode = mode;
+		if (mode.equals("linear")) {
+			c = input;
+		}
+		else if (mode.equals("quadratic")) {
+			c0 = 8.0;
+			a = -3.0 + 2.0 * Math.sqrt(2.0);
+			computeCoefficients();
+		}
+		else if (mode.equals("cubic")) {
+			c0 = 6.0;
+			a = -2.0 + Math.sqrt(3.0);
+			computeCoefficients();
+		}
+
+		// test reconstruction
+		/*
+		 * double[][] s2 = new double[ntheta][nr]; for (int y=0;y<nr;y++) { for
+		 * (int x=0;x<ntheta;x++) { s2[x][y] = interpolatedValue(x+1, y+1); } }
+		 * IJtools.show(s2, "reconstruction");
+		 */
+	}
+
+	/**
+	 * Return the value interpolated at (x,y).
+	 */
+	public double getValue(double x, double y) {
+		int xi = (int) x;
+		int yi = (int) y;
+		int x0, x1, y0, y1;
+
+		if (mode.equals("linear")) {
+			double dx = x - xi;
+			double dy = y - yi;
+			if (x < 0) {
+				dx = -dx;
+				x1 = mirror(xi - 1, nx);
+			}
+			else {
+				x1 = mirror(xi + 1, nx);
+			}
+			if (y < 0) {
+				dy = -dy;
+				y1 = mirror(yi - 1, ny);
+			}
+			else {
+				y1 = mirror(yi + 1, ny);
+			}
+			x0 = mirror(xi, nx);
+			y0 = mirror(yi, ny);
+			return (1.0 - dy) * (dx * input[x1 + y0 * nx] + (1.0 - dx) * input[x0 + y0 * nx]) + dy * (dx * input[x1 + y1 * nx] + (1.0 - dx) * input[x0 + y1 * nx]);
+		}
+		else {
+			double dx = x - xi;
+			double dy = y - yi;
+			double[] wx, wy;
+			int x2, x3, y2, y3;
+
+			if (x < 0) {
+				xi = xi - 1;
+				dx = 1.0 + dx;
+			}
+			if (y < 0) {
+				yi = yi - 1;
+				dy = 1.0 + dy;
+			}
+
+			if (mode.equals("quadratic")) {
+				wx = getQuadraticSpline(dx);
+				wy = getQuadraticSpline(dy);
+			}
+			else { // if (mode.equals("cubic")) {
+				wx = getCubicSpline(dx);
+				wy = getCubicSpline(dy);
+			}
+
+			x0 = xi - 1;
+			x1 = xi;
+			x2 = xi + 1;
+			x3 = xi + 2;
+			x0 = mirror(x0, nx);
+			x1 = mirror(x1, nx);
+			x2 = mirror(x2, nx);
+			x3 = mirror(x3, nx);
+
+			y0 = yi - 1;
+			y1 = yi;
+			y2 = yi + 1;
+			y3 = yi + 2;
+			y0 = mirror(y0, ny);
+			y1 = mirror(y1, ny);
+			y2 = mirror(y2, ny);
+			y3 = mirror(y3, ny);
+			y0 *= nx;
+			y1 *= nx;
+			y2 *= nx;
+			y3 *= nx;
+
+			double v = wx[0] * (wy[0] * c[x0 + y0] + wy[1] * c[x0 + y1] + wy[2] * c[x0 + y2] + wy[3] * c[x0 + y3]) + wx[1]
+					* (wy[0] * c[x1 + y0] + wy[1] * c[x1 + y1] + wy[2] * c[x1 + y2] + wy[3] * c[x1 + y3]) + wx[2]
+					* (wy[0] * c[x2 + y0] + wy[1] * c[x2 + y1] + wy[2] * c[x2 + y2] + wy[3] * c[x2 + y3]) + wx[3]
+					* (wy[0] * c[x3 + y0] + wy[1] * c[x3 + y1] + wy[2] * c[x3 + y2] + wy[3] * c[x3 + y3]);
+			return v;
+		}
+
+	}
+
+	private int mirror(int x, int nx) {
+		if (x >= 0 && x < nx) {
+			return x;
+		}
+		else if (x < 0) {
+			return -x;
+		}
+		else {
+			return 2 * nx - 2 - x;
+		}
+	}
+
+	// public double[] interpolate (double[][] coordinates) {};
+
+	/*
+	 * public static double interpolateLinear(double[] image, int[] dims, double
+	 * x, double y, int z) {
+	 * 
+	 * int nx = dims[0]; int ny = dims[1]; int i = (int)x; int j = (int)y;
+	 * double dx = x - (double)(i); double dy = y - (double)(j);
+	 * 
+	 * if (x < 0) { dx = -dx; i = 0; } if (i >= nx-1) { dx = 1-dx; i--; i =
+	 * nx-2; } if (y < 0) { dy = -dy; j = 0; } if (j >= ny-1) { dy = 1-dy; j--;
+	 * j = ny-2;}
+	 * 
+	 * int idx1 = i+nx*(j+z*ny); int idx2 = idx1+nx; double v00 = image[idx1];
+	 * double v10 = image[idx1+1]; double v01 = image[idx2]; double v11 =
+	 * image[idx2+1];
+	 * 
+	 * return dx*((v11-v10)*dy + v10) - (dx-1.0)*((v01-v00)*dy + v00); }
+	 */
+
+	private void computeCoefficients() {
+		// Horizontal
+		double[] cp = getCausalInitHorizontal(input, a);
+		int i;
+		for (int y = 0; y < ny; y++) {
+			for (int x = 1; x < nx; x++) { // causal
+				i = x + y * nx;
+				cp[i] = input[i] + a * cp[i - 1];
+			}
+		}
+		c = getAntiCausalInitHorizontal(cp, a); // cn
+		for (int y = 0; y < ny; y++) {
+			for (int x = nx - 2; x >= 0; x--) { // anticausal
+				i = x + y * nx;
+				c[i] = a * (c[i + 1] - cp[i]);
+			}
+		}
+		// Vertical
+		cp = getCausalInitVertical(c, a);
+		for (int x = 0; x < nx; x++) {
+			for (int y = 1; y < ny; y++) {
+				i = x + y * nx;
+				cp[i] = c[i] + a * cp[i - nx];
+			}
+		}
+		c = getAntiCausalInitVertical(cp, a);
+		for (int x = 0; x < nx; x++) {
+			for (int y = ny - 2; y >= 0; y--) {
+				i = x + y * nx;
+				c[i] = a * (c[i + nx] - cp[i]);
+			}
+		}
+		// constant
+		double c02 = c0 * c0;
+		for (i = 0; i < ny * ny; i++) {
+			c[i] = c02 * c[i];
+		}
+	}
+
+	/**
+	 * Computes the quadratic spline basis function at a position t.
+	 * 
+	 * @param t
+	 *            argument between 0 and 1.
+	 * @return 4 sampled values of the cubic B-spline (B3[t+1], B3[t], B3[t-1],
+	 *         B3[t-2]).
+	 */
+	private static double[] getQuadraticSpline(double t) {
+		if (t < 0.0 || t > 1.0) {
+			throw new ArrayStoreException("Argument t for quadratic B-spline outside of expected range [0, 1]: " + t);
+		}
+		double v[] = new double[4];
+		if (t <= 0.5) {
+			v[0] = (t - 0.5) * (t - 0.5) / 2.0;
+			v[1] = 0.75 - t * t;
+			v[2] = 1.0 - v[1] - v[0]; // (t+0.5)*(t+0.5)/2.0;
+			v[3] = 0.0;
+		}
+		else {
+			v[0] = 0.0;
+			v[1] = (t - 1.5) * (t - 1.5) / 2.0;
+			v[3] = (t - 0.5) * (t - 0.5) / 2.0;
+			v[2] = 1.0 - v[3] - v[1];
+		}
+		return v;
+	}
+
+	/**
+	 * Computes the cubic spline basis function at a position t.
+	 * 
+	 * @param t
+	 *            argument between 0 and 1.
+	 * @return 4 sampled values of the cubic B-spline (B3[t+1], B3[t], B3[t-1],
+	 *         B3[t-2]).
+	 */
+	private static double[] getCubicSpline(double t) {
+		if (t < 0.0 || t > 1.0) {
+			throw new ArrayStoreException("Argument t for cubic B-spline outside of expected range [0, 1]: " + t);
+		}
+		double v[] = new double[4];
+		double t1 = 1.0 - t;
+		double t2 = t * t;
+		v[0] = (t1 * t1 * t1) / 6.0;
+		v[1] = (2.0 / 3.0) + 0.5 * t2 * (t - 2);
+		v[3] = (t2 * t) / 6.0;
+		v[2] = 1.0 - v[3] - v[1] - v[0];
+		return v;
+	}
+
+	private double[] getAntiCausalInitVertical(double[] s, double a) {
+		double[] cn = new double[nx * ny];
+		int idx = (ny - 1) * nx;
+		double d = a * a - 1.0;
+		for (int x = 0; x < nx; x++) {
+			cn[x + idx] = a * (s[x + idx] + a * s[x + idx - nx]) / d;
+		}
+		return cn;
+	}
+
+	private double[] getAntiCausalInitHorizontal(double[] s, double a) {
+		double[] cn = new double[nx * ny];
+		double d = a * a - 1.0;
+		for (int y = 0; y < ny; y++) {
+			cn[nx - 1 + y * nx] = a * (s[nx - 1 + y * nx] + a * s[nx - 2 + y * nx]) / d;
+		}
+		return cn;
+	}
+
+	// Exact method
+	private double[] getCausalInitVertical(double[] s, double z) {
+		double[] cp = new double[nx * ny];
+		double zd, sum, den, za;
+		for (int x = 0; x < nx; x++) {
+			zd = Math.pow(z, ny - 1);
+			sum = s[x] + s[x + (ny - 1) * nx] * zd;
+			den = zd * zd;
+			zd = den / z;
+			za = z;
+			for (int y = 1; y < ny - 1; y++) {
+				sum += s[x + y * nx] * (za + zd);
+				za *= z;
+				zd /= z;
+			}
+			sum /= (1 - den);
+			cp[x] = sum;
+		}
+		return cp;
+	}
+
+	private double[] getCausalInitHorizontal(double[] s, double z) {
+		double[] cp = new double[nx * ny];
+		double zd, sum, den, za;
+		for (int y = 0; y < ny; y++) {
+			zd = Math.pow(z, nx - 1);
+			sum = s[y * nx] + s[nx - 1 + y * nx] * zd;
+			den = zd * zd;
+			zd = den / z;
+			za = z;
+			for (int x = 1; x < nx - 1; x++) {
+				sum += s[x + y * nx] * (za + zd);
+				za *= z;
+				zd /= z;
+			}
+			sum /= (1 - den);
+			cp[y * nx] = sum;
+		}
+		return cp;
+	}
+
+}
diff --git a/src/bilib/src/ijtools/SplineConvolver.java b/src/bilib/src/ijtools/SplineConvolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..d0ed39423d071aeddf72aca620746a0f41780c45
--- /dev/null
+++ b/src/bilib/src/ijtools/SplineConvolver.java
@@ -0,0 +1,437 @@
+package ijtools;
+
+//package ijtools;
+import ij.IJ;
+
+/**
+ * Convolution routines for spline kernels of arbitrary integer orders and
+ * scales.
+ * 
+ * @author Francois Aguet
+ * @version 1.0
+ */
+public class SplineConvolver {
+
+	public static double[][] convolve(double[][] input, int n, int scale) {
+
+		int nx = input.length;
+		int ny = input[0].length;
+
+		double[][] output;// = new double[nx][ny];
+		double[][] reference;// = new double[nx][ny];
+
+		double[] splineKernelFine = splineSamples(n, scale);
+		double[] splineKernelCoarse = splineSamples(n, 1);
+		// IJtools.show("spline", splineKernelFine, splineKernelFine.length, 1);
+
+		long start1 = System.currentTimeMillis();
+		reference = Convolver2D.convolveEvenX(input, splineKernelFine);
+		reference = Convolver2D.convolveEvenY(reference, splineKernelFine);
+		long end1 = System.currentTimeMillis();
+		IJtools.show("Reference", reference);
+
+		long start2 = System.currentTimeMillis();
+		if (n % 2 == 1) { // odd n
+			output = Convolver2D.convolveEvenX(input, splineKernelCoarse);
+			output = Convolver2D.convolveEvenY(output, splineKernelCoarse);
+			for (int k = 1; k <= (n + 1) / 2; k++) {
+				output = movingSum(output, scale);
+				output = movingSumSansFrontieres(output, scale);
+			}
+		}
+		else { // even n
+			if (scale % 2 == 1) { // odd scale
+				output = Convolver2D.convolveEvenX(input, splineKernelCoarse);
+				output = Convolver2D.convolveEvenY(output, splineKernelCoarse);
+				for (int k = 0; k <= n; k++) {
+					output = movingSumOdd(output, scale);
+				}
+			}
+			else { // even scale
+				output = movingSum(input, scale);
+				output = movingSumSansFrontieres(output, scale);
+				for (int k = 2; k <= n / 2; k++) {
+					output = movingSum(output, scale);
+					output = movingSumSansFrontieres(output, scale);
+				}
+				output = movingSum(output, scale);
+
+				splineKernelCoarse = splineSamplesHalfStep(n);
+				for (int k = 0; k < splineKernelCoarse.length; k++) {
+					IJ.write("" + splineKernelCoarse[k]);
+				}
+				output = convEvenKernelX(output, splineKernelCoarse);
+				output = convEvenKernelY(output, splineKernelCoarse);
+				output = crop(output, (scale + splineKernelCoarse.length) / 2 - 1);
+			}
+		}
+		double norm = Math.pow(scale, n + 1);
+		for (int x = 0; x < nx; x++) {
+			for (int y = 0; y < ny; y++) {
+				output[x][y] /= (norm * norm);
+			}
+		}
+		long end2 = System.currentTimeMillis();
+		IJ.write("ref time: " + (end1 - start1));
+		IJ.write("sum time: " + (end2 - start2));
+
+		IJtools.show("Moving sums", output);
+
+		return output;
+
+	}
+
+	private static double[][] movingSum(double[][] input, int length) {
+		int nx = input.length;
+		int ny = input[0].length;
+		double[][] temp = new double[nx + length - 1][ny];
+		double[][] output = new double[nx + length - 1][ny + length - 1];
+		int x, y;
+
+		for (y = 0; y < ny; y++) {
+			temp[0][y] = input[0][y];
+			for (x = 1; x < length; x++) {
+				temp[0][y] += input[x][y];
+			}
+			for (x = 1; x < length; x++) { // left border: mirror
+				temp[x][y] = temp[x - 1][y] - input[length - x][y] + input[x][y]; // remove
+																					// 0-(x-length)
+			}
+			for (x = length; x < nx; x++) {
+				temp[x][y] = temp[x - 1][y] - input[x - length][y] + input[x][y];
+			}
+			for (x = nx; x < nx + length - 1; x++) { // right border: mirror
+				temp[x][y] = temp[x - 1][y] - input[x - length][y] + input[2 * nx - x - 2][y]; // (ns-1)
+																								// -
+																								// (x
+																								// -
+																								// (ns-1))
+			}
+		}
+		for (x = 0; x < nx + length - 1; x++) {
+			output[x][0] = temp[x][0];
+			for (y = 1; y < length; y++) {
+				output[x][0] += temp[x][y];
+			}
+			for (y = 1; y < length; y++) { // left border: mirror
+				output[x][y] = output[x][y - 1] - temp[x][length - y] + temp[x][y];
+			}
+			for (y = length; y < ny; y++) {
+				output[x][y] = output[x][y - 1] - temp[x][y - length] + temp[x][y];
+			}
+			for (y = ny; y < ny + length - 1; y++) { // right border: mirror
+				output[x][y] = output[x][y - 1] - temp[x][y - length] + temp[x][2 * ny - y - 2];
+			}
+		}
+		return output;
+	}
+
+	private static double[][] movingSumSansFrontieres(double[][] input, int length) {
+		int nx = input.length;
+		int ny = input[0].length;
+		double[][] temp = new double[nx - length + 1][ny];
+		double[][] output = new double[nx - length + 1][ny - length + 1];
+		int x, y;
+
+		for (y = 0; y < ny; y++) {
+			temp[0][y] = input[0][y];
+			for (x = 1; x < length; x++) {
+				temp[0][y] += input[x][y];
+			}
+			for (x = 1; x < nx - length + 1; x++) {
+				temp[x][y] = temp[x - 1][y] - input[x - 1][y] + input[x + length - 1][y];
+			}
+		}
+		for (x = 0; x < nx - length + 1; x++) { // ///// + 1 ??
+			output[x][0] = temp[x][0];
+			for (y = 1; y < length; y++) {
+				output[x][0] += temp[x][y];
+			}
+			for (y = 1; y < ny - length + 1; y++) {
+				output[x][y] = output[x][y - 1] - temp[x][y - 1] + temp[x][y + length - 1];
+			}
+		}
+		return output;
+	}
+
+	private static double[][] movingSumOdd(double[][] input, int length) {
+		int nx = input.length;
+		int ny = input[0].length;
+		double[][] temp = new double[nx][ny];
+		double[][] output = new double[nx][ny];
+		int c = (length - 1) / 2;
+		int x, y;
+		for (y = 0; y < ny; y++) {
+			temp[0][y] = input[0][y];
+			for (x = 1; x <= c; x++) {
+				temp[0][y] += 2.0 * input[x][y];
+			}
+			for (x = 1; x <= c; x++) { // left border
+				temp[x][y] = temp[x - 1][y] - input[1 + c - x][y] + input[x + c][y];
+			}
+			for (x = c + 1; x < nx - c; x++) {
+				temp[x][y] = temp[x - 1][y] - input[x - c - 1][y] + input[x + c][y];
+			}
+			for (x = nx - c; x < nx; x++) { // right border
+				temp[x][y] = temp[x - 1][y] - input[x - c - 1][y] + input[2 * nx - 2 - x - c][y];
+			}
+		}
+		for (x = 0; x < nx; x++) {
+			output[x][0] = temp[x][0];
+			for (y = 1; y <= c; y++) {
+				output[x][0] += 2.0 * temp[x][y];
+			}
+			for (y = 1; y <= c; y++) { // top border
+				output[x][y] = output[x][y - 1] - temp[x][1 + c - y] + temp[x][y + c];
+			}
+			for (y = c + 1; y < ny - c; y++) {
+				output[x][y] = output[x][y - 1] - temp[x][y - c - 1] + temp[x][y + c];
+			}
+			for (y = ny - c; y < ny; y++) { // bottom border
+				output[x][y] = output[x][y - 1] - temp[x][y - c - 1] + temp[x][2 * ny - 2 - y - c];
+			}
+		}
+		return output;
+	}
+
+	private static double[][] convEvenKernelX(double[][] input, double[] kernel) {
+		int nx = input.length;
+		int ny = input[0].length;
+		int w = kernel.length;
+		double[][] output = new double[nx + w - 1][ny];
+		int x, y, k;
+
+		for (y = 0; y < ny; y++) {
+			for (x = 0; x <= w - 2; x++) { // left border
+				output[x][y] = 0.0;
+				for (k = 0; k <= w - x - 2; k++) { // (w-1) - (x+1)
+					output[x][y] += kernel[k] * input[w - x - 1 - k][y]; // 0 -
+																			// (0
+																			// -
+																			// (x-w+1+k))
+				}
+				for (k = w - x - 1; k < w; k++) {
+					output[x][y] += kernel[k] * input[x - w + 1 + k][y];
+				}
+			}
+			for (x = w - 1; x < nx; x++) {
+				output[x][y] = 0.0;
+				for (k = 0; k < w; k++) {
+					output[x][y] += kernel[k] * input[x - w + 1 + k][y];
+				}
+			}
+			for (x = nx; x <= nx + w - 2; x++) { // right border
+				output[x][y] = 0.0;
+				for (k = 0; k <= nx - 2 - x + w; k++) {
+					output[x][y] += kernel[k] * input[x - w + 1 + k][y];
+				}
+				for (k = nx - x - 1 + w; k < w; k++) {
+					output[x][y] += kernel[k] * input[2 * nx - 3 - x + w - k][y];
+				}
+			}
+		}
+		return output;
+	}
+
+	private static double[][] convEvenKernelY(double[][] input, double[] kernel) {
+		int nx = input.length;
+		int ny = input[0].length;
+		int w = kernel.length;
+		double[][] output = new double[nx][ny + w - 1];
+		int x, y, k;
+
+		for (x = 0; x < nx; x++) {
+			for (y = 0; y <= w - 2; y++) { // top border
+				output[x][y] = 0.0;
+				for (k = 0; k <= w - y - 2; k++) { // outside of signal ->
+													// mirror:
+					output[x][y] += kernel[k] * input[x][w - y - 1 - k];
+				}
+				for (k = w - y - 1; k < w; k++) {
+					output[x][y] += kernel[k] * input[x][y - w + 1 + k];
+				}
+			}
+			for (y = w - 1; y < ny; y++) {
+				output[x][y] = 0.0;
+				for (k = 0; k < w; k++) {
+					output[x][y] += kernel[k] * input[x][y - w + 1 + k];
+				}
+			}
+			for (y = ny; y <= ny + w - 2; y++) { // bottom border
+				output[x][y] = 0.0;
+				for (k = 0; k <= ny - 2 - y + w; k++) {
+					output[x][y] += kernel[k] * input[x][y - w + 1 + k];
+				}
+				for (k = ny - 1 - y + w; k < w; k++) {
+					output[x][y] += kernel[k] * input[x][2 * ny - 3 - y + w - k];
+				}
+			}
+		}
+		return output;
+	}
+
+	private static double[][] crop(double[][] input, int t) {
+		int nx = input.length - 2 * t;
+		int ny = input[0].length - 2 * t;
+		double[][] output = new double[nx][ny];
+		for (int y = 0; y < ny; y++) {
+			for (int x = 0; x < nx; x++) {
+				output[x][y] = input[x + t][y + t];
+			}
+		}
+		return output;
+	}
+
+	/*
+	 * private static double[] mavg(double[] signal, int scale) { int ns =
+	 * signal.length; double[] out = new double[ns+scale-1]; out[0] =
+	 * sum(signal, 0, scale-1); for (int i=1;i<scale;i++) { out[i] = out[i-1] -
+	 * signal[scale-i+1] + signal[i]; } for (int i=scale;i<ns;i++) { out[i] =
+	 * out[i-1] - signal[i-scale] + signal[i]; } for (int
+	 * i=ns;i<=ns+scale-2;i++) { out[i] = out[i-1] - signal[i-scale] +
+	 * signal[2*ns-i]; } return out; }
+	 */
+
+	private static double[] mavg_odd(double[] signal, int scale) {
+		int ns = signal.length;
+		int c = (scale - 1) / 2;
+		double[] out = new double[ns];
+		out[0] = signal[0] + 2.0 * sum(signal, 1, c);
+		for (int i = 1; i <= c; i++) {
+			out[i] = out[i - 1] - signal[2 - i + c] + signal[i + c];
+		}
+		for (int i = c + 1; i < ns - c; i++) {
+			out[i] = out[i - 1] - signal[i - c - 1] + signal[i + c];
+		}
+		for (int i = ns - c; i < ns; i++) {
+			out[i] = out[i - 1] - signal[i - c - 1] + signal[2 * ns - c - i];
+		}
+		return out;
+	}
+
+	private static double[] convEvenKernel(double[] signal, double[] kernel) {
+		int ns = signal.length;
+		int w = kernel.length;
+		double[] out = new double[ns + w - 1];
+		for (int i = 0; i <= w - 2; i++) {
+			out[i] = 0.0;
+			for (int k = 0; k <= w - i - 1; k++) {
+				out[i] += kernel[k] * signal[2 - i + w - k];
+			}
+			for (int k = w - i; k < w; k++) {
+				out[i] += kernel[k] * signal[i - w + k];
+			}
+		}
+		for (int i = w - 1; i < ns; i++) {
+			out[i] = 0.0;
+			for (int k = 0; k < w; k++) {
+				out[i] += kernel[k] * signal[i - w + k];
+			}
+		}
+		for (int i = ns; i <= ns + w - 2; i++) {
+			for (int k = 0; k < ns - i + w; k++) {
+				out[i] += kernel[k] * signal[i - w + k];
+			}
+			for (int k = ns - i + w; k < w; k++) {
+				out[i] += kernel[k] * signal[ns - 1 - w + k];
+			}
+		}
+		return out;
+	}
+
+	private static double[] mavg_nob(double[] signal, int scale) {
+		int ns = signal.length;
+		double[] out = new double[ns - scale + 1];
+		out[0] = sum(signal, 0, scale - 1);
+		for (int i = 1; i <= ns - scale; i++) {
+			out[i] = out[i - 1] + signal[i + scale - 1] - signal[i - 1];
+		}
+		return out;
+	}
+
+	private static double sum(double[] s, int a, int b) {
+		double t = 0;
+		for (int i = a; i <= b; i++) {
+			t += s[i];
+		}
+		return t;
+	}
+
+	/**
+	 * returns the samples of a degree n, scale s B-spline samples returned for
+	 * [0 b+1/2]
+	 */
+	private static double[] splineSamples(int n, int s) {
+
+		double b = ((double) n + 1.0) / 2.0;
+		double normalization = (double) (s * fact(n));
+		int nx = (int) (b * s); // +1 unnecessary, =0
+		if (nx != b * s) {
+			nx++;
+		}
+		double[] samples = new double[nx];
+		double ksign;
+		double x, klimit;
+		int[] coeffs = binomialCoefficients(n + 1);
+		for (int i = 0; i < nx; i++) {
+			x = (double) i / (double) s;
+			klimit = x + b;
+			samples[i] = Math.pow(x + b, n);
+			ksign = 1.0;
+			for (int k = 1; k <= klimit; k++) {
+				ksign *= -1.0;
+				samples[i] += (double) coeffs[k] * ksign * Math.pow(x - k + b, n);
+			}
+			samples[i] /= normalization;
+		}
+		return samples;
+	}
+
+	/**
+	 * returns the samples of a degree n at half-integer positions samples
+	 * returned for [-(b+1)/2 (b+1)/2]
+	 */
+	private static double[] splineSamplesHalfStep(int n) {
+		double b = ((double) n + 1.0) / 2.0;
+		double normalization = (double) fact(n);
+		double[] samples = new double[n];
+		double ksign;
+		double x, klimit;
+		int[] coeffs = binomialCoefficients(n + 1);
+		for (int i = 0; i < n; i++) {
+			x = (double) i - ((double) n - 1.0) / 2.0;
+			klimit = x + b;
+			ksign = 1.0;
+			samples[i] = Math.pow(x + b, n);
+			for (int k = 1; k <= klimit; k++) {
+				ksign *= -1.0;
+				samples[i] += (double) coeffs[k] * ksign * Math.pow(x - k + b, n);
+			}
+			samples[i] /= normalization;
+		}
+		return samples;
+	}
+
+	private static int[] binomialCoefficients(int n) {
+		int[] coeffs = new int[n + 1];
+		for (int k = 0; k <= n; k++) {
+			coeffs[k] = (int) (fact(n) / (fact(k) * fact(n - k)));
+		}
+		return coeffs;
+	}
+
+	public static long fact(int n) {
+
+		if (n <= 1) {
+			return 1;
+		}
+		else {
+			long k = n;
+			for (int i = n - 1; i > 1; i--) {
+				k *= i;
+			}
+			return k;
+		}
+	}
+
+}
diff --git a/src/bilib/src/imageware.zip b/src/bilib/src/imageware.zip
new file mode 100644
index 0000000000000000000000000000000000000000..4685044fa0b9ca7d6a7683c0a320929b0051f837
Binary files /dev/null and b/src/bilib/src/imageware.zip differ
diff --git a/src/bilib/src/imageware/Access.java b/src/bilib/src/imageware/Access.java
new file mode 100644
index 0000000000000000000000000000000000000000..d345d9b0a660f91ce52736dde3781979df5be5ec
--- /dev/null
+++ b/src/bilib/src/imageware/Access.java
@@ -0,0 +1,256 @@
+package imageware;
+
+/**
+ * Class Access.
+ * 
+ * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
+ *         Lausanne, Lausanne, Switzerland
+ */
+
+public interface Access extends Buffer {
+
+	// getPixel section
+	public double getPixel(int x, int y, int z);
+
+	public double getPixel(int x, int y, int z, byte boundaryConditions);
+
+	public double getInterpolatedPixel(double x, double y, double z);
+
+	public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions);
+
+	// putPixel section
+	public void putPixel(int x, int y, int z, double value);
+
+	// getBounded section
+
+	public void getBoundedX(int x, int y, int z, byte[] buffer);
+
+	public void getBoundedY(int x, int y, int z, byte[] buffer);
+
+	public void getBoundedZ(int x, int y, int z, byte[] buffer);
+
+	public void getBoundedXY(int x, int y, int z, byte[][] buffer);
+
+	public void getBoundedXZ(int x, int y, int z, byte[][] buffer);
+
+	public void getBoundedYZ(int x, int y, int z, byte[][] buffer);
+
+	public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer);
+
+	public void getBoundedX(int x, int y, int z, short[] buffer);
+
+	public void getBoundedY(int x, int y, int z, short[] buffer);
+
+	public void getBoundedZ(int x, int y, int z, short[] buffer);
+
+	public void getBoundedXY(int x, int y, int z, short[][] buffer);
+
+	public void getBoundedXZ(int x, int y, int z, short[][] buffer);
+
+	public void getBoundedYZ(int x, int y, int z, short[][] buffer);
+
+	public void getBoundedXYZ(int x, int y, int z, short[][][] buffer);
+
+	public void getBoundedX(int x, int y, int z, float[] buffer);
+
+	public void getBoundedY(int x, int y, int z, float[] buffer);
+
+	public void getBoundedZ(int x, int y, int z, float[] buffer);
+
+	public void getBoundedXY(int x, int y, int z, float[][] buffer);
+
+	public void getBoundedXZ(int x, int y, int z, float[][] buffer);
+
+	public void getBoundedYZ(int x, int y, int z, float[][] buffer);
+
+	public void getBoundedXYZ(int x, int y, int z, float[][][] buffer);
+
+	public void getBoundedX(int x, int y, int z, double[] buffer);
+
+	public void getBoundedY(int x, int y, int z, double[] buffer);
+
+	public void getBoundedZ(int x, int y, int z, double[] buffer);
+
+	public void getBoundedXY(int x, int y, int z, double[][] buffer);
+
+	public void getBoundedXZ(int x, int y, int z, double[][] buffer);
+
+	public void getBoundedYZ(int x, int y, int z, double[][] buffer);
+
+	public void getBoundedXYZ(int x, int y, int z, double[][][] buffer);
+
+	// getBlock with boundary conditions section
+
+	public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions);
+
+	public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions);
+
+	public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions);
+
+	public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions);
+
+	public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions);
+
+	public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions);
+
+	public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions);
+
+	public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions);
+
+	public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions);
+
+	public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions);
+
+	public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions);
+
+	public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions);
+
+	public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions);
+
+	public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions);
+
+	public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions);
+
+	public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions);
+
+	public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions);
+
+	public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions);
+
+	public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions);
+
+	public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions);
+
+	public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions);
+
+	public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions);
+
+	public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions);
+
+	public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions);
+
+	public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions);
+
+	public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions);
+
+	public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions);
+
+	public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions);
+
+	// getNeighborhood with boundary conditions section
+
+	public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions);
+
+	public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions);
+
+	// putBounded section
+
+	public void putBoundedX(int x, int y, int z, byte[] buffer);
+
+	public void putBoundedY(int x, int y, int z, byte[] buffer);
+
+	public void putBoundedZ(int x, int y, int z, byte[] buffer);
+
+	public void putBoundedXY(int x, int y, int z, byte[][] buffer);
+
+	public void putBoundedXZ(int x, int y, int z, byte[][] buffer);
+
+	public void putBoundedYZ(int x, int y, int z, byte[][] buffer);
+
+	public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer);
+
+	public void putBoundedX(int x, int y, int z, short[] buffer);
+
+	public void putBoundedY(int x, int y, int z, short[] buffer);
+
+	public void putBoundedZ(int x, int y, int z, short[] buffer);
+
+	public void putBoundedXY(int x, int y, int z, short[][] buffer);
+
+	public void putBoundedXZ(int x, int y, int z, short[][] buffer);
+
+	public void putBoundedYZ(int x, int y, int z, short[][] buffer);
+
+	public void putBoundedXYZ(int x, int y, int z, short[][][] buffer);
+
+	public void putBoundedX(int x, int y, int z, float[] buffer);
+
+	public void putBoundedY(int x, int y, int z, float[] buffer);
+
+	public void putBoundedZ(int x, int y, int z, float[] buffer);
+
+	public void putBoundedXY(int x, int y, int z, float[][] buffer);
+
+	public void putBoundedXZ(int x, int y, int z, float[][] buffer);
+
+	public void putBoundedYZ(int x, int y, int z, float[][] buffer);
+
+	public void putBoundedXYZ(int x, int y, int z, float[][][] buffer);
+
+	public void putBoundedX(int x, int y, int z, double[] buffer);
+
+	public void putBoundedY(int x, int y, int z, double[] buffer);
+
+	public void putBoundedZ(int x, int y, int z, double[] buffer);
+
+	public void putBoundedXY(int x, int y, int z, double[][] buffer);
+
+	public void putBoundedXZ(int x, int y, int z, double[][] buffer);
+
+	public void putBoundedYZ(int x, int y, int z, double[][] buffer);
+
+	public void putBoundedXYZ(int x, int y, int z, double[][][] buffer);
+
+}
diff --git a/src/bilib/src/imageware/Buffer.java b/src/bilib/src/imageware/Buffer.java
new file mode 100644
index 0000000000000000000000000000000000000000..29206a3ca2ccd7a4599b5b3991881584431fb483
--- /dev/null
+++ b/src/bilib/src/imageware/Buffer.java
@@ -0,0 +1,188 @@
+package imageware;
+
+/**
+ * Class Buffer.
+ * 
+ * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
+ *         Lausanne, Lausanne, Switzerland
+ */
+
+public interface Buffer {
+	public int getType();
+
+	public String getTypeToString();
+
+	public int getDimension();
+
+	public int[] getSize();
+
+	public int getSizeX();
+
+	public int getSizeY();
+
+	public int getSizeZ();
+
+	public int getWidth();
+
+	public int getHeight();
+
+	public int getDepth();
+
+	public int getTotalSize();
+
+	public boolean isSameSize(ImageWare imageware);
+
+	// get section
+	public void getX(int x, int y, int z, ImageWare buffer);
+
+	public void getY(int x, int y, int z, ImageWare buffer);
+
+	public void getZ(int x, int y, int z, ImageWare buffer);
+
+	public void getXY(int x, int y, int z, ImageWare buffer);
+
+	public void getXZ(int x, int y, int z, ImageWare buffer);
+
+	public void getYZ(int x, int y, int z, ImageWare buffer);
+
+	public void getXYZ(int x, int y, int z, ImageWare buffer);
+
+	public void getX(int x, int y, int z, byte[] buffer);
+
+	public void getY(int x, int y, int z, byte[] buffer);
+
+	public void getZ(int x, int y, int z, byte[] buffer);
+
+	public void getXY(int x, int y, int z, byte[][] buffer);
+
+	public void getXZ(int x, int y, int z, byte[][] buffer);
+
+	public void getYZ(int x, int y, int z, byte[][] buffer);
+
+	public void getXYZ(int x, int y, int z, byte[][][] buffer);
+
+	public void getX(int x, int y, int z, short[] buffer);
+
+	public void getY(int x, int y, int z, short[] buffer);
+
+	public void getZ(int x, int y, int z, short[] buffer);
+
+	public void getXY(int x, int y, int z, short[][] buffer);
+
+	public void getXZ(int x, int y, int z, short[][] buffer);
+
+	public void getYZ(int x, int y, int z, short[][] buffer);
+
+	public void getXYZ(int x, int y, int z, short[][][] buffer);
+
+	public void getX(int x, int y, int z, float[] buffer);
+
+	public void getY(int x, int y, int z, float[] buffer);
+
+	public void getZ(int x, int y, int z, float[] buffer);
+
+	public void getXY(int x, int y, int z, float[][] buffer);
+
+	public void getXZ(int x, int y, int z, float[][] buffer);
+
+	public void getYZ(int x, int y, int z, float[][] buffer);
+
+	public void getXYZ(int x, int y, int z, float[][][] buffer);
+
+	public void getX(int x, int y, int z, double[] buffer);
+
+	public void getY(int x, int y, int z, double[] buffer);
+
+	public void getZ(int x, int y, int z, double[] buffer);
+
+	public void getXY(int x, int y, int z, double[][] buffer);
+
+	public void getXZ(int x, int y, int z, double[][] buffer);
+
+	public void getYZ(int x, int y, int z, double[][] buffer);
+
+	public void getXYZ(int x, int y, int z, double[][][] buffer);
+
+	// put section
+	public void putX(int x, int y, int z, ImageWare buffer);
+
+	public void putY(int x, int y, int z, ImageWare buffer);
+
+	public void putZ(int x, int y, int z, ImageWare buffer);
+
+	public void putXY(int x, int y, int z, ImageWare buffer);
+
+	public void putXZ(int x, int y, int z, ImageWare buffer);
+
+	public void putYZ(int x, int y, int z, ImageWare buffer);
+
+	public void putXYZ(int x, int y, int z, ImageWare buffer);
+
+	public void putX(int x, int y, int z, byte[] buffer);
+
+	public void putY(int x, int y, int z, byte[] buffer);
+
+	public void putZ(int x, int y, int z, byte[] buffer);
+
+	public void putXY(int x, int y, int z, byte[][] buffer);
+
+	public void putXZ(int x, int y, int z, byte[][] buffer);
+
+	public void putYZ(int x, int y, int z, byte[][] buffer);
+
+	public void putXYZ(int x, int y, int z, byte[][][] buffer);
+
+	public void putX(int x, int y, int z, short[] buffer);
+
+	public void putY(int x, int y, int z, short[] buffer);
+
+	public void putZ(int x, int y, int z, short[] buffer);
+
+	public void putXY(int x, int y, int z, short[][] buffer);
+
+	public void putXZ(int x, int y, int z, short[][] buffer);
+
+	public void putYZ(int x, int y, int z, short[][] buffer);
+
+	public void putXYZ(int x, int y, int z, short[][][] buffer);
+
+	public void putX(int x, int y, int z, float[] buffer);
+
+	public void putY(int x, int y, int z, float[] buffer);
+
+	public void putZ(int x, int y, int z, float[] buffer);
+
+	public void putXY(int x, int y, int z, float[][] buffer);
+
+	public void putXZ(int x, int y, int z, float[][] buffer);
+
+	public void putYZ(int x, int y, int z, float[][] buffer);
+
+	public void putXYZ(int x, int y, int z, float[][][] buffer);
+
+	public void putX(int x, int y, int z, double[] buffer);
+
+	public void putY(int x, int y, int z, double[] buffer);
+
+	public void putZ(int x, int y, int z, double[] buffer);
+
+	public void putXY(int x, int y, int z, double[][] buffer);
+
+	public void putXZ(int x, int y, int z, double[][] buffer);
+
+	public void putYZ(int x, int y, int z, double[][] buffer);
+
+	public void putXYZ(int x, int y, int z, double[][][] buffer);
+
+	// get Slice in a specific type for a fast and direct access
+	public Object[] getVolume();
+
+	public byte[] getSliceByte(int z);
+
+	public short[] getSliceShort(int z);
+
+	public float[] getSliceFloat(int z);
+
+	public double[] getSliceDouble(int z);
+
+}
diff --git a/src/bilib/src/imageware/Builder.java b/src/bilib/src/imageware/Builder.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bf68a3ab4ffbdc4554514370daadea982b64a56
--- /dev/null
+++ b/src/bilib/src/imageware/Builder.java
@@ -0,0 +1,732 @@
+package imageware;
+
+import ij.ImagePlus;
+import ij.ImageStack;
+import ij.WindowManager;
+import ij.process.ByteProcessor;
+import ij.process.FloatProcessor;
+import ij.process.ImageProcessor;
+import ij.process.ShortProcessor;
+
+import java.awt.Image;
+
+/**
+ * Class Builder.
+ * 
+ * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
+ *         Lausanne, Lausanne, Switzerland
+ */
+
+public class Builder {
+
+	/**
+	 * Wrap a imageware of from the focused image of ImageJ.
+	 */
+	public static ImageWare wrapOnFocus() {
+		ImagePlus imp = WindowManager.getCurrentImage();
+		return wrap(imp.getStack());
+	}
+
+	/**
+	 * Wrap a imageware around a ImagePlus object.
+	 * 
+	 * @param imp
+	 *            an ImagePlus object to wrap
+	 */
+	public static ImageWare wrap(ImagePlus imp) {
+		return wrap(imp.getStack());
+	}
+
+	/**
+	 * Wrap a imageware around a ImageStack object.
+	 * 
+	 * @param stack
+	 *            an ImageStack object to wrap
+	 */
+	public static ImageWare wrap(ImageStack stack) {
+		if (stack == null)
+			throw_null();
+		ImageProcessor ip = stack.getProcessor(1);
+		if (ip instanceof ByteProcessor) {
+			return new ByteSet(stack, ImageWare.WRAP);
+		}
+		else if (ip instanceof ShortProcessor) {
+			return new ShortSet(stack, ImageWare.WRAP);
+		}
+		else if (ip instanceof FloatProcessor) {
+			return new FloatSet(stack, ImageWare.WRAP);
+		}
+		else {
+			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to wrap this ImageStack object.\n"
+					+ "Support only the 8-bit, 16-bit and 32-bits type.\n" + "-------------------------------------------------------\n");
+		}
+	}
+
+	/**
+	 * Create an empty imageware of a specified type.
+	 * 
+	 * @param nx
+	 *            size in X axis
+	 * @param ny
+	 *            size in Y axis
+	 * @param nz
+	 *            size in Z axis
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(int nx, int ny, int nz, int type) {
+		switch (type) {
+		case ImageWare.BYTE:
+			return new ByteSet(nx, ny, nz);
+		case ImageWare.SHORT:
+			return new ShortSet(nx, ny, nz);
+		case ImageWare.FLOAT:
+			return new FloatSet(nx, ny, nz);
+		case ImageWare.DOUBLE:
+			return new DoubleSet(nx, ny, nz);
+		default:
+			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n"
+					+ "-------------------------------------------------------\n");
+		}
+	}
+
+	/**
+	 * Create a imageware of from an Java AWT Image.
+	 * 
+	 * @param image
+	 *            an Java AWT Image object
+	 */
+	public static ImageWare create(Image image) {
+		if (image == null)
+			throw_null();
+		return new ByteSet(image, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from the focussed image of ImageJ.
+	 */
+	public static ImageWare createOnFocus() {
+		return create(WindowManager.getCurrentImage());
+	}
+
+	/**
+	 * Create a imageware of from an ImageStack.
+	 * 
+	 * @param stack
+	 *            an ImageStack object
+	 */
+	public static ImageWare create(ImageStack stack) {
+		if (stack == null)
+			throw_null();
+		ImageWare wrapped = wrap(stack);
+		return wrapped.duplicate();
+	}
+
+	/**
+	 * Create a imageware of from an ImagePlus.
+	 * 
+	 * @param imp
+	 *            an ImagePlus object
+	 */
+	public static ImageWare create(ImagePlus imp) {
+		if (imp == null)
+			throw_null();
+		ImageWare wrapped = wrap(imp);
+		return wrapped.duplicate();
+	}
+
+	/**
+	 * Create an array of 3 datasets from an ImagePlus.
+	 * 
+	 * @param imp
+	 *            an ImagePlus object
+	 */
+	public static ImageWare[] createColors(ImagePlus imp) {
+		if (imp == null)
+			throw_null();
+		return createColors(imp.getStack());
+	}
+
+	/**
+	 * Create an array of 3 imageware from an ImageStack.
+	 * 
+	 * @param stack
+	 *            an ImageStack object
+	 */
+	public static ImageWare[] createColors(ImageStack stack) {
+		if (stack == null)
+			throw_null();
+		ImageWare color[] = new ImageWare[3];
+		color[0] = new ByteSet(stack, ImageWare.RED);
+		color[1] = new ByteSet(stack, ImageWare.GREEN);
+		color[2] = new ByteSet(stack, ImageWare.BLUE);
+		return color;
+	}
+
+	/**
+	 * Create a imageware of a specific channel if the ImagePlus is a color
+	 * image.
+	 * 
+	 * @param imp
+	 *            an ImagePlus object
+	 */
+	public static ImageWare createColorChannel(ImagePlus imp, byte channel) {
+		if (imp == null)
+			throw_null();
+		return createColorChannel(imp.getStack(), channel);
+	}
+
+	/**
+	 * Create a imageware of a specific channel if the ImageStack is a color
+	 * image.
+	 * 
+	 * @param stack
+	 *            an ImageStack object
+	 */
+	public static ImageWare createColorChannel(ImageStack stack, byte channel) {
+		if (stack == null)
+			throw_null();
+		return new ByteSet(stack, channel);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            byte 1D array
+	 */
+	public static ImageWare create(byte[] object) {
+		if (object == null)
+			throw_null();
+		return new ByteSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            short 1D array
+	 */
+	public static ImageWare create(short[] object) {
+		if (object == null)
+			throw_null();
+		return new ShortSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            float 1D array
+	 */
+	public static ImageWare create(float[] object) {
+		if (object == null)
+			throw_null();
+		return new FloatSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            double 1D array
+	 */
+	public static ImageWare create(double[] object) {
+		if (object == null)
+			throw_null();
+		return new DoubleSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            byte 2D array
+	 */
+	public static ImageWare create(byte[][] object) {
+		if (object == null)
+			throw_null();
+		return new ByteSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            short 2D array
+	 */
+	public static ImageWare create(short[][] object) {
+		if (object == null)
+			throw_null();
+		return new ByteSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            float 2D array
+	 */
+	public static ImageWare create(float[][] object) {
+		if (object == null)
+			throw_null();
+		return new FloatSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            double 2D array
+	 */
+	public static ImageWare create(double[][] object) {
+		if (object == null)
+			throw_null();
+		return new DoubleSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            byte 3D array
+	 */
+	public static ImageWare create(byte[][][] object) {
+		if (object == null)
+			throw_null();
+		return new ByteSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            short 3D array
+	 */
+	public static ImageWare create(short[][][] object) {
+		if (object == null)
+			throw_null();
+		return new ShortSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            float 3D array
+	 */
+	public static ImageWare create(float[][][] object) {
+		if (object == null)
+			throw_null();
+		return new FloatSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of from an array.
+	 * 
+	 * @param object
+	 *            double 3D array
+	 */
+	public static ImageWare create(double[][][] object) {
+		if (object == null)
+			throw_null();
+		return new DoubleSet(object, ImageWare.CREATE);
+	}
+
+	/**
+	 * Create a imageware of a specified type from an Java AWT Image object.
+	 * 
+	 * @param image
+	 *            an Java AWT Image object
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(Image image, int type) {
+		if (image == null)
+			throw_null();
+		switch (type) {
+		case ImageWare.BYTE:
+			return new ByteSet(image, ImageWare.CREATE);
+		case ImageWare.SHORT:
+			return new ShortSet(image, ImageWare.CREATE);
+		case ImageWare.FLOAT:
+			return new FloatSet(image, ImageWare.CREATE);
+		case ImageWare.DOUBLE:
+			return new DoubleSet(image, ImageWare.CREATE);
+		default:
+			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n"
+					+ "-------------------------------------------------------\n");
+		}
+	}
+
+	/**
+	 * Create a imageware of from the focused image of ImageJ.
+	 * 
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare createOnFocus(int type) {
+		return create(WindowManager.getCurrentImage(), type);
+	}
+
+	/**
+	 * Create a imageware of a specified type from an ImagePlus object.
+	 * 
+	 * @param imp
+	 *            an ImagePlus object
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(ImagePlus imp, int type) {
+		if (imp == null)
+			throw_null();
+		switch (type) {
+		case ImageWare.BYTE:
+			return new ByteSet(imp.getStack(), ImageWare.CREATE);
+		case ImageWare.SHORT:
+			return new ShortSet(imp.getStack(), ImageWare.CREATE);
+		case ImageWare.FLOAT:
+			return new FloatSet(imp.getStack(), ImageWare.CREATE);
+		case ImageWare.DOUBLE:
+			return new DoubleSet(imp.getStack(), ImageWare.CREATE);
+		default:
+			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n"
+					+ "-------------------------------------------------------\n");
+		}
+	}
+
+	/**
+	 * Create a imageware of a specified type from an ImageStack object.
+	 * 
+	 * @param object
+	 *            an ImageStack object
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(ImageStack object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare wrapped = wrap(object);
+		return wrapped.convert(type);
+	}
+
+	/**
+	 * Create an array of 3 datasets from an ImagePlus.
+	 * 
+	 * @param imp
+	 *            an ImagePlus object
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare[] createColor(ImagePlus imp, int type) {
+		if (imp == null)
+			throw_null();
+		return createColor(imp.getStack(), type);
+	}
+
+	/**
+	 * Create an array of 3 imageware from an ColorProcessor.
+	 * 
+	 * @param stack
+	 *            an ColorProcessor object
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare[] createColor(ImageStack stack, int type) {
+		if (stack == null)
+			throw_null();
+		ImageWare color[] = new ImageWare[3];
+		switch (type) {
+		case ImageWare.BYTE:
+			color[0] = new ByteSet(stack, ImageWare.RED);
+			color[1] = new ByteSet(stack, ImageWare.GREEN);
+			color[2] = new ByteSet(stack, ImageWare.BLUE);
+			break;
+		case ImageWare.SHORT:
+			color[0] = new ShortSet(stack, ImageWare.RED);
+			color[1] = new ShortSet(stack, ImageWare.GREEN);
+			color[2] = new ShortSet(stack, ImageWare.BLUE);
+			break;
+		case ImageWare.FLOAT:
+			color[0] = new FloatSet(stack, ImageWare.RED);
+			color[1] = new FloatSet(stack, ImageWare.GREEN);
+			color[2] = new FloatSet(stack, ImageWare.BLUE);
+			break;
+		case ImageWare.DOUBLE:
+			color[0] = new DoubleSet(stack, ImageWare.RED);
+			color[1] = new DoubleSet(stack, ImageWare.GREEN);
+			color[2] = new DoubleSet(stack, ImageWare.BLUE);
+			break;
+		default:
+			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n"
+					+ "-------------------------------------------------------\n");
+		}
+		return color;
+	}
+
+	/**
+	 * Create a imageware of a specific channel if the ImagePlus is a color
+	 * image.
+	 * 
+	 * @param imp
+	 *            an ImagePlus object
+	 */
+	public static ImageWare createColorChannel(ImagePlus imp, byte channel, int type) {
+		if (imp == null)
+			throw_null();
+		return createColorChannel(imp.getStack(), channel, type);
+	}
+
+	/**
+	 * Create a imageware of a specific channel if the ImageStack is a color
+	 * image.
+	 * 
+	 * @param stack
+	 *            an ImageStack object
+	 */
+	public static ImageWare createColorChannel(ImageStack stack, byte channel, int type) {
+		if (stack == null)
+			throw_null();
+		ImageWare out = null;
+		switch (type) {
+		case ImageWare.BYTE:
+			out = new ByteSet(stack, channel);
+			break;
+		case ImageWare.SHORT:
+			out = new ShortSet(stack, channel);
+			break;
+		case ImageWare.FLOAT:
+			out = new FloatSet(stack, channel);
+			break;
+		case ImageWare.DOUBLE:
+			out = new DoubleSet(stack, channel);
+			break;
+		default:
+			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n"
+					+ "-------------------------------------------------------\n");
+		}
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            byte 1D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(byte[] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, 1, 1, type);
+		out.putX(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            short 1D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(short[] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, 1, 1, type);
+		out.putX(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            float 1D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(float[] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, 1, 1, type);
+		out.putX(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            double 1D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(double[] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, 1, 1, type);
+		out.putX(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            byte 2D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(byte[][] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, object[0].length, 1, type);
+		out.putXY(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            short 2D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(short[][] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, object[0].length, 1, type);
+		out.putXY(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            float 2D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(float[][] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, object[0].length, 1, type);
+		out.putXY(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            double 2D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(double[][] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, object[0].length, 1, type);
+		out.putXY(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            byte 3D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(byte[][][] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, object[0].length, object[0][0].length, type);
+		out.putXYZ(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            short 3D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(short[][][] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, object[0].length, object[0][0].length, type);
+		out.putXYZ(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            float 3D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(float[][][] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, object[0].length, object[0][0].length, type);
+		out.putXYZ(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	 * Create a imageware of a specified type from an array.
+	 * 
+	 * @param object
+	 *            double 3D array
+	 * @param type
+	 *            type of the imageware
+	 */
+	public static ImageWare create(double[][][] object, int type) {
+		if (object == null)
+			throw_null();
+		ImageWare out = createType(object.length, object[0].length, object[0][0].length, type);
+		out.putXYZ(0, 0, 0, object);
+		return out;
+	}
+
+	/**
+	*/
+	private static ImageWare createType(int nx, int ny, int nz, int type) {
+		ImageWare out = null;
+		switch (type) {
+		case ImageWare.BYTE:
+			out = new ByteSet(nx, ny, nz);
+			break;
+		case ImageWare.SHORT:
+			out = new ShortSet(nx, ny, nz);
+			break;
+		case ImageWare.FLOAT:
+			out = new FloatSet(nx, ny, nz);
+			break;
+		case ImageWare.DOUBLE:
+			out = new DoubleSet(nx, ny, nz);
+			break;
+		default:
+			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create this object.\n"
+					+ "-------------------------------------------------------\n");
+		}
+		return out;
+	}
+
+	private static void throw_null() {
+		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to wrap the ImagePlus.\n"
+				+ "The object parameter is null.\n" + "-------------------------------------------------------\n");
+	}
+
+}
diff --git a/src/bilib/src/imageware/ByteAccess.java b/src/bilib/src/imageware/ByteAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..888b0d0c76b5f52539853ef60512220825a5162a
--- /dev/null
+++ b/src/bilib/src/imageware/ByteAccess.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class ByteAccess.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class ByteAccess extends ByteBuffer implements Access {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected ByteAccess(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected ByteAccess(Image image, int mode) {
		super(image, mode);
	}

	protected ByteAccess(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected ByteAccess(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected ByteAccess(byte[] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(byte[][] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(short[] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(short[][] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(short[][][] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(float[] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(float[][] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(float[][][] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(double[] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(double[][] array, int mode) {
		super(array, mode);
	}

	protected ByteAccess(double[][][] array, int mode) {
		super(array, mode);
	}

	// ------------------------------------------------------------------
	//
	// getPixel section
	//
	// ------------------------------------------------------------------

	/**
	 * Get a pixel at specific position without specific boundary conditions
	 * 
	 * If the positions is outside of this imageware, the method return 0.0.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a pixel value
	 */
	public double getPixel(int x, int y, int z) {
		if (x >= nx)
			return 0.0;
		if (y >= ny)
			return 0.0;
		if (z >= nz)
			return 0.0;
		if (x < 0)
			return 0.0;
		if (y < 0)
			return 0.0;
		if (z < 0)
			return 0.0;
		return ((byte[]) data[z])[x + y * nx] & 0xFF;
	}

	/**
	 * Get a pixel at specific position with specific boundary conditions
	 * 
	 * If the positions is outside of this imageware, the method apply the
	 * boundary conditions to return a value.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a pixel value
	 */
	public double getPixel(int x, int y, int z, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a pixel \n" + "at the position (" + x
					+ "," + y + "," + z + ".\n" + "-------------------------------------------------------\n");
		}
		int xp = x;
		while (xp < 0)
			xp += xperiod;
		while (xp >= nx) {
			xp = xperiod - xp;
			xp = (xp < 0 ? -xp : xp);
		}
		int yp = y;
		while (yp < 0)
			yp += yperiod;
		while (yp >= ny) {
			yp = yperiod - yp;
			yp = (yp < 0 ? -yp : yp);
		}
		int zp = z;
		while (zp < 0)
			zp += zperiod;
		while (zp >= nz) {
			zp = zperiod - zp;
			zp = (zp < 0 ? -zp : zp);
		}
		return ((byte[]) data[zp])[xp + yp * nx] & 0xFF;
	}

	/**
	 * Get a interpolated pixel value at specific position without specific
	 * boundary conditions.
	 * 
	 * If the positions is not on the pixel grid, the method return a
	 * interpolated value of the pixel (linear interpolation). If the positions
	 * is outside of this imageware, the method return 0.0.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a interpolated value
	 */
	public double getInterpolatedPixel(double x, double y, double z) {
		if (x > nx - 1)
			return 0.0;
		if (y > ny - 1)
			return 0.0;
		if (z > nz - 1)
			return 0.0;
		if (x < 0)
			return 0.0;
		if (y < 0)
			return 0.0;
		if (z < 0)
			return 0.0;
		double output = 0.0;
		/*
		 * int i = (x >= 0.0 ? ((int)x) : ((int)x - 1)); int j = (y >= 0.0 ?
		 * ((int)y) : ((int)y - 1)); int k = (z >= 0.0 ? ((int)z) : ((int)z -
		 * 1));
		 */
		int i = (x >= 0.0 ? ((int) x) : ((int) x - 1));
		int j = (y >= 0.0 ? ((int) y) : ((int) y - 1));
		int k = (z >= 0.0 ? ((int) z) : ((int) z - 1));
		boolean fi = (i == nx - 1);
		boolean fj = (j == ny - 1);
		boolean fk = (k == nz - 1);
		int index = i + j * nx;
		switch (getDimension()) {
		case 1:
			double v1_0 = (double) (((byte[]) data[k])[index] & 0xFF);
			double v1_1 = (fi ? v1_0 : (double) (((byte[]) data[k])[index + 1] & 0xFF));
			double dx1 = x - (double) i;
			return v1_1 * dx1 - v1_0 * (dx1 - 1.0);
		case 2:
			double v2_00 = (double) (((byte[]) data[k])[index] & 0xFF);
			double v2_10 = (fi ? v2_00 : (double) (((byte[]) data[k])[index + 1] & 0xFF));
			double v2_01 = (fj ? v2_00 : (double) (((byte[]) data[k])[index + nx] & 0xFF));
			double v2_11 = (fi ? (fj ? v2_00 : v2_01) : (double) (((byte[]) data[k])[index + 1 + nx] & 0xFF));
			double dx2 = x - (double) i;
			double dy2 = y - (double) j;
			return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0)));
		case 3:
			double v3_000 = (double) (((byte[]) data[k])[index] & 0xFF);
			double v3_100 = (fi ? v3_000 : (double) (((byte[]) data[k])[index + 1] & 0xFF));
			double v3_010 = (fj ? v3_000 : (double) (((byte[]) data[k])[index + nx] & 0xFF));
			double v3_110 = (fi ? (fj ? v3_000 : v3_010) : (double) (((byte[]) data[k])[index + 1 + nx] & 0xFF));
			double v3_001 = (fk ? v3_000 : (double) (((byte[]) data[k + 1])[index] & 0xFF));
			double v3_011 = (fk ? (fj ? v3_000 : v3_010) : (double) (((byte[]) data[k + 1])[index + 1] & 0xFF));
			double v3_101 = (fk ? (fi ? v3_000 : v3_100) : (double) (((byte[]) data[k + 1])[index + nx] & 0xFF));
			double v3_111 = (fk ? (fj ? (fi ? v3_000 : v3_100) : v3_110) : (double) (((byte[]) data[k + 1])[index + 1 + nx] & 0xFF));
			double dx3 = x - (double) i;
			double dy3 = y - (double) j;
			double dz3 = z - (double) k;
			double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0)));
			double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0)));
			return z2 * dz3 - z1 * (dz3 - 1.0);
		}
		return output;
	}

	/**
	 * Get a interpolated pixel value at specific position with specific
	 * boundary conditions.
	 * 
	 * If the positions is not on the pixel grid, the method return a
	 * interpolated value of the pixel (linear interpolation). If the positions
	 * is outside of this imageware, the method apply the boundary conditions to
	 * return a value.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @param boundaryConditions
	 *            MIRROR or PERIODIC boundary conditions
	 * @return a interpolated value
	 */
	public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions) {
		double output = 0.0;
		int i = (x >= 0.0 ? ((int) x) : ((int) x - 1));
		int j = (y >= 0.0 ? ((int) y) : ((int) y - 1));
		int k = (z >= 0.0 ? ((int) z) : ((int) z - 1));
		switch (getDimension()) {
		case 1:
			double v1_0 = getPixel(i, j, k, boundaryConditions);
			double v1_1 = getPixel(i + 1, j, k, boundaryConditions);
			double dx1 = x - (double) i;
			return v1_1 * dx1 - v1_0 * (dx1 - 1.0);
		case 2:
			double v2_00 = getPixel(i, j, k, boundaryConditions);
			double v2_10 = getPixel(i + 1, j, k, boundaryConditions);
			double v2_01 = getPixel(i, j + 1, k, boundaryConditions);
			double v2_11 = getPixel(i + 1, j + 1, k, boundaryConditions);
			double dx2 = x - (double) i;
			double dy2 = y - (double) j;
			return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0)));
		case 3:
			double v3_000 = getPixel(i, j, k, boundaryConditions);
			double v3_100 = getPixel(i + 1, j, k, boundaryConditions);
			double v3_010 = getPixel(i, j + 1, k, boundaryConditions);
			double v3_110 = getPixel(i + 1, j + 1, k, boundaryConditions);
			double v3_001 = getPixel(i, j, k + 1, boundaryConditions);
			double v3_011 = getPixel(i + 1, j, k + 1, boundaryConditions);
			double v3_101 = getPixel(i, j + 1, k + 1, boundaryConditions);
			double v3_111 = getPixel(i + 1, j + 1, k + 1, boundaryConditions);
			double dx3 = x - (double) i;
			double dy3 = y - (double) j;
			double dz3 = z - (double) k;
			double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0)));
			double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0)));
			return z2 * dz3 - z1 * (dz3 - 1.0);
		}
		return output;
	}

	// ------------------------------------------------------------------
	//
	// putPixel section
	//
	// ------------------------------------------------------------------

	/**
	 * Put a pixel at specific position
	 * 
	 * If the positions is outside of this imageware, the method does nothing.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 */
	public void putPixel(int x, int y, int z, double value) {
		if (x >= nx)
			return;
		if (y >= ny)
			return;
		if (z >= nz)
			return;
		if (x < 0)
			return;
		if (y < 0)
			return;
		if (z < 0)
			return;
		((byte[]) data[z])[x + y * nx] = (byte) value;
	}

	// ------------------------------------------------------------------
	//
	// getBounded section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (tmp[offset] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (tmp[offset] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (tmp[offset] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (tmp[offset] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (tmp[offset] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (tmp[offset] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (tmp[offset] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (tmp[offset] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (((byte[]) data[k])[offset] & 0xFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (((byte[]) data[k])[offset] & 0xFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (((byte[]) data[k])[offset] & 0xFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (((byte[]) data[k])[offset] & 0xFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			byte[] tmp = (byte[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (tmp[offset] & 0xFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			byte[] tmp = (byte[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (tmp[offset] & 0xFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			byte[] tmp = (byte[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (tmp[offset] & 0xFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			byte[] tmp = (byte[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (tmp[offset] & 0xFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (((byte[]) data[z])[offset] & 0xFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (((byte[]) data[z])[offset] & 0xFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (((byte[]) data[z])[offset] & 0xFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (((byte[]) data[z])[offset] & 0xFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (((byte[]) data[z])[offset] & 0xFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (((byte[]) data[z])[offset] & 0xFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (((byte[]) data[z])[offset] & 0xFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (((byte[]) data[z])[offset] & 0xFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				byte[] tmp = (byte[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (byte) (tmp[offset] & 0xFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				byte[] tmp = (byte[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (short) (tmp[offset] & 0xFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				byte[] tmp = (byte[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (float) (tmp[offset] & 0xFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				byte[] tmp = (byte[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (double) (tmp[offset] & 0xFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// getBlock section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			byte[] tmp = (byte[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (byte) (tmp[xp + yp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			byte[] tmp = (byte[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (short) (tmp[xp + yp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			byte[] tmp = (byte[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (float) (tmp[xp + yp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			byte[] tmp = (byte[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (double) (tmp[xp + yp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			byte[] tmp = (byte[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (byte) (tmp[xp + yp * nx] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			byte[] tmp = (byte[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (short) (tmp[xp + yp * nx] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			byte[] tmp = (byte[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (float) (tmp[xp + yp * nx] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			byte[] tmp = (byte[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (double) (tmp[xp + yp * nx] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (byte) (((byte[]) data[zp])[xyp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (short) (((byte[]) data[zp])[xyp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (float) (((byte[]) data[zp])[xyp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (double) (((byte[]) data[zp])[xyp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			byte[] tmp = (byte[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (tmp[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			byte[] tmp = (byte[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (tmp[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			byte[] tmp = (byte[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (tmp[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			byte[] tmp = (byte[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (tmp[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (((byte[]) data[zp])[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (((byte[]) data[zp])[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (((byte[]) data[zp])[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (((byte[]) data[zp])[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (byte) (((byte[]) data[zp])[xp + yp * nx] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (short) (((byte[]) data[zp])[xp + yp * nx] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (float) (((byte[]) data[zp])[xp + yp * nx] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (double) (((byte[]) data[zp])[xp + yp * nx] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				byte[] tmp = (byte[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (byte) (tmp[xp + yp] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				byte[] tmp = (byte[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (short) (tmp[xp + yp] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				byte[] tmp = (byte[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (float) (tmp[xp + yp] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				byte[] tmp = (byte[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (double) (tmp[xp + yp] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// getBlock section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (byte) (tmp[xp + yp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (short) (tmp[xp + yp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (float) (tmp[xp + yp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (double) (tmp[xp + yp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (byte) (tmp[xp + yp * nx] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (short) (tmp[xp + yp * nx] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (float) (tmp[xp + yp * nx] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (double) (tmp[xp + yp * nx] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (byte) (((byte[]) data[zp])[xyp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (short) (((byte[]) data[zp])[xyp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (float) (((byte[]) data[zp])[xyp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (double) (((byte[]) data[zp])[xyp] & 0xFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (tmp[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (tmp[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (tmp[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			byte[] tmp = ((byte[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (tmp[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {

			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (((byte[]) data[zp])[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (((byte[]) data[zp])[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (((byte[]) data[zp])[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (((byte[]) data[zp])[xp + yp] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (byte) (((byte[]) data[zp])[xp + yp * nx] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (short) (((byte[]) data[zp])[xp + yp * nx] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {

			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (float) (((byte[]) data[zp])[xp + yp * nx] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (double) (((byte[]) data[zp])[xp + yp * nx] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				byte[] tmp = ((byte[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (byte) (tmp[xp + yp] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				byte[] tmp = ((byte[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (short) (tmp[xp + yp] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				byte[] tmp = ((byte[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (float) (tmp[xp + yp] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				byte[] tmp = ((byte[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (double) (tmp[xp + yp] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// putBounded section
	//
	// ------------------------------------------------------------------

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (byte) (buffer[i] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (byte) (buffer[i] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (byte) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (byte) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (byte) (buffer[i] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (byte) (buffer[i] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (byte) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			byte[] tmp = (byte[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (byte) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((byte[]) data[k])[offset] = (byte) (buffer[i] & 0xFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((byte[]) data[k])[offset] = (byte) (buffer[i] & 0xFFFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((byte[]) data[k])[offset] = (byte) (buffer[i]);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((byte[]) data[k])[offset] = (byte) (buffer[i]);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			byte[] tmp = (byte[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (byte) (buffer[i][j] & 0xFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			byte[] tmp = (byte[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (byte) (buffer[i][j] & 0xFFFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			byte[] tmp = (byte[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (byte) (buffer[i][j]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			byte[] tmp = (byte[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (byte) (buffer[i][j]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((byte[]) data[k])[offset] = (byte) (buffer[i][j] & 0xFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((byte[]) data[k])[offset] = (byte) (buffer[i][j] & 0xFFFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((byte[]) data[k])[offset] = (byte) (buffer[i][j]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((byte[]) data[k])[offset] = (byte) (buffer[i][j]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((byte[]) data[k])[offset] = (byte) (buffer[i][j] & 0xFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((byte[]) data[k])[offset] = (byte) (buffer[i][j] & 0xFFFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((byte[]) data[k])[offset] = (byte) (buffer[i][j]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((byte[]) data[k])[offset] = (byte) (buffer[i][j]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				byte[] tmp = (byte[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (byte) (buffer[i][j][k] & 0xFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				byte[] tmp = (byte[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (byte) (buffer[i][j][k] & 0xFFFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				byte[] tmp = (byte[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (byte) (buffer[i][j][k]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				byte[] tmp = (byte[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (byte) (buffer[i][j][k]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/ByteBuffer.java b/src/bilib/src/imageware/ByteBuffer.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9d7a5eaf6eabe18abf115e2b5c49f23024d563b
--- /dev/null
+++ b/src/bilib/src/imageware/ByteBuffer.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;
import ij.process.ByteProcessor;
import ij.process.ColorProcessor;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;

import java.awt.Image;
import java.awt.image.ImageObserver;
import java.awt.image.PixelGrabber;

/**
 * Class ByteBuffer.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class ByteBuffer implements Buffer {

	protected Object[]	data	= null;
	protected int		nx		= 0;
	protected int		ny		= 0;
	protected int		nz		= 0;
	protected int		nxy		= 0;

	/**
	 * Constructor of a empty 3D byte buffer.
	 * 
	 * @param nx
	 *            size of the 3D buffer in the X axis
	 * @param ny
	 *            size of the 3D buffer in the Y axis
	 * @param nz
	 *            size of the 3D buffer in the Z axis
	 */
	protected ByteBuffer(int nx, int ny, int nz) {
		this.nx = nx;
		this.ny = ny;
		this.nz = nz;
		if (nx <= 0 || ny <= 0 || nz <= 0)
			throw_constructor(nx, ny, nz);
		allocate();
	}

	/**
	 * Constructor of a byte buffer from a object Image of Java.
	 * 
	 * @param image
	 *            source to build a new imageware
	 */
	protected ByteBuffer(Image image, int mode) {
		if (image == null) {
			throw_constructor();
		}
		ImageObserver observer = null;
		this.nx = image.getWidth(observer);
		this.ny = image.getHeight(observer);
		this.nz = 1;
		this.nxy = nx * ny;
		byte[] pixels = new byte[nxy];
		PixelGrabber pg = new PixelGrabber(image, 0, 0, nx, ny, false);
		try {
			pg.grabPixels();
			pixels = (byte[]) (pg.getPixels());
		}
		catch (Exception e) {
			throw_constructor();
		}
		allocate();
		for (int k = 0; k < nxy; k++)
			((byte[]) data[0])[k] = (byte) (pixels[k] & 0xFF);
	}

	/**
	 * Constructor of a byte buffer from a ImageStack.
	 * 
	 * New data are allocated if the mode is CREATE, the imageware use the data
	 * of ImageJ if the mode is WRAP.
	 * 
	 * @param stack
	 *            source to build a new imageware
	 * @param mode
	 *            WRAP or CREATE
	 */
	protected ByteBuffer(ImageStack stack, int mode) {
		if (stack == null) {
			throw_constructor();
		}
		this.nx = stack.getWidth();
		this.ny = stack.getHeight();
		this.nz = stack.getSize();
		this.nxy = nx * ny;
		switch (mode) {
		case ImageWare.WRAP:
			this.data = stack.getImageArray();
			break;
		case ImageWare.CREATE:
			allocate();
			ImageProcessor ip = stack.getProcessor(1);
			if (ip instanceof ByteProcessor) {
				Object[] vol = stack.getImageArray();
				for (int z = 0; z < nz; z++) {
					byte[] slice = (byte[]) vol[z];
					for (int k = 0; k < nxy; k++) {
						((byte[]) data[z])[k] = (byte) (slice[k] & 0xFF);
					}
				}
			}
			else if (ip instanceof ShortProcessor) {
				Object[] vol = stack.getImageArray();
				for (int z = 0; z < nz; z++) {
					short[] slice = (short[]) vol[z];
					for (int k = 0; k < nxy; k++) {
						((byte[]) data[z])[k] = (byte) (slice[k] & 0xFFFF);
					}
				}
			}
			else if (ip instanceof FloatProcessor) {
				Object[] vol = stack.getImageArray();
				for (int z = 0; z < nz; z++) {
					float[] slice = (float[]) vol[z];
					for (int k = 0; k < nxy; k++) {
						((byte[]) data[z])[k] = (byte) slice[k];
					}
				}
			}
			else if (ip instanceof ColorProcessor) {
				double r, g, b;
				int c;
				ColorProcessor cp;
				int[] pixels;
				for (int z = 0; z < nz; z++) {
					cp = (ColorProcessor) stack.getProcessor(z + 1);
					pixels = (int[]) cp.getPixels();
					for (int k = 0; k < nxy; k++) {
						c = pixels[k];
						r = (double) ((c & 0xFF0000) >> 16);
						g = (double) ((c & 0xFF00) >> 8);
						b = (double) ((c & 0xFF));
						((byte[]) data[z])[k] = (byte) ((r + g + b) / 3.0);
					}
				}
			}
			else {
				throw_constructor();
			}
			break;
		default:
			throw_constructor();
			break;
		}
	}

	/**
	 * Constructor of a byte buffer from a specific color channel of ImageStack.
	 * 
	 * New data are always allocated. If it is a gray image the imageware is
	 * created and fill up with data of the source ImageStack. If it is a color
	 * image only the selected channel is used to create this imageware.
	 * 
	 * @param stack
	 *            source to build a new imageware
	 * @param channel
	 *            RED, GREEN or BLUE
	 */
	protected ByteBuffer(ImageStack stack, byte channel) {
		if (stack == null) {
			throw_constructor();
		}
		this.nx = stack.getWidth();
		this.ny = stack.getHeight();
		this.nz = stack.getSize();
		this.nxy = nx * ny;
		allocate();
		ImageProcessor ip = stack.getProcessor(1);
		if (ip instanceof ByteProcessor) {
			Object[] vol = stack.getImageArray();
			for (int z = 0; z < nz; z++) {
				byte[] slice = (byte[]) vol[z];
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] = (byte) (slice[k] & 0xFF);
				}
			}
		}
		else if (ip instanceof ShortProcessor) {
			Object[] vol = stack.getImageArray();
			for (int z = 0; z < nz; z++) {
				short[] slice = (short[]) vol[z];
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] = (byte) (slice[k] & 0xFFFF);
				}
			}
		}
		else if (ip instanceof FloatProcessor) {
			Object[] vol = stack.getImageArray();
			for (int z = 0; z < nz; z++) {
				float[] slice = (float[]) vol[z];
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] = (byte) slice[k];
				}
			}
		}
		else if (ip instanceof ColorProcessor) {
			ColorProcessor cp;
			int[] pixels;
			for (int z = 0; z < nz; z++) {
				cp = (ColorProcessor) stack.getProcessor(z + 1);
				pixels = (int[]) cp.getPixels();
				switch (channel) {
				case ImageWare.RED:
					for (int k = 0; k < nxy; k++) {
						((byte[]) data[z])[k] = (byte) ((pixels[k] & 0xFF0000) >> 16);
					}
					break;
				case ImageWare.GREEN:
					for (int k = 0; k < nxy; k++) {
						((byte[]) data[z])[k] = (byte) ((pixels[k] & 0xFF00) >> 8);
					}
					break;
				case ImageWare.BLUE:
					for (int k = 0; k < nxy; k++) {
						((byte[]) data[z])[k] = (byte) (pixels[k] & 0xFF);
					}
					break;
				default:
					throw_constructor();
				}
			}
		}
		else {
			throw_constructor();
		}
	}

	/**
	 * Constructor of a byte buffer from a byte array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(byte[] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = 1;
		this.nz = 1;
		allocate();
		putX(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a byte array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(byte[][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = 1;
		allocate();
		putXY(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a byte array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(byte[][][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = array[0][0].length;
		allocate();
		putXYZ(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a short array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(short[] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = 1;
		this.nz = 1;
		allocate();
		putX(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a short array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(short[][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = 1;
		allocate();
		putXY(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a short array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(short[][][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = array[0][0].length;
		allocate();
		putXYZ(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a float array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(float[] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = 1;
		this.nz = 1;
		allocate();
		putX(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a float array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(float[][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = 1;
		allocate();
		putXY(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a float array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(float[][][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = array[0][0].length;
		allocate();
		putXYZ(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a double array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(double[] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = 1;
		this.nz = 1;
		allocate();
		putX(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a double array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(double[][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = 1;
		allocate();
		putXY(0, 0, 0, array);
	}

	/**
	 * Constructor of a byte buffer from a double array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected ByteBuffer(double[][][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = array[0][0].length;
		allocate();
		putXYZ(0, 0, 0, array);
	}

	/**
	 * Return the type of this imageware.
	 * 
	 * @return the type of this imageware
	 */
	public int getType() {
		return ImageWare.BYTE;
	}

	/**
	 * Return the type of this imageware in a string format.
	 * 
	 * @return the type of this imageware translated in a string format
	 */
	public String getTypeToString() {
		return "Byte";
	}

	/**
	 * Return the number of dimension of this imageware (1, 2 or 3).
	 * 
	 * @return the number of dimension of this imageware
	 */
	public int getDimension() {
		int dims = 0;
		dims += (nx > 1 ? 1 : 0);
		dims += (ny > 1 ? 1 : 0);
		dims += (nz > 1 ? 1 : 0);
		return dims;
	}

	/**
	 * Return the size of the imageware int[0] : x, int[1] : y, int[2] : z.
	 * 
	 * @return an array given the size of the imageware
	 */
	public int[] getSize() {
		int[] size = { nx, ny, nz };
		return size;
	}

	/**
	 * Return the size in the X axis.
	 * 
	 * @return the size in the X axis
	 */
	public int getSizeX() {
		return nx;
	}

	/**
	 * Return the size in the Y axis.
	 * 
	 * @return the size in the Y axis
	 */
	public int getSizeY() {
		return ny;
	}

	/**
	 * Return the size in the Z axis.
	 * 
	 * @return the size in the Z axis
	 */
	public int getSizeZ() {
		return nz;
	}

	/**
	 * Return the size in the X axis.
	 * 
	 * @return the size in the X axis
	 */
	public int getWidth() {
		return nx;
	}

	/**
	 * Return the size in the Y axis.
	 * 
	 * @return the size in the Y axis
	 */
	public int getHeight() {
		return ny;
	}

	/**
	 * Return the size in the Z axis.
	 * 
	 * @return the size in the Z axis
	 */
	public int getDepth() {
		return nz;
	}

	/**
	 * Return the number of pixels in the imageware.
	 * 
	 * @return number of pixels in the imageware
	 */
	public int getTotalSize() {
		return nxy * nz;
	}

	/**
	 * Return true is this imageware has the same size the imageware given as
	 * parameter.
	 * 
	 * @param imageware
	 *            imageware to be compared
	 * @return true if the imageware of the same size than this imageware
	 */
	public boolean isSameSize(ImageWare imageware) {
		if (nx != imageware.getSizeX())
			return false;
		if (ny != imageware.getSizeY())
			return false;
		if (nz != imageware.getSizeZ())
			return false;
		return true;
	}

	// ------------------------------------------------------------------
	//
	// put Section
	//
	// ------------------------------------------------------------------

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putX(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		double buf[] = new double[bnx];
		buffer.getX(0, 0, 0, buf);
		putX(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putY(int x, int y, int z, ImageWare buffer) {
		int bny = buffer.getSizeY();
		double buf[] = new double[bny];
		buffer.getY(0, 0, 0, buf);
		putY(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putZ(int x, int y, int z, ImageWare buffer) {
		int bnz = buffer.getSizeZ();
		double buf[] = new double[bnz];
		buffer.getZ(0, 0, 0, buf);
		putZ(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putXY(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bny = buffer.getSizeY();
		double buf[][] = new double[bnx][bny];
		buffer.getXY(0, 0, 0, buf);
		putXY(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putXZ(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bnz = buffer.getSizeZ();
		double buf[][] = new double[bnx][bnz];
		buffer.getXZ(0, 0, 0, buf);
		putXZ(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putYZ(int x, int y, int z, ImageWare buffer) {
		int bny = buffer.getSizeY();
		int bnz = buffer.getSizeZ();
		double buf[][] = new double[bny][bnz];
		buffer.getYZ(0, 0, 0, buf);
		putYZ(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bny = buffer.getSizeY();
		int bnz = buffer.getSizeZ();
		double buf[][][] = new double[bnx][bny][bnz];
		buffer.getXYZ(0, 0, 0, buf);
		putXYZ(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putX(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];

			System.arraycopy(buffer, 0, tmp, offset, leni);
		}
		catch (Exception e) {
			throw_put("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putX(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];

			for (int i = 0; i < leni; i++) {
				tmp[offset] = (byte) (buffer[i] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putX(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];

			for (int i = 0; i < leni; i++) {
				tmp[offset] = (byte) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putX(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];

			for (int i = 0; i < leni; i++) {
				tmp[offset] = (byte) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putY(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];
			for (int i = 0; i < leni; i++) {
				tmp[offset] = (byte) (buffer[i] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putY(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];
			for (int i = 0; i < leni; i++) {
				tmp[offset] = (byte) (buffer[i] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putY(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];
			for (int i = 0; i < leni; i++) {
				tmp[offset] = (byte) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putY(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];
			for (int i = 0; i < leni; i++) {
				tmp[offset] = (byte) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            bybytete 1D array to put into the imageware
	 */
	public void putZ(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				((byte[]) data[z])[offset] = (byte) (buffer[i] & 0xFF);
				z++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byshortte 1D array to put into the imageware
	 */
	public void putZ(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				((byte[]) data[z])[offset] = (byte) (buffer[i] & 0xFFFF);
				z++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byfloatte 1D array to put into the imageware
	 */
	public void putZ(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				((byte[]) data[z])[offset] = (byte) (buffer[i]);
				z++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            bydoublete 1D array to put into the imageware
	 */
	public void putZ(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				((byte[]) data[z])[offset] = (byte) (buffer[i]);
				z++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putXY(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			byte[] tmp = (byte[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					tmp[offset] = (byte) (buffer[i][j] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putXY(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			byte[] tmp = (byte[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					tmp[offset] = (byte) (buffer[i][j] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putXY(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			byte[] tmp = (byte[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					tmp[offset] = (byte) (buffer[i][j]);
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putXY(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			byte[] tmp = (byte[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					tmp[offset] = (byte) (buffer[i][j]);
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putXZ(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + j * nx;
				for (int i = 0; i < leni; i++, offset++) {
					((byte[]) data[z])[offset] = (byte) (buffer[i][j] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_put("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putXZ(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + j * nx;
				for (int i = 0; i < leni; i++, offset++) {
					((byte[]) data[z])[offset] = (byte) (buffer[i][j] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_put("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putXZ(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + j * nx;
				for (int i = 0; i < leni; i++, offset++) {
					((byte[]) data[z])[offset] = (byte) (buffer[i][j]);
				}
			}
		}
		catch (Exception e) {
			throw_put("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putXZ(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + j * nx;
				for (int i = 0; i < leni; i++, offset++) {
					((byte[]) data[z])[offset] = (byte) (buffer[i][j]);
				}
			}
		}
		catch (Exception e) {
			throw_put("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putYZ(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
				for (int i = 0; i < leni; i++, offset += nx) {
					((byte[]) data[z])[offset] = (byte) (buffer[i][j] & 0xFF);
				}
		}
		catch (Exception e) {
			throw_put("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putYZ(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
				for (int i = 0; i < leni; i++, offset += nx) {
					((byte[]) data[z])[offset] = (byte) (buffer[i][j] & 0xFFFF);
				}
		}
		catch (Exception e) {
			throw_put("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putYZ(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
				for (int i = 0; i < leni; i++, offset += nx) {
					((byte[]) data[z])[offset] = (byte) (buffer[i][j]);
				}
		}
		catch (Exception e) {
			throw_put("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putYZ(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
				for (int i = 0; i < leni; i++, offset += nx) {
					((byte[]) data[z])[offset] = (byte) (buffer[i][j]);
				}
		}
		catch (Exception e) {
			throw_put("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				byte[] tmp = (byte[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						tmp[offset] = (byte) (buffer[i][j][k] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				byte[] tmp = (byte[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						tmp[offset] = (byte) (buffer[i][j][k] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				byte[] tmp = (byte[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						tmp[offset] = (byte) (buffer[i][j][k]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				byte[] tmp = (byte[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						tmp[offset] = (byte) (buffer[i][j][k]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "No check", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// get Section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getX(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		double buf[] = new double[bnx];
		getX(x, y, z, buf);
		buffer.putX(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getY(int x, int y, int z, ImageWare buffer) {
		int bny = buffer.getSizeY();
		double buf[] = new double[bny];
		getY(x, y, z, buf);
		buffer.putY(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getZ(int x, int y, int z, ImageWare buffer) {
		int bnz = buffer.getSizeZ();
		double buf[] = new double[bnz];
		getZ(x, y, z, buf);
		buffer.putZ(0, 0, 0, buf);
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getXY(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bny = buffer.getSizeY();
		double buf[][] = new double[bnx][bny];
		getXY(x, y, z, buf);
		buffer.putXY(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getXZ(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bnz = buffer.getSizeZ();
		double buf[][] = new double[bnx][bnz];
		getXZ(x, y, z, buf);
		buffer.putXZ(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the datase
	 */
	public void getYZ(int x, int y, int z, ImageWare buffer) {
		int bny = buffer.getSizeY();
		int bnz = buffer.getSizeZ();
		double buf[][] = new double[bny][bnz];
		getYZ(x, y, z, buf);
		buffer.putYZ(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bny = buffer.getSizeY();
		int bnz = buffer.getSizeZ();
		double buf[][][] = new double[bnx][bny][bnz];
		getXYZ(x, y, z, buf);
		buffer.putXYZ(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getX(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];

			System.arraycopy(tmp, offset, buffer, 0, leni);
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getX(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];

			for (int i = 0; i < leni; i++) {
				buffer[i] = (short) (tmp[offset] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getX(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];

			for (int i = 0; i < leni; i++) {
				buffer[i] = (float) (tmp[offset] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getX(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];

			for (int i = 0; i < leni; i++) {
				buffer[i] = (double) (tmp[offset] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getY(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];
			for (int i = 0; i < leni; i++) {
				buffer[i] = (byte) (tmp[offset] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getY(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];
			for (int i = 0; i < leni; i++) {
				buffer[i] = (short) (tmp[offset] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getY(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];
			for (int i = 0; i < leni; i++) {
				buffer[i] = (float) (tmp[offset] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getY(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			byte[] tmp = (byte[]) data[z];
			for (int i = 0; i < leni; i++) {
				buffer[i] = (double) (tmp[offset] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getZ(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				buffer[i] = (byte) (((byte[]) data[z])[offset] & 0xFF);
				z++;
			}
		}
		catch (Exception e) {
			throw_get("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getZ(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				buffer[i] = (short) (((byte[]) data[z])[offset] & 0xFF);
				z++;
			}
		}
		catch (Exception e) {
			throw_get("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getZ(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				buffer[i] = (float) (((byte[]) data[z])[offset] & 0xFF);
				z++;
			}
		}
		catch (Exception e) {
			throw_get("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getZ(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				buffer[i] = (double) (((byte[]) data[z])[offset] & 0xFF);
				z++;
			}
		}
		catch (Exception e) {
			throw_get("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getXY(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			byte[] tmp = (byte[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (byte) (tmp[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getXY(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			byte[] tmp = (byte[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (short) (tmp[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getXY(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			byte[] tmp = (byte[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (float) (tmp[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getXY(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			byte[] tmp = (byte[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (double) (tmp[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getXZ(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + y * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (byte) (((byte[]) data[z])[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getXZ(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + y * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (short) (((byte[]) data[z])[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getXZ(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + y * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (float) (((byte[]) data[z])[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getXZ(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + y * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (double) (((byte[]) data[z])[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the datase
	 */
	public void getYZ(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
				for (int i = 0; i < leni; i++, offset += nx) {
					buffer[i][j] = (byte) (((byte[]) data[z])[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the datase
	 */
	public void getYZ(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
				for (int i = 0; i < leni; i++, offset += nx) {
					buffer[i][j] = (short) (((byte[]) data[z])[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the datase
	 */
	public void getYZ(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
				for (int i = 0; i < leni; i++, offset += nx) {
					buffer[i][j] = (float) (((byte[]) data[z])[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the datase
	 */
	public void getYZ(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
				for (int i = 0; i < leni; i++, offset += nx) {
					buffer[i][j] = (double) (((byte[]) data[z])[offset] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				byte[] tmp = (byte[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						buffer[i][j][k] = (byte) (tmp[offset] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				byte[] tmp = (byte[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						buffer[i][j][k] = (short) (tmp[offset] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				byte[] tmp = (byte[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						buffer[i][j][k] = (float) (tmp[offset] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				byte[] tmp = (byte[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						buffer[i][j][k] = (double) (tmp[offset] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "No check", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// Private Section
	//
	// ------------------------------------------------------------------

	/**
	 * Prepare a complete error message from the errors coming the constructors.
	 */
	protected void throw_constructor() {
		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a byte imageware.\n"
				+ "-------------------------------------------------------\n");
	}

	/**
	 * Prepare a complete error message from the errors coming the constructors.
	 */
	protected void throw_constructor(int nx, int ny, int nz) {
		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a byte imageware " + nx + "," + ny
				+ "," + nz + "].\n" + "-------------------------------------------------------\n");
	}

	/**
	 * Prepare a complete error message from the errors coming the get routines.
	 */
	protected void throw_get(String direction, String border, Object buffer, int x, int y, int z) {
		int leni = 0;
		int lenj = 0;
		int lenk = 0;
		String type = " unknown type";
		if (buffer instanceof byte[]) {
			leni = ((byte[]) buffer).length;
			type = " 1D byte";
		}
		else if (buffer instanceof short[]) {
			leni = ((short[]) buffer).length;
			type = " 1D short";
		}
		else if (buffer instanceof float[]) {
			leni = ((float[]) buffer).length;
			type = " 1D float";
		}
		else if (buffer instanceof double[]) {
			leni = ((double[]) buffer).length;
			type = " 1D double";
		}
		else if (buffer instanceof byte[][]) {
			leni = ((byte[][]) buffer).length;
			lenj = ((byte[][]) buffer)[0].length;
			type = " 2D byte";
		}
		else if (buffer instanceof short[][]) {
			leni = ((short[][]) buffer).length;
			lenj = ((short[][]) buffer)[0].length;
			type = " 2D short";
		}
		else if (buffer instanceof float[][]) {
			leni = ((float[][]) buffer).length;
			lenj = ((float[][]) buffer)[0].length;
			type = " 2D float";
		}
		else if (buffer instanceof double[][]) {
			leni = ((double[][]) buffer).length;
			lenj = ((double[][]) buffer)[0].length;
			type = " 2D double";
		}
		else if (buffer instanceof byte[][][]) {
			leni = ((byte[][][]) buffer).length;
			lenj = ((byte[][][]) buffer)[0].length;
			lenk = ((byte[][][]) buffer)[0][0].length;
			type = " 3D byte";
		}
		else if (buffer instanceof short[][][]) {
			leni = ((short[][][]) buffer).length;
			lenj = ((short[][][]) buffer)[0].length;
			lenk = ((short[][][]) buffer)[0][0].length;
			type = " 3D short";
		}
		else if (buffer instanceof float[][][]) {
			leni = ((float[][][]) buffer).length;
			lenj = ((float[][][]) buffer)[0].length;
			lenk = ((float[][][]) buffer)[0][0].length;
			type = " 3D float";
		}
		else if (buffer instanceof double[][][]) {
			leni = ((double[][][]) buffer).length;
			lenj = ((double[][][]) buffer)[0].length;
			lenk = ((double[][][]) buffer)[0][0].length;
			type = " 3D double";
		}
		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get a" + type + " buffer ["
				+ (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "from the byte imageware [" + nx + "," + ny + "," + nz + "]\n"
				+ "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n");
	}

	/**
	 * Prepare a complete error message from the errors coming the put routines.
	 */
	protected void throw_put(String direction, String border, Object buffer, int x, int y, int z) {
		int leni = 0;
		int lenj = 0;
		int lenk = 0;
		String type = " unknown type";
		if (buffer instanceof byte[]) {
			leni = ((byte[]) buffer).length;
			type = " 1D byte";
		}
		else if (buffer instanceof short[]) {
			leni = ((short[]) buffer).length;
			type = " 1D short";
		}
		else if (buffer instanceof float[]) {
			leni = ((float[]) buffer).length;
			type = " 1D float";
		}
		else if (buffer instanceof double[]) {
			leni = ((double[]) buffer).length;
			type = " 1D double";
		}
		else if (buffer instanceof byte[][]) {
			leni = ((byte[][]) buffer).length;
			lenj = ((byte[][]) buffer)[0].length;
			type = " 2D byte";
		}
		else if (buffer instanceof short[][]) {
			leni = ((short[][]) buffer).length;
			lenj = ((short[][]) buffer)[0].length;
			type = " 2D short";
		}
		else if (buffer instanceof float[][]) {
			leni = ((float[][]) buffer).length;
			lenj = ((float[][]) buffer)[0].length;
			type = " 2D float";
		}
		else if (buffer instanceof double[][]) {
			leni = ((double[][]) buffer).length;
			lenj = ((double[][]) buffer)[0].length;
			type = " 2D double";
		}
		else if (buffer instanceof byte[][][]) {
			leni = ((byte[][][]) buffer).length;
			lenj = ((byte[][][]) buffer)[0].length;
			lenk = ((byte[][][]) buffer)[0][0].length;
			type = " 3D byte";
		}
		else if (buffer instanceof short[][][]) {
			leni = ((short[][][]) buffer).length;
			lenj = ((short[][][]) buffer)[0].length;
			lenk = ((short[][][]) buffer)[0][0].length;
			type = " 3D short";
		}
		else if (buffer instanceof float[][][]) {
			leni = ((float[][][]) buffer).length;
			lenj = ((float[][][]) buffer)[0].length;
			lenk = ((float[][][]) buffer)[0][0].length;
			type = " 3D float";
		}
		else if (buffer instanceof double[][][]) {
			leni = ((double[][][]) buffer).length;
			lenj = ((double[][][]) buffer)[0].length;
			lenk = ((double[][][]) buffer)[0][0].length;
			type = " 3D double";
		}
		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a" + type + " buffer ["
				+ (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "into the byte imageware [" + nx + "," + ny + "," + nz + "]\n"
				+ "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n");
	}

	// ------------------------------------------------------------------
	//
	// Get slice fast and direct access Section
	//
	// ------------------------------------------------------------------

	/**
	 * Get a reference of the whole volume data.
	 * 
	 * @return a reference of the data of this imageware
	 */
	public Object[] getVolume() {
		return data;
	}

	/**
	 * Get a specific slice, fast and direct access, but only for byte
	 * imageware.
	 * 
	 * @param z
	 *            number of the requested slice
	 * @return a reference of the data of one slice of this imageware
	 */
	public byte[] getSliceByte(int z) {
		return (byte[]) data[z];
	}

	/**
	 * Get a specific slice, fast and direct access, but only for short
	 * imageware.
	 * 
	 * @param z
	 *            number of the requested slice
	 * @return a reference of the data of one slice of this imageware
	 */
	public short[] getSliceShort(int z) {
		return null;
	}

	/**
	 * Get a specific slice, fast and direct access, but only for float
	 * imageware.
	 * 
	 * @param z
	 *            number of the requested slice
	 * @return a reference of the data of one slice of this imageware
	 */
	public float[] getSliceFloat(int z) {
		return null;
	}

	/**
	 * Get a specific slice, fast and direct access, but only for double
	 * imageware.
	 * 
	 * @param z
	 *            number of the requested slice
	 * @return a reference of the data of one slice of this imageware
	 */
	public double[] getSliceDouble(int z) {
		return null;
	}

	/**
	 * Allocate a buffer of size [nx,ny,nz].
	 */
	private void allocate() {
		try {
			this.data = new Object[nz];
			this.nxy = nx * ny;
			for (int z = 0; z < nz; z++)
				this.data[z] = new byte[nxy];
		}
		catch (Exception e) {
			throw_constructor(nx, ny, nz);
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/BytePointwise.java b/src/bilib/src/imageware/BytePointwise.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b75f0484801585c31c3a8e907fe89e7ac2ef2da
--- /dev/null
+++ b/src/bilib/src/imageware/BytePointwise.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;
import ij.process.ByteProcessor;

import java.awt.Image;
import java.util.Random;

/**
 * Class BytePointwise.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class BytePointwise extends ByteAccess implements Pointwise {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected BytePointwise(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected BytePointwise(Image image, int mode) {
		super(image, mode);
	}

	protected BytePointwise(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected BytePointwise(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected BytePointwise(byte[] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(byte[][] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(short[] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(short[][] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(short[][][] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(float[] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(float[][] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(float[][][] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(double[] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(double[][] array, int mode) {
		super(array, mode);
	}

	protected BytePointwise(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Fill this imageware with a constant value.
	 * 
	 * @param value
	 *            the constant value
	 */
	public void fillConstant(double value) {
		byte typedValue = (byte) value;
		byte[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++)
				slice[k] = typedValue;
		}
	}

	/**
	 * Fill this imageware with ramp.
	 */
	public void fillRamp() {
		int off = 0;
		byte[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++)
				slice[k] = (byte) (off + k);
			off += nxy;
		}
	}

	/**
	 * Generate a gaussian noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void fillGaussianNoise(double amplitude) {
		Random rnd = new Random();
		byte[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) ((rnd.nextGaussian()) * amplitude);
			}
		}
	}

	/**
	 * Generate a uniform noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void fillUniformNoise(double amplitude) {
		Random rnd = new Random();
		byte[] slice = null;
		amplitude *= 2.0;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) ((rnd.nextDouble() - 0.5) * amplitude);
			}
		}
	}

	/**
	 * Generate a salt and pepper noise.
	 * 
	 * @param amplitudeSalt
	 *            amplitude of the salt noise
	 * @param amplitudePepper
	 *            amplitude of the pepper noise
	 * @param percentageSalt
	 *            percentage of the salt noise
	 * @param percentagePepper
	 *            percentage of the pepper noise
	 */
	public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) {
		Random rnd = new Random();
		int index, z;
		if (percentageSalt > 0) {
			double nbSalt = nxy * nz / percentageSalt;
			for (int k = 0; k < nbSalt; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((byte[]) data[z])[index] = (byte) (rnd.nextDouble() * amplitudeSalt);
			}
		}
		if (percentagePepper > 0) {
			double nbPepper = nxy * nz / percentagePepper;
			for (int k = 0; k < nbPepper; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((byte[]) data[z])[index] = (byte) (-rnd.nextDouble() * amplitudeSalt);
			}
		}
	}

	/**
	 * Build an ImageStack of ImageJ.
	 */
	public ImageStack buildImageStack() {
		ImageStack imagestack = new ImageStack(nx, ny);
		for (int z = 0; z < nz; z++) {
			ByteProcessor ip = new ByteProcessor(nx, ny);
			byte pix[] = (byte[]) ip.getPixels();
			for (int k = 0; k < nxy; k++)
				pix[k] = (byte) (((byte[]) data[z])[k]);
			imagestack.addSlice("" + z, ip);
		}
		return imagestack;
	}

	/**
	 * Invert the pixel intensity.
	 */
	public void invert() {
		double max = -Double.MAX_VALUE;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFF) > max)
					max = slice[k] & 0xFF;
			}
		}
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) (max - ((double) (slice[k] & 0xFF)));
			}
		}
	}

	/**
	 * Negate the pixel intensity.
	 */
	public void negate() {
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) (-((double) (slice[k] & 0xFF)));
			}
		}
	}

	/**
	 * Clip the pixel intensity into [0..255].
	 */
	public void clip() {
		clip(0.0, 255.0);
	}

	/**
	 * Clip the pixel intensity into [minLevel..maxLevel].
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void clip(double minLevel, double maxLevel) {
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			byte value;
			byte min = (byte) minLevel;
			byte max = (byte) maxLevel;
			for (int k = 0; k < nxy; k++) {
				value = (byte) (slice[k] & 0xFF);
				if (value < min)
					slice[k] = min;
				if (value > max)
					slice[k] = max;
			}
		}
	}

	/**
	 * Rescale the pixel intensity into [0..255].
	 */
	public void rescale() {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFF) > maxImage)
					maxImage = slice[k] & 0xFF;
				if ((slice[k] & 0xFF) < minImage)
					minImage = slice[k] & 0xFF;
			}
		}
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = 128.0;
		}
		else {
			a = 255.0 / (maxImage - minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) (a * (((double) (slice[k] & 0xFF)) - minImage));
			}
		}
	}

	/**
	 * Rescale the pixel intensity into [minLevel..maxLevel].
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void rescale(double minLevel, double maxLevel) {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFF) > maxImage)
					maxImage = slice[k] & 0xFF;
				if ((slice[k] & 0xFF) < minImage)
					minImage = slice[k] & 0xFF;
			}
		}
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = (maxLevel - minLevel) / 2.0;
		}
		else {
			a = (maxLevel - minLevel) / (maxImage - minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) (a * (((double) (slice[k] & 0xFF)) - minImage) + minLevel);
			}
		}
	}

	/**
	 * Rescale the pixel intensity with a linear curve passing through
	 * (maxLevel-minLevel)/2 at the 0 input intensity.
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void rescaleCenter(double minLevel, double maxLevel) {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFF) > maxImage)
					maxImage = slice[k] & 0xFF;
				if ((slice[k] & 0xFF) < minImage)
					minImage = slice[k] & 0xFF;
			}
		}
		double center = (maxLevel + minLevel) / 2.0;
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = (maxLevel - minLevel) / 2.0;
		}
		else {
			if (Math.abs(maxImage) > Math.abs(minImage))
				a = (maxLevel - center) / Math.abs(maxImage);
			else
				a = (center - minLevel) / Math.abs(minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) (a * (((double) (slice[k] & 0xFF)) - minImage) + center);
			}
		}
	}

	/**
	 * Compute the absolute value of this imageware.
	 */
	public void abs() {
	}

	/**
	 * Compute the log of this imageware.
	 */
	public void log() {
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) Math.log(slice[k]);
			}
		}
	}

	/**
	 * Compute the exponential of this imageware.
	 */
	public void exp() {
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) Math.exp(slice[k]);
			}
		}
	}

	/**
	 * Compute the square root of this imageware.
	 */
	public void sqrt() {
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) Math.sqrt(slice[k]);
			}
		}
	}

	/**
	 * Compute the square of this imageware.
	 */
	public void sqr() {
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] *= slice[k];
			}
		}
	}

	/**
	 * Compute the power of a of this imageware.
	 * 
	 * @param a
	 *            exponent
	 */
	public void pow(double a) {
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (byte) Math.pow(slice[k], a);
			}
		}
	}

	/**
	 * Add a constant value to this imageware.
	 */
	public void add(double constant) {
		byte cst = (byte) constant;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += cst;
			}
		}
	}

	/**
	 * Multiply a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void multiply(double constant) {
		byte cst = (byte) constant;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] *= cst;
			}
		}
	}

	/**
	 * Subtract a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void subtract(double constant) {
		byte cst = (byte) constant;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] -= cst;
			}
		}
	}

	/**
	 * Divide by a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void divide(double constant) {
		if (constant == 0.0)
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the constant is 0.\n"
					+ "-------------------------------------------------------\n");
		byte cst = (byte) constant;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] /= cst;
			}
		}
	}

	/**
	 * Threshold a imageware in two levels 0 and 255.
	 * 
	 * All the pixels values strictly greater than 'thresholdValue' and are set
	 * to 0. The remaining values are set to 255.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void threshold(double thresholdValue) {
		threshold(thresholdValue, 0.0, 255.0);
	}

	/**
	 * Threshold a imageware in two levels minLevel and maxLevel.
	 * 
	 * All the pixels values strictly greater than 'thresholdValue' and are set
	 * to maxLevel. The remaining values are set to minLevel.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 * @param minLevel
	 *            double value given the minimum level
	 * @param maxLevel
	 *            double value given the maximum level
	 */
	public void threshold(double thresholdValue, double minLevel, double maxLevel) {
		byte low = (byte) (minLevel);
		byte high = (byte) (maxLevel);
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = ((double) (slice[k] & 0xFF) > thresholdValue ? high : low);
			}
		}
	}

	/**
	 * Apply a soft thresholding.
	 * 
	 * All the pixels values strictly greater than '-thresholdValue' and stricty
	 * lower than 'thresholdValue' set to 0. The remaining positive values are
	 * reduced by 'thresholdvalue'; the remaining negative values are augmented
	 * by 'thresholdValue'.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void thresholdSoft(double thresholdValue) {
		byte zero = (byte) (0.0);
		double pixel;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				pixel = (double) (slice[k] & 0xFF);
				slice[k] = (pixel <= -thresholdValue ? (byte) (pixel + thresholdValue) : (pixel > thresholdValue ? (byte) (pixel - thresholdValue) : zero));
			}
		}
	}

	/**
	 * Apply a hard thresholding.
	 * 
	 * All the pixels values strictly greater than '-thresholdValue' and stricty
	 * lower than 'thresholdValue' are set to 0. The remaining values are
	 * unchanged.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void thresholdHard(double thresholdValue) {
		byte zero = (byte) (0.0);
		double pixel;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				pixel = (double) (slice[k] & 0xFF);
				if (pixel > -thresholdValue && pixel < thresholdValue)
					slice[k] = zero;
			}
		}
	}

	/**
	 * Add a gaussian noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void addGaussianNoise(double amplitude) {
		Random rnd = new Random();
		byte[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += (byte) ((rnd.nextGaussian()) * amplitude);
			}
		}
	}

	/**
	 * Add a uniform noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void addUniformNoise(double amplitude) {
		Random rnd = new Random();
		byte[] slice = null;
		amplitude *= 2.0;
		for (int z = 0; z < nz; z++) {
			slice = (byte[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += (byte) ((rnd.nextDouble() - 0.5) * amplitude);
			}
		}
	}

	/**
	 * Add a salt and pepper noise.
	 * 
	 * @param amplitudeSalt
	 *            amplitude of the salt noise
	 * @param amplitudePepper
	 *            amplitude of the pepper noise
	 * @param percentageSalt
	 *            percentage of the salt noise
	 * @param percentagePepper
	 *            percentage of the pepper noise
	 */
	public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) {
		Random rnd = new Random();
		int index, z;
		if (percentageSalt > 0) {
			double nbSalt = nxy * nz / percentageSalt;
			for (int k = 0; k < nbSalt; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((byte[]) data[z])[index] += (byte) (rnd.nextDouble() * amplitudeSalt);
			}
		}
		if (percentagePepper > 0) {
			double nbPepper = nxy * nz / percentagePepper;
			for (int k = 0; k < nbPepper; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((byte[]) data[z])[index] -= (byte) (rnd.nextDouble() * amplitudeSalt);
			}
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/ByteProcess.java b/src/bilib/src/imageware/ByteProcess.java
new file mode 100644
index 0000000000000000000000000000000000000000..8156fbac956b9f324c3f975d44468a6c76e6d62c
--- /dev/null
+++ b/src/bilib/src/imageware/ByteProcess.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class ByteProcess.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class ByteProcess extends BytePointwise implements Process {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected ByteProcess(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected ByteProcess(Image image, int mode) {
		super(image, mode);
	}

	protected ByteProcess(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected ByteProcess(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected ByteProcess(byte[] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(byte[][] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(short[] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(short[][] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(short[][][] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(float[] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(float[][] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(float[][][] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(double[] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(double[][] array, int mode) {
		super(array, mode);
	}

	protected ByteProcess(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Apply a separable gaussian smoothing over the image with the same
	 * strengthness in all directions. To have a smmothing effect the
	 * strengthness should be strictly greater than 0 and the size in the
	 * considered directions should be greater strictly than 1.
	 * 
	 * @param sigma
	 *            Strengthness of the smoothing
	 */
	public void smoothGaussian(double sigma) {
		smoothGaussian(sigma, sigma, sigma);
	}

	/**
	 * Apply a separablegaussian smoothing over the image with an independant
	 * strengthness in the different directions. To have a smmothing effect the
	 * strengthness should be strictly greater than 0 and the size in the
	 * considered directions should be greater strictly than 1.
	 * 
	 * @param sigmaX
	 *            Strengthness of the smoothing in X axis
	 * @param sigmaY
	 *            Strengthness of the smoothing in X axis
	 * @param sigmaZ
	 *            Strengthness of the smoothing in X axis
	 */
	public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ) {
		int n = 3;
		double N = (double) n;
		double poles[] = new double[n];

		if (nx > 1 && sigmaX > 0.0) {
			double s2 = sigmaX * sigmaX;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[nx];
			for (int z = 0; z < nz; z++) {
				for (int y = 0; y < ny; y++) {
					getX(0, y, z, line);
					putX(0, y, z, Convolver.convolveIIR(line, poles));
				}
			}
		}

		if (ny > 1 && sigmaY > 0.0) {
			double s2 = sigmaY * sigmaY;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[ny];
			for (int x = 0; x < nx; x++) {
				for (int z = 0; z < nz; z++) {
					getY(x, 0, z, line);
					putY(x, 0, z, Convolver.convolveIIR(line, poles));
				}
			}
		}

		if (nz > 1 && sigmaZ > 0.0) {
			double s2 = sigmaZ * sigmaZ;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[nz];
			for (int y = 0; y < ny; y++) {
				for (int x = 0; x < nx; x++) {
					getZ(x, y, 0, line);
					putZ(x, y, 0, Convolver.convolveIIR(line, poles));
				}
			}
		}
	}

	/**
	 * Get the maximum of this imageware and a imageware.
	 * 
	 * @param imageware
	 *            imageware to max
	 */
	public void max(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to get the maximum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					if (((byte[]) data[z])[k] < (byte) tmp[k])
						((byte[]) data[z])[k] = (byte) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					if (((byte[]) data[z])[k] < (byte) tmp[k])
						((byte[]) data[z])[k] = (byte) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					if (((byte[]) data[z])[k] < (byte) tmp[k])
						((byte[]) data[z])[k] = (byte) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					if (((byte[]) data[z])[k] < (byte) tmp[k])
						((byte[]) data[z])[k] = (byte) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Get the minimum of this imageware and a imageware.
	 * 
	 * @param imageware
	 *            imageware to min
	 */
	public void min(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to get the minimum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					if (((byte[]) data[z])[k] > (byte) tmp[k])
						((byte[]) data[z])[k] = (byte) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					if (((byte[]) data[z])[k] > (byte) tmp[k])
						((byte[]) data[z])[k] = (byte) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					if (((byte[]) data[z])[k] > (byte) tmp[k])
						((byte[]) data[z])[k] = (byte) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					if (((byte[]) data[z])[k] > (byte) tmp[k])
						((byte[]) data[z])[k] = (byte) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Add a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to add
	 */
	public void add(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to add because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] += (byte) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] += (byte) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] += (byte) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] += (byte) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Multiply a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to multiply
	 */
	public void multiply(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to multiply because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] *= (byte) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] *= (byte) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] *= (byte) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] *= (byte) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Subtract a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to subtract
	 */
	public void subtract(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to subtract because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] -= (byte) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] -= (byte) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] -= (byte) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] -= (byte) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Divide a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to divide
	 */
	public void divide(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to divide because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] /= (byte) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] /= (byte) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] /= (byte) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((byte[]) data[z])[k] /= (byte) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/ByteSet.java b/src/bilib/src/imageware/ByteSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..d354ff419105232460d2a68981a2b0b844c53847
--- /dev/null
+++ b/src/bilib/src/imageware/ByteSet.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class ByteSet.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class ByteSet extends ByteProcess implements ImageWare {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected ByteSet(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected ByteSet(Image image, int mode) {
		super(image, mode);
	}

	protected ByteSet(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected ByteSet(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected ByteSet(byte[] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(byte[][] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(short[] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(short[][] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(short[][][] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(float[] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(float[][] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(float[][][] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(double[] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(double[][] array, int mode) {
		super(array, mode);
	}

	protected ByteSet(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Duplicate the imageware.
	 * 
	 * Create a new imageware with the same size, same type and same data than
	 * the calling one.
	 * 
	 * @return a duplicated version of this imageware
	 */
	public ImageWare duplicate() {
		ImageWare out = new ByteSet(nx, ny, nz);
		byte[] outdata;
		for (int z = 0; z < nz; z++) {
			outdata = (byte[]) (((ByteSet) out).data[z]);
			System.arraycopy(data[z], 0, outdata, 0, nxy);
		}
		return out;
	}

	/**
	 * Replicate the imageware.
	 * 
	 * Create a new imageware with the same size, same type than the calling
	 * one. The data are not copied.
	 * 
	 * @return a replicated version of this imageware
	 */
	public ImageWare replicate() {
		return new ByteSet(nx, ny, nz);
	}

	/**
	 * Replicate the imageware.
	 * 
	 * Create a new imageware with the same size and a specified type than the
	 * calling one. The data are not copied.
	 * 
	 * @param type
	 *            requested type
	 * @return a replicated version of this imageware
	 */
	public ImageWare replicate(int type) {
		switch (type) {
		case ImageWare.BYTE:
			return new ByteSet(nx, ny, nz);
		case ImageWare.SHORT:
			return new ShortSet(nx, ny, nz);
		case ImageWare.FLOAT:
			return new FloatSet(nx, ny, nz);
		case ImageWare.DOUBLE:
			return new DoubleSet(nx, ny, nz);
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Copy all the data of source in the current imageware. The source should
	 * have the same size and same type than the calling one.
	 * 
	 * @param source
	 *            a source imageware
	 */
	public void copy(ImageWare source) {
		if (nx != source.getSizeX())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ nx + " != " + source.getSizeX() + ").\n" + "-------------------------------------------------------\n");
		if (ny != source.getSizeY())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ ny + " != " + source.getSizeY() + ").\n" + "-------------------------------------------------------\n");
		if (nz != source.getSizeZ())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ nz + " != " + source.getSizeZ() + ").\n" + "-------------------------------------------------------\n");
		if (getType() != source.getType())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same type ("
					+ getType() + " != " + source.getType() + ").\n" + "-------------------------------------------------------\n");
		byte[] src;
		for (int z = 0; z < nz; z++) {
			src = (byte[]) (((ByteSet) source).data[z]);
			System.arraycopy(src, 0, data[z], 0, nxy);
		}
	}

	/**
	 * convert the imageware in a specified type.
	 * 
	 * Create a new imageware with the same size and converted data than the
	 * calling one.
	 * 
	 * @param type
	 *            indicates the type of the output
	 * @return a converted version of this imageware
	 */
	public ImageWare convert(int type) {
		if (type == ImageWare.BYTE)
			return duplicate();
		ImageWare out = null;
		switch (type) {
		case ImageWare.BYTE: {
			byte[] slice;
			out = new ByteSet(nx, ny, nz);
			byte[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((byte[]) data[z]);
				outslice = ((byte[]) ((ByteSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (byte) (slice[k] & 0xFF);
				}
			}
		}
			break;
		case ImageWare.SHORT: {
			byte[] slice;
			out = new ShortSet(nx, ny, nz);
			short[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((byte[]) data[z]);
				outslice = ((short[]) ((ShortSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (short) (slice[k] & 0xFF);
				}
			}
		}
			break;
		case ImageWare.FLOAT: {
			byte[] slice;
			out = new FloatSet(nx, ny, nz);
			float[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((byte[]) data[z]);
				outslice = ((float[]) ((FloatSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (float) (slice[k] & 0xFF);
				}
			}
		}
			break;
		case ImageWare.DOUBLE: {
			byte[] slice;
			out = new DoubleSet(nx, ny, nz);
			double[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((byte[]) data[z]);
				outslice = ((double[]) ((DoubleSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (double) (slice[k] & 0xFF);
				}
			}
		}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n"
					+ "-------------------------------------------------------\n");
		}
		return out;
	}

	/**
	 * Print information of this ImageWare object.
	 */
	public void printInfo() {
		System.out.println("ImageWare object information");
		System.out.println("Dimension: " + getDimension());
		System.out.println("Size: [" + nx + ", " + ny + ", " + nz + "]");
		System.out.println("TotalSize: " + getTotalSize());
		System.out.println("Type: " + getTypeToString());
		System.out.println("Maximun: " + getMaximum());
		System.out.println("Minimun: " + getMinimum());
		System.out.println("Mean: " + getMean());
		System.out.println("Norm1: " + getNorm1());
		System.out.println("Norm2: " + getNorm2());
		System.out.println("Total: " + getTotal());
		System.out.println("");
	}

	/**
	 * Show this ImageWare object.
	 */
	public void show() {
		String title = getTypeToString();
		switch (getDimension()) {
		case 1:
			title += " line";
			break;
		case 2:
			title += " image";
			break;
		case 3:
			title += " volume";
			break;
		}
		Display.show(title, this);
		// ImagePlus imp = new ImagePlus(title, buildImageStack());
		// imp.show();
	}

	/**
	 * Show the data in ImagePlus object with a specify title.
	 * 
	 * @param title
	 *            a string given the title of the window
	 */
	public void show(String title) {
		Display.show(title, this);
		// ImagePlus imp = new ImagePlus(title, buildImageStack());
		// imp.show();
	}

	/**
	 * Return the minimum value of this imageware.
	 * 
	 * @return the min value of this imageware
	 */
	public double getMinimum() {
		double min = Double.MAX_VALUE;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((byte[]) data[z]);
			for (int k = 0; k < nxy; k++)
				if ((slice[k] & 0xFF) < min)
					min = slice[k] & 0xFF;
		}
		return min;
	}

	/**
	 * Return the maximum value of this imageware.
	 * 
	 * @return the max value of this imageware
	 */
	public double getMaximum() {
		double max = -Double.MAX_VALUE;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((byte[]) data[z]);
			for (int k = 0; k < nxy; k++)
				if ((slice[k] & 0xFF) > max)
					max = slice[k] & 0xFF;
		}
		return max;
	}

	/**
	 * Return the mean value of this imageware.
	 * 
	 * @return the mean value of this imageware
	 */
	public double getMean() {
		return getTotal() / (nz * nxy);
	}

	/**
	 * Return the norm value of order 1.
	 * 
	 * @return the norm value of this imageware in L1 sense
	 */
	public double getNorm1() {
		double norm = 0.0;
		double value = 0;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((byte[]) data[z]);
			for (int k = 0; k < nxy; k++) {
				value = (double) (slice[k] & 0xFF);
				norm += (value > 0.0 ? value : -value);
			}
		}
		return norm;
	}

	/**
	 * Return the norm value of order 2.
	 * 
	 * @return the norm value of this imageware in L2 sense
	 */
	public double getNorm2() {
		double norm = 0.0;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((byte[]) data[z]);
			for (int k = 0; k < nxy; k++)
				norm += (slice[k] & 0xFF) * (slice[k] & 0xFF);
		}
		return norm;
	}

	/**
	 * Return the sum of all pixel in this imageware.
	 * 
	 * @return the total sum of all pixel in this imageware
	 */
	public double getTotal() {
		double total = 0.0;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((byte[]) data[z]);
			for (int k = 0; k < nxy; k++)
				total += slice[k] & 0xFF;
		}
		return total;
	}

	/**
	 * Return the the minumum [0] and the maximum [1] value of this imageware.
	 * Faster routine than call one getMinimum() and then one getMaximum().
	 * 
	 * @return an array of two values, the min and the max values of the images
	 */
	public double[] getMinMax() {
		double max = -Double.MAX_VALUE;
		double min = Double.MAX_VALUE;
		byte[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((byte[]) data[z]);
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFF) > max)
					max = slice[k] & 0xFF;
				if ((slice[k] & 0xFF) < min)
					min = slice[k] & 0xFF;
			}
		}
		double minmax[] = { min, max };
		return minmax;
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/Convolver.java b/src/bilib/src/imageware/Convolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..ffeffeefdbfe5ce793dca736ca3bd8098176f11b
--- /dev/null
+++ b/src/bilib/src/imageware/Convolver.java
@@ -0,0 +1 @@
+package imageware;

/**
 * Class Convolver. Routines to convolve a 1D signal applying mirror boundary
 * conditions.
 * 
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class Convolver extends Object {
	private static double	tolerance	= 10e-6;

	/**
	 * Convolution with a Finite Impulse Response (FIR) filter.
	 * 
	 * Note: Only with the periodic boundary conditions.
	 * 
	 * @param input
	 *            1D input signal
	 * @param kernel
	 *            kernel of the filter
	 */
	public static double[] convolveFIR(double[] input, double[] kernel) {
		int l = input.length;
		if (l <= 1)
			throw new IllegalArgumentException("convolveFIR: input signal too short");
		double[] output = new double[l];

		int indexq = kernel.length - 1;
		int indexp = 0;
		int n2 = 2 * (l - 1);
		int origin = kernel.length / 2;
		int m = 1 + origin - kernel.length;
		m -= (m < 0L) ? (n2 * ((m + 1 - n2) / n2)) : (n2 * (m / n2));
		int k;
		for (int i = 0; i < l; i++) {
			int j = -kernel.length;
			k = m;
			indexq = kernel.length - 1;
			double Sum = 0.0;
			while (j < 0) {
				indexp = k;
				int kp = ((k - l) < j) ? (j) : (k - l);
				if (kp < 0L) {
					for (int n = kp; n < 0; n++) {
						Sum += input[indexp] * kernel[indexq];
						indexq--;
						indexp++;
					}
					k -= kp;
					j -= kp;
				}
				indexp = n2 - k;
				int km = ((k - n2) < j) ? (j) : (k - n2);
				if (km < 0L) {
					for (int n = km; n < 0; n++) {
						Sum += input[indexp] * kernel[indexq];
						indexq--;
						indexp--;
					}
					j -= km;
				}
				k = 0;
			}
			if (++m == n2) {
				m = 0;
			}
			output[i] = Sum;
		}
		return output;
	}

	/**
	 * Convolve with with a Infinite Impluse Response filter (IIR)
	 * 
	 * @param input
	 *            1D input signal
	 * @param poles
	 *            1D array containing the poles of the filter
	 */
	public static double[] convolveIIR(double[] input, double poles[]) {
		double lambda = 1.0;
		int l = input.length;
		double[] output = new double[l];
		for (int k = 0; k < poles.length; k++) {
			lambda = lambda * (1.0 - poles[k]) * (1.0 - 1.0 / poles[k]);
		}
		for (int n = 0; n < l; n++) {
			output[n] = input[n] * lambda;
		}
		for (int k = 0; k < poles.length; k++) {
			output[0] = getInitialCausalCoefficientMirror(output, poles[k]);
			for (int n = 1; n < l; n++) {
				output[n] = output[n] + poles[k] * output[n - 1];
			}
			output[l - 1] = getInitialAntiCausalCoefficientMirror(output, poles[k]);
			for (int n = l - 2; 0 <= n; n--) {
				output[n] = poles[k] * (output[n + 1] - output[n]);
			}
		}
		return output;
	}

	/**
	 * Convolve a 1D signal with a Infinite Impluse Response 2nd order (IIR2)
	 * 
	 * Note: Only with the mirror (on bounds) boundary conditions.
	 * 
	 * Purpose: Recursive implementation of a symmetric 2nd order filter with
	 * mirror symmetry boundary conditions :
	 * 
	 * 1 1 H[z] = --------------- * --------------- (1-b1*z-b2*z^2)
	 * (1-b1/z-b2/z^2)
	 * 
	 * implemented in the following form:
	 * 
	 * a1+a2*z a1+a2/z H[z] = --------------- + --------------- - a1
	 * (1-b1*z+b2*z^2) (1-b1/z+b2/z^2)
	 * 
	 * where : a1 = -(b2 + 1.0) * (1 - b1 + b2) / ((b2 - 1.0) * (1 + b1 + b2));
	 * a2 = - a1 * b2 * b1 / (b2 + 1.0);
	 * 
	 * @param input
	 *            1D input signal
	 * @param b1
	 *            first pole of the filter
	 * @param b2
	 *            second pole of the filter
	 */
	public static double[] convolveIIR2(double input[], double b1, double b2) {
		int l = input.length;
		int n2 = 2 * l;

		double a1 = -(b2 + 1.0) * (1 - b1 + b2) / ((b2 - 1.0) * (1 + b1 + b2));
		double a2 = -a1 * b2 * b1 / (b2 + 1.0);

		// cBuffer stores temporary spline coefficients
		double cBuffer[] = new double[n2];

		// sBuffer contains a copy of s[] and a time reversed version of s[]
		double sBuffer[] = new double[n2];

		// copy signal s[] and its time reversed version to sBuffer[]
		for (int n = 0; n < l; n++) {
			sBuffer[n] = input[n];
			sBuffer[n2 - n - 1] = input[n];
		}

		// Determine the start index n0 for the causal recursion. n0 is chosen
		// such
		// that the error of cBuffer[0] and cBuffer[1] is smaller than the
		// specified 'Tolerance'.
		int n0 = 2;
		if ((tolerance > 0.0) && (b2 != 1.0)) {
			n0 = n2 - (int) Math.ceil(2.0 * Math.log(tolerance) / Math.log(b2));
		}

		if (n0 < 2) {
			n0 = 2;
		}

		cBuffer[n0 - 1] = 0.0;
		cBuffer[n0 - 2] = 0.0;
		for (int n = n0; n < n2; n++) {
			cBuffer[n] = a1 * sBuffer[n] + a2 * sBuffer[n - 1] + b1 * cBuffer[n - 1] - b2 * cBuffer[n - 2];
		}

		cBuffer[0] = a1 * sBuffer[0] + a2 * sBuffer[n2 - 1] + b1 * cBuffer[n2 - 1] - b2 * cBuffer[n2 - 2];
		cBuffer[1] = a1 * sBuffer[1] + a2 * sBuffer[0] + b1 * cBuffer[0] - b2 * cBuffer[n2 - 1];

		// compute the remaining spline coefficients cBuffer(z) = H_{+}(z) *
		// sBuffer(z) by
		// recursive filtering
		for (int n = 2; n < n2; n++) {
			cBuffer[n] = a1 * sBuffer[n] + a2 * sBuffer[n - 1] + b1 * cBuffer[n - 1] - b2 * cBuffer[n - 2];
		}

		// add together the temporary filter outputs to obtain the final spline
		// coefficients
		double[] output = new double[l];
		for (int n = 0; n < l; n++) {
			output[n] = cBuffer[n] + cBuffer[n2 - n - 1] - a1 * input[n];
		}
		return output;
	}

	/**
	 */
	private static double getInitialAntiCausalCoefficientMirror(double[] c, double z) {
		return ((z * c[c.length - 2] + c[c.length - 1]) * z / (z * z - 1.0));
	}

	/**
	 */
	private static double getInitialCausalCoefficientMirror(double[] c, double z) {
		double z1 = z, zn = Math.pow(z, c.length - 1);
		double sum = c[0] + zn * c[c.length - 1];
		int horizon = c.length;

		if (0.0 < tolerance) {
			horizon = 2 + (int) (Math.log(tolerance) / Math.log(Math.abs(z)));
			horizon = (horizon < c.length) ? (horizon) : (c.length);
		}
		zn = zn * zn;
		for (int n = 1; (n < (horizon - 1)); n++) {
			zn = zn / z;
			sum = sum + (z1 + zn) * c[n];
			z1 = z1 * z;
		}
		return (sum / (1.0 - Math.pow(z, 2 * c.length - 2)));
	}

} // end of classe
\ No newline at end of file
diff --git a/src/bilib/src/imageware/Display.java b/src/bilib/src/imageware/Display.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a85ab450bcea8bda63e0cd3b7fec08f041184b0
--- /dev/null
+++ b/src/bilib/src/imageware/Display.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.ImageCanvas;
import ij.gui.ImageWindow;
import ij.process.ColorProcessor;

import javax.swing.SwingUtilities;

/**
 * Class Display.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class Display {

	/**
	 * Class Show. Display an imageware in the swing thread.
	 */
	static class Show implements Runnable {
		private String		title;
		private ImageStack	stack;

		public Show(String title, ImageStack stack) {
			this.title = title;
			this.stack = stack;
		}

		public void run() {
			(new ImagePlus(title, stack)).show();
		}
	}

	/**
	 * Class ShowZoom. Display an imageware in the swing thread.
	 */
	static class ShowZoom implements Runnable {
		private String		title;
		private ImageStack	stack;
		private double		magnification;

		public ShowZoom(String title, ImageStack stack, double magnification) {
			this.title = title;
			this.stack = stack;
		}

		public void run() {
			ImagePlus imp = new ImagePlus(title, stack);
			imp.show();
			ImageWindow win = imp.getWindow();
			ImageCanvas canvas = win.getCanvas();
			canvas.setMagnification(magnification);
			canvas.setDrawingSize((int) Math.ceil(stack.getWidth() * magnification), (int) Math.ceil(stack.getHeight() * magnification));
			win.pack();
			imp.updateAndRepaintWindow();
		}
	}

	/**
	 * Shows a imageware with a specifc title.
	 * 
	 * @param title
	 *            a specific title
	 * @param ds
	 *            the imageware to be shown
	 */
	static public void show(String title, ImageWare ds) {
		ImageStack stack = ds.buildImageStack();
		SwingUtilities.invokeLater(new Show(title, stack));
	}

	/**
	 * Shows color image composed by three datasets with a specific title.
	 * 
	 * @param title
	 *            a specific title
	 * @param red
	 *            the imageware to be shown in the red channel
	 * @param green
	 *            the imageware to be shown in the green channel
	 * @param blue
	 *            the imageware to be shown in the blue channel
	 */
	static public void showColor(String title, ImageWare red, ImageWare green, ImageWare blue) {
		ImageStack stack = buildColor(red, green, blue);
		(new ImagePlus(title, stack)).show();
	}

	/**
	 * Shows a imageware with a specific title and with a specific
	 * magnification.
	 * 
	 * @param title
	 *            a specific title
	 * @param ds
	 *            the imageware to be shown
	 * @param magnification
	 *            zoom factor
	 */
	static public void show(String title, ImageWare ds, double magnification) {
		ImageStack stack = ds.buildImageStack();
		SwingUtilities.invokeLater(new ShowZoom(title, stack, magnification));
	}

	/**
	 * Shows color image composed by three datasets with a specifc title and
	 * with a specific magnification.
	 * 
	 * @param title
	 *            a specific title
	 * @param red
	 *            the imageware to be shown in the red channel
	 * @param green
	 *            the imageware to be shown in the green channel
	 * @param blue
	 *            the imageware to be shown in the blue channel
	 * @param magnification
	 *            zoom factor
	 */
	static public void showColor(String title, ImageWare red, ImageWare green, ImageWare blue, double magnification) {
		ImageStack stack = buildColor(red, green, blue);
		SwingUtilities.invokeLater(new ShowZoom(title, stack, magnification));
	}

	/**
	*/
	static private ImageStack buildColor(ImageWare red, ImageWare green, ImageWare blue) {
		if (!red.isSameSize(green)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to create a ImageStack the channel are not the same size.\n" + "[" + red.getSizeX() + "," + red.getSizeY() + "," + red.getSizeZ() + "] != " + "["
					+ green.getSizeX() + "," + green.getSizeY() + "," + green.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		if (!red.isSameSize(blue)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to create a ImageStack the channel are not the same size.\n" + "[" + red.getSizeX() + "," + red.getSizeY() + "," + red.getSizeZ() + "] != " + "["
					+ blue.getSizeX() + "," + blue.getSizeY() + "," + blue.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		int nx = red.getSizeX();
		int ny = red.getSizeY();
		int nz = red.getSizeZ();
		int nxy = nx * ny;
		ImageStack imagestack = new ImageStack(nx, ny);
		ColorProcessor cp;
		byte[] r = new byte[nxy];
		byte[] g = new byte[nxy];
		byte[] b = new byte[nxy];
		for (int z = 0; z < nz; z++) {
			cp = new ColorProcessor(nx, ny);
			switch (red.getType()) {
			case ImageWare.DOUBLE:
				double[] dpixred = red.getSliceDouble(z);
				for (int k = 0; k < nxy; k++)
					r[k] = (byte) (dpixred[k]);
				break;
			case ImageWare.FLOAT:
				float[] fpixred = red.getSliceFloat(z);
				for (int k = 0; k < nxy; k++)
					r[k] = (byte) (fpixred[k]);
				break;
			case ImageWare.SHORT:
				short[] spixred = red.getSliceShort(z);
				for (int k = 0; k < nxy; k++)
					r[k] = (byte) (spixred[k]);
				break;
			case ImageWare.BYTE:
				r = red.getSliceByte(z);
				break;
			}
			switch (green.getType()) {
			case ImageWare.DOUBLE:
				double[] dpixgreen = green.getSliceDouble(z);
				for (int k = 0; k < nxy; k++)
					g[k] = (byte) (dpixgreen[k]);
				break;
			case ImageWare.FLOAT:
				float[] fpixgreen = green.getSliceFloat(z);
				for (int k = 0; k < nxy; k++)
					g[k] = (byte) (fpixgreen[k]);
				break;
			case ImageWare.SHORT:
				short[] spixgreen = green.getSliceShort(z);
				for (int k = 0; k < nxy; k++)
					g[k] = (byte) (spixgreen[k]);
				break;
			case ImageWare.BYTE:
				g = green.getSliceByte(z);
				break;
			}
			switch (blue.getType()) {
			case ImageWare.DOUBLE:
				double[] dpixblue = blue.getSliceDouble(z);
				for (int k = 0; k < nxy; k++)
					b[k] = (byte) (dpixblue[k]);
				break;
			case ImageWare.FLOAT:
				float[] fpixblue = blue.getSliceFloat(z);
				for (int k = 0; k < nxy; k++)
					b[k] = (byte) (fpixblue[k]);
				break;
			case ImageWare.SHORT:
				short[] spixblue = blue.getSliceShort(z);
				for (int k = 0; k < nxy; k++)
					b[k] = (byte) (spixblue[k]);
				break;
			case ImageWare.BYTE:
				b = blue.getSliceByte(z);
				break;
			}
			cp.setRGB(r, g, b);
			imagestack.addSlice("" + z, cp);
		}
		return imagestack;
	}

}
\ No newline at end of file
diff --git a/src/bilib/src/imageware/DoubleAccess.java b/src/bilib/src/imageware/DoubleAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..0662b87291e33928acb55ab439fb11804c454232
--- /dev/null
+++ b/src/bilib/src/imageware/DoubleAccess.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class DoubleAccess.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class DoubleAccess extends DoubleBuffer implements Access {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected DoubleAccess(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected DoubleAccess(Image image, int mode) {
		super(image, mode);
	}

	protected DoubleAccess(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected DoubleAccess(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected DoubleAccess(byte[] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(byte[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(short[] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(short[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(short[][][] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(float[] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(float[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(float[][][] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(double[] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(double[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleAccess(double[][][] array, int mode) {
		super(array, mode);
	}

	// ------------------------------------------------------------------
	//
	// getPixel section
	//
	// ------------------------------------------------------------------

	/**
	 * Get a pixel at specific position without specific boundary conditions
	 * 
	 * If the positions is outside of this imageware, the method return 0.0.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a pixel value
	 */
	public double getPixel(int x, int y, int z) {
		if (x >= nx)
			return 0.0;
		if (y >= ny)
			return 0.0;
		if (z >= nz)
			return 0.0;
		if (x < 0)
			return 0.0;
		if (y < 0)
			return 0.0;
		if (z < 0)
			return 0.0;
		return ((double[]) data[z])[x + y * nx];
	}

	/**
	 * Get a pixel at specific position with specific boundary conditions
	 * 
	 * If the positions is outside of this imageware, the method apply the
	 * boundary conditions to return a value.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a pixel value
	 */
	public double getPixel(int x, int y, int z, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a pixel \n" + "at the position (" + x
					+ "," + y + "," + z + ".\n" + "-------------------------------------------------------\n");
		}
		int xp = x;
		while (xp < 0)
			xp += xperiod;
		while (xp >= nx) {
			xp = xperiod - xp;
			xp = (xp < 0 ? -xp : xp);
		}
		int yp = y;
		while (yp < 0)
			yp += yperiod;
		while (yp >= ny) {
			yp = yperiod - yp;
			yp = (yp < 0 ? -yp : yp);
		}
		int zp = z;
		while (zp < 0)
			zp += zperiod;
		while (zp >= nz) {
			zp = zperiod - zp;
			zp = (zp < 0 ? -zp : zp);
		}
		return ((double[]) data[zp])[xp + yp * nx];
	}

	/**
	 * Get a interpolated pixel value at specific position without specific
	 * boundary conditions.
	 * 
	 * If the positions is not on the pixel grid, the method return a
	 * interpolated value of the pixel (linear interpolation). If the positions
	 * is outside of this imageware, the method return 0.0.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a interpolated value
	 */
	public double getInterpolatedPixel(double x, double y, double z) {
		if (x > nx - 1)
			return 0.0;
		if (y > ny - 1)
			return 0.0;
		if (z > nz - 1)
			return 0.0;
		if (x < 0)
			return 0.0;
		if (y < 0)
			return 0.0;
		if (z < 0)
			return 0.0;
		double output = 0.0;
		/*
		 * int i = (x >= 0.0 ? ((int)x) : ((int)x - 1)); int j = (y >= 0.0 ?
		 * ((int)y) : ((int)y - 1)); int k = (z >= 0.0 ? ((int)z) : ((int)z -
		 * 1));
		 */
		int i = (x >= 0.0 ? ((int) x) : ((int) x - 1));
		int j = (y >= 0.0 ? ((int) y) : ((int) y - 1));
		int k = (z >= 0.0 ? ((int) z) : ((int) z - 1));
		boolean fi = (i == nx - 1);
		boolean fj = (j == ny - 1);
		boolean fk = (k == nz - 1);
		int index = i + j * nx;
		switch (getDimension()) {
		case 1:
			double v1_0 = (double) (((double[]) data[k])[index]);
			double v1_1 = (fi ? v1_0 : (double) (((double[]) data[k])[index + 1]));
			double dx1 = x - (double) i;
			return v1_1 * dx1 - v1_0 * (dx1 - 1.0);
		case 2:
			double v2_00 = (double) (((double[]) data[k])[index]);
			double v2_10 = (fi ? v2_00 : (double) (((double[]) data[k])[index + 1]));
			double v2_01 = (fj ? v2_00 : (double) (((double[]) data[k])[index + nx]));
			double v2_11 = (fi ? (fj ? v2_00 : v2_01) : (double) (((double[]) data[k])[index + 1 + nx]));
			double dx2 = x - (double) i;
			double dy2 = y - (double) j;
			return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0)));
		case 3:
			double v3_000 = (double) (((double[]) data[k])[index]);
			double v3_100 = (fi ? v3_000 : (double) (((double[]) data[k])[index + 1]));
			double v3_010 = (fj ? v3_000 : (double) (((double[]) data[k])[index + nx]));
			double v3_110 = (fi ? (fj ? v3_000 : v3_010) : (double) (((double[]) data[k])[index + 1 + nx]));
			double v3_001 = (fk ? v3_000 : (double) (((double[]) data[k + 1])[index]));
			double v3_011 = (fk ? (fj ? v3_000 : v3_010) : (double) (((double[]) data[k + 1])[index + 1]));
			double v3_101 = (fk ? (fi ? v3_000 : v3_100) : (double) (((double[]) data[k + 1])[index + nx]));
			double v3_111 = (fk ? (fj ? (fi ? v3_000 : v3_100) : v3_110) : (double) (((double[]) data[k + 1])[index + 1 + nx]));
			double dx3 = x - (double) i;
			double dy3 = y - (double) j;
			double dz3 = z - (double) k;
			double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0)));
			double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0)));
			return z2 * dz3 - z1 * (dz3 - 1.0);
		}
		return output;
	}

	/**
	 * Get a interpolated pixel value at specific position with specific
	 * boundary conditions.
	 * 
	 * If the positions is not on the pixel grid, the method return a
	 * interpolated value of the pixel (linear interpolation). If the positions
	 * is outside of this imageware, the method apply the boundary conditions to
	 * return a value.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @param boundaryConditions
	 *            MIRROR or PERIODIC boundary conditions
	 * @return a interpolated value
	 */
	public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions) {
		double output = 0.0;
		int i = (x >= 0.0 ? ((int) x) : ((int) x - 1));
		int j = (y >= 0.0 ? ((int) y) : ((int) y - 1));
		int k = (z >= 0.0 ? ((int) z) : ((int) z - 1));
		switch (getDimension()) {
		case 1:
			double v1_0 = getPixel(i, j, k, boundaryConditions);
			double v1_1 = getPixel(i + 1, j, k, boundaryConditions);
			double dx1 = x - (double) i;
			return v1_1 * dx1 - v1_0 * (dx1 - 1.0);
		case 2:
			double v2_00 = getPixel(i, j, k, boundaryConditions);
			double v2_10 = getPixel(i + 1, j, k, boundaryConditions);
			double v2_01 = getPixel(i, j + 1, k, boundaryConditions);
			double v2_11 = getPixel(i + 1, j + 1, k, boundaryConditions);
			double dx2 = x - (double) i;
			double dy2 = y - (double) j;
			return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0)));
		case 3:
			double v3_000 = getPixel(i, j, k, boundaryConditions);
			double v3_100 = getPixel(i + 1, j, k, boundaryConditions);
			double v3_010 = getPixel(i, j + 1, k, boundaryConditions);
			double v3_110 = getPixel(i + 1, j + 1, k, boundaryConditions);
			double v3_001 = getPixel(i, j, k + 1, boundaryConditions);
			double v3_011 = getPixel(i + 1, j, k + 1, boundaryConditions);
			double v3_101 = getPixel(i, j + 1, k + 1, boundaryConditions);
			double v3_111 = getPixel(i + 1, j + 1, k + 1, boundaryConditions);
			double dx3 = x - (double) i;
			double dy3 = y - (double) j;
			double dz3 = z - (double) k;
			double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0)));
			double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0)));
			return z2 * dz3 - z1 * (dz3 - 1.0);
		}
		return output;
	}

	// ------------------------------------------------------------------
	//
	// putPixel section
	//
	// ------------------------------------------------------------------

	/**
	 * Put a pixel at specific position
	 * 
	 * If the positions is outside of this imageware, the method does nothing.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 */
	public void putPixel(int x, int y, int z, double value) {
		if (x >= nx)
			return;
		if (y >= ny)
			return;
		if (z >= nz)
			return;
		if (x < 0)
			return;
		if (y < 0)
			return;
		if (z < 0)
			return;
		((double[]) data[z])[x + y * nx] = (double) value;
	}

	// ------------------------------------------------------------------
	//
	// getBounded section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (((double[]) data[k])[offset]);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (((double[]) data[k])[offset]);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (((double[]) data[k])[offset]);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (((double[]) data[k])[offset]);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			double[] tmp = (double[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (tmp[offset]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			double[] tmp = (double[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (tmp[offset]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			double[] tmp = (double[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (tmp[offset]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			double[] tmp = (double[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (tmp[offset]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (((double[]) data[z])[offset]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (((double[]) data[z])[offset]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (((double[]) data[z])[offset]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (((double[]) data[z])[offset]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (((double[]) data[z])[offset]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (((double[]) data[z])[offset]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (((double[]) data[z])[offset]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (((double[]) data[z])[offset]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				double[] tmp = (double[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (byte) (tmp[offset]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				double[] tmp = (double[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (short) (tmp[offset]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				double[] tmp = (double[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (float) (tmp[offset]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				double[] tmp = (double[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (double) (tmp[offset]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// getBlock section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			double[] tmp = (double[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (byte) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			double[] tmp = (double[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (short) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			double[] tmp = (double[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (float) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			double[] tmp = (double[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (double) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			double[] tmp = (double[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (byte) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			double[] tmp = (double[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (short) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			double[] tmp = (double[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (float) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			double[] tmp = (double[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (double) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (byte) (((double[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (short) (((double[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (float) (((double[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (double) (((double[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			double[] tmp = (double[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			double[] tmp = (double[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			double[] tmp = (double[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			double[] tmp = (double[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (((double[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (((double[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (((double[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (((double[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (byte) (((double[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (short) (((double[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (float) (((double[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (double) (((double[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				double[] tmp = (double[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (byte) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				double[] tmp = (double[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (short) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				double[] tmp = (double[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (float) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				double[] tmp = (double[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (double) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// getBlock section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (byte) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (short) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (float) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (double) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (byte) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (short) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (float) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (double) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (byte) (((double[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (short) (((double[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (float) (((double[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (double) (((double[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			double[] tmp = ((double[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (((double[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (((double[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (((double[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (((double[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (byte) (((double[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (short) (((double[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (float) (((double[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (double) (((double[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				double[] tmp = ((double[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (byte) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				double[] tmp = ((double[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (short) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				double[] tmp = ((double[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (float) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				double[] tmp = ((double[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (double) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// putBounded section
	//
	// ------------------------------------------------------------------

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (double) (buffer[i] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (double) (buffer[i] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (double) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (double) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (double) (buffer[i] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (double) (buffer[i] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (double) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			double[] tmp = (double[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (double) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((double[]) data[k])[offset] = (double) (buffer[i] & 0xFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((double[]) data[k])[offset] = (double) (buffer[i] & 0xFFFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((double[]) data[k])[offset] = (double) (buffer[i]);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((double[]) data[k])[offset] = (double) (buffer[i]);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			double[] tmp = (double[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (double) (buffer[i][j] & 0xFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			double[] tmp = (double[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (double) (buffer[i][j] & 0xFFFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			double[] tmp = (double[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (double) (buffer[i][j]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;

			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			double[] tmp = (double[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (double) (buffer[i][j]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((double[]) data[k])[offset] = (double) (buffer[i][j] & 0xFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((double[]) data[k])[offset] = (double) (buffer[i][j] & 0xFFFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((double[]) data[k])[offset] = (double) (buffer[i][j]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((double[]) data[k])[offset] = (double) (buffer[i][j]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((double[]) data[k])[offset] = (double) (buffer[i][j] & 0xFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((double[]) data[k])[offset] = (double) (buffer[i][j] & 0xFFFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((double[]) data[k])[offset] = (double) (buffer[i][j]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((double[]) data[k])[offset] = (double) (buffer[i][j]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				double[] tmp = (double[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (double) (buffer[i][j][k] & 0xFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				double[] tmp = (double[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (double) (buffer[i][j][k] & 0xFFFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				double[] tmp = (double[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (double) (buffer[i][j][k]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				double[] tmp = (double[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (double) (buffer[i][j][k]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/DoubleBuffer.java b/src/bilib/src/imageware/DoubleBuffer.java
new file mode 100644
index 0000000000000000000000000000000000000000..7995ce66485d5d5eafe901f60b15d6c18f776dbb
--- /dev/null
+++ b/src/bilib/src/imageware/DoubleBuffer.java
@@ -0,0 +1,2769 @@
+package imageware;
+
+import ij.ImageStack;
+import ij.process.ByteProcessor;
+import ij.process.ColorProcessor;
+import ij.process.FloatProcessor;
+import ij.process.ImageProcessor;
+import ij.process.ShortProcessor;
+
+import java.awt.Image;
+import java.awt.image.ImageObserver;
+import java.awt.image.PixelGrabber;
+
+/**
+ * Class DoubleBuffer.
+ * 
+ * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
+ *         Lausanne, Lausanne, Switzerland
+ */
+
+public class DoubleBuffer implements Buffer {
+
+	protected Object[]	data	= null;
+	protected int		nx		= 0;
+	protected int		ny		= 0;
+	protected int		nz		= 0;
+	protected int		nxy		= 0;
+
+	/**
+	 * Constructor of a empty 3D double buffer.
+	 * 
+	 * @param nx
+	 *            size of the 3D buffer in the X axis
+	 * @param ny
+	 *            size of the 3D buffer in the Y axis
+	 * @param nz
+	 *            size of the 3D buffer in the Z axis
+	 */
+	protected DoubleBuffer(int nx, int ny, int nz) {
+		this.nx = nx;
+		this.ny = ny;
+		this.nz = nz;
+		if (nx <= 0 || ny <= 0 || nz <= 0)
+			throw_constructor(nx, ny, nz);
+		allocate();
+	}
+
+	/**
+	 * Constructor of a double buffer from a object Image of Java.
+	 * 
+	 * @param image
+	 *            source to build a new imageware
+	 */
+	protected DoubleBuffer(Image image, int mode) {
+		if (image == null) {
+			throw_constructor();
+		}
+		ImageObserver observer = null;
+		this.nx = image.getWidth(observer);
+		this.ny = image.getHeight(observer);
+		this.nz = 1;
+		this.nxy = nx * ny;
+		byte[] pixels = new byte[nxy];
+		PixelGrabber pg = new PixelGrabber(image, 0, 0, nx, ny, false);
+		try {
+			pg.grabPixels();
+			pixels = (byte[]) (pg.getPixels());
+		}
+		catch (Exception e) {
+			throw_constructor();
+		}
+		allocate();
+		for (int k = 0; k < nxy; k++)
+			((double[]) data[0])[k] = (double) (pixels[k] & 0xFF);
+	}
+
+	/**
+	 * Constructor of a double buffer from a ImageStack.
+	 * 
+	 * New data are allocated if the mode is CREATE, the imageware use the data
+	 * of ImageJ if the mode is WRAP.
+	 * 
+	 * @param stack
+	 *            source to build a new imageware
+	 * @param mode
+	 *            WRAP or CREATE
+	 */
+	protected DoubleBuffer(ImageStack stack, int mode) {
+		if (stack == null) {
+			throw_constructor();
+		}
+		this.nx = stack.getWidth();
+		this.ny = stack.getHeight();
+		this.nz = stack.getSize();
+		this.nxy = nx * ny;
+		switch (mode) {
+		case ImageWare.WRAP:
+			throw_constructor();
+			break;
+		case ImageWare.CREATE:
+			allocate();
+			ImageProcessor ip = stack.getProcessor(1);
+			if (ip instanceof ByteProcessor) {
+				Object[] vol = stack.getImageArray();
+				for (int z = 0; z < nz; z++) {
+					byte[] slice = (byte[]) vol[z];
+					for (int k = 0; k < nxy; k++) {
+						((double[]) data[z])[k] = (double) (slice[k] & 0xFF);
+					}
+				}
+			}
+			else if (ip instanceof ShortProcessor) {
+				Object[] vol = stack.getImageArray();
+				for (int z = 0; z < nz; z++) {
+					short[] slice = (short[]) vol[z];
+					for (int k = 0; k < nxy; k++) {
+						((double[]) data[z])[k] = (double) (slice[k] & 0xFFFF);
+					}
+				}
+			}
+			else if (ip instanceof FloatProcessor) {
+				Object[] vol = stack.getImageArray();
+				for (int z = 0; z < nz; z++) {
+					float[] slice = (float[]) vol[z];
+					for (int k = 0; k < nxy; k++) {
+						((double[]) data[z])[k] = (double) slice[k];
+					}
+				}
+			}
+			else if (ip instanceof ColorProcessor) {
+				double r, g, b;
+				int c;
+				ColorProcessor cp;
+				int[] pixels;
+				for (int z = 0; z < nz; z++) {
+					cp = (ColorProcessor) stack.getProcessor(z + 1);
+					pixels = (int[]) cp.getPixels();
+					for (int k = 0; k < nxy; k++) {
+						c = pixels[k];
+						r = (double) ((c & 0xFF0000) >> 16);
+						g = (double) ((c & 0xFF00) >> 8);
+						b = (double) ((c & 0xFF));
+						((double[]) data[z])[k] = (double) ((r + g + b) / 3.0);
+					}
+				}
+			}
+			else {
+				throw_constructor();
+			}
+			break;
+		default:
+			throw_constructor();
+			break;
+		}
+	}
+
+	/**
+	 * Constructor of a double buffer from a specific color channel of
+	 * ImageStack.
+	 * 
+	 * New data are always allocated. If it is a gray image the imageware is
+	 * created and fill up with data of the source ImageStack. If it is a color
+	 * image only the selected channel is used to create this imageware.
+	 * 
+	 * @param stack
+	 *            source to build a new imageware
+	 * @param channel
+	 *            RED, GREEN or BLUE
+	 */
+	protected DoubleBuffer(ImageStack stack, byte channel) {
+		if (stack == null) {
+			throw_constructor();
+		}
+		this.nx = stack.getWidth();
+		this.ny = stack.getHeight();
+		this.nz = stack.getSize();
+		this.nxy = nx * ny;
+		allocate();
+		ImageProcessor ip = stack.getProcessor(1);
+		if (ip instanceof ByteProcessor) {
+			Object[] vol = stack.getImageArray();
+			for (int z = 0; z < nz; z++) {
+				byte[] slice = (byte[]) vol[z];
+				for (int k = 0; k < nxy; k++) {
+					((double[]) data[z])[k] = (double) (slice[k] & 0xFF);
+				}
+			}
+		}
+		else if (ip instanceof ShortProcessor) {
+			Object[] vol = stack.getImageArray();
+			for (int z = 0; z < nz; z++) {
+				short[] slice = (short[]) vol[z];
+				for (int k = 0; k < nxy; k++) {
+					((double[]) data[z])[k] = (double) (slice[k] & 0xFFFF);
+				}
+			}
+		}
+		else if (ip instanceof FloatProcessor) {
+			Object[] vol = stack.getImageArray();
+			for (int z = 0; z < nz; z++) {
+				float[] slice = (float[]) vol[z];
+				for (int k = 0; k < nxy; k++) {
+					((double[]) data[z])[k] = (double) slice[k];
+				}
+			}
+		}
+		else if (ip instanceof ColorProcessor) {
+			ColorProcessor cp;
+			int[] pixels;
+			for (int z = 0; z < nz; z++) {
+				cp = (ColorProcessor) stack.getProcessor(z + 1);
+				pixels = (int[]) cp.getPixels();
+				switch (channel) {
+				case ImageWare.RED:
+					for (int k = 0; k < nxy; k++) {
+						((double[]) data[z])[k] = (double) ((pixels[k] & 0xFF0000) >> 16);
+					}
+					break;
+				case ImageWare.GREEN:
+					for (int k = 0; k < nxy; k++) {
+						((double[]) data[z])[k] = (double) ((pixels[k] & 0xFF00) >> 8);
+					}
+					break;
+				case ImageWare.BLUE:
+					for (int k = 0; k < nxy; k++) {
+						((double[]) data[z])[k] = (double) (pixels[k] & 0xFF);
+					}
+					break;
+				default:
+					throw_constructor();
+				}
+			}
+		}
+		else {
+			throw_constructor();
+		}
+	}
+
+	/**
+	 * Constructor of a double buffer from a byte array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(byte[] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = 1;
+		this.nz = 1;
+		allocate();
+		putX(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a byte array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(byte[][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = 1;
+		allocate();
+		putXY(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a byte array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(byte[][][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = array[0][0].length;
+		allocate();
+		putXYZ(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a short array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(short[] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = 1;
+		this.nz = 1;
+		allocate();
+		putX(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a short array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(short[][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = 1;
+		allocate();
+		putXY(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a short array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(short[][][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = array[0][0].length;
+		allocate();
+		putXYZ(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a float array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(float[] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = 1;
+		this.nz = 1;
+		allocate();
+		putX(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a float array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(float[][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = 1;
+		allocate();
+		putXY(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a float array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(float[][][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = array[0][0].length;
+		allocate();
+		putXYZ(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a double array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(double[] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = 1;
+		this.nz = 1;
+		allocate();
+		putX(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a double array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(double[][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = 1;
+		allocate();
+		putXY(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a double buffer from a double array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected DoubleBuffer(double[][][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = array[0][0].length;
+		allocate();
+		putXYZ(0, 0, 0, array);
+	}
+
+	/**
+	 * Return the type of this imageware.
+	 * 
+	 * @return the type of this imageware
+	 */
+	public int getType() {
+		return ImageWare.DOUBLE;
+	}
+
+	/**
+	 * Return the type of this imageware in a string format.
+	 * 
+	 * @return the type of this imageware translated in a string format
+	 */
+	public String getTypeToString() {
+		return "Double";
+	}
+
+	/**
+	 * Return the number of dimension of this imageware (1, 2 or 3).
+	 * 
+	 * @return the number of dimension of this imageware
+	 */
+	public int getDimension() {
+		int dims = 0;
+		dims += (nx > 1 ? 1 : 0);
+		dims += (ny > 1 ? 1 : 0);
+		dims += (nz > 1 ? 1 : 0);
+		return dims;
+	}
+
+	/**
+	 * Return the size of the imageware int[0] : x, int[1] : y, int[2] : z.
+	 * 
+	 * @return an array given the size of the imageware
+	 */
+	public int[] getSize() {
+		int[] size = { nx, ny, nz };
+		return size;
+	}
+
+	/**
+	 * Return the size in the X axis.
+	 * 
+	 * @return the size in the X axis
+	 */
+	public int getSizeX() {
+		return nx;
+	}
+
+	/**
+	 * Return the size in the Y axis.
+	 * 
+	 * @return the size in the Y axis
+	 */
+	public int getSizeY() {
+		return ny;
+	}
+
+	/**
+	 * Return the size in the Z axis.
+	 * 
+	 * @return the size in the Z axis
+	 */
+	public int getSizeZ() {
+		return nz;
+	}
+
+	/**
+	 * Return the size in the X axis.
+	 * 
+	 * @return the size in the X axis
+	 */
+	public int getWidth() {
+		return nx;
+	}
+
+	/**
+	 * Return the size in the Y axis.
+	 * 
+	 * @return the size in the Y axis
+	 */
+	public int getHeight() {
+		return ny;
+	}
+
+	/**
+	 * Return the size in the Z axis.
+	 * 
+	 * @return the size in the Z axis
+	 */
+	public int getDepth() {
+		return nz;
+	}
+
+	/**
+	 * Return the number of pixels in the imageware.
+	 * 
+	 * @return number of pixels in the imageware
+	 */
+	public int getTotalSize() {
+		return nxy * nz;
+	}
+
+	/**
+	 * Return true is this imageware has the same size the imageware given as
+	 * parameter.
+	 * 
+	 * @param imageware
+	 *            imageware to be compared
+	 * @return true if the imageware of the same size than this imageware
+	 */
+	public boolean isSameSize(ImageWare imageware) {
+		if (nx != imageware.getSizeX())
+			return false;
+		if (ny != imageware.getSizeY())
+			return false;
+		if (nz != imageware.getSizeZ())
+			return false;
+		return true;
+	}
+
+	// ------------------------------------------------------------------
+	//
+	// put Section
+	//
+	// ------------------------------------------------------------------
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putX(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		double buf[] = new double[bnx];
+		buffer.getX(0, 0, 0, buf);
+		putX(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putY(int x, int y, int z, ImageWare buffer) {
+		int bny = buffer.getSizeY();
+		double buf[] = new double[bny];
+		buffer.getY(0, 0, 0, buf);
+		putY(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, ImageWare buffer) {
+		int bnz = buffer.getSizeZ();
+		double buf[] = new double[bnz];
+		buffer.getZ(0, 0, 0, buf);
+		putZ(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bny = buffer.getSizeY();
+		double buf[][] = new double[bnx][bny];
+		buffer.getXY(0, 0, 0, buf);
+		putXY(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bnz = buffer.getSizeZ();
+		double buf[][] = new double[bnx][bnz];
+		buffer.getXZ(0, 0, 0, buf);
+		putXZ(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, ImageWare buffer) {
+		int bny = buffer.getSizeY();
+		int bnz = buffer.getSizeZ();
+		double buf[][] = new double[bny][bnz];
+		buffer.getYZ(0, 0, 0, buf);
+		putYZ(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bny = buffer.getSizeY();
+		int bnz = buffer.getSizeZ();
+		double buf[][][] = new double[bnx][bny][bnz];
+		buffer.getXYZ(0, 0, 0, buf);
+		putXYZ(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 1D array to put into the imageware
+	 */
+	public void putX(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (double) (buffer[i] & 0xFF);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 1D array to put into the imageware
+	 */
+	public void putX(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (double) (buffer[i] & 0xFFFF);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 1D array to put into the imageware
+	 */
+	public void putX(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (double) (buffer[i]);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 1D array to put into the imageware
+	 */
+	public void putX(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+
+			System.arraycopy(buffer, 0, tmp, offset, leni);
+		}
+		catch (Exception e) {
+			throw_put("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 1D array to put into the imageware
+	 */
+	public void putY(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (double) (buffer[i] & 0xFF);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 1D array to put into the imageware
+	 */
+	public void putY(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (double) (buffer[i] & 0xFFFF);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 1D array to put into the imageware
+	 */
+	public void putY(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (double) (buffer[i]);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 1D array to put into the imageware
+	 */
+	public void putY(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (double) (buffer[i]);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            bybytete 1D array to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				((double[]) data[z])[offset] = (double) (buffer[i] & 0xFF);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Z", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byshortte 1D array to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				((double[]) data[z])[offset] = (double) (buffer[i] & 0xFFFF);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Z", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byfloatte 1D array to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				((double[]) data[z])[offset] = (double) (buffer[i]);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Z", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            bydoublete 1D array to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				((double[]) data[z])[offset] = (double) (buffer[i]);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Z", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 2D array to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			double[] tmp = (double[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					tmp[offset] = (double) (buffer[i][j] & 0xFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 2D array to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			double[] tmp = (double[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					tmp[offset] = (double) (buffer[i][j] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 2D array to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			double[] tmp = (double[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					tmp[offset] = (double) (buffer[i][j]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 2D array to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			double[] tmp = (double[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					tmp[offset] = (double) (buffer[i][j]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 2D array to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + j * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					((double[]) data[z])[offset] = (double) (buffer[i][j] & 0xFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 2D array to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + j * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					((double[]) data[z])[offset] = (double) (buffer[i][j] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 2D array to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + j * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					((double[]) data[z])[offset] = (double) (buffer[i][j]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 2D array to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + j * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					((double[]) data[z])[offset] = (double) (buffer[i][j]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 2D array to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
+				for (int i = 0; i < leni; i++, offset += nx) {
+					((double[]) data[z])[offset] = (double) (buffer[i][j] & 0xFF);
+				}
+		}
+		catch (Exception e) {
+			throw_put("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 2D array to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
+				for (int i = 0; i < leni; i++, offset += nx) {
+					((double[]) data[z])[offset] = (double) (buffer[i][j] & 0xFFFF);
+				}
+		}
+		catch (Exception e) {
+			throw_put("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 2D array to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
+				for (int i = 0; i < leni; i++, offset += nx) {
+					((double[]) data[z])[offset] = (double) (buffer[i][j]);
+				}
+		}
+		catch (Exception e) {
+			throw_put("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 2D array to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
+				for (int i = 0; i < leni; i++, offset += nx) {
+					((double[]) data[z])[offset] = (double) (buffer[i][j]);
+				}
+		}
+		catch (Exception e) {
+			throw_put("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 3D array to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, byte[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				double[] tmp = (double[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						tmp[offset] = (double) (buffer[i][j][k] & 0xFF);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 3D array to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, short[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				double[] tmp = (double[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						tmp[offset] = (double) (buffer[i][j][k] & 0xFFFF);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 3D array to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, float[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				double[] tmp = (double[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						tmp[offset] = (double) (buffer[i][j][k]);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 3D array to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, double[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				double[] tmp = (double[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						tmp[offset] = (double) (buffer[i][j][k]);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	// ------------------------------------------------------------------
+	//
+	// get Section
+	//
+	// ------------------------------------------------------------------
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getX(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		double buf[] = new double[bnx];
+		getX(x, y, z, buf);
+		buffer.putX(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getY(int x, int y, int z, ImageWare buffer) {
+		int bny = buffer.getSizeY();
+		double buf[] = new double[bny];
+		getY(x, y, z, buf);
+		buffer.putY(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, ImageWare buffer) {
+		int bnz = buffer.getSizeZ();
+		double buf[] = new double[bnz];
+		getZ(x, y, z, buf);
+		buffer.putZ(0, 0, 0, buf);
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bny = buffer.getSizeY();
+		double buf[][] = new double[bnx][bny];
+		getXY(x, y, z, buf);
+		buffer.putXY(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bnz = buffer.getSizeZ();
+		double buf[][] = new double[bnx][bnz];
+		getXZ(x, y, z, buf);
+		buffer.putXZ(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, ImageWare buffer) {
+		int bny = buffer.getSizeY();
+		int bnz = buffer.getSizeZ();
+		double buf[][] = new double[bny][bnz];
+		getYZ(x, y, z, buf);
+		buffer.putYZ(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bny = buffer.getSizeY();
+		int bnz = buffer.getSizeZ();
+		double buf[][][] = new double[bnx][bny][bnz];
+		getXYZ(x, y, z, buf);
+		buffer.putXYZ(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 1D array to get into the imageware
+	 */
+	public void getX(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (byte) (tmp[offset]);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 1D array to get into the imageware
+	 */
+	public void getX(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (short) (tmp[offset]);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 1D array to get into the imageware
+	 */
+	public void getX(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (float) (tmp[offset]);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 1D array to get into the imageware
+	 */
+	public void getX(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+
+			System.arraycopy(tmp, offset, buffer, 0, leni);
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 1D array to get into the imageware
+	 */
+	public void getY(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (byte) (tmp[offset]);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 1D array to get into the imageware
+	 */
+	public void getY(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (short) (tmp[offset]);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 1D array to get into the imageware
+	 */
+	public void getY(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (float) (tmp[offset]);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 1D array to get into the imageware
+	 */
+	public void getY(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			double[] tmp = (double[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (double) (tmp[offset]);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 1D array to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (byte) (((double[]) data[z])[offset]);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 1D array to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (short) (((double[]) data[z])[offset]);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 1D array to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (float) (((double[]) data[z])[offset]);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 1D array to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (double) (((double[]) data[z])[offset]);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 2D array to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			double[] tmp = (double[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (byte) (tmp[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 2D array to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			double[] tmp = (double[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (short) (tmp[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 2D array to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			double[] tmp = (double[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (float) (tmp[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 2D array to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			double[] tmp = (double[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (double) (tmp[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 2D array to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + y * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (byte) (((double[]) data[z])[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 2D array to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + y * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (short) (((double[]) data[z])[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 2D array to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + y * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (float) (((double[]) data[z])[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 2D array to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + y * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (double) (((double[]) data[z])[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 2D array to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
+				for (int i = 0; i < leni; i++, offset += nx) {
+					buffer[i][j] = (byte) (((double[]) data[z])[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 2D array to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
+				for (int i = 0; i < leni; i++, offset += nx) {
+					buffer[i][j] = (short) (((double[]) data[z])[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 2D array to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
+				for (int i = 0; i < leni; i++, offset += nx) {
+					buffer[i][j] = (float) (((double[]) data[z])[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 2D array to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
+				for (int i = 0; i < leni; i++, offset += nx) {
+					buffer[i][j] = (double) (((double[]) data[z])[offset]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 3D array to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, byte[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				double[] tmp = (double[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						buffer[i][j][k] = (byte) (tmp[offset]);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 3D array to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, short[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				double[] tmp = (double[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						buffer[i][j][k] = (short) (tmp[offset]);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 3D array to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, float[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				double[] tmp = (double[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						buffer[i][j][k] = (float) (tmp[offset]);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 3D array to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, double[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				double[] tmp = (double[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						buffer[i][j][k] = (double) (tmp[offset]);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	// ------------------------------------------------------------------
+	//
+	// Private Section
+	//
+	// ------------------------------------------------------------------
+
+	/**
+	 * Prepare a complete error message from the errors coming the constructors.
+	 */
+	protected void throw_constructor() {
+		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a double imageware.\n"
+				+ "-------------------------------------------------------\n");
+	}
+
+	/**
+	 * Prepare a complete error message from the errors coming the constructors.
+	 */
+	protected void throw_constructor(int nx, int ny, int nz) {
+		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a double imageware " + nx + "," + ny
+				+ "," + nz + "].\n" + "-------------------------------------------------------\n");
+	}
+
+	/**
+	 * Prepare a complete error message from the errors coming the get routines.
+	 */
+	protected void throw_get(String direction, String border, Object buffer, int x, int y, int z) {
+		int leni = 0;
+		int lenj = 0;
+		int lenk = 0;
+		String type = " unknown type";
+		if (buffer instanceof byte[]) {
+			leni = ((byte[]) buffer).length;
+			type = " 1D byte";
+		}
+		else if (buffer instanceof short[]) {
+			leni = ((short[]) buffer).length;
+			type = " 1D short";
+		}
+		else if (buffer instanceof float[]) {
+			leni = ((float[]) buffer).length;
+			type = " 1D float";
+		}
+		else if (buffer instanceof double[]) {
+			leni = ((double[]) buffer).length;
+			type = " 1D double";
+		}
+		else if (buffer instanceof byte[][]) {
+			leni = ((byte[][]) buffer).length;
+			lenj = ((byte[][]) buffer)[0].length;
+			type = " 2D byte";
+		}
+		else if (buffer instanceof short[][]) {
+			leni = ((short[][]) buffer).length;
+			lenj = ((short[][]) buffer)[0].length;
+			type = " 2D short";
+		}
+		else if (buffer instanceof float[][]) {
+			leni = ((float[][]) buffer).length;
+			lenj = ((float[][]) buffer)[0].length;
+			type = " 2D float";
+		}
+		else if (buffer instanceof double[][]) {
+			leni = ((double[][]) buffer).length;
+			lenj = ((double[][]) buffer)[0].length;
+			type = " 2D double";
+		}
+		else if (buffer instanceof byte[][][]) {
+			leni = ((byte[][][]) buffer).length;
+			lenj = ((byte[][][]) buffer)[0].length;
+			lenk = ((byte[][][]) buffer)[0][0].length;
+			type = " 3D byte";
+		}
+		else if (buffer instanceof short[][][]) {
+			leni = ((short[][][]) buffer).length;
+			lenj = ((short[][][]) buffer)[0].length;
+			lenk = ((short[][][]) buffer)[0][0].length;
+			type = " 3D short";
+		}
+		else if (buffer instanceof float[][][]) {
+			leni = ((float[][][]) buffer).length;
+			lenj = ((float[][][]) buffer)[0].length;
+			lenk = ((float[][][]) buffer)[0][0].length;
+			type = " 3D float";
+		}
+		else if (buffer instanceof double[][][]) {
+			leni = ((double[][][]) buffer).length;
+			lenj = ((double[][][]) buffer)[0].length;
+			lenk = ((double[][][]) buffer)[0][0].length;
+			type = " 3D double";
+		}
+		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get a" + type + " buffer ["
+				+ (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "from the double imageware [" + nx + "," + ny + "," + nz
+				+ "]\n" + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n"
+				+ "-------------------------------------------------------\n");
+	}
+
+	/**
+	 * Prepare a complete error message from the errors coming the put routines.
+	 */
+	protected void throw_put(String direction, String border, Object buffer, int x, int y, int z) {
+		int leni = 0;
+		int lenj = 0;
+		int lenk = 0;
+		String type = " unknown type";
+		if (buffer instanceof byte[]) {
+			leni = ((byte[]) buffer).length;
+			type = " 1D byte";
+		}
+		else if (buffer instanceof short[]) {
+			leni = ((short[]) buffer).length;
+			type = " 1D short";
+		}
+		else if (buffer instanceof float[]) {
+			leni = ((float[]) buffer).length;
+			type = " 1D float";
+		}
+		else if (buffer instanceof double[]) {
+			leni = ((double[]) buffer).length;
+			type = " 1D double";
+		}
+		else if (buffer instanceof byte[][]) {
+			leni = ((byte[][]) buffer).length;
+			lenj = ((byte[][]) buffer)[0].length;
+			type = " 2D byte";
+		}
+		else if (buffer instanceof short[][]) {
+			leni = ((short[][]) buffer).length;
+			lenj = ((short[][]) buffer)[0].length;
+			type = " 2D short";
+		}
+		else if (buffer instanceof float[][]) {
+			leni = ((float[][]) buffer).length;
+			lenj = ((float[][]) buffer)[0].length;
+			type = " 2D float";
+		}
+		else if (buffer instanceof double[][]) {
+			leni = ((double[][]) buffer).length;
+			lenj = ((double[][]) buffer)[0].length;
+			type = " 2D double";
+		}
+		else if (buffer instanceof byte[][][]) {
+			leni = ((byte[][][]) buffer).length;
+			lenj = ((byte[][][]) buffer)[0].length;
+			lenk = ((byte[][][]) buffer)[0][0].length;
+			type = " 3D byte";
+		}
+		else if (buffer instanceof short[][][]) {
+			leni = ((short[][][]) buffer).length;
+			lenj = ((short[][][]) buffer)[0].length;
+			lenk = ((short[][][]) buffer)[0][0].length;
+			type = " 3D short";
+		}
+		else if (buffer instanceof float[][][]) {
+			leni = ((float[][][]) buffer).length;
+			lenj = ((float[][][]) buffer)[0].length;
+			lenk = ((float[][][]) buffer)[0][0].length;
+			type = " 3D float";
+		}
+		else if (buffer instanceof double[][][]) {
+			leni = ((double[][][]) buffer).length;
+			lenj = ((double[][][]) buffer)[0].length;
+			lenk = ((double[][][]) buffer)[0][0].length;
+			type = " 3D double";
+		}
+		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a" + type + " buffer ["
+				+ (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "into the double imageware [" + nx + "," + ny + "," + nz
+				+ "]\n" + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n"
+				+ "-------------------------------------------------------\n");
+	}
+
+	// ------------------------------------------------------------------
+	//
+	// Get slice fast and direct access Section
+	//
+	// ------------------------------------------------------------------
+
+	/**
+	 * Get a reference of the whole volume data.
+	 * 
+	 * @return a reference of the data of this imageware
+	 */
+	public Object[] getVolume() {
+		return data;
+	}
+
+	/**
+	 * Get a specific slice, fast and direct access, but only for byte
+	 * imageware.
+	 * 
+	 * @param z
+	 *            number of the requested slice
+	 * @return a reference of the data of one slice of this imageware
+	 */
+	public byte[] getSliceByte(int z) {
+		return null;
+	}
+
+	/**
+	 * Get a specific slice, fast and direct access, but only for short
+	 * imageware.
+	 * 
+	 * @param z
+	 *            number of the requested slice
+	 * @return a reference of the data of one slice of this imageware
+	 */
+	public short[] getSliceShort(int z) {
+		return null;
+	}
+
+	/**
+	 * Get a specific slice, fast and direct access, but only for float
+	 * imageware.
+	 * 
+	 * @param z
+	 *            number of the requested slice
+	 * @return a reference of the data of one slice of this imageware
+	 */
+	public float[] getSliceFloat(int z) {
+		return null;
+	}
+
+	/**
+	 * Get a specific slice, fast and direct access, but only for double
+	 * imageware.
+	 * 
+	 * @param z
+	 *            number of the requested slice
+	 * @return a reference of the data of one slice of this imageware
+	 */
+	public double[] getSliceDouble(int z) {
+		return (double[]) data[z];
+	}
+
+	/**
+	 * Allocate a buffer of size [nx,ny,nz].
+	 */
+	private void allocate() {
+		try {
+			this.data = new Object[nz];
+			this.nxy = nx * ny;
+			for (int z = 0; z < nz; z++)
+				this.data[z] = new double[nxy];
+		}
+		catch (Exception e) {
+			throw_constructor(nx, ny, nz);
+		}
+	}
+
+} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/DoublePointwise.java b/src/bilib/src/imageware/DoublePointwise.java
new file mode 100644
index 0000000000000000000000000000000000000000..42f5b069fa9a8378ba4ba33a89fcb4993f2d0daf
--- /dev/null
+++ b/src/bilib/src/imageware/DoublePointwise.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;
import ij.process.FloatProcessor;

import java.awt.Image;
import java.util.Random;

/**
 * Class DoublePointwise.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class DoublePointwise extends DoubleAccess implements Pointwise {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected DoublePointwise(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected DoublePointwise(Image image, int mode) {
		super(image, mode);
	}

	protected DoublePointwise(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected DoublePointwise(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected DoublePointwise(byte[] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(byte[][] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(short[] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(short[][] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(short[][][] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(float[] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(float[][] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(float[][][] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(double[] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(double[][] array, int mode) {
		super(array, mode);
	}

	protected DoublePointwise(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Fill this imageware with a constant value.
	 * 
	 * @param value
	 *            the constant value
	 */
	public void fillConstant(double value) {
		double typedValue = (double) value;
		double[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++)
				slice[k] = typedValue;
		}
	}

	/**
	 * Fill this imageware with ramp.
	 */
	public void fillRamp() {
		int off = 0;
		double[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++)
				slice[k] = (double) (off + k);
			off += nxy;
		}
	}

	/**
	 * Generate a gaussian noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void fillGaussianNoise(double amplitude) {
		Random rnd = new Random();
		double[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) ((rnd.nextGaussian()) * amplitude);
			}
		}
	}

	/**
	 * Generate a uniform noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void fillUniformNoise(double amplitude) {
		Random rnd = new Random();
		double[] slice = null;
		amplitude *= 2.0;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) ((rnd.nextDouble() - 0.5) * amplitude);
			}
		}
	}

	/**
	 * Generate a salt and pepper noise.
	 * 
	 * @param amplitudeSalt
	 *            amplitude of the salt noise
	 * @param amplitudePepper
	 *            amplitude of the pepper noise
	 * @param percentageSalt
	 *            percentage of the salt noise
	 * @param percentagePepper
	 *            percentage of the pepper noise
	 */
	public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) {
		Random rnd = new Random();
		int index, z;
		if (percentageSalt > 0) {
			double nbSalt = nxy * nz / percentageSalt;
			for (int k = 0; k < nbSalt; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((double[]) data[z])[index] = (double) (rnd.nextDouble() * amplitudeSalt);
			}
		}
		if (percentagePepper > 0) {
			double nbPepper = nxy * nz / percentagePepper;
			for (int k = 0; k < nbPepper; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((double[]) data[z])[index] = (double) (-rnd.nextDouble() * amplitudeSalt);
			}
		}
	}

	/**
	 * Build an ImageStack of ImageJ.
	 */
	public ImageStack buildImageStack() {
		ImageStack imagestack = new ImageStack(nx, ny);
		for (int z = 0; z < nz; z++) {

			FloatProcessor ip = new FloatProcessor(nx, ny);
			float pix[] = (float[]) ip.getPixels();
			for (int k = 0; k < nxy; k++)
				pix[k] = (float) (((double[]) data[z])[k]);
			imagestack.addSlice("" + z, ip);
		}
		return imagestack;
	}

	/**
	 * Invert the pixel intensity.
	 */
	public void invert() {
		double max = -Double.MAX_VALUE;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > max)
					max = slice[k];
			}
		}
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) (max - ((double) (slice[k])));
			}
		}
	}

	/**
	 * Negate the pixel intensity.
	 */
	public void negate() {
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) (-((double) (slice[k])));
			}
		}
	}

	/**
	 * Clip the pixel intensity into [0..255].
	 */
	public void clip() {
		clip(0.0, 255.0);
	}

	/**
	 * Clip the pixel intensity into [minLevel..maxLevel].
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void clip(double minLevel, double maxLevel) {
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			double value;
			double min = (double) minLevel;
			double max = (double) maxLevel;
			for (int k = 0; k < nxy; k++) {
				value = (double) (slice[k]);
				if (value < min)
					slice[k] = min;
				if (value > max)
					slice[k] = max;
			}
		}
	}

	/**
	 * Rescale the pixel intensity into [0..255].
	 */
	public void rescale() {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > maxImage)
					maxImage = slice[k];
				if ((slice[k]) < minImage)
					minImage = slice[k];
			}
		}
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = 128.0;
		}
		else {
			a = 255.0 / (maxImage - minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) (a * (((double) (slice[k])) - minImage));
			}
		}
	}

	/**
	 * Rescale the pixel intensity into [minLevel..maxLevel].
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void rescale(double minLevel, double maxLevel) {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > maxImage)
					maxImage = slice[k];
				if ((slice[k]) < minImage)
					minImage = slice[k];
			}
		}
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = (maxLevel - minLevel) / 2.0;
		}
		else {
			a = (maxLevel - minLevel) / (maxImage - minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) (a * (((double) (slice[k])) - minImage) + minLevel);
			}
		}
	}

	/**
	 * Rescale the pixel intensity with a linear curve passing through
	 * (maxLevel-minLevel)/2 at the 0 input intensity.
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void rescaleCenter(double minLevel, double maxLevel) {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > maxImage)
					maxImage = slice[k];
				if ((slice[k]) < minImage)
					minImage = slice[k];
			}
		}
		double center = (maxLevel + minLevel) / 2.0;
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = (maxLevel - minLevel) / 2.0;
		}
		else {
			if (Math.abs(maxImage) > Math.abs(minImage))
				a = (maxLevel - center) / Math.abs(maxImage);
			else
				a = (center - minLevel) / Math.abs(minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) (a * (((double) (slice[k])) - minImage) + center);
			}
		}
	}

	/**
	 * Compute the absolute value of this imageware.
	 */
	public void abs() {
		double zero = (double) 0.0;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if (slice[k] < zero)
					slice[k] = -slice[k];
			}
		}
	}

	/**
	 * Compute the log of this imageware.
	 */
	public void log() {
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) Math.log(slice[k]);
			}
		}
	}

	/**
	 * Compute the exponential of this imageware.
	 */
	public void exp() {
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) Math.exp(slice[k]);
			}
		}
	}

	/**
	 * Compute the square root of this imageware.
	 */
	public void sqrt() {
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) Math.sqrt(slice[k]);
			}
		}
	}

	/**
	 * Compute the square of this imageware.
	 */
	public void sqr() {
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] *= slice[k];
			}
		}
	}

	/**
	 * Compute the power of a of this imageware.
	 * 
	 * @param a
	 *            exponent
	 */
	public void pow(double a) {
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (double) Math.pow(slice[k], a);
			}
		}
	}

	/**
	 * Add a constant value to this imageware.
	 */
	public void add(double constant) {
		double cst = (double) constant;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += cst;
			}
		}
	}

	/**
	 * Multiply a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void multiply(double constant) {
		double cst = (double) constant;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] *= cst;
			}
		}
	}

	/**
	 * Subtract a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void subtract(double constant) {
		double cst = (double) constant;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] -= cst;
			}
		}
	}

	/**
	 * Divide by a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void divide(double constant) {
		if (constant == 0.0)
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the constant is 0.\n"
					+ "-------------------------------------------------------\n");
		double cst = (double) constant;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] /= cst;
			}
		}
	}

	/**
	 * Threshold a imageware in two levels 0 and 255.
	 * 
	 * All the pixels values strictly greater than 'thresholdValue' and are set
	 * to 0. The remaining values are set to 255.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void threshold(double thresholdValue) {
		threshold(thresholdValue, 0.0, 255.0);
	}

	/**
	 * Threshold a imageware in two levels minLevel and maxLevel.
	 * 
	 * All the pixels values strictly greater than 'thresholdValue' and are set
	 * to maxLevel. The remaining values are set to minLevel.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 * @param minLevel
	 *            double value given the minimum level
	 * @param maxLevel
	 *            double value given the maximum level
	 */
	public void threshold(double thresholdValue, double minLevel, double maxLevel) {
		double low = (double) (minLevel);
		double high = (double) (maxLevel);
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = ((double) (slice[k]) > thresholdValue ? high : low);
			}
		}
	}

	/**
	 * Apply a soft thresholding.
	 * 
	 * All the pixels values strictly greater than '-thresholdValue' and stricty
	 * lower than 'thresholdValue' set to 0. The remaining positive values are
	 * reduced by 'thresholdvalue'; the remaining negative values are augmented
	 * by 'thresholdValue'.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void thresholdSoft(double thresholdValue) {
		double zero = (double) (0.0);
		double pixel;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				pixel = (double) (slice[k]);
				slice[k] = (pixel <= -thresholdValue ? (double) (pixel + thresholdValue) : (pixel > thresholdValue ? (double) (pixel - thresholdValue) : zero));
			}
		}
	}

	/**
	 * Apply a hard thresholding.
	 * 
	 * All the pixels values strictly greater than '-thresholdValue' and stricty
	 * lower than 'thresholdValue' are set to 0. The remaining values are
	 * unchanged.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void thresholdHard(double thresholdValue) {
		double zero = (double) (0.0);
		double pixel;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				pixel = (double) (slice[k]);
				if (pixel > -thresholdValue && pixel < thresholdValue)
					slice[k] = zero;
			}
		}
	}

	/**
	 * Add a gaussian noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void addGaussianNoise(double amplitude) {
		Random rnd = new Random();
		double[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += (double) ((rnd.nextGaussian()) * amplitude);
			}
		}
	}

	/**
	 * Add a uniform noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void addUniformNoise(double amplitude) {
		Random rnd = new Random();
		double[] slice = null;
		amplitude *= 2.0;
		for (int z = 0; z < nz; z++) {
			slice = (double[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += (double) ((rnd.nextDouble() - 0.5) * amplitude);
			}
		}
	}

	/**
	 * Add a salt and pepper noise.
	 * 
	 * @param amplitudeSalt
	 *            amplitude of the salt noise
	 * @param amplitudePepper
	 *            amplitude of the pepper noise
	 * @param percentageSalt
	 *            percentage of the salt noise
	 * @param percentagePepper
	 *            percentage of the pepper noise
	 */
	public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) {
		Random rnd = new Random();
		int index, z;
		if (percentageSalt > 0) {
			double nbSalt = nxy * nz / percentageSalt;
			for (int k = 0; k < nbSalt; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((double[]) data[z])[index] += (double) (rnd.nextDouble() * amplitudeSalt);
			}
		}
		if (percentagePepper > 0) {
			double nbPepper = nxy * nz / percentagePepper;
			for (int k = 0; k < nbPepper; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((double[]) data[z])[index] -= (double) (rnd.nextDouble() * amplitudeSalt);
			}
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/DoubleProcess.java b/src/bilib/src/imageware/DoubleProcess.java
new file mode 100644
index 0000000000000000000000000000000000000000..412d139aefcccf3dcae9442f45eaf0022c69986d
--- /dev/null
+++ b/src/bilib/src/imageware/DoubleProcess.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class DoubleProcess.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class DoubleProcess extends DoublePointwise implements Process {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected DoubleProcess(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected DoubleProcess(Image image, int mode) {
		super(image, mode);
	}

	protected DoubleProcess(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected DoubleProcess(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected DoubleProcess(byte[] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(byte[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(short[] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(short[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(short[][][] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(float[] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(float[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(float[][][] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(double[] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(double[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleProcess(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Apply a separable gaussian smoothing over the image with the same
	 * strengthness in all directions. To have a smmothing effect the
	 * strengthness should be strictly greater than 0 and the size in the
	 * considered directions should be greater strictly than 1.
	 * 
	 * @param sigma
	 *            Strengthness of the smoothing
	 */
	public void smoothGaussian(double sigma) {
		smoothGaussian(sigma, sigma, sigma);
	}

	/**
	 * Apply a separablegaussian smoothing over the image with an independant
	 * strengthness in the different directions. To have a smmothing effect the
	 * strengthness should be strictly greater than 0 and the size in the
	 * considered directions should be greater strictly than 1.
	 * 
	 * @param sigmaX
	 *            Strengthness of the smoothing in X axis
	 * @param sigmaY
	 *            Strengthness of the smoothing in X axis
	 * @param sigmaZ
	 *            Strengthness of the smoothing in X axis
	 */
	public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ) {
		int n = 3;
		double N = (double) n;
		double poles[] = new double[n];

		if (nx > 1 && sigmaX > 0.0) {
			double s2 = sigmaX * sigmaX;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[nx];
			for (int z = 0; z < nz; z++) {
				for (int y = 0; y < ny; y++) {
					getX(0, y, z, line);
					putX(0, y, z, Convolver.convolveIIR(line, poles));
				}
			}
		}

		if (ny > 1 && sigmaY > 0.0) {
			double s2 = sigmaY * sigmaY;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[ny];
			for (int x = 0; x < nx; x++) {
				for (int z = 0; z < nz; z++) {
					getY(x, 0, z, line);
					putY(x, 0, z, Convolver.convolveIIR(line, poles));
				}
			}
		}

		if (nz > 1 && sigmaZ > 0.0) {
			double s2 = sigmaZ * sigmaZ;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[nz];
			for (int y = 0; y < ny; y++) {
				for (int x = 0; x < nx; x++) {
					getZ(x, y, 0, line);
					putZ(x, y, 0, Convolver.convolveIIR(line, poles));
				}
			}
		}
	}

	/**
	 * Get the maximum of this imageware and a imageware.
	 * 
	 * @param imageware
	 *            imageware to max
	 */
	public void max(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to get the maximum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					if (((double[]) data[z])[k] < (double) tmp[k])
						((double[]) data[z])[k] = (double) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					if (((double[]) data[z])[k] < (double) tmp[k])
						((double[]) data[z])[k] = (double) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					if (((double[]) data[z])[k] < (double) tmp[k])
						((double[]) data[z])[k] = (double) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					if (((double[]) data[z])[k] < (double) tmp[k])
						((double[]) data[z])[k] = (double) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Get the minimum of this imageware and a imageware.
	 * 
	 * @param imageware
	 *            imageware to min
	 */
	public void min(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to get the minimum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					if (((double[]) data[z])[k] > (double) tmp[k])
						((double[]) data[z])[k] = (double) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					if (((double[]) data[z])[k] > (double) tmp[k])
						((double[]) data[z])[k] = (double) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					if (((double[]) data[z])[k] > (double) tmp[k])
						((double[]) data[z])[k] = (double) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					if (((double[]) data[z])[k] > (double) tmp[k])
						((double[]) data[z])[k] = (double) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Add a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to add
	 */
	public void add(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to add because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] += (double) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] += (double) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] += (double) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] += (double) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Multiply a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to multiply
	 */
	public void multiply(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to multiply because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] *= (double) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] *= (double) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] *= (double) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] *= (double) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Subtract a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to subtract
	 */
	public void subtract(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to subtract because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] -= (double) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] -= (double) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] -= (double) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] -= (double) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Divide a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to divide
	 */
	public void divide(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to divide because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] /= (double) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] /= (double) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] /= (double) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((double[]) data[z])[k] /= (double) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/DoubleSet.java b/src/bilib/src/imageware/DoubleSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d9ed25f24603f2e0385849d358e1c8142de9bbe
--- /dev/null
+++ b/src/bilib/src/imageware/DoubleSet.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class DoubleSet.
 * 
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class DoubleSet extends DoubleProcess implements ImageWare {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected DoubleSet(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected DoubleSet(Image image, int mode) {
		super(image, mode);
	}

	protected DoubleSet(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected DoubleSet(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected DoubleSet(byte[] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(byte[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(short[] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(short[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(short[][][] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(float[] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(float[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(float[][][] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(double[] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(double[][] array, int mode) {
		super(array, mode);
	}

	protected DoubleSet(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Duplicate the imageware.
	 * 
	 * Create a new imageware with the same size, same type and same data than
	 * the calling one.
	 * 
	 * @return a duplicated version of this imageware
	 */
	public ImageWare duplicate() {
		ImageWare out = new DoubleSet(nx, ny, nz);
		double[] outdata;
		for (int z = 0; z < nz; z++) {
			outdata = (double[]) (((DoubleSet) out).data[z]);
			System.arraycopy(data[z], 0, outdata, 0, nxy);
		}
		return out;
	}

	/**
	 * Replicate the imageware.
	 * 
	 * Create a new imageware with the same size, same type than the calling
	 * one. The data are not copied.
	 * 
	 * @return a replicated version of this imageware
	 */
	public ImageWare replicate() {
		return new DoubleSet(nx, ny, nz);
	}

	/**
	 * Replicate the imageware.
	 * 
	 * Create a new imageware with the same size and a specified type than the
	 * calling one. The data are not copied.
	 * 
	 * @param type
	 *            requested type
	 * @return a replicated version of this imageware
	 */
	public ImageWare replicate(int type) {
		switch (type) {
		case ImageWare.BYTE:
			return new ByteSet(nx, ny, nz);
		case ImageWare.SHORT:
			return new ShortSet(nx, ny, nz);
		case ImageWare.FLOAT:
			return new FloatSet(nx, ny, nz);
		case ImageWare.DOUBLE:
			return new DoubleSet(nx, ny, nz);
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Copy all the data of source in the current imageware. The source should
	 * have the same size and same type than the calling one.
	 * 
	 * @param source
	 *            a source imageware
	 */
	public void copy(ImageWare source) {
		if (nx != source.getSizeX())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ nx + " != " + source.getSizeX() + ").\n" + "-------------------------------------------------------\n");
		if (ny != source.getSizeY())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ ny + " != " + source.getSizeY() + ").\n" + "-------------------------------------------------------\n");
		if (nz != source.getSizeZ())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ nz + " != " + source.getSizeZ() + ").\n" + "-------------------------------------------------------\n");
		if (getType() != source.getType())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same type ("
					+ getType() + " != " + source.getType() + ").\n" + "-------------------------------------------------------\n");
		double[] src;
		for (int z = 0; z < nz; z++) {
			src = (double[]) (((DoubleSet) source).data[z]);
			System.arraycopy(src, 0, data[z], 0, nxy);
		}
	}

	/**
	 * convert the imageware in a specified type.
	 * 
	 * Create a new imageware with the same size and converted data than the
	 * calling one.
	 * 
	 * @param type
	 *            indicates the type of the output
	 * @return a converted version of this imageware
	 */
	public ImageWare convert(int type) {
		if (type == ImageWare.DOUBLE)
			return duplicate();
		ImageWare out = null;
		switch (type) {
		case ImageWare.BYTE: {
			double[] slice;
			out = new ByteSet(nx, ny, nz);
			byte[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((double[]) data[z]);
				outslice = ((byte[]) ((ByteSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (byte) (slice[k]);
				}
			}
		}
			break;
		case ImageWare.SHORT: {
			double[] slice;
			out = new ShortSet(nx, ny, nz);
			short[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((double[]) data[z]);
				outslice = ((short[]) ((ShortSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (short) (slice[k]);
				}
			}
		}
			break;
		case ImageWare.FLOAT: {
			double[] slice;
			out = new FloatSet(nx, ny, nz);
			float[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((double[]) data[z]);
				outslice = ((float[]) ((FloatSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (float) (slice[k]);
				}
			}
		}
			break;
		case ImageWare.DOUBLE: {
			double[] slice;
			out = new DoubleSet(nx, ny, nz);
			double[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((double[]) data[z]);
				outslice = ((double[]) ((DoubleSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (double) (slice[k]);
				}
			}
		}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n"
					+ "-------------------------------------------------------\n");
		}
		return out;
	}

	/**
	 * Print information of this ImageWare object.
	 */
	public void printInfo() {
		System.out.println("ImageWare object information");
		System.out.println("Dimension: " + getDimension());
		System.out.println("Size: [" + nx + ", " + ny + ", " + nz + "]");
		System.out.println("TotalSize: " + getTotalSize());
		System.out.println("Type: " + getTypeToString());
		System.out.println("Maximun: " + getMaximum());
		System.out.println("Minimun: " + getMinimum());
		System.out.println("Mean: " + getMean());
		System.out.println("Norm1: " + getNorm1());
		System.out.println("Norm2: " + getNorm2());
		System.out.println("Total: " + getTotal());
		System.out.println("");
	}

	/**
	 * Show this ImageWare object.
	 */
	public void show() {
		String title = getTypeToString();
		switch (getDimension()) {
		case 1:
			title += " line";
			break;
		case 2:
			title += " image";
			break;
		case 3:
			title += " volume";
			break;
		}
		Display.show(title, this);
		// ImagePlus imp = new ImagePlus(title, buildImageStack());
		// imp.show();
	}

	/**
	 * Show the data in ImagePlus object with a specify title.
	 * 
	 * @param title
	 *            a string given the title of the window
	 */
	public void show(String title) {
		Display.show(title, this);
		// ImagePlus imp = new ImagePlus(title, buildImageStack());
		// imp.show();
	}

	/**
	 * Return the minimum value of this imageware.
	 * 
	 * @return the min value of this imageware
	 */
	public double getMinimum() {
		double min = Double.MAX_VALUE;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((double[]) data[z]);
			for (int k = 0; k < nxy; k++)
				if ((slice[k]) < min)
					min = slice[k];
		}
		return min;
	}

	/**
	 * Return the maximum value of this imageware.
	 * 
	 * @return the max value of this imageware
	 */
	public double getMaximum() {
		double max = -Double.MAX_VALUE;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((double[]) data[z]);
			for (int k = 0; k < nxy; k++)
				if ((slice[k]) > max)
					max = slice[k];
		}
		return max;
	}

	/**
	 * Return the mean value of this imageware.
	 * 
	 * @return the mean value of this imageware
	 */
	public double getMean() {
		return getTotal() / (nz * nxy);
	}

	/**
	 * Return the norm value of order 1.
	 * 
	 * @return the norm value of this imageware in L1 sense
	 */
	public double getNorm1() {
		double norm = 0.0;
		double value = 0;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((double[]) data[z]);
			for (int k = 0; k < nxy; k++) {
				value = (double) (slice[k]);
				norm += (value > 0.0 ? value : -value);
			}
		}
		return norm;
	}

	/**
	 * Return the norm value of order 2.
	 * 
	 * @return the norm value of this imageware in L2 sense
	 */
	public double getNorm2() {
		double norm = 0.0;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((double[]) data[z]);
			for (int k = 0; k < nxy; k++)
				norm += (slice[k]) * (slice[k]);
		}
		return norm;
	}

	/**
	 * Return the sum of all pixel in this imageware.
	 * 
	 * @return the total sum of all pixel in this imageware
	 */
	public double getTotal() {
		double total = 0.0;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((double[]) data[z]);
			for (int k = 0; k < nxy; k++)
				total += slice[k];
		}
		return total;
	}

	/**
	 * Return the the minumum [0] and the maximum [1] value of this imageware.
	 * Faster routine than call one getMinimum() and then one getMaximum().
	 * 
	 * @return an array of two values, the min and the max values of the images
	 */
	public double[] getMinMax() {
		double max = -Double.MAX_VALUE;
		double min = Double.MAX_VALUE;
		double[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((double[]) data[z]);
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > max)
					max = slice[k];
				if ((slice[k]) < min)
					min = slice[k];
			}
		}
		double minmax[] = { min, max };
		return minmax;
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/FMath.java b/src/bilib/src/imageware/FMath.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae00013ead516ba738d815074d336593b77a0784
--- /dev/null
+++ b/src/bilib/src/imageware/FMath.java
@@ -0,0 +1 @@
+package imageware;

/**
 * Class FMath. The <b>FMath</b> class provides methods for carrying out a
 * number of elementary mathematical operations.<br>
 * <br>
 * 
 * @author Erik Meijering Biomedical Imaging Group Swiss Federal Institute of
 *         Technology Lausanne EPFL, CH-1015 Lausanne, Switzerland
 */

public final class FMath {

	/**
	 * Returns the largest integral value that is not greater than the argument.
	 * If the argument value is already equal to a mathematical integer, then
	 * the result is the same as the argument. Note that the method works only
	 * for <b>float</b> values that fall within the range spanned by the
	 * integers. In that case, this method gives the same result as the
	 * corresponding method in Java's <b>Math</b> class, but is in general much
	 * faster.
	 */
	public static int floor(final float f) {
		if (f >= 0.0f)
			return (int) f;
		else {
			final int iAdd = (int) f - 1;
			return (((int) (f - iAdd)) + iAdd);
		}
	}

	/**
	 * Returns the largest integral value that is not greater than the argument.
	 * If the argument value is already equal to a mathematical integer, then
	 * the result is the same as the argument. Note that the method works only
	 * for <b>double</b> values that fall within the range spanned by the
	 * integers. In that case, this method gives the same result as the
	 * corresponding method in Java's <b>Math</b> class, but is in general much
	 * faster.
	 */
	public static int floor(final double d) {
		if (d >= 0.0)
			return (int) d;
		else {
			final int iAdd = (int) d - 1;
			return (((int) (d - iAdd)) + iAdd);
		}
	}

	/**
	 * Returns the smallest integral value that is not less than the argument.
	 * If the argument value is already equal to a mathematical integer, then
	 * the result is the same as the argument. Note that the method works only
	 * for <b>float</b> values that fall within the range spanned by the
	 * integers. In that case, this method gives the same result as the
	 * corresponding method in Java's <b>Math</b> class, but is in general much
	 * faster.
	 */
	public static int ceil(final float f) {
		final float mf = -f;
		if (mf >= 0.0f)
			return -((int) mf);
		else {
			final int iAdd = (int) mf - 1;
			return -(((int) (mf - iAdd)) + iAdd);
		}
	}

	/**
	 * Returns the smallest integral value that is not less than the argument.
	 * If the argument value is already equal to a mathematical integer, then
	 * the result is the same as the argument. Note that the method works only
	 * for <b>double</b> values that fall within the range spanned by the
	 * integers. In that case, this method gives the same result as the
	 * corresponding method in Java's <b>Math</b> class, but is in general much
	 * faster.
	 */
	public static int ceil(final double d) {
		final double md = -d;
		if (md >= 0.0)
			return -((int) md);
		else {
			final int iAdd = (int) md - 1;
			return -(((int) (md - iAdd)) + iAdd);
		}
	}

	/**
	 * Returns the integral value closest to the argument. Note that the method
	 * works only for <b>float</b> values that fall within the range spanned by
	 * the integers. In that case, this method gives the same result as the
	 * corresponding method in Java's <b>Math</b> class, but is in general much
	 * faster.
	 */
	public static int round(final float f) {
		final float f05 = f + 0.5f;
		if (f05 >= 0.0)
			return (int) f05;
		else {
			final int iAdd = (int) f05 - 1;
			return (((int) (f05 - iAdd)) + iAdd);
		}
	}

	/**
	 * Returns the integral value closest to the argument. Note that the method
	 * works only for <b>double</b> values that fall within the range spanned by
	 * the integers. In that case, this method gives the same result as the
	 * corresponding method in Java's <b>Math</b> class, but is in general much
	 * faster.
	 */
	public static int round(final double d) {
		final double d05 = d + 0.5;
		if (d05 >= 0.0)
			return (int) d05;
		else {
			final int iAdd = (int) d05 - 1;
			return (((int) (d05 - iAdd)) + iAdd);
		}
	}

	/** Returns the minimum value of the two arguments. */
	public static float min(final float f1, final float f2) {
		return ((f1 < f2) ? f1 : f2);
	}

	/** Returns the minimum value of the two arguments. */
	public static double min(final double d1, final double d2) {
		return ((d1 < d2) ? d1 : d2);
	}

	/** Returns the minimum value of the two arguments. */
	public static float max(final float f1, final float f2) {
		return ((f1 > f2) ? f1 : f2);
	}

	/** Returns the maximum value of the two arguments. */
	public static double max(final double d1, final double d2) {
		return ((d1 > d2) ? d1 : d2);
	}

}
\ No newline at end of file
diff --git a/src/bilib/src/imageware/FloatAccess.java b/src/bilib/src/imageware/FloatAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..16823a8b13f52b2b4cff6557717d5d0263c005e8
--- /dev/null
+++ b/src/bilib/src/imageware/FloatAccess.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class FloatAccess.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class FloatAccess extends FloatBuffer implements Access {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected FloatAccess(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected FloatAccess(Image image, int mode) {
		super(image, mode);
	}

	protected FloatAccess(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected FloatAccess(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected FloatAccess(byte[] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(byte[][] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(short[] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(short[][] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(short[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(float[] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(float[][] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(float[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(double[] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(double[][] array, int mode) {
		super(array, mode);
	}

	protected FloatAccess(double[][][] array, int mode) {
		super(array, mode);
	}

	// ------------------------------------------------------------------
	//
	// getPixel section
	//
	// ------------------------------------------------------------------

	/**
	 * Get a pixel at specific position without specific boundary conditions
	 * 
	 * If the positions is outside of this imageware, the method return 0.0.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a pixel value
	 */
	public double getPixel(int x, int y, int z) {
		if (x >= nx)
			return 0.0;
		if (y >= ny)
			return 0.0;
		if (z >= nz)
			return 0.0;
		if (x < 0)
			return 0.0;
		if (y < 0)
			return 0.0;
		if (z < 0)
			return 0.0;
		return ((float[]) data[z])[x + y * nx];
	}

	/**
	 * Get a pixel at specific position with specific boundary conditions
	 * 
	 * If the positions is outside of this imageware, the method apply the
	 * boundary conditions to return a value.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a pixel value
	 */
	public double getPixel(int x, int y, int z, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a pixel \n" + "at the position (" + x
					+ "," + y + "," + z + ".\n" + "-------------------------------------------------------\n");
		}
		int xp = x;
		while (xp < 0)
			xp += xperiod;
		while (xp >= nx) {
			xp = xperiod - xp;
			xp = (xp < 0 ? -xp : xp);
		}
		int yp = y;
		while (yp < 0)
			yp += yperiod;
		while (yp >= ny) {
			yp = yperiod - yp;
			yp = (yp < 0 ? -yp : yp);
		}
		int zp = z;
		while (zp < 0)
			zp += zperiod;
		while (zp >= nz) {
			zp = zperiod - zp;
			zp = (zp < 0 ? -zp : zp);
		}
		return ((float[]) data[zp])[xp + yp * nx];
	}

	/**
	 * Get a interpolated pixel value at specific position without specific
	 * boundary conditions.
	 * 
	 * If the positions is not on the pixel grid, the method return a
	 * interpolated value of the pixel (linear interpolation). If the positions
	 * is outside of this imageware, the method return 0.0.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a interpolated value
	 */
	public double getInterpolatedPixel(double x, double y, double z) {
		if (x > nx - 1)
			return 0.0;
		if (y > ny - 1)
			return 0.0;
		if (z > nz - 1)
			return 0.0;
		if (x < 0)
			return 0.0;
		if (y < 0)
			return 0.0;
		if (z < 0)
			return 0.0;
		double output = 0.0;
		/*
		 * int i = (x >= 0.0 ? ((int)x) : ((int)x - 1)); int j = (y >= 0.0 ?
		 * ((int)y) : ((int)y - 1)); int k = (z >= 0.0 ? ((int)z) : ((int)z -
		 * 1));
		 */
		int i = (x >= 0.0 ? ((int) x) : ((int) x - 1));
		int j = (y >= 0.0 ? ((int) y) : ((int) y - 1));
		int k = (z >= 0.0 ? ((int) z) : ((int) z - 1));
		boolean fi = (i == nx - 1);
		boolean fj = (j == ny - 1);
		boolean fk = (k == nz - 1);
		int index = i + j * nx;
		switch (getDimension()) {
		case 1:
			double v1_0 = (double) (((float[]) data[k])[index]);
			double v1_1 = (fi ? v1_0 : (double) (((float[]) data[k])[index + 1]));
			double dx1 = x - (double) i;
			return v1_1 * dx1 - v1_0 * (dx1 - 1.0);
		case 2:
			double v2_00 = (double) (((float[]) data[k])[index]);
			double v2_10 = (fi ? v2_00 : (double) (((float[]) data[k])[index + 1]));
			double v2_01 = (fj ? v2_00 : (double) (((float[]) data[k])[index + nx]));
			double v2_11 = (fi ? (fj ? v2_00 : v2_01) : (double) (((float[]) data[k])[index + 1 + nx]));
			double dx2 = x - (double) i;
			double dy2 = y - (double) j;
			return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0)));
		case 3:
			double v3_000 = (double) (((float[]) data[k])[index]);
			double v3_100 = (fi ? v3_000 : (double) (((float[]) data[k])[index + 1]));
			double v3_010 = (fj ? v3_000 : (double) (((float[]) data[k])[index + nx]));
			double v3_110 = (fi ? (fj ? v3_000 : v3_010) : (double) (((float[]) data[k])[index + 1 + nx]));
			double v3_001 = (fk ? v3_000 : (double) (((float[]) data[k + 1])[index]));
			double v3_011 = (fk ? (fj ? v3_000 : v3_010) : (double) (((float[]) data[k + 1])[index + 1]));
			double v3_101 = (fk ? (fi ? v3_000 : v3_100) : (double) (((float[]) data[k + 1])[index + nx]));
			double v3_111 = (fk ? (fj ? (fi ? v3_000 : v3_100) : v3_110) : (double) (((float[]) data[k + 1])[index + 1 + nx]));
			double dx3 = x - (double) i;
			double dy3 = y - (double) j;
			double dz3 = z - (double) k;
			double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0)));
			double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0)));
			return z2 * dz3 - z1 * (dz3 - 1.0);
		}
		return output;
	}

	/**
	 * Get a interpolated pixel value at specific position with specific
	 * boundary conditions.
	 * 
	 * If the positions is not on the pixel grid, the method return a
	 * interpolated value of the pixel (linear interpolation). If the positions
	 * is outside of this imageware, the method apply the boundary conditions to
	 * return a value.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @param boundaryConditions
	 *            MIRROR or PERIODIC boundary conditions
	 * @return a interpolated value
	 */
	public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions) {
		double output = 0.0;
		int i = (x >= 0.0 ? ((int) x) : ((int) x - 1));
		int j = (y >= 0.0 ? ((int) y) : ((int) y - 1));
		int k = (z >= 0.0 ? ((int) z) : ((int) z - 1));
		switch (getDimension()) {
		case 1:
			double v1_0 = getPixel(i, j, k, boundaryConditions);
			double v1_1 = getPixel(i + 1, j, k, boundaryConditions);
			double dx1 = x - (double) i;
			return v1_1 * dx1 - v1_0 * (dx1 - 1.0);
		case 2:
			double v2_00 = getPixel(i, j, k, boundaryConditions);
			double v2_10 = getPixel(i + 1, j, k, boundaryConditions);
			double v2_01 = getPixel(i, j + 1, k, boundaryConditions);
			double v2_11 = getPixel(i + 1, j + 1, k, boundaryConditions);
			double dx2 = x - (double) i;
			double dy2 = y - (double) j;
			return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0)));
		case 3:
			double v3_000 = getPixel(i, j, k, boundaryConditions);
			double v3_100 = getPixel(i + 1, j, k, boundaryConditions);
			double v3_010 = getPixel(i, j + 1, k, boundaryConditions);
			double v3_110 = getPixel(i + 1, j + 1, k, boundaryConditions);
			double v3_001 = getPixel(i, j, k + 1, boundaryConditions);
			double v3_011 = getPixel(i + 1, j, k + 1, boundaryConditions);
			double v3_101 = getPixel(i, j + 1, k + 1, boundaryConditions);
			double v3_111 = getPixel(i + 1, j + 1, k + 1, boundaryConditions);
			double dx3 = x - (double) i;
			double dy3 = y - (double) j;
			double dz3 = z - (double) k;
			double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0)));
			double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0)));
			return z2 * dz3 - z1 * (dz3 - 1.0);
		}
		return output;
	}

	// ------------------------------------------------------------------
	//
	// putPixel section
	//
	// ------------------------------------------------------------------

	/**
	 * Put a pixel at specific position
	 * 
	 * If the positions is outside of this imageware, the method does nothing.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 */
	public void putPixel(int x, int y, int z, double value) {
		if (x >= nx)
			return;
		if (y >= ny)
			return;
		if (z >= nz)
			return;
		if (x < 0)
			return;
		if (y < 0)
			return;
		if (z < 0)
			return;
		((float[]) data[z])[x + y * nx] = (float) value;
	}

	// ------------------------------------------------------------------
	//
	// getBounded section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (((float[]) data[k])[offset]);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (((float[]) data[k])[offset]);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (((float[]) data[k])[offset]);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (((float[]) data[k])[offset]);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			float[] tmp = (float[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (tmp[offset]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			float[] tmp = (float[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (tmp[offset]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			float[] tmp = (float[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (tmp[offset]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			float[] tmp = (float[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (tmp[offset]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (((float[]) data[z])[offset]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (((float[]) data[z])[offset]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (((float[]) data[z])[offset]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (((float[]) data[z])[offset]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (((float[]) data[z])[offset]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (((float[]) data[z])[offset]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (((float[]) data[z])[offset]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (((float[]) data[z])[offset]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				float[] tmp = (float[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (byte) (tmp[offset]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				float[] tmp = (float[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (short) (tmp[offset]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				float[] tmp = (float[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (float) (tmp[offset]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				float[] tmp = (float[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (double) (tmp[offset]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// getBlock section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			float[] tmp = (float[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (byte) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			float[] tmp = (float[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (short) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			float[] tmp = (float[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (float) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			float[] tmp = (float[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (double) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			float[] tmp = (float[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (byte) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			float[] tmp = (float[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (short) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			float[] tmp = (float[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (float) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			float[] tmp = (float[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (double) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (byte) (((float[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (short) (((float[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (float) (((float[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (double) (((float[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			float[] tmp = (float[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			float[] tmp = (float[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			float[] tmp = (float[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			float[] tmp = (float[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (((float[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (((float[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (((float[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (((float[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (byte) (((float[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (short) (((float[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (float) (((float[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (double) (((float[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				float[] tmp = (float[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (byte) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				float[] tmp = (float[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (short) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				float[] tmp = (float[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (float) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				float[] tmp = (float[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (double) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// getBlock section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (byte) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (short) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (float) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (double) (tmp[xp + yp]);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (byte) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (short) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (float) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (double) (tmp[xp + yp * nx]);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (byte) (((float[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (short) (((float[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (float) (((float[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (double) (((float[]) data[zp])[xyp]);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			float[] tmp = ((float[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (tmp[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (((float[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (((float[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (((float[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (((float[]) data[zp])[xp + yp]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (byte) (((float[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (short) (((float[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (float) (((float[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (double) (((float[]) data[zp])[xp + yp * nx]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				float[] tmp = ((float[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (byte) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				float[] tmp = ((float[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (short) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				float[] tmp = ((float[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (float) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				float[] tmp = ((float[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (double) (tmp[xp + yp]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// putBounded section
	//
	// ------------------------------------------------------------------

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (float) (buffer[i] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (float) (buffer[i] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (float) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (float) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (float) (buffer[i] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (float) (buffer[i] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (float) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			float[] tmp = (float[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (float) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((float[]) data[k])[offset] = (float) (buffer[i] & 0xFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((float[]) data[k])[offset] = (float) (buffer[i] & 0xFFFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((float[]) data[k])[offset] = (float) (buffer[i]);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((float[]) data[k])[offset] = (float) (buffer[i]);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			float[] tmp = (float[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (float) (buffer[i][j] & 0xFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			float[] tmp = (float[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (float) (buffer[i][j] & 0xFFFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			float[] tmp = (float[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (float) (buffer[i][j]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			float[] tmp = (float[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (float) (buffer[i][j]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((float[]) data[k])[offset] = (float) (buffer[i][j] & 0xFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((float[]) data[k])[offset] = (float) (buffer[i][j] & 0xFFFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((float[]) data[k])[offset] = (float) (buffer[i][j]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((float[]) data[k])[offset] = (float) (buffer[i][j]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((float[]) data[k])[offset] = (float) (buffer[i][j] & 0xFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((float[]) data[k])[offset] = (float) (buffer[i][j] & 0xFFFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((float[]) data[k])[offset] = (float) (buffer[i][j]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((float[]) data[k])[offset] = (float) (buffer[i][j]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				float[] tmp = (float[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (float) (buffer[i][j][k] & 0xFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				float[] tmp = (float[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (float) (buffer[i][j][k] & 0xFFFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				float[] tmp = (float[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (float) (buffer[i][j][k]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				float[] tmp = (float[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (float) (buffer[i][j][k]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/FloatBuffer.java b/src/bilib/src/imageware/FloatBuffer.java
new file mode 100644
index 0000000000000000000000000000000000000000..2da717da435bef43f70dc12dae12521015d9443f
--- /dev/null
+++ b/src/bilib/src/imageware/FloatBuffer.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;
import ij.process.ByteProcessor;
import ij.process.ColorProcessor;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;

import java.awt.Image;
import java.awt.image.ImageObserver;
import java.awt.image.PixelGrabber;

/**
 * Class FloatBuffer.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class FloatBuffer implements Buffer {

	protected Object[]	data	= null;
	protected int		nx		= 0;
	protected int		ny		= 0;
	protected int		nz		= 0;
	protected int		nxy		= 0;

	/**
	 * Constructor of a empty 3D float buffer.
	 * 
	 * @param nx
	 *            size of the 3D buffer in the X axis
	 * @param ny
	 *            size of the 3D buffer in the Y axis
	 * @param nz
	 *            size of the 3D buffer in the Z axis
	 */
	protected FloatBuffer(int nx, int ny, int nz) {
		this.nx = nx;
		this.ny = ny;
		this.nz = nz;
		if (nx <= 0 || ny <= 0 || nz <= 0)
			throw_constructor(nx, ny, nz);
		allocate();
	}

	/**
	 * Constructor of a float buffer from a object Image of Java.
	 * 
	 * @param image
	 *            source to build a new imageware
	 */
	protected FloatBuffer(Image image, int mode) {
		if (image == null) {
			throw_constructor();
		}
		ImageObserver observer = null;
		this.nx = image.getWidth(observer);
		this.ny = image.getHeight(observer);
		this.nz = 1;
		this.nxy = nx * ny;
		byte[] pixels = new byte[nxy];
		PixelGrabber pg = new PixelGrabber(image, 0, 0, nx, ny, false);
		try {
			pg.grabPixels();
			pixels = (byte[]) (pg.getPixels());
		}
		catch (Exception e) {
			throw_constructor();
		}
		allocate();
		for (int k = 0; k < nxy; k++)
			((float[]) data[0])[k] = (float) (pixels[k] & 0xFF);
	}

	/**
	 * Constructor of a float buffer from a ImageStack.
	 * 
	 * New data are allocated if the mode is CREATE, the imageware use the data
	 * of ImageJ if the mode is WRAP.
	 * 
	 * @param stack
	 *            source to build a new imageware
	 * @param mode
	 *            WRAP or CREATE
	 */
	protected FloatBuffer(ImageStack stack, int mode) {
		if (stack == null) {
			throw_constructor();
		}
		this.nx = stack.getWidth();
		this.ny = stack.getHeight();
		this.nz = stack.getSize();
		this.nxy = nx * ny;
		switch (mode) {
		case ImageWare.WRAP:
			this.data = stack.getImageArray();
			break;
		case ImageWare.CREATE:
			allocate();
			ImageProcessor ip = stack.getProcessor(1);
			if (ip instanceof ByteProcessor) {
				Object[] vol = stack.getImageArray();
				for (int z = 0; z < nz; z++) {
					byte[] slice = (byte[]) vol[z];
					for (int k = 0; k < nxy; k++) {
						((float[]) data[z])[k] = (float) (slice[k] & 0xFF);
					}
				}
			}
			else if (ip instanceof ShortProcessor) {
				Object[] vol = stack.getImageArray();
				for (int z = 0; z < nz; z++) {
					short[] slice = (short[]) vol[z];
					for (int k = 0; k < nxy; k++) {
						((float[]) data[z])[k] = (float) (slice[k] & 0xFFFF);
					}
				}
			}
			else if (ip instanceof FloatProcessor) {
				Object[] vol = stack.getImageArray();
				for (int z = 0; z < nz; z++) {
					float[] slice = (float[]) vol[z];
					for (int k = 0; k < nxy; k++) {
						((float[]) data[z])[k] = (float) slice[k];
					}
				}
			}
			else if (ip instanceof ColorProcessor) {
				double r, g, b;
				int c;
				ColorProcessor cp;
				int[] pixels;
				for (int z = 0; z < nz; z++) {
					cp = (ColorProcessor) stack.getProcessor(z + 1);
					pixels = (int[]) cp.getPixels();
					for (int k = 0; k < nxy; k++) {
						c = pixels[k];
						r = (double) ((c & 0xFF0000) >> 16);
						g = (double) ((c & 0xFF00) >> 8);
						b = (double) ((c & 0xFF));
						((float[]) data[z])[k] = (float) ((r + g + b) / 3.0);
					}
				}
			}
			else {
				throw_constructor();
			}
			break;
		default:
			throw_constructor();
			break;
		}
	}

	/**
	 * Constructor of a float buffer from a specific color channel of
	 * ImageStack.
	 * 
	 * New data are always allocated. If it is a gray image the imageware is
	 * created and fill up with data of the source ImageStack. If it is a color
	 * image only the selected channel is used to create this imageware.
	 * 
	 * @param stack
	 *            source to build a new imageware
	 * @param channel
	 *            RED, GREEN or BLUE
	 */
	protected FloatBuffer(ImageStack stack, byte channel) {
		if (stack == null) {
			throw_constructor();
		}
		this.nx = stack.getWidth();
		this.ny = stack.getHeight();
		this.nz = stack.getSize();
		this.nxy = nx * ny;
		allocate();
		ImageProcessor ip = stack.getProcessor(1);
		if (ip instanceof ByteProcessor) {
			Object[] vol = stack.getImageArray();
			for (int z = 0; z < nz; z++) {
				byte[] slice = (byte[]) vol[z];
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] = (float) (slice[k] & 0xFF);
				}
			}
		}
		else if (ip instanceof ShortProcessor) {
			Object[] vol = stack.getImageArray();
			for (int z = 0; z < nz; z++) {
				short[] slice = (short[]) vol[z];
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] = (float) (slice[k] & 0xFFFF);
				}
			}
		}
		else if (ip instanceof FloatProcessor) {
			Object[] vol = stack.getImageArray();
			for (int z = 0; z < nz; z++) {
				float[] slice = (float[]) vol[z];
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] = (float) slice[k];
				}
			}
		}
		else if (ip instanceof ColorProcessor) {
			ColorProcessor cp;
			int[] pixels;
			for (int z = 0; z < nz; z++) {
				cp = (ColorProcessor) stack.getProcessor(z + 1);
				pixels = (int[]) cp.getPixels();
				switch (channel) {
				case ImageWare.RED:
					for (int k = 0; k < nxy; k++) {
						((float[]) data[z])[k] = (float) ((pixels[k] & 0xFF0000) >> 16);
					}
					break;
				case ImageWare.GREEN:
					for (int k = 0; k < nxy; k++) {
						((float[]) data[z])[k] = (float) ((pixels[k] & 0xFF00) >> 8);
					}
					break;
				case ImageWare.BLUE:
					for (int k = 0; k < nxy; k++) {
						((float[]) data[z])[k] = (float) (pixels[k] & 0xFF);
					}
					break;
				default:
					throw_constructor();
				}
			}
		}
		else {
			throw_constructor();
		}
	}

	/**
	 * Constructor of a float buffer from a byte array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(byte[] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = 1;
		this.nz = 1;
		allocate();
		putX(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a byte array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(byte[][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = 1;
		allocate();
		putXY(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a byte array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(byte[][][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = array[0][0].length;
		allocate();
		putXYZ(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a short array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(short[] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = 1;
		this.nz = 1;
		allocate();
		putX(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a short array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(short[][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = 1;
		allocate();
		putXY(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a short array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(short[][][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = array[0][0].length;
		allocate();
		putXYZ(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a float array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(float[] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = 1;
		this.nz = 1;
		allocate();
		putX(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a float array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(float[][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = 1;
		allocate();
		putXY(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a float array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(float[][][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = array[0][0].length;
		allocate();
		putXYZ(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a double array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(double[] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = 1;
		this.nz = 1;
		allocate();
		putX(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a double array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(double[][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = 1;
		allocate();
		putXY(0, 0, 0, array);
	}

	/**
	 * Constructor of a float buffer from a double array.
	 * 
	 * @param array
	 *            source to build this new imageware
	 */
	protected FloatBuffer(double[][][] array, int mode) {
		if (array == null) {
			throw_constructor();
		}
		this.nx = array.length;
		this.ny = array[0].length;
		this.nz = array[0][0].length;
		allocate();
		putXYZ(0, 0, 0, array);
	}

	/**
	 * Return the type of this imageware.
	 * 
	 * @return the type of this imageware
	 */
	public int getType() {
		return ImageWare.FLOAT;
	}

	/**
	 * Return the type of this imageware in a string format.
	 * 
	 * @return the type of this imageware translated in a string format
	 */
	public String getTypeToString() {
		return "Float";
	}

	/**
	 * Return the number of dimension of this imageware (1, 2 or 3).
	 * 
	 * @return the number of dimension of this imageware
	 */
	public int getDimension() {
		int dims = 0;
		dims += (nx > 1 ? 1 : 0);
		dims += (ny > 1 ? 1 : 0);
		dims += (nz > 1 ? 1 : 0);
		return dims;
	}

	/**
	 * Return the size of the imageware int[0] : x, int[1] : y, int[2] : z.
	 * 
	 * @return an array given the size of the imageware
	 */
	public int[] getSize() {
		int[] size = { nx, ny, nz };
		return size;
	}

	/**
	 * Return the size in the X axis.
	 * 
	 * @return the size in the X axis
	 */
	public int getSizeX() {
		return nx;
	}

	/**
	 * Return the size in the Y axis.
	 * 
	 * @return the size in the Y axis
	 */
	public int getSizeY() {
		return ny;
	}

	/**
	 * Return the size in the Z axis.
	 * 
	 * @return the size in the Z axis
	 */
	public int getSizeZ() {
		return nz;
	}

	/**
	 * Return the size in the X axis.
	 * 
	 * @return the size in the X axis
	 */
	public int getWidth() {
		return nx;
	}

	/**
	 * Return the size in the Y axis.
	 * 
	 * @return the size in the Y axis
	 */
	public int getHeight() {
		return ny;
	}

	/**
	 * Return the size in the Z axis.
	 * 
	 * @return the size in the Z axis
	 */
	public int getDepth() {
		return nz;
	}

	/**
	 * Return the number of pixels in the imageware.
	 * 
	 * @return number of pixels in the imageware
	 */
	public int getTotalSize() {
		return nxy * nz;
	}

	/**
	 * Return true is this imageware has the same size the imageware given as
	 * parameter.
	 * 
	 * @param imageware
	 *            imageware to be compared
	 * @return true if the imageware of the same size than this imageware
	 */
	public boolean isSameSize(ImageWare imageware) {
		if (nx != imageware.getSizeX())
			return false;
		if (ny != imageware.getSizeY())
			return false;
		if (nz != imageware.getSizeZ())
			return false;
		return true;
	}

	// ------------------------------------------------------------------
	//
	// put Section
	//
	// ------------------------------------------------------------------

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putX(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		double buf[] = new double[bnx];
		buffer.getX(0, 0, 0, buf);
		putX(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putY(int x, int y, int z, ImageWare buffer) {
		int bny = buffer.getSizeY();
		double buf[] = new double[bny];
		buffer.getY(0, 0, 0, buf);
		putY(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putZ(int x, int y, int z, ImageWare buffer) {
		int bnz = buffer.getSizeZ();
		double buf[] = new double[bnz];
		buffer.getZ(0, 0, 0, buf);
		putZ(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putXY(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bny = buffer.getSizeY();
		double buf[][] = new double[bnx][bny];
		buffer.getXY(0, 0, 0, buf);
		putXY(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putXZ(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bnz = buffer.getSizeZ();
		double buf[][] = new double[bnx][bnz];
		buffer.getXZ(0, 0, 0, buf);
		putXZ(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putYZ(int x, int y, int z, ImageWare buffer) {
		int bny = buffer.getSizeY();
		int bnz = buffer.getSizeZ();
		double buf[][] = new double[bny][bnz];
		buffer.getYZ(0, 0, 0, buf);
		putYZ(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            ImageWare object to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bny = buffer.getSizeY();
		int bnz = buffer.getSizeZ();
		double buf[][][] = new double[bnx][bny][bnz];
		buffer.getXYZ(0, 0, 0, buf);
		putXYZ(x, y, z, buf);
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putX(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];

			for (int i = 0; i < leni; i++) {
				tmp[offset] = (float) (buffer[i] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putX(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];

			for (int i = 0; i < leni; i++) {
				tmp[offset] = (float) (buffer[i] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putX(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];

			System.arraycopy(buffer, 0, tmp, offset, leni);
		}
		catch (Exception e) {
			throw_put("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putX(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];

			for (int i = 0; i < leni; i++) {
				tmp[offset] = (float) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putY(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];
			for (int i = 0; i < leni; i++) {
				tmp[offset] = (float) (buffer[i] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putY(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];
			for (int i = 0; i < leni; i++) {
				tmp[offset] = (float) (buffer[i] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putY(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];
			for (int i = 0; i < leni; i++) {
				tmp[offset] = (float) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putY(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];
			for (int i = 0; i < leni; i++) {
				tmp[offset] = (float) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            bybytete 1D array to put into the imageware
	 */
	public void putZ(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				((float[]) data[z])[offset] = (float) (buffer[i] & 0xFF);
				z++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byshortte 1D array to put into the imageware
	 */
	public void putZ(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				((float[]) data[z])[offset] = (float) (buffer[i] & 0xFFFF);
				z++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byfloatte 1D array to put into the imageware
	 */
	public void putZ(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				((float[]) data[z])[offset] = (float) (buffer[i]);
				z++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            bydoublete 1D array to put into the imageware
	 */
	public void putZ(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				((float[]) data[z])[offset] = (float) (buffer[i]);
				z++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putXY(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			float[] tmp = (float[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					tmp[offset] = (float) (buffer[i][j] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putXY(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			float[] tmp = (float[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					tmp[offset] = (float) (buffer[i][j] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putXY(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			float[] tmp = (float[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					tmp[offset] = (float) (buffer[i][j]);
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putXY(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			float[] tmp = (float[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					tmp[offset] = (float) (buffer[i][j]);
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putXZ(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + j * nx;
				for (int i = 0; i < leni; i++, offset++) {
					((float[]) data[z])[offset] = (float) (buffer[i][j] & 0xFF);
				}
			}
		}
		catch (Exception e) {
			throw_put("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putXZ(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + j * nx;
				for (int i = 0; i < leni; i++, offset++) {
					((float[]) data[z])[offset] = (float) (buffer[i][j] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_put("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putXZ(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + j * nx;
				for (int i = 0; i < leni; i++, offset++) {
					((float[]) data[z])[offset] = (float) (buffer[i][j]);
				}
			}
		}
		catch (Exception e) {
			throw_put("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putXZ(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + j * nx;
				for (int i = 0; i < leni; i++, offset++) {
					((float[]) data[z])[offset] = (float) (buffer[i][j]);
				}
			}
		}
		catch (Exception e) {
			throw_put("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putYZ(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
				for (int i = 0; i < leni; i++, offset += nx) {
					((float[]) data[z])[offset] = (float) (buffer[i][j] & 0xFF);
				}
		}
		catch (Exception e) {
			throw_put("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putYZ(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
				for (int i = 0; i < leni; i++, offset += nx) {
					((float[]) data[z])[offset] = (float) (buffer[i][j] & 0xFFFF);
				}
		}
		catch (Exception e) {
			throw_put("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putYZ(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
				for (int i = 0; i < leni; i++, offset += nx) {
					((float[]) data[z])[offset] = (float) (buffer[i][j]);
				}
		}
		catch (Exception e) {
			throw_put("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putYZ(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
				for (int i = 0; i < leni; i++, offset += nx) {
					((float[]) data[z])[offset] = (float) (buffer[i][j]);
				}
		}
		catch (Exception e) {
			throw_put("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				float[] tmp = (float[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						tmp[offset] = (float) (buffer[i][j][k] & 0xFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				float[] tmp = (float[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						tmp[offset] = (float) (buffer[i][j][k] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				float[] tmp = (float[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						tmp[offset] = (float) (buffer[i][j][k]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to put into the imageware
	 */
	public void putXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				float[] tmp = (float[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						tmp[offset] = (float) (buffer[i][j][k]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "No check", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// get Section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getX(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		double buf[] = new double[bnx];
		getX(x, y, z, buf);
		buffer.putX(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getY(int x, int y, int z, ImageWare buffer) {
		int bny = buffer.getSizeY();
		double buf[] = new double[bny];
		getY(x, y, z, buf);
		buffer.putY(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getZ(int x, int y, int z, ImageWare buffer) {
		int bnz = buffer.getSizeZ();
		double buf[] = new double[bnz];
		getZ(x, y, z, buf);
		buffer.putZ(0, 0, 0, buf);
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getXY(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bny = buffer.getSizeY();
		double buf[][] = new double[bnx][bny];
		getXY(x, y, z, buf);
		buffer.putXY(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getXZ(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bnz = buffer.getSizeZ();
		double buf[][] = new double[bnx][bnz];
		getXZ(x, y, z, buf);
		buffer.putXZ(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the datase
	 */
	public void getYZ(int x, int y, int z, ImageWare buffer) {
		int bny = buffer.getSizeY();
		int bnz = buffer.getSizeZ();
		double buf[][] = new double[bny][bnz];
		getYZ(x, y, z, buf);
		buffer.putYZ(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            ImageWare object to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, ImageWare buffer) {
		int bnx = buffer.getSizeX();
		int bny = buffer.getSizeY();
		int bnz = buffer.getSizeZ();
		double buf[][][] = new double[bnx][bny][bnz];
		getXYZ(x, y, z, buf);
		buffer.putXYZ(0, 0, 0, buf);
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getX(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];

			for (int i = 0; i < leni; i++) {
				buffer[i] = (byte) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getX(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];

			for (int i = 0; i < leni; i++) {
				buffer[i] = (short) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getX(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];

			System.arraycopy(tmp, offset, buffer, 0, leni);
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getX(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];

			for (int i = 0; i < leni; i++) {
				buffer[i] = (double) (tmp[offset]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getY(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];
			for (int i = 0; i < leni; i++) {
				buffer[i] = (byte) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getY(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];
			for (int i = 0; i < leni; i++) {
				buffer[i] = (short) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getY(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];
			for (int i = 0; i < leni; i++) {
				buffer[i] = (float) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getY(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			float[] tmp = (float[]) data[z];
			for (int i = 0; i < leni; i++) {
				buffer[i] = (double) (tmp[offset]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("X", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getZ(int x, int y, int z, byte[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				buffer[i] = (byte) (((float[]) data[z])[offset]);
				z++;
			}
		}
		catch (Exception e) {
			throw_get("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getZ(int x, int y, int z, short[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				buffer[i] = (short) (((float[]) data[z])[offset]);
				z++;
			}
		}
		catch (Exception e) {
			throw_get("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getZ(int x, int y, int z, float[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				buffer[i] = (float) (((float[]) data[z])[offset]);
				z++;
			}
		}
		catch (Exception e) {
			throw_get("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getZ(int x, int y, int z, double[] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			for (int i = 0; i < leni; i++) {
				buffer[i] = (double) (((float[]) data[z])[offset]);
				z++;
			}
		}
		catch (Exception e) {
			throw_get("Y", "No check", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getXY(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			float[] tmp = (float[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (byte) (tmp[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getXY(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			float[] tmp = (float[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (short) (tmp[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getXY(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			float[] tmp = (float[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (float) (tmp[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the position (x,y,z) in XY axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getXY(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			float[] tmp = (float[]) data[z];
			for (int j = 0; j < lenj; j++) {
				offset = x + (y + j) * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (double) (tmp[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getXZ(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + y * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (byte) (((float[]) data[z])[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getXZ(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + y * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (short) (((float[]) data[z])[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getXZ(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + y * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (float) (((float[]) data[z])[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getXZ(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++) {
				offset = x + y * nx;
				for (int i = 0; i < leni; i++, offset++) {
					buffer[i][j] = (double) (((float[]) data[z])[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the datase
	 */
	public void getYZ(int x, int y, int z, byte[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
				for (int i = 0; i < leni; i++, offset += nx) {
					buffer[i][j] = (byte) (((float[]) data[z])[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the datase
	 */
	public void getYZ(int x, int y, int z, short[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
				for (int i = 0; i < leni; i++, offset += nx) {
					buffer[i][j] = (short) (((float[]) data[z])[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the datase
	 */
	public void getYZ(int x, int y, int z, float[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
				for (int i = 0; i < leni; i++, offset += nx) {
					buffer[i][j] = (float) (((float[]) data[z])[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the datase
	 */
	public void getYZ(int x, int y, int z, double[][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
				for (int i = 0; i < leni; i++, offset += nx) {
					buffer[i][j] = (double) (((float[]) data[z])[offset]);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				float[] tmp = (float[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						buffer[i][j][k] = (byte) (tmp[offset]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				float[] tmp = (float[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						buffer[i][j][k] = (short) (tmp[offset]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				float[] tmp = (float[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						buffer[i][j][k] = (float) (tmp[offset]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "No check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
	 * check are performed if the array is outside of the imageware.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 */
	public void getXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			int offset = x + y * nx;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++, z++) {
				float[] tmp = (float[]) data[z];
				for (int j = 0; j < lenj; j++) {
					offset = x + (j + y) * nx;
					for (int i = 0; i < leni; i++, offset++) {
						buffer[i][j][k] = (double) (tmp[offset]);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "No check", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// Private Section
	//
	// ------------------------------------------------------------------

	/**
	 * Prepare a complete error message from the errors coming the constructors.
	 */
	protected void throw_constructor() {
		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a float imageware.\n"
				+ "-------------------------------------------------------\n");
	}

	/**
	 * Prepare a complete error message from the errors coming the constructors.
	 */
	protected void throw_constructor(int nx, int ny, int nz) {
		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a float imageware " + nx + "," + ny
				+ "," + nz + "].\n" + "-------------------------------------------------------\n");
	}

	/**
	 * Prepare a complete error message from the errors coming the get routines.
	 */
	protected void throw_get(String direction, String border, Object buffer, int x, int y, int z) {
		int leni = 0;
		int lenj = 0;
		int lenk = 0;
		String type = " unknown type";
		if (buffer instanceof byte[]) {
			leni = ((byte[]) buffer).length;
			type = " 1D byte";
		}
		else if (buffer instanceof short[]) {
			leni = ((short[]) buffer).length;
			type = " 1D short";
		}
		else if (buffer instanceof float[]) {
			leni = ((float[]) buffer).length;
			type = " 1D float";
		}
		else if (buffer instanceof double[]) {
			leni = ((double[]) buffer).length;
			type = " 1D double";
		}
		else if (buffer instanceof byte[][]) {
			leni = ((byte[][]) buffer).length;
			lenj = ((byte[][]) buffer)[0].length;
			type = " 2D byte";
		}
		else if (buffer instanceof short[][]) {
			leni = ((short[][]) buffer).length;
			lenj = ((short[][]) buffer)[0].length;
			type = " 2D short";
		}
		else if (buffer instanceof float[][]) {
			leni = ((float[][]) buffer).length;
			lenj = ((float[][]) buffer)[0].length;
			type = " 2D float";
		}
		else if (buffer instanceof double[][]) {
			leni = ((double[][]) buffer).length;
			lenj = ((double[][]) buffer)[0].length;
			type = " 2D double";
		}
		else if (buffer instanceof byte[][][]) {
			leni = ((byte[][][]) buffer).length;
			lenj = ((byte[][][]) buffer)[0].length;
			lenk = ((byte[][][]) buffer)[0][0].length;
			type = " 3D byte";
		}
		else if (buffer instanceof short[][][]) {
			leni = ((short[][][]) buffer).length;
			lenj = ((short[][][]) buffer)[0].length;
			lenk = ((short[][][]) buffer)[0][0].length;
			type = " 3D short";
		}
		else if (buffer instanceof float[][][]) {
			leni = ((float[][][]) buffer).length;
			lenj = ((float[][][]) buffer)[0].length;
			lenk = ((float[][][]) buffer)[0][0].length;
			type = " 3D float";
		}
		else if (buffer instanceof double[][][]) {
			leni = ((double[][][]) buffer).length;
			lenj = ((double[][][]) buffer)[0].length;
			lenk = ((double[][][]) buffer)[0][0].length;
			type = " 3D double";
		}
		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get a" + type + " buffer ["
				+ (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "from the float imageware [" + nx + "," + ny + "," + nz + "]\n"
				+ "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n");
	}

	/**
	 * Prepare a complete error message from the errors coming the put routines.
	 */
	protected void throw_put(String direction, String border, Object buffer, int x, int y, int z) {
		int leni = 0;
		int lenj = 0;
		int lenk = 0;
		String type = " unknown type";
		if (buffer instanceof byte[]) {
			leni = ((byte[]) buffer).length;
			type = " 1D byte";
		}
		else if (buffer instanceof short[]) {
			leni = ((short[]) buffer).length;
			type = " 1D short";
		}
		else if (buffer instanceof float[]) {
			leni = ((float[]) buffer).length;
			type = " 1D float";
		}
		else if (buffer instanceof double[]) {
			leni = ((double[]) buffer).length;
			type = " 1D double";
		}
		else if (buffer instanceof byte[][]) {
			leni = ((byte[][]) buffer).length;
			lenj = ((byte[][]) buffer)[0].length;
			type = " 2D byte";
		}
		else if (buffer instanceof short[][]) {
			leni = ((short[][]) buffer).length;
			lenj = ((short[][]) buffer)[0].length;
			type = " 2D short";
		}
		else if (buffer instanceof float[][]) {
			leni = ((float[][]) buffer).length;
			lenj = ((float[][]) buffer)[0].length;
			type = " 2D float";
		}
		else if (buffer instanceof double[][]) {
			leni = ((double[][]) buffer).length;
			lenj = ((double[][]) buffer)[0].length;
			type = " 2D double";
		}
		else if (buffer instanceof byte[][][]) {
			leni = ((byte[][][]) buffer).length;
			lenj = ((byte[][][]) buffer)[0].length;
			lenk = ((byte[][][]) buffer)[0][0].length;
			type = " 3D byte";
		}
		else if (buffer instanceof short[][][]) {
			leni = ((short[][][]) buffer).length;
			lenj = ((short[][][]) buffer)[0].length;
			lenk = ((short[][][]) buffer)[0][0].length;
			type = " 3D short";
		}
		else if (buffer instanceof float[][][]) {
			leni = ((float[][][]) buffer).length;
			lenj = ((float[][][]) buffer)[0].length;
			lenk = ((float[][][]) buffer)[0][0].length;
			type = " 3D float";
		}
		else if (buffer instanceof double[][][]) {
			leni = ((double[][][]) buffer).length;
			lenj = ((double[][][]) buffer)[0].length;
			lenk = ((double[][][]) buffer)[0][0].length;
			type = " 3D double";
		}
		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a" + type + " buffer ["
				+ (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "into the float imageware [" + nx + "," + ny + "," + nz + "]\n"
				+ "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n");
	}

	// ------------------------------------------------------------------
	//
	// Get slice fast and direct access Section
	//
	// ------------------------------------------------------------------

	/**
	 * Get a reference of the whole volume data.
	 * 
	 * @return a reference of the data of this imageware
	 */
	public Object[] getVolume() {
		return data;
	}

	/**
	 * Get a specific slice, fast and direct access, but only for byte
	 * imageware.
	 * 
	 * @param z
	 *            number of the requested slice
	 * @return a reference of the data of one slice of this imageware
	 */
	public byte[] getSliceByte(int z) {
		return null;
	}

	/**
	 * Get a specific slice, fast and direct access, but only for short
	 * imageware.
	 * 
	 * @param z
	 *            number of the requested slice
	 * @return a reference of the data of one slice of this imageware
	 */
	public short[] getSliceShort(int z) {
		return null;
	}

	/**
	 * Get a specific slice, fast and direct access, but only for float
	 * imageware.
	 * 
	 * @param z
	 *            number of the requested slice
	 * @return a reference of the data of one slice of this imageware
	 */
	public float[] getSliceFloat(int z) {
		return (float[]) data[z];
	}

	/**
	 * Get a specific slice, fast and direct access, but only for double
	 * imageware.
	 * 
	 * @param z
	 *            number of the requested slice
	 * @return a reference of the data of one slice of this imageware
	 */
	public double[] getSliceDouble(int z) {
		return null;
	}

	/**
	 * Allocate a buffer of size [nx,ny,nz].
	 */
	private void allocate() {
		try {
			this.data = new Object[nz];
			this.nxy = nx * ny;
			for (int z = 0; z < nz; z++)
				this.data[z] = new float[nxy];
		}
		catch (Exception e) {
			throw_constructor(nx, ny, nz);
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/FloatPointwise.java b/src/bilib/src/imageware/FloatPointwise.java
new file mode 100644
index 0000000000000000000000000000000000000000..0883b25d0a135c67b62be7589414fa78789cb992
--- /dev/null
+++ b/src/bilib/src/imageware/FloatPointwise.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;
import ij.process.FloatProcessor;

import java.awt.Image;
import java.util.Random;

/**
 * Class FloatPointwise.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class FloatPointwise extends FloatAccess implements Pointwise {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected FloatPointwise(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected FloatPointwise(Image image, int mode) {
		super(image, mode);
	}

	protected FloatPointwise(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected FloatPointwise(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected FloatPointwise(byte[] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(byte[][] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(short[] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(short[][] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(short[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(float[] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(float[][] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(float[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(double[] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(double[][] array, int mode) {
		super(array, mode);
	}

	protected FloatPointwise(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Fill this imageware with a constant value.
	 * 
	 * @param value
	 *            the constant value
	 */
	public void fillConstant(double value) {
		float typedValue = (float) value;
		float[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++)
				slice[k] = typedValue;
		}
	}

	/**
	 * Fill this imageware with ramp.
	 */
	public void fillRamp() {
		int off = 0;
		float[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++)
				slice[k] = (float) (off + k);
			off += nxy;
		}
	}

	/**
	 * Generate a gaussian noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void fillGaussianNoise(double amplitude) {
		Random rnd = new Random();
		float[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) ((rnd.nextGaussian()) * amplitude);
			}
		}
	}

	/**
	 * Generate a uniform noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void fillUniformNoise(double amplitude) {
		Random rnd = new Random();
		float[] slice = null;
		amplitude *= 2.0;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) ((rnd.nextDouble() - 0.5) * amplitude);
			}
		}
	}

	/**
	 * Generate a salt and pepper noise.
	 * 
	 * @param amplitudeSalt
	 *            amplitude of the salt noise
	 * @param amplitudePepper
	 *            amplitude of the pepper noise
	 * @param percentageSalt
	 *            percentage of the salt noise
	 * @param percentagePepper
	 *            percentage of the pepper noise
	 */
	public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) {
		Random rnd = new Random();
		int index, z;
		if (percentageSalt > 0) {
			double nbSalt = nxy * nz / percentageSalt;
			for (int k = 0; k < nbSalt; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((float[]) data[z])[index] = (float) (rnd.nextDouble() * amplitudeSalt);
			}
		}
		if (percentagePepper > 0) {
			double nbPepper = nxy * nz / percentagePepper;
			for (int k = 0; k < nbPepper; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((float[]) data[z])[index] = (float) (-rnd.nextDouble() * amplitudeSalt);
			}
		}
	}

	/**
	 * Build an ImageStack of ImageJ.
	 */
	public ImageStack buildImageStack() {
		ImageStack imagestack = new ImageStack(nx, ny);
		for (int z = 0; z < nz; z++) {
			FloatProcessor ip = new FloatProcessor(nx, ny);
			float pix[] = (float[]) ip.getPixels();
			for (int k = 0; k < nxy; k++)
				pix[k] = (float) (((float[]) data[z])[k]);
			imagestack.addSlice("" + z, ip);
		}
		return imagestack;
	}

	/**
	 * Invert the pixel intensity.
	 */
	public void invert() {
		double max = -Double.MAX_VALUE;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > max)
					max = slice[k];
			}
		}
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) (max - ((double) (slice[k])));
			}
		}
	}

	/**
	 * Negate the pixel intensity.
	 */
	public void negate() {
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) (-((double) (slice[k])));
			}
		}
	}

	/**
	 * Clip the pixel intensity into [0..255].
	 */
	public void clip() {
		clip(0.0, 255.0);
	}

	/**
	 * Clip the pixel intensity into [minLevel..maxLevel].
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void clip(double minLevel, double maxLevel) {
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			float value;
			float min = (float) minLevel;
			float max = (float) maxLevel;
			for (int k = 0; k < nxy; k++) {
				value = (float) (slice[k]);
				if (value < min)
					slice[k] = min;
				if (value > max)
					slice[k] = max;
			}
		}
	}

	/**
	 * Rescale the pixel intensity into [0..255].
	 */
	public void rescale() {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > maxImage)
					maxImage = slice[k];
				if ((slice[k]) < minImage)
					minImage = slice[k];
			}
		}
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = 128.0;
		}
		else {
			a = 255.0 / (maxImage - minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) (a * (((double) (slice[k])) - minImage));
			}
		}
	}

	/**
	 * Rescale the pixel intensity into [minLevel..maxLevel].
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void rescale(double minLevel, double maxLevel) {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > maxImage)
					maxImage = slice[k];
				if ((slice[k]) < minImage)
					minImage = slice[k];
			}
		}
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = (maxLevel - minLevel) / 2.0;
		}
		else {
			a = (maxLevel - minLevel) / (maxImage - minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) (a * (((double) (slice[k])) - minImage) + minLevel);
			}
		}
	}

	/**
	 * Rescale the pixel intensity with a linear curve passing through
	 * (maxLevel-minLevel)/2 at the 0 input intensity.
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void rescaleCenter(double minLevel, double maxLevel) {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > maxImage)
					maxImage = slice[k];
				if ((slice[k]) < minImage)
					minImage = slice[k];
			}
		}
		double center = (maxLevel + minLevel) / 2.0;
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = (maxLevel - minLevel) / 2.0;
		}
		else {
			if (Math.abs(maxImage) > Math.abs(minImage))
				a = (maxLevel - center) / Math.abs(maxImage);
			else
				a = (center - minLevel) / Math.abs(minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) (a * (((double) (slice[k])) - minImage) + center);
			}
		}
	}

	/**
	 * Compute the absolute value of this imageware.
	 */
	public void abs() {
		float zero = (float) 0.0;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if (slice[k] < zero)
					slice[k] = -slice[k];
			}
		}
	}

	/**
	 * Compute the log of this imageware.
	 */
	public void log() {
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) Math.log(slice[k]);
			}
		}
	}

	/**
	 * Compute the exponential of this imageware.
	 */
	public void exp() {
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) Math.exp(slice[k]);
			}
		}
	}

	/**
	 * Compute the square root of this imageware.
	 */
	public void sqrt() {
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) Math.sqrt(slice[k]);
			}
		}
	}

	/**
	 * Compute the square of this imageware.
	 */
	public void sqr() {
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] *= slice[k];
			}
		}
	}

	/**
	 * Compute the power of a of this imageware.
	 * 
	 * @param a
	 *            exponent
	 */
	public void pow(double a) {
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (float) Math.pow(slice[k], a);
			}
		}
	}

	/**
	 * Add a constant value to this imageware.
	 */
	public void add(double constant) {
		float cst = (float) constant;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += cst;
			}
		}
	}

	/**
	 * Multiply a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void multiply(double constant) {
		float cst = (float) constant;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] *= cst;
			}
		}
	}

	/**
	 * Subtract a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void subtract(double constant) {
		float cst = (float) constant;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] -= cst;
			}
		}
	}

	/**
	 * Divide by a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void divide(double constant) {
		if (constant == 0.0)
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the constant is 0.\n"
					+ "-------------------------------------------------------\n");
		float cst = (float) constant;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] /= cst;
			}
		}
	}

	/**
	 * Threshold a imageware in two levels 0 and 255.
	 * 
	 * All the pixels values strictly greater than 'thresholdValue' and are set
	 * to 0. The remaining values are set to 255.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void threshold(double thresholdValue) {
		threshold(thresholdValue, 0.0, 255.0);
	}

	/**
	 * Threshold a imageware in two levels minLevel and maxLevel.
	 * 
	 * All the pixels values strictly greater than 'thresholdValue' and are set
	 * to maxLevel. The remaining values are set to minLevel.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 * @param minLevel
	 *            double value given the minimum level
	 * @param maxLevel
	 *            double value given the maximum level
	 */
	public void threshold(double thresholdValue, double minLevel, double maxLevel) {
		float low = (float) (minLevel);
		float high = (float) (maxLevel);
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = ((double) (slice[k]) > thresholdValue ? high : low);
			}
		}
	}

	/**
	 * Apply a soft thresholding.
	 * 
	 * All the pixels values strictly greater than '-thresholdValue' and stricty
	 * lower than 'thresholdValue' set to 0. The remaining positive values are
	 * reduced by 'thresholdvalue'; the remaining negative values are augmented
	 * by 'thresholdValue'.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void thresholdSoft(double thresholdValue) {
		float zero = (float) (0.0);
		double pixel;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				pixel = (double) (slice[k]);
				slice[k] = (pixel <= -thresholdValue ? (float) (pixel + thresholdValue) : (pixel > thresholdValue ? (float) (pixel - thresholdValue) : zero));
			}
		}
	}

	/**
	 * Apply a hard thresholding.
	 * 
	 * All the pixels values strictly greater than '-thresholdValue' and stricty
	 * lower than 'thresholdValue' are set to 0. The remaining values are
	 * unchanged.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void thresholdHard(double thresholdValue) {
		float zero = (float) (0.0);
		double pixel;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				pixel = (double) (slice[k]);
				if (pixel > -thresholdValue && pixel < thresholdValue)
					slice[k] = zero;
			}
		}
	}

	/**
	 * Add a gaussian noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void addGaussianNoise(double amplitude) {
		Random rnd = new Random();
		float[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += (float) ((rnd.nextGaussian()) * amplitude);
			}
		}
	}

	/**
	 * Add a uniform noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void addUniformNoise(double amplitude) {
		Random rnd = new Random();
		float[] slice = null;
		amplitude *= 2.0;
		for (int z = 0; z < nz; z++) {
			slice = (float[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += (float) ((rnd.nextDouble() - 0.5) * amplitude);
			}
		}
	}

	/**
	 * Add a salt and pepper noise.
	 * 
	 * @param amplitudeSalt
	 *            amplitude of the salt noise
	 * @param amplitudePepper
	 *            amplitude of the pepper noise
	 * @param percentageSalt
	 *            percentage of the salt noise
	 * @param percentagePepper
	 *            percentage of the pepper noise
	 */
	public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) {
		Random rnd = new Random();
		int index, z;
		if (percentageSalt > 0) {
			double nbSalt = nxy * nz / percentageSalt;
			for (int k = 0; k < nbSalt; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((float[]) data[z])[index] += (float) (rnd.nextDouble() * amplitudeSalt);
			}
		}
		if (percentagePepper > 0) {
			double nbPepper = nxy * nz / percentagePepper;
			for (int k = 0; k < nbPepper; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((float[]) data[z])[index] -= (float) (rnd.nextDouble() * amplitudeSalt);
			}
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/FloatProcess.java b/src/bilib/src/imageware/FloatProcess.java
new file mode 100644
index 0000000000000000000000000000000000000000..bfdf542e50351f7830972edf6ef7790d9924a2a4
--- /dev/null
+++ b/src/bilib/src/imageware/FloatProcess.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class FloatProcess.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class FloatProcess extends FloatPointwise implements Process {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected FloatProcess(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected FloatProcess(Image image, int mode) {
		super(image, mode);
	}

	protected FloatProcess(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected FloatProcess(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected FloatProcess(byte[] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(byte[][] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(short[] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(short[][] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(short[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(float[] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(float[][] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(float[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(double[] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(double[][] array, int mode) {
		super(array, mode);
	}

	protected FloatProcess(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Apply a separable gaussian smoothing over the image with the same
	 * strengthness in all directions. To have a smmothing effect the
	 * strengthness should be strictly greater than 0 and the size in the
	 * considered directions should be greater strictly than 1.
	 * 
	 * @param sigma
	 *            Strengthness of the smoothing
	 */
	public void smoothGaussian(double sigma) {
		smoothGaussian(sigma, sigma, sigma);
	}

	/**
	 * Apply a separablegaussian smoothing over the image with an independant
	 * strengthness in the different directions. To have a smmothing effect the
	 * strengthness should be strictly greater than 0 and the size in the
	 * considered directions should be greater strictly than 1.
	 * 
	 * @param sigmaX
	 *            Strengthness of the smoothing in X axis
	 * @param sigmaY
	 *            Strengthness of the smoothing in X axis
	 * @param sigmaZ
	 *            Strengthness of the smoothing in X axis
	 */
	public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ) {
		int n = 3;
		double N = (double) n;
		double poles[] = new double[n];

		if (nx > 1 && sigmaX > 0.0) {
			double s2 = sigmaX * sigmaX;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[nx];
			for (int z = 0; z < nz; z++) {
				for (int y = 0; y < ny; y++) {
					getX(0, y, z, line);
					putX(0, y, z, Convolver.convolveIIR(line, poles));
				}
			}
		}

		if (ny > 1 && sigmaY > 0.0) {
			double s2 = sigmaY * sigmaY;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[ny];
			for (int x = 0; x < nx; x++) {
				for (int z = 0; z < nz; z++) {
					getY(x, 0, z, line);
					putY(x, 0, z, Convolver.convolveIIR(line, poles));
				}
			}
		}

		if (nz > 1 && sigmaZ > 0.0) {
			double s2 = sigmaZ * sigmaZ;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[nz];
			for (int y = 0; y < ny; y++) {
				for (int x = 0; x < nx; x++) {
					getZ(x, y, 0, line);
					putZ(x, y, 0, Convolver.convolveIIR(line, poles));
				}
			}
		}
	}

	/**
	 * Get the maximum of this imageware and a imageware.
	 * 
	 * @param imageware
	 *            imageware to max
	 */
	public void max(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to get the maximum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					if (((float[]) data[z])[k] < (float) tmp[k])
						((float[]) data[z])[k] = (float) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					if (((float[]) data[z])[k] < (float) tmp[k])
						((float[]) data[z])[k] = (float) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					if (((float[]) data[z])[k] < (float) tmp[k])
						((float[]) data[z])[k] = (float) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					if (((float[]) data[z])[k] < (float) tmp[k])
						((float[]) data[z])[k] = (float) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Get the minimum of this imageware and a imageware.
	 * 
	 * @param imageware
	 *            imageware to min
	 */
	public void min(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to get the minimum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					if (((float[]) data[z])[k] > (float) tmp[k])
						((float[]) data[z])[k] = (float) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					if (((float[]) data[z])[k] > (float) tmp[k])
						((float[]) data[z])[k] = (float) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					if (((float[]) data[z])[k] > (float) tmp[k])
						((float[]) data[z])[k] = (float) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					if (((float[]) data[z])[k] > (float) tmp[k])
						((float[]) data[z])[k] = (float) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Add a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to add
	 */
	public void add(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to add because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] += (float) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] += (float) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] += (float) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] += (float) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Multiply a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to multiply
	 */
	public void multiply(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to multiply because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] *= (float) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] *= (float) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] *= (float) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] *= (float) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Subtract a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to subtract
	 */
	public void subtract(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to subtract because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] -= (float) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] -= (float) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] -= (float) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] -= (float) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Divide a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to divide
	 */
	public void divide(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to divide because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] /= (float) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] /= (float) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] /= (float) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((float[]) data[z])[k] /= (float) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/FloatSet.java b/src/bilib/src/imageware/FloatSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..706b92217f203b4e0a60f3bfc7ecb959cdd1f2c1
--- /dev/null
+++ b/src/bilib/src/imageware/FloatSet.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class FloatSet.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class FloatSet extends FloatProcess implements ImageWare {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected FloatSet(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected FloatSet(Image image, int mode) {
		super(image, mode);
	}

	protected FloatSet(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected FloatSet(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected FloatSet(byte[] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(byte[][] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(short[] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(short[][] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(short[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(float[] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(float[][] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(float[][][] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(double[] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(double[][] array, int mode) {
		super(array, mode);
	}

	protected FloatSet(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Duplicate the imageware.
	 * 
	 * Create a new imageware with the same size, same type and same data than
	 * the calling one.
	 * 
	 * @return a duplicated version of this imageware
	 */
	public ImageWare duplicate() {
		ImageWare out = new FloatSet(nx, ny, nz);
		float[] outdata;
		for (int z = 0; z < nz; z++) {
			outdata = (float[]) (((FloatSet) out).data[z]);
			System.arraycopy(data[z], 0, outdata, 0, nxy);
		}
		return out;
	}

	/**
	 * Replicate the imageware.
	 * 
	 * Create a new imageware with the same size, same type than the calling
	 * one. The data are not copied.
	 * 
	 * @return a replicated version of this imageware
	 */
	public ImageWare replicate() {
		return new FloatSet(nx, ny, nz);
	}

	/**
	 * Replicate the imageware.
	 * 
	 * Create a new imageware with the same size and a specified type than the
	 * calling one. The data are not copied.
	 * 
	 * @param type
	 *            requested type
	 * @return a replicated version of this imageware
	 */
	public ImageWare replicate(int type) {
		switch (type) {
		case ImageWare.BYTE:
			return new ByteSet(nx, ny, nz);
		case ImageWare.SHORT:
			return new ShortSet(nx, ny, nz);
		case ImageWare.FLOAT:
			return new FloatSet(nx, ny, nz);
		case ImageWare.DOUBLE:
			return new DoubleSet(nx, ny, nz);
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Copy all the data of source in the current imageware. The source should
	 * have the same size and same type than the calling one.
	 * 
	 * @param source
	 *            a source imageware
	 */
	public void copy(ImageWare source) {
		if (nx != source.getSizeX())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ nx + " != " + source.getSizeX() + ").\n" + "-------------------------------------------------------\n");
		if (ny != source.getSizeY())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ ny + " != " + source.getSizeY() + ").\n" + "-------------------------------------------------------\n");
		if (nz != source.getSizeZ())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ nz + " != " + source.getSizeZ() + ").\n" + "-------------------------------------------------------\n");
		if (getType() != source.getType())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same type ("
					+ getType() + " != " + source.getType() + ").\n" + "-------------------------------------------------------\n");
		float[] src;
		for (int z = 0; z < nz; z++) {
			src = (float[]) (((FloatSet) source).data[z]);
			System.arraycopy(src, 0, data[z], 0, nxy);
		}
	}

	/**
	 * convert the imageware in a specified type.
	 * 
	 * Create a new imageware with the same size and converted data than the
	 * calling one.
	 * 
	 * @param type
	 *            indicates the type of the output
	 * @return a converted version of this imageware
	 */
	public ImageWare convert(int type) {
		if (type == ImageWare.FLOAT)
			return duplicate();
		ImageWare out = null;
		switch (type) {
		case ImageWare.BYTE: {
			float[] slice;
			out = new ByteSet(nx, ny, nz);
			byte[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((float[]) data[z]);
				outslice = ((byte[]) ((ByteSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (byte) (slice[k]);
				}
			}
		}
			break;
		case ImageWare.SHORT: {
			float[] slice;
			out = new ShortSet(nx, ny, nz);
			short[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((float[]) data[z]);
				outslice = ((short[]) ((ShortSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (short) (slice[k]);
				}
			}
		}
			break;
		case ImageWare.FLOAT: {
			float[] slice;
			out = new FloatSet(nx, ny, nz);
			float[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((float[]) data[z]);
				outslice = ((float[]) ((FloatSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (float) (slice[k]);
				}
			}
		}
			break;
		case ImageWare.DOUBLE: {
			float[] slice;
			out = new DoubleSet(nx, ny, nz);
			double[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((float[]) data[z]);
				outslice = ((double[]) ((DoubleSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (double) (slice[k]);
				}
			}
		}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n"
					+ "-------------------------------------------------------\n");
		}
		return out;
	}

	/**
	 * Print information of this ImageWare object.
	 */
	public void printInfo() {
		System.out.println("ImageWare object information");
		System.out.println("Dimension: " + getDimension());
		System.out.println("Size: [" + nx + ", " + ny + ", " + nz + "]");
		System.out.println("TotalSize: " + getTotalSize());
		System.out.println("Type: " + getTypeToString());
		System.out.println("Maximun: " + getMaximum());
		System.out.println("Minimun: " + getMinimum());
		System.out.println("Mean: " + getMean());
		System.out.println("Norm1: " + getNorm1());
		System.out.println("Norm2: " + getNorm2());
		System.out.println("Total: " + getTotal());
		System.out.println("");
	}

	/**
	 * Show this ImageWare object.
	 */
	public void show() {
		String title = getTypeToString();
		switch (getDimension()) {
		case 1:
			title += " line";
			break;
		case 2:
			title += " image";
			break;
		case 3:
			title += " volume";
			break;
		}
		Display.show(title, this);
		// ImagePlus imp = new ImagePlus(title, buildImageStack());
		// imp.show();
	}

	/**
	 * Show the data in ImagePlus object with a specify title.
	 * 
	 * @param title
	 *            a string given the title of the window
	 */
	public void show(String title) {
		Display.show(title, this);
		// ImagePlus imp = new ImagePlus(title, buildImageStack());
		// imp.show();
	}

	/**
	 * Return the minimum value of this imageware.
	 * 
	 * @return the min value of this imageware
	 */
	public double getMinimum() {
		double min = Double.MAX_VALUE;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((float[]) data[z]);
			for (int k = 0; k < nxy; k++)
				if ((slice[k]) < min)
					min = slice[k];
		}
		return min;
	}

	/**
	 * Return the maximum value of this imageware.
	 * 
	 * @return the max value of this imageware
	 */
	public double getMaximum() {
		double max = -Double.MAX_VALUE;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((float[]) data[z]);
			for (int k = 0; k < nxy; k++)
				if ((slice[k]) > max)
					max = slice[k];
		}
		return max;
	}

	/**
	 * Return the mean value of this imageware.
	 * 
	 * @return the mean value of this imageware
	 */
	public double getMean() {
		return getTotal() / (nz * nxy);
	}

	/**
	 * Return the norm value of order 1.
	 * 
	 * @return the norm value of this imageware in L1 sense
	 */
	public double getNorm1() {
		double norm = 0.0;
		double value = 0;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((float[]) data[z]);
			for (int k = 0; k < nxy; k++) {
				value = (double) (slice[k]);
				norm += (value > 0.0 ? value : -value);
			}
		}
		return norm;
	}

	/**
	 * Return the norm value of order 2.
	 * 
	 * @return the norm value of this imageware in L2 sense
	 */
	public double getNorm2() {
		double norm = 0.0;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((float[]) data[z]);
			for (int k = 0; k < nxy; k++)
				norm += (slice[k]) * (slice[k]);
		}
		return norm;
	}

	/**
	 * Return the sum of all pixel in this imageware.
	 * 
	 * @return the total sum of all pixel in this imageware
	 */
	public double getTotal() {
		double total = 0.0;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((float[]) data[z]);
			for (int k = 0; k < nxy; k++)
				total += slice[k];
		}
		return total;
	}

	/**
	 * Return the the minumum [0] and the maximum [1] value of this imageware.
	 * Faster routine than call one getMinimum() and then one getMaximum().
	 * 
	 * @return an array of two values, the min and the max values of the images
	 */
	public double[] getMinMax() {
		double max = -Double.MAX_VALUE;
		double min = Double.MAX_VALUE;
		float[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((float[]) data[z]);
			for (int k = 0; k < nxy; k++) {
				if ((slice[k]) > max)
					max = slice[k];
				if ((slice[k]) < min)
					min = slice[k];
			}
		}
		double minmax[] = { min, max };
		return minmax;
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/ImageAccess.java b/src/bilib/src/imageware/ImageAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a3222786a13367dde412fad6cf284168a2b9d75
--- /dev/null
+++ b/src/bilib/src/imageware/ImageAccess.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImagePlus;
import ij.process.ByteProcessor;
import ij.process.ColorProcessor;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;

/**
 * ImageAccess is an interface layer to facilitate the access to the pixels of
 * ImageJ images. Methods of ImageAccess provides an easy and robust way to
 * access to the pixels of images.
 * 
 * The data are stored in an double array. Many methods get/put allows to access
 * to the data. If the user try to access outside of the image, the mirror
 * boundary conditions are applied.
 * 
 * This version of ImageAccess is based on the imageware library.
 * 
 * @author Daniel Sage Biomedical Imaging Group Swiss Federal Institute of
 *         Technology Lausanne EPFL, CH-1015 Lausanne, Switzerland
 */

public class ImageAccess extends Object {

	public static final int	PATTERN_SQUARE_3x3	= 0;
	public static final int	PATTERN_CROSS_3x3	= 1;

	private ImageWare		imageware			= null;
	private int				nx					= 0;
	private int				ny					= 0;

	/**
	 * Creates a new ImageAccess object from a 2D double array of pixels. The
	 * size of the array determines the size of the image.
	 * 
	 * @param array
	 *            an array of pixel (2D)
	 */
	public ImageAccess(double[][] array) {
		if (array == null)
			throw new ArrayStoreException("Constructor: array == null.");
		imageware = Builder.create(array);
		this.nx = imageware.getSizeX();
		this.ny = imageware.getSizeY();
	}

	/**
	 * Creates a new object of the class ImageAccess from an ImageProcessor
	 * object.
	 * 
	 * ImageProcessor object contains the image data, the size and the type of
	 * the image. The ImageProcessor is provided by ImageJ, it should by a
	 * 8-bit, 16-bit.
	 * 
	 * @param ip
	 *            an ImageProcessor object provided by ImageJ
	 */
	public ImageAccess(ImageProcessor ip) {
		if (ip == null)
			throw new ArrayStoreException("Constructor: ImageProcessor == null.");
		ImagePlus imp = new ImagePlus("", ip);
		if (ip instanceof ByteProcessor)
			imageware = Builder.create(imp, ImageWare.DOUBLE);
		else if (ip instanceof ShortProcessor)
			imageware = Builder.create(imp, ImageWare.DOUBLE);
		else if (ip instanceof FloatProcessor)
			imageware = Builder.create(imp, ImageWare.DOUBLE);
		this.nx = imageware.getSizeX();
		this.ny = imageware.getSizeY();
	}

	/**
	 * Creates a new object of the class ImageAccess from an ColorProcessor
	 * object.
	 * 
	 * ImageProcessor object contains the image data, the size and the type of
	 * the image. The ColorProcessor is provided by ImageJ, The ImageAccess
	 * contains one plane (red, green or blue) selected with the colorPlane
	 * parameter.
	 * 
	 * @param cp
	 *            an ColorProcessor object
	 * @param colorPlane
	 *            index of the color plane 0, 1 or 2
	 */
	public ImageAccess(ColorProcessor cp, int colorPlane) {
		if (cp == null)
			throw new ArrayStoreException("Constructor: ColorProcessor == null.");
		if (colorPlane < 0)
			throw new ArrayStoreException("Constructor: colorPlane < 0.");
		if (colorPlane > 2)
			throw new ArrayStoreException("Constructor: colorPlane > 2.");
		this.nx = cp.getWidth();
		this.ny = cp.getHeight();
		ImagePlus imp = new ImagePlus("", cp);
		imageware = new DoubleSet(imp.getStack(), (byte) colorPlane);
	}

	/**
	 * Creates a new object of the class tImageAccess.
	 * 
	 * The size of the image are given as parameter. The data pixels are empty
	 * and are not initialized.
	 * 
	 * @param nx
	 *            the size of the image along the X-axis
	 * @param ny
	 *            the size of the image along the Y-axis
	 */
	public ImageAccess(int nx, int ny) {
		imageware = new DoubleSet(nx, ny, 1);
		this.nx = nx;
		this.ny = ny;
	}

	/**
	 * Return the imageware of the image.
	 * 
	 * @return the imageware object
	 */
	public ImageWare getDataset() {
		return imageware;
	}

	/**
	 * Return the width of the image.
	 * 
	 * @return the image width
	 */
	public int getWidth() {
		return nx;
	}

	/**
	 * Return the height of the image.
	 * 
	 * @return the image height
	 */
	public int getHeight() {
		return ny;
	}

	/**
	 * Return the maximum value of ImageAccess.
	 * 
	 * @return the maximum value
	 */
	public double getMaximum() {
		return imageware.getMaximum();
	}

	/**
	 * Return the minimum value of ImageAccess.
	 * 
	 * @return the minimum value
	 */
	public double getMinimum() {
		return imageware.getMinimum();
	}

	/**
	 * Return the mean value of ImageAccess.
	 * 
	 * @return the mean value
	 */
	public double getMean() {
		return imageware.getMean();
	}

	/**
	 * Returns a copy of the pixel data organize in a 2D array.
	 * 
	 * @return the 2D double array
	 */
	public double[][] getArrayPixels() {
		double[][] array = new double[nx][ny];
		imageware.getXY(0, 0, 0, array);
		return array;
	}

	/**
	 * Returns a reference to the pixel data in double (1D).
	 * 
	 * @return the 1D double array
	 */
	public double[] getPixels() {
		return imageware.getSliceDouble(0);
	}

	/**
	 * Create a FloatProcessor from the pixel data.
	 * 
	 * @return the FloatProcessor
	 */
	public FloatProcessor createFloatProcessor() {
		FloatProcessor fp = new FloatProcessor(nx, ny);
		double[] pixels = getPixels();
		int size = pixels.length;
		float[] fsrc = new float[size];
		for (int k = 0; k < size; k++)
			fsrc[k] = (float) (pixels[k]);
		fp.setPixels(fsrc);
		return fp;
	}

	/**
	 * Create a ByteProcessor from the pixel data.
	 * 
	 * @return the ByteProcessor
	 */
	public ByteProcessor createByteProcessor() {
		ByteProcessor bp = new ByteProcessor(nx, ny);
		double[] pixels = getPixels();
		int size = pixels.length;
		byte[] bsrc = new byte[size];
		double p;
		for (int k = 0; k < size; k++) {
			p = pixels[k];
			if (p < 0)
				p = 0.0;
			if (p > 255.0)
				p = 255.0;
			bsrc[k] = (byte) p;
		}
		bp.setPixels(bsrc);
		return bp;
	}

	/**
	 * Create a new ImageAccess object by duplication of the current the
	 * ImageAccess object.
	 * 
	 * @return a new ImageAccess object
	 **/
	public ImageAccess duplicate() {
		double[][] array = new double[nx][ny];
		imageware.getXY(0, 0, 0, array);
		ImageAccess ia = new ImageAccess(array);
		return ia;
	}

	/**
	 * An ImageAccess object calls this method for getting the gray level of a
	 * selected pixel.
	 * 
	 * Mirror border conditions are applied.
	 * 
	 * @param x
	 *            input, the integer x-coordinate of a pixel
	 * @param y
	 *            input, the integer y-coordinate of a pixel
	 * @return the gray level of the pixel (double)
	 */
	public double getPixel(int x, int y) {
		return imageware.getPixel(x, y, 0, ImageWare.MIRROR);
	}

	/**
	 * An ImageAccess object calls this method for getting the gray level of a
	 * selected pixel using a bilinear interpolation. The coordinates can be
	 * given in double and the bilinear interpolation is applied the find the
	 * gray level.
	 * 
	 * Mirror border conditions are applied.
	 * 
	 * @param x
	 *            input, the double x-coordinate of a pixel
	 * @param y
	 *            input, the double y-coordinate of a pixel
	 * @return the gray level of the pixel (double)
	 */
	public double getInterpolatedPixel(double x, double y) {
		return imageware.getInterpolatedPixel(x, y, 0, ImageWare.MIRROR);
	}

	/**
	 * An ImageAccess object calls this method for getting a whole column of the
	 * image.
	 * 
	 * The column should already created with the correct size [ny].
	 * 
	 * @param x
	 *            input, the integer x-coordinate of a column
	 * @param column
	 *            output, an array of the type double
	 */
	public void getColumn(int x, double[] column) {
		if (x < 0)
			throw new IndexOutOfBoundsException("getColumn: x < 0.");
		if (x >= nx)
			throw new IndexOutOfBoundsException("getColumn: x >= nx.");
		if (column == null)
			throw new ArrayStoreException("getColumn: column == null.");

		if (column.length != ny)
			throw new ArrayStoreException("getColumn: column.length != ny.");
		imageware.getBlockY(x, 0, 0, column, ImageWare.MIRROR);
	}

	/**
	 * An ImageAccess object calls this method for getting a part of column. The
	 * starting point is given by the y parameter and the ending determine by
	 * the size of the column parameter. The column parameter should already
	 * created.
	 * 
	 * @param x
	 *            input, the integer x-coordinate of a column
	 * @param y
	 *            input, starting point
	 * @param column
	 *            output, an array of the type double
	 */
	public void getColumn(int x, int y, double[] column) {
		if (x < 0)
			throw new IndexOutOfBoundsException("getColumn: x < 0.");
		if (x >= nx)
			throw new IndexOutOfBoundsException("getColumn: x >= nx.");
		if (column == null)
			throw new ArrayStoreException("getColumn: column == null.");
		imageware.getBlockY(x, y, 0, column, ImageWare.MIRROR);
	}

	/**
	 * An ImageAccess object calls this method for getting a whole row of the
	 * image.
	 * 
	 * The row should already created with the correct size [nx].
	 * 
	 * @param y
	 *            input, the integer y-coordinate of a row
	 * @param row
	 *            output, an array of the type double
	 */
	public void getRow(int y, double[] row) {
		if (y < 0)
			throw new IndexOutOfBoundsException("getRow: y < 0.");
		if (y >= ny)
			throw new IndexOutOfBoundsException("getRow: y >= ny.");
		if (row == null)
			throw new ArrayStoreException("getColumn: row == null.");
		if (row.length != nx)
			throw new ArrayStoreException("getColumn: row.length != nx.");
		imageware.getBlockX(0, y, 0, row, ImageWare.MIRROR);
	}

	/**
	 * An ImageAccess object calls this method for getting a part of row. The
	 * starting point is given by the y parameter and the ending determine by
	 * the size of the row parameter. The row parameter should already created.
	 * 
	 * @param x
	 *            input, starting point
	 * @param y
	 *            input, the integer y-coordinate of a row
	 * @param row
	 *            output, an array of the type double
	 */
	public void getRow(int x, int y, double[] row) {
		if (y < 0)
			throw new IndexOutOfBoundsException("getRow: y < 0.");
		if (y >= ny)
			throw new IndexOutOfBoundsException("getRow: y >= ny.");
		if (row == null)
			throw new ArrayStoreException("getRow: row == null.");
		imageware.getBlockX(x, y, 0, row, ImageWare.MIRROR);
	}

	/**
	 * An ImageAccess object calls this method for getting a neighborhood
	 * arround a pixel position.
	 * 
	 * The neigh parameter should already created. The size of the array
	 * determines the neighborhood block.
	 * 
	 * <br>
	 * Mirror border conditions are applied. <br>
	 * <br>
	 * The pixel value of (x-n/2, y-n/2) is put into neigh[0][0] <br>
	 * ... <br>
	 * The pixel value of (x+n/2, y+n/2) is put into neigh[n-1][n-1] <br>
	 * <br>
	 * For example if neigh is a double[4][4]: <br>
	 * The pixel value of (x-1, y-1) is put into neigh[0][0] <br>
	 * The pixel value of (x , y ) is put into neigh[1][1] <br>
	 * The pixel value of (x+1, y+1) is put into neigh[2][2] <br>
	 * The pixel value of (x+2, y+2) is put into neigh[3][3] <br>
	 * ... <br>
	 * For example if neigh is a double[5][5]: <br>
	 * The pixel value of (x-2, y-2) is put into neigh[0][0] <br>
	 * The pixel value of (x-1, y-1) is put into neigh[1][1] <br>
	 * The pixel value of (x , y ) is put into neigh[2][2] <br>
	 * The pixel value of (x+1, y+1) is put into neigh[3][3] <br>
	 * The pixel value of (x+2, y+2) is put into neigh[4][4]
	 * 
	 * @param x
	 *            the integer x-coordinate of a selected central pixel
	 * @param y
	 *            the integer y-coordinate of a selected central pixel
	 * @param neigh
	 *            output, a 2D array s
	 */
	public void getNeighborhood(int x, int y, double neigh[][]) {
		imageware.getNeighborhoodXY(x, y, 0, neigh, ImageWare.MIRROR);
	}

	/**
	 * An ImageAccess object calls this method for getting a neighborhood of a
	 * predefined pattern around a selected pixel (x,y). <br>
	 * The available patterns are: <br>
	 * - a 3*3 block: PATTERN_SQUARE_3x3 (8-connect) <br>
	 * - a 3*3 cross: PATTERN_CROSS_3x3 (4-connect) <br>
	 * <br>
	 * Mirror border conditions are applied. <br>
	 * The pixel is arranged in a 1D array according the following rules: <br>
	 * <br>
	 * If the pattern is PATTERN_SQUARE_3x3 (8-connect) <br>
	 * The pixel value of (x-1, y-1) are put into neigh[0] <br>
	 * The pixel value of (x , y-1) are put into neigh[1] <br>
	 * The pixel value of (x+1, y-1) are put into neigh[2] <br>
	 * The pixel value of (x-1, y ) are put into neigh[3] <br>
	 * The pixel value of (x , y ) are put into neigh[4] <br>
	 * The pixel value of (x+1, y ) are put into neigh[5] <br>
	 * The pixel value of (x-1, y+1) are put into neigh[6] <br>
	 * The pixel value of (x , y+1) are put into neigh[7] <br>
	 * The pixel value of (x+1, y+1) are put into neigh[8] <br>
	 * <br>
	 * If the pattern is PATTERN_CROSS_3x3 (4-connect) <br>
	 * The pixel value of (x , y-1) are put into neigh[0] <br>
	 * The pixel value of (x-1, y ) are put into neigh[1] <br>
	 * The pixel value of (x , y ) are put into neigh[2] <br>
	 * The pixel value of (x+1, y ) are put into neigh[3] <br>
	 * The pixel value of (x , y+1) are put into neigh[4] <br>
	 * <br>
	 * The neigh should already created as a double array of 9 elements for the
	 * PATTERN_SQUARE_3x3 or 5 elements for the PATTERN_CROSS_3x3.
	 * 
	 * @param x
	 *            x-coordinate of a selected central pixel
	 * @param y
	 *            y-coordinate of a selected central pixel
	 * @param neigh
	 *            output, an array consisting of 9 or 5 elements
	 * @param pattern
	 *            PATTERN_SQUARE_3x3 or PATTERN_CROSS_3x3.
	 */
	public void getPattern(int x, int y, double neigh[], int pattern) {
		if (neigh == null)
			throw new ArrayStoreException("getPattern: neigh == null.");

		double block[][] = new double[3][3];
		imageware.getNeighborhoodXY(x, y, 0, block, ImageWare.MIRROR);

		switch (pattern) {
		case PATTERN_SQUARE_3x3:
			if (neigh.length != 9)
				throw new ArrayStoreException("getPattern: neigh.length != 9.");
			neigh[0] = block[0][0];
			neigh[1] = block[1][0];
			neigh[2] = block[2][0];
			neigh[3] = block[0][1];
			neigh[4] = block[1][1];
			neigh[5] = block[2][1];
			neigh[6] = block[0][2];
			neigh[7] = block[1][2];
			neigh[8] = block[2][2];
			break;

		case PATTERN_CROSS_3x3:
			if (neigh.length != 5)
				throw new ArrayStoreException("getPattern: neigh.length != 5");
			neigh[0] = block[1][0];
			neigh[1] = block[0][1];
			neigh[2] = block[1][1];
			neigh[3] = block[2][1];
			neigh[4] = block[0][1];
			break;

		default:
			throw new ArrayStoreException("getPattern: unexpected pattern.");
		}
	}

	/**
	 * An ImageAccess object calls this method to get a sub-image with the upper
	 * left corner in the coordinate (x,y).
	 * 
	 * The sub-image ouptut should be already created.
	 * 
	 * @param x
	 *            x-coordinate in the source image
	 * @param y
	 *            y-coordinate in the source image
	 * @param output
	 *            an ImageAccess object with the sub-image;
	 */
	public void getSubImage(int x, int y, ImageAccess output) {
		if (output == null)
			throw new ArrayStoreException("getSubImage: output == null.");
		if (x < 0)
			throw new ArrayStoreException("getSubImage: Incompatible image size");
		if (y < 0)
			throw new ArrayStoreException("getSubImage: Incompatible image size");
		if (x >= nx)
			throw new ArrayStoreException("getSubImage: Incompatible image size");
		if (y >= ny)
			throw new ArrayStoreException("getSubImage: Incompatible image size");
		int nxcopy = output.getWidth();
		int nycopy = output.getHeight();
		double[][] neigh = new double[nxcopy][nycopy];
		imageware.getBlockXY(x, y, 0, neigh, ImageWare.MIRROR);
		output.putArrayPixels(neigh);
	}

	/**
	 * An ImageAccess object calls this method in order a value of the gray
	 * level to be put to a position inside it given by the coordinates.
	 * 
	 * @param x
	 *            input, the integer x-coordinate of a pixel
	 * @param y
	 *            input, the integer y-coordinate of a pixel
	 * @param value
	 *            input, a value of the gray level of the type double
	 */
	public void putPixel(int x, int y, double value) {
		if (x < 0)
			throw new IndexOutOfBoundsException("putPixel: x < 0");
		if (x >= nx)
			throw new IndexOutOfBoundsException("putPixel: x >= nx");
		if (y < 0)
			throw new IndexOutOfBoundsException("putPixel:  y < 0");
		if (y >= ny)
			throw new IndexOutOfBoundsException("putPixel:  y >= ny");
		imageware.putPixel(x, y, 0, value);
	}

	/**
	 * An ImageAccess object calls this method to put a whole column in a
	 * specified position into the image.
	 * 
	 * @param x
	 *            input, the integer x-coordinate of a column
	 * @param column
	 *            input, an array of the type double
	 */
	public void putColumn(int x, double[] column) {
		if (x < 0)
			throw new IndexOutOfBoundsException("putColumn: x < 0.");
		if (x >= nx)
			throw new IndexOutOfBoundsException("putColumn: x >= nx.");
		if (column == null)
			throw new ArrayStoreException("putColumn: column == null.");
		if (column.length != ny)
			throw new ArrayStoreException("putColumn: column.length != ny.");
		imageware.putBoundedY(x, 0, 0, column);
	}

	/**
	 * An ImageAccess object calls this method to put a part of column into the
	 * image. The starting poisition in given by y and the ending position is
	 * determined by the size of the column array.
	 * 
	 * @param x
	 *            input, the integer x-coordinate of a column
	 * @param y
	 *            input, the integer y-coordinate of a column
	 * @param column
	 *            input, an array of the type double
	 */
	public void putColumn(int x, int y, double[] column) {
		if (x < 0)
			throw new IndexOutOfBoundsException("putColumn: x < 0.");
		if (x >= nx)
			throw new IndexOutOfBoundsException("putColumn: x >= nx.");
		if (column == null)
			throw new ArrayStoreException("putColumn: column == null.");
		imageware.putBoundedY(x, y, 0, column);
	}

	/**
	 * An ImageAccess object calls this method to put a whole row in a specified
	 * position into the image.
	 * 
	 * @param y
	 *            input, the integer x-coordinate of a column
	 * @param row
	 *            input, an array of the type double
	 */
	public void putRow(int y, double[] row) {
		if (y < 0)
			throw new IndexOutOfBoundsException("putRow: y < 0.");
		if (y >= ny)
			throw new IndexOutOfBoundsException("putRow: y >= ny.");
		if (row == null)
			throw new ArrayStoreException("putRow: row == null.");
		if (row.length != nx)
			throw new ArrayStoreException("putRow: row.length != nx.");
		imageware.putBoundedX(0, y, 0, row);
	}

	/**
	 * An ImageAccess object calls this method to put a part of row into the
	 * image. The starting poisition in given by x and the ending position is
	 * determined by the size of the row array.
	 * 
	 * @param x
	 *            input, the integer x-coordinate of a column
	 * @param y
	 *            input, the integer y-coordinate of a column
	 * @param row
	 *            input, an array of the type double
	 */
	public void putRow(int x, int y, double[] row) {
		if (y < 0)
			throw new IndexOutOfBoundsException("putRow: y < 0.");
		if (y >= ny)
			throw new IndexOutOfBoundsException("putRow: y >= ny.");
		if (row == null)
			throw new ArrayStoreException("putRow: row == null.");
		imageware.putBoundedX(x, y, 0, row);
	}

	/**
	 * An ImageAccess object calls this method in order to put an 2D array of
	 * double in an ImageAccess.
	 * 
	 * @param array
	 *            input, the double array
	 */
	public void putArrayPixels(double[][] array) {
		if (array == null)
			throw new IndexOutOfBoundsException("putArrayPixels: array == null.");
		imageware.putBoundedXY(0, 0, 0, array);
	}

	/**
	 * An ImageAccess object calls this method to put a sub-image with the upper
	 * left corner in the coordinate (x,y).
	 * 
	 * The sub-image input should be already created.
	 * 
	 * @param x
	 *            x-coordinate in the source image
	 * @param y
	 *            y-coordinate in the source image
	 * @param input
	 *            an ImageAccess object that we want to put;
	 */
	public void putSubImage(int x, int y, ImageAccess input) {
		if (input == null)
			throw new ArrayStoreException("putSubImage: input == null.");
		if (x < 0)
			throw new IndexOutOfBoundsException("putSubImage: x < 0.");
		if (y < 0)
			throw new IndexOutOfBoundsException("putSubImage: y < 0.");
		if (x >= nx)
			throw new IndexOutOfBoundsException("putSubImage: x >= nx.");
		if (y >= ny)
			throw new IndexOutOfBoundsException("putSubImage: y >= ny.");

		double[][] sub = input.getArrayPixels();
		imageware.putBoundedXY(x, y, 0, sub);
	}

	/**
	 * An ImageAccess object calls this method to set a constant value to all
	 * pixels of the image.
	 * 
	 * @param constant
	 *            a constant value
	 */
	public void setConstant(double constant) {
		imageware.fillConstant(constant);
	}

	/**
	 * Stretches the contrast inside an image so that the gray levels are in the
	 * range 0 to 255.
	 */
	public void normalizeContrast() {
		imageware.rescale();
	}

	/**
	 * Display an image at a specific position (x, y).
	 * 
	 * @param title
	 *            a string for the title
	 * @param loc
	 *            Point for the location
	 */
	public void show(String title, java.awt.Point loc) {
		FloatProcessor fp = createFloatProcessor();
		fp.resetMinAndMax();
		ImagePlus impResult = new ImagePlus(title, fp);
		ij.gui.ImageWindow window = impResult.getWindow();
		window.setLocation(loc.x, loc.y);
		impResult.show();
	}

	/**
	 * Display an image.
	 * 
	 * @param title
	 *            a string for the title of the window
	 */
	public void show(String title) {
		imageware.show(title);
	}

	/**
	 * Compute the absolute value.
	 */
	public void abs() {
		imageware.abs();
	}

	/**
	 * Compute the square root of an ImageAccess.
	 */
	public void sqrt() {
		imageware.sqrt();
	}

	/**
	 * Raised an ImageAccess object to the power a.
	 * 
	 * @param a
	 *            input
	 */
	public void pow(final double a) {
		imageware.pow(a);
	}

	/**
	 * An ImageAccess object calls this method for adding a constant to each
	 * pixel.
	 * 
	 * @param constant
	 *            a constant to be added
	 */
	public void add(double constant) {
		imageware.add(constant);
	}

	/**
	 * An ImageAccess object calls this method for multiplying a constant to
	 * each pixel.
	 * 
	 * @param constant
	 *            a constant to be multiplied
	 */
	public void multiply(final double constant) {
		imageware.multiply(constant);
	}

	/**
	 * An ImageAccess object calls this method for adding a constant to each
	 * pixel.
	 * 
	 * @param constant
	 *            a constant to be added
	 */
	public void subtract(final double constant) {
		imageware.add(-constant);
	}

	/**
	 * An ImageAccess object calls this method for dividing a constant to each
	 * pixel.
	 * 
	 * @param constant
	 *            a constant to be multiplied
	 */
	public void divide(final double constant) {
		if (constant == 0.0)
			throw new ArrayStoreException("divide: Divide by 0");
		imageware.multiply(1.0 / constant);
	}

	/**
	 * An ImageAccess object calls this method for adding two ImageAccess
	 * objects.
	 * 
	 * [this = im1 + im2]
	 * 
	 * The resulting ImageAccess and the two operands should have the same size.
	 * 
	 * @param im1
	 *            an ImageAccess object to be added
	 * @param im2
	 *            an ImageAccess object to be added
	 */
	public void add(ImageAccess im1, ImageAccess im2) {
		if (im1.getWidth() != nx)
			throw new ArrayStoreException("add: incompatible size.");

		if (im1.getHeight() != ny)
			throw new ArrayStoreException("add: incompatible size.");

		if (im2.getWidth() != nx)
			throw new ArrayStoreException("add: incompatible size.");

		if (im2.getHeight() != ny)
			throw new ArrayStoreException("add: incompatible size.");
		imageware.copy(im1.getDataset());
		imageware.add(im2.getDataset());
	}

	/**
	 * An ImageAccess object calls this method for multiplying two ImageAccess
	 * objects.
	 * 
	 * The resulting ImageAccess and the two operands should have the same size.
	 * 
	 * [this = im1 * im2]
	 * 
	 * @param im1
	 *            an ImageAccess object to be multiplied
	 * @param im2
	 *            an ImageAccess object to be multiplied
	 */
	public void multiply(ImageAccess im1, ImageAccess im2) {
		if (im1.getWidth() != nx)
			throw new ArrayStoreException("multiply: incompatible size.");

		if (im1.getHeight() != ny)
			throw new ArrayStoreException("multiply: incompatible size.");

		if (im2.getWidth() != nx)
			throw new ArrayStoreException("multiply: incompatible size.");

		if (im2.getHeight() != ny)
			throw new ArrayStoreException("multiply: incompatible size.");

		imageware.copy(im1.getDataset());
		imageware.multiply(im2.getDataset());
	}

	/**
	 * An ImageAccess object calls this method for subtracting two ImageAccess
	 * objects.
	 * 
	 * The resulting ImageAccess and the two operands should have the same size.
	 * 
	 * [this = im1 - im2]
	 * 
	 * @param im1
	 *            an ImageAccess object to be subtracted
	 * @param im2
	 *            an ImageAccess object to be subtracted
	 */
	public void subtract(ImageAccess im1, ImageAccess im2) {
		if (im1.getWidth() != nx)
			throw new ArrayStoreException("subtract: incompatible size.");

		if (im1.getHeight() != ny)
			throw new ArrayStoreException("subtract: incompatible size.");

		if (im2.getWidth() != nx)
			throw new ArrayStoreException("subtract: incompatible size.");

		if (im2.getHeight() != ny)
			throw new ArrayStoreException("subtract: incompatible size.");

		imageware.copy(im1.getDataset());
		imageware.subtract(im2.getDataset());
	}

	/**
	 * An ImageAccess object calls this method for dividing two ImageAccess
	 * objects.
	 * 
	 * [this = im1 / im2]
	 * 
	 * The resulting ImageAccess and the two operands should have the same size.
	 * 
	 * @param im1
	 *            numerator
	 * @param im2
	 *            denominator
	 */
	public void divide(ImageAccess im1, ImageAccess im2) {
		if (im1.getWidth() != nx)
			throw new ArrayStoreException("divide: incompatible size.");

		if (im1.getHeight() != ny)
			throw new ArrayStoreException("divide: incompatible size.");

		if (im2.getWidth() != nx)
			throw new ArrayStoreException("divide: incompatible size.");

		if (im2.getHeight() != ny)
			throw new ArrayStoreException("divide: incompatible size.");

		imageware.copy(im1.getDataset());
		imageware.divide(im2.getDataset());
	}

} // end of class ImageAccess

\ No newline at end of file
diff --git a/src/bilib/src/imageware/ImageWare.java b/src/bilib/src/imageware/ImageWare.java
new file mode 100644
index 0000000000000000000000000000000000000000..885f8ced701b8b49d6f136480ea9589743856040
--- /dev/null
+++ b/src/bilib/src/imageware/ImageWare.java
@@ -0,0 +1,62 @@
+package imageware;
+
+/**
+ * Class ImageWare.
+ * 
+ * 
+ * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
+ *         Lausanne, Lausanne, Switzerland
+ */
+
+public interface ImageWare extends Process {
+
+	public static final int		UNDEFINED_TYPE		= 0;
+	public static final int		BYTE				= 1;
+	public static final int		SHORT				= 2;
+	public static final int		FLOAT				= 3;
+	public static final int		DOUBLE				= 4;
+
+	public static final byte	UNDEFINED_BOUNDARY	= 0;
+	public static final byte	NONE				= 1;
+	public static final byte	MIRROR				= 2;
+	public static final byte	PERIODIC			= 3;
+
+	public static final int		UNDEFINED			= 0;
+	public static final int		CREATE				= 1;
+	public static final int		WRAP				= 2;
+
+	public static final byte	RED					= 0;
+	public static final byte	GREEN				= 1;
+	public static final byte	BLUE				= 2;
+
+	public ImageWare duplicate();
+
+	public ImageWare replicate();
+
+	public ImageWare replicate(int type);
+
+	public void copy(ImageWare source);
+
+	public ImageWare convert(int type);
+
+	public void printInfo();
+
+	public void show();
+
+	public void show(String title);
+
+	public double getMinimum();
+
+	public double getMaximum();
+
+	public double getMean();
+
+	public double getNorm1();
+
+	public double getNorm2();
+
+	public double getTotal();
+
+	public double[] getMinMax();
+
+}
diff --git a/src/bilib/src/imageware/Pointwise.java b/src/bilib/src/imageware/Pointwise.java
new file mode 100644
index 0000000000000000000000000000000000000000..60c7698de433045f4c97a35e3caafdac632e37cf
--- /dev/null
+++ b/src/bilib/src/imageware/Pointwise.java
@@ -0,0 +1,72 @@
+package imageware;
+
+import ij.ImageStack;
+
+/**
+ * Class Pointwise.
+ * 
+ * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
+ *         Lausanne, Lausanne, Switzerland
+ */
+
+public interface Pointwise extends Access {
+	public void fillConstant(double value);
+
+	public void fillRamp();
+
+	public void fillGaussianNoise(double amplitude);
+
+	public void fillUniformNoise(double amplitude);
+
+	public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper);
+
+	public ImageStack buildImageStack();
+
+	public void invert();
+
+	public void negate();
+
+	public void rescale();
+
+	public void clip();
+
+	public void clip(double minLevel, double maxLevel);
+
+	public void rescale(double minLevel, double maxLevel);
+
+	public void rescaleCenter(double minLevel, double maxLevel);
+
+	public void abs();
+
+	public void log();
+
+	public void exp();
+
+	public void sqrt();
+
+	public void sqr();
+
+	public void pow(double a);
+
+	public void add(double constant);
+
+	public void multiply(double constant);
+
+	public void subtract(double constant);
+
+	public void divide(double constant);
+
+	public void threshold(double thresholdValue);
+
+	public void threshold(double thresholdValue, double minLevel, double maxLevel);
+
+	public void thresholdHard(double thresholdValue);
+
+	public void thresholdSoft(double thresholdValue);
+
+	public void addGaussianNoise(double amplitude);
+
+	public void addUniformNoise(double amplitude);
+
+	public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper);
+}
diff --git a/src/bilib/src/imageware/Process.java b/src/bilib/src/imageware/Process.java
new file mode 100644
index 0000000000000000000000000000000000000000..47209c45018802d54dc2a8c811a4ffbf3d63532d
--- /dev/null
+++ b/src/bilib/src/imageware/Process.java
@@ -0,0 +1,27 @@
+package imageware;
+
+/**
+ * Class Process.
+ * 
+ * 
+ * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
+ *         Lausanne, Lausanne, Switzerland
+ */
+
+public interface Process extends Pointwise {
+	public void smoothGaussian(double sigma);
+
+	public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ);
+
+	public void max(ImageWare imageware);
+
+	public void min(ImageWare imageware);
+
+	public void add(ImageWare imageware);
+
+	public void multiply(ImageWare imageware);
+
+	public void subtract(ImageWare imageware);
+
+	public void divide(ImageWare imageware);
+}
diff --git a/src/bilib/src/imageware/ShortAccess.java b/src/bilib/src/imageware/ShortAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..e24d4f69970c6abfaa84310384b71d6f4447ea2f
--- /dev/null
+++ b/src/bilib/src/imageware/ShortAccess.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class ShortAccess.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class ShortAccess extends ShortBuffer implements Access {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected ShortAccess(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected ShortAccess(Image image, int mode) {
		super(image, mode);
	}

	protected ShortAccess(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected ShortAccess(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected ShortAccess(byte[] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(byte[][] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(short[] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(short[][] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(short[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(float[] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(float[][] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(float[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(double[] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(double[][] array, int mode) {
		super(array, mode);
	}

	protected ShortAccess(double[][][] array, int mode) {
		super(array, mode);
	}

	// ------------------------------------------------------------------
	//
	// getPixel section
	//
	// ------------------------------------------------------------------

	/**
	 * Get a pixel at specific position without specific boundary conditions
	 * 
	 * If the positions is outside of this imageware, the method return 0.0.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a pixel value
	 */
	public double getPixel(int x, int y, int z) {
		if (x >= nx)
			return 0.0;
		if (y >= ny)
			return 0.0;
		if (z >= nz)
			return 0.0;
		if (x < 0)
			return 0.0;
		if (y < 0)
			return 0.0;
		if (z < 0)
			return 0.0;
		return ((short[]) data[z])[x + y * nx] & 0xFFFF;
	}

	/**
	 * Get a pixel at specific position with specific boundary conditions
	 * 
	 * If the positions is outside of this imageware, the method apply the
	 * boundary conditions to return a value.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a pixel value
	 */
	public double getPixel(int x, int y, int z, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a pixel \n" + "at the position (" + x
					+ "," + y + "," + z + ".\n" + "-------------------------------------------------------\n");
		}
		int xp = x;
		while (xp < 0)
			xp += xperiod;
		while (xp >= nx) {
			xp = xperiod - xp;
			xp = (xp < 0 ? -xp : xp);
		}
		int yp = y;
		while (yp < 0)
			yp += yperiod;
		while (yp >= ny) {
			yp = yperiod - yp;
			yp = (yp < 0 ? -yp : yp);
		}
		int zp = z;
		while (zp < 0)
			zp += zperiod;
		while (zp >= nz) {
			zp = zperiod - zp;
			zp = (zp < 0 ? -zp : zp);
		}
		return ((short[]) data[zp])[xp + yp * nx] & 0xFFFF;
	}

	/**
	 * Get a interpolated pixel value at specific position without specific
	 * boundary conditions.
	 * 
	 * If the positions is not on the pixel grid, the method return a
	 * interpolated value of the pixel (linear interpolation). If the positions
	 * is outside of this imageware, the method return 0.0.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @return a interpolated value
	 */
	public double getInterpolatedPixel(double x, double y, double z) {
		if (x > nx - 1)
			return 0.0;
		if (y > ny - 1)
			return 0.0;
		if (z > nz - 1)
			return 0.0;
		if (x < 0)
			return 0.0;
		if (y < 0)
			return 0.0;
		if (z < 0)
			return 0.0;
		double output = 0.0;
		/*
		 * int i = (x >= 0.0 ? ((int)x) : ((int)x - 1)); int j = (y >= 0.0 ?
		 * ((int)y) : ((int)y - 1)); int k = (z >= 0.0 ? ((int)z) : ((int)z -
		 * 1));
		 */
		int i = (x >= 0.0 ? ((int) x) : ((int) x - 1));
		int j = (y >= 0.0 ? ((int) y) : ((int) y - 1));
		int k = (z >= 0.0 ? ((int) z) : ((int) z - 1));
		boolean fi = (i == nx - 1);
		boolean fj = (j == ny - 1);
		boolean fk = (k == nz - 1);
		int index = i + j * nx;
		switch (getDimension()) {
		case 1:
			double v1_0 = (double) (((short[]) data[k])[index] & 0xFFFF);
			double v1_1 = (fi ? v1_0 : (double) (((short[]) data[k])[index + 1] & 0xFFFF));
			double dx1 = x - (double) i;
			return v1_1 * dx1 - v1_0 * (dx1 - 1.0);
		case 2:
			double v2_00 = (double) (((short[]) data[k])[index] & 0xFFFF);
			double v2_10 = (fi ? v2_00 : (double) (((short[]) data[k])[index + 1] & 0xFFFF));
			double v2_01 = (fj ? v2_00 : (double) (((short[]) data[k])[index + nx] & 0xFFFF));
			double v2_11 = (fi ? (fj ? v2_00 : v2_01) : (double) (((short[]) data[k])[index + 1 + nx] & 0xFFFF));
			double dx2 = x - (double) i;
			double dy2 = y - (double) j;
			return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0)));
		case 3:
			double v3_000 = (double) (((short[]) data[k])[index] & 0xFFFF);
			double v3_100 = (fi ? v3_000 : (double) (((short[]) data[k])[index + 1] & 0xFFFF));
			double v3_010 = (fj ? v3_000 : (double) (((short[]) data[k])[index + nx] & 0xFFFF));
			double v3_110 = (fi ? (fj ? v3_000 : v3_010) : (double) (((short[]) data[k])[index + 1 + nx] & 0xFFFF));
			double v3_001 = (fk ? v3_000 : (double) (((short[]) data[k + 1])[index] & 0xFFFF));
			double v3_011 = (fk ? (fj ? v3_000 : v3_010) : (double) (((short[]) data[k + 1])[index + 1] & 0xFFFF));
			double v3_101 = (fk ? (fi ? v3_000 : v3_100) : (double) (((short[]) data[k + 1])[index + nx] & 0xFFFF));
			double v3_111 = (fk ? (fj ? (fi ? v3_000 : v3_100) : v3_110) : (double) (((short[]) data[k + 1])[index + 1 + nx] & 0xFFFF));
			double dx3 = x - (double) i;
			double dy3 = y - (double) j;
			double dz3 = z - (double) k;
			double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0)));
			double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0)));
			return z2 * dz3 - z1 * (dz3 - 1.0);
		}
		return output;
	}

	/**
	 * Get a interpolated pixel value at specific position with specific
	 * boundary conditions.
	 * 
	 * If the positions is not on the pixel grid, the method return a
	 * interpolated value of the pixel (linear interpolation). If the positions
	 * is outside of this imageware, the method apply the boundary conditions to
	 * return a value.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 * @param boundaryConditions
	 *            MIRROR or PERIODIC boundary conditions
	 * @return a interpolated value
	 */
	public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions) {
		double output = 0.0;
		int i = (x >= 0.0 ? ((int) x) : ((int) x - 1));
		int j = (y >= 0.0 ? ((int) y) : ((int) y - 1));
		int k = (z >= 0.0 ? ((int) z) : ((int) z - 1));
		switch (getDimension()) {
		case 1:
			double v1_0 = getPixel(i, j, k, boundaryConditions);
			double v1_1 = getPixel(i + 1, j, k, boundaryConditions);
			double dx1 = x - (double) i;
			return v1_1 * dx1 - v1_0 * (dx1 - 1.0);
		case 2:
			double v2_00 = getPixel(i, j, k, boundaryConditions);
			double v2_10 = getPixel(i + 1, j, k, boundaryConditions);
			double v2_01 = getPixel(i, j + 1, k, boundaryConditions);
			double v2_11 = getPixel(i + 1, j + 1, k, boundaryConditions);
			double dx2 = x - (double) i;
			double dy2 = y - (double) j;
			return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0)));
		case 3:
			double v3_000 = getPixel(i, j, k, boundaryConditions);
			double v3_100 = getPixel(i + 1, j, k, boundaryConditions);
			double v3_010 = getPixel(i, j + 1, k, boundaryConditions);
			double v3_110 = getPixel(i + 1, j + 1, k, boundaryConditions);
			double v3_001 = getPixel(i, j, k + 1, boundaryConditions);
			double v3_011 = getPixel(i + 1, j, k + 1, boundaryConditions);
			double v3_101 = getPixel(i, j + 1, k + 1, boundaryConditions);
			double v3_111 = getPixel(i + 1, j + 1, k + 1, boundaryConditions);
			double dx3 = x - (double) i;
			double dy3 = y - (double) j;
			double dz3 = z - (double) k;
			double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0)));
			double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0)));
			return z2 * dz3 - z1 * (dz3 - 1.0);
		}
		return output;
	}

	// ------------------------------------------------------------------
	//
	// putPixel section
	//
	// ------------------------------------------------------------------

	/**
	 * Put a pixel at specific position
	 * 
	 * If the positions is outside of this imageware, the method does nothing.
	 * 
	 * @param x
	 *            position in the X axis
	 * @param y
	 *            position in the Y axis
	 * @param z
	 *            position in the Z axis
	 */
	public void putPixel(int x, int y, int z, double value) {
		if (x >= nx)
			return;
		if (y >= ny)
			return;
		if (z >= nz)
			return;
		if (x < 0)
			return;
		if (y < 0)
			return;
		if (z < 0)
			return;
		((short[]) data[z])[x + y * nx] = (short) value;
	}

	// ------------------------------------------------------------------
	//
	// getBounded section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (tmp[offset] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (tmp[offset] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (tmp[offset] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedX(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (tmp[offset] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_get("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (tmp[offset] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (tmp[offset] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (tmp[offset] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedY(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (tmp[offset] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_get("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (byte) (((short[]) data[k])[offset] & 0xFFFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (short) (((short[]) data[k])[offset] & 0xFFFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (float) (((short[]) data[k])[offset] & 0xFFFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 */
	public void getBoundedZ(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				buffer[i] = (double) (((short[]) data[k])[offset] & 0xFFFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_get("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			short[] tmp = (short[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (tmp[offset] & 0xFFFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			short[] tmp = (short[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (tmp[offset] & 0xFFFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			short[] tmp = (short[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (tmp[offset] & 0xFFFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedXY(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			short[] tmp = (short[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (tmp[offset] & 0xFFFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (((short[]) data[z])[offset] & 0xFFFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (((short[]) data[z])[offset] & 0xFFFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (((short[]) data[z])[offset] & 0xFFFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedXZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (((short[]) data[z])[offset] & 0xFFFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (byte) (((short[]) data[z])[offset] & 0xFFFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (short) (((short[]) data[z])[offset] & 0xFFFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (float) (((short[]) data[z])[offset] & 0xFFFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 */
	public void getBoundedYZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					buffer[i][j] = (double) (((short[]) data[z])[offset] & 0xFFFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				short[] tmp = (short[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (byte) (tmp[offset] & 0xFFFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				short[] tmp = (short[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (short) (tmp[offset] & 0xFFFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				short[] tmp = (short[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (float) (tmp[offset] & 0xFFFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 */
	public void getBoundedXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				short[] tmp = (short[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						buffer[i][j][k] = (double) (tmp[offset] & 0xFFFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// getBlock section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			short[] tmp = (short[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (byte) (tmp[xp + yp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			short[] tmp = (short[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (short) (tmp[xp + yp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			short[] tmp = (short[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (float) (tmp[xp + yp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			short[] tmp = (short[]) data[zp];
			for (int i = 0; i < leni; i++) {
				xp = x + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (double) (tmp[xp + yp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			short[] tmp = (short[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (byte) (tmp[xp + yp * nx] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			short[] tmp = (short[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (short) (tmp[xp + yp * nx] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			short[] tmp = (short[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (float) (tmp[xp + yp * nx] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			short[] tmp = (short[]) data[zp];
			for (int i = 0; i < leni; i++) {
				yp = y + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (double) (tmp[xp + yp * nx] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (byte) (((short[]) data[zp])[xyp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (short) (((short[]) data[zp])[xyp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (float) (((short[]) data[zp])[xyp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			for (int i = 0; i < leni; i++) {
				zp = z + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (double) (((short[]) data[zp])[xyp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			short[] tmp = (short[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (tmp[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			short[] tmp = (short[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (tmp[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			short[] tmp = (short[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (tmp[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the start position (x,y,z) in XY axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) {

		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			short[] tmp = (short[]) data[zp];
			for (int j = 0; j < lenj; j++) {
				yp = y + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (tmp[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (((short[]) data[zp])[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (((short[]) data[zp])[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (((short[]) data[zp])[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = x + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (((short[]) data[zp])[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (byte) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (short) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (float) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in YZ axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			for (int j = 0; j < lenj; j++) {
				zp = z + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = y + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (double) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				short[] tmp = (short[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (byte) (tmp[xp + yp] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				short[] tmp = (short[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (short) (tmp[xp + yp] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				short[] tmp = (short[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (float) (tmp[xp + yp] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the start position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) {
		int xperiod = 0;
		int yperiod = 0;
		int zperiod = 0;
		switch (boundaryConditions) {
		case ImageWare.MIRROR:
			xperiod = (nx <= 1 ? 1 : 2 * nx - 2);
			yperiod = (ny <= 1 ? 1 : 2 * ny - 2);
			zperiod = (nz <= 1 ? 1 : 2 * nz - 2);
			break;
		case ImageWare.PERIODIC:
			xperiod = nx;
			yperiod = ny;
			zperiod = nz;
			break;
		default:
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}

		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			for (int k = 0; k < lenk; k++) {
				zp = z + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				short[] tmp = (short[]) data[zp];
				for (int j = 0; j < lenj; j++) {
					yp = y + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = x + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (double) (tmp[xp + yp] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// getBlock section
	//
	// ------------------------------------------------------------------

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (byte) (tmp[xp + yp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (short) (tmp[xp + yp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (float) (tmp[xp + yp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in X axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				xp = xs + i;
				while (xp < 0)
					xp += xperiod;
				while (xp >= nx) {
					xp = xperiod - xp;
					xp = (xp < 0 ? -xp : xp);
				}
				buffer[i] = (double) (tmp[xp + yp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (byte) (tmp[xp + yp * nx] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (short) (tmp[xp + yp * nx] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (float) (tmp[xp + yp * nx] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Y axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int i = 0; i < leni; i++) {
				yp = ys + i;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				buffer[i] = (double) (tmp[xp + yp * nx] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (byte) (((short[]) data[zp])[xyp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (short) (((short[]) data[zp])[xyp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (float) (((short[]) data[zp])[xyp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in Z axis.
	 * Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 1D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions) {

		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			int xyp = xp + yp * nx;
			int zs = z - leni / 2;
			for (int i = 0; i < leni; i++) {
				zp = zs + i;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				buffer[i] = (double) (((short[]) data[zp])[xyp] & 0xFFFF);
			}
		}
		catch (Exception e) {
			throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (tmp[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (tmp[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (tmp[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * get an array into the imageware at the center position (x,y,z) in XY
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			zp = z;
			while (zp < 0)
				zp += zperiod;
			while (zp >= nz) {
				zp = zperiod - zp;
				zp = (zp < 0 ? -zp : zp);
			}
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			short[] tmp = ((short[]) data[zp]);
			for (int j = 0; j < lenj; j++) {
				yp = ys + j;
				while (yp < 0)
					yp += yperiod;
				while (yp >= ny) {
					yp = yperiod - yp;
					yp = (yp < 0 ? -yp : yp);
				}
				yp *= nx;
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (tmp[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (byte) (((short[]) data[zp])[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (short) (((short[]) data[zp])[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (float) (((short[]) data[zp])[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			yp = y;
			while (yp < 0)
				yp += yperiod;
			while (yp >= ny) {
				yp = yperiod - yp;
				yp = (yp < 0 ? -yp : yp);
			}
			yp *= nx;
			int xs = x - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - yp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					xp = xs + i;
					while (xp < 0)
						xp += xperiod;
					while (xp >= nx) {
						xp = xperiod - xp;
						xp = (xp < 0 ? -xp : xp);
					}
					buffer[i][j] = (double) (((short[]) data[zp])[xp + yp] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (byte) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (short) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (float) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in YZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 2D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			xp = x;
			while (xp < 0)
				xp += xperiod;
			while (xp >= nx) {
				xp = xperiod - xp;
				xp = (xp < 0 ? -xp : xp);
			}
			int ys = y - leni / 2;
			int zs = z - lenj / 2;
			for (int j = 0; j < lenj; j++) {
				zp = zs + j;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				for (int i = 0; i < leni; i++) {
					yp = ys + i;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					buffer[i][j] = (double) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF);
				}
			}
		}
		catch (Exception e) {
			throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            byte 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				short[] tmp = ((short[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (byte) (tmp[xp + yp] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            short 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				short[] tmp = ((short[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (short) (tmp[xp + yp] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            float 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				short[] tmp = ((short[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (float) (tmp[xp + yp] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	/**
	 * Get an array from the imageware at the center position (x,y,z) in XYZ
	 * axis. Apply boundary conditions to get the outside area.
	 * 
	 * @param x
	 *            X starting position to get the buffer
	 * @param y
	 *            Y starting position to get the buffer
	 * @param z
	 *            Z starting position to get the buffer
	 * @param buffer
	 *            double 3D array to get into the imageware
	 * @param boundaryConditions
	 *            mirror or periodic boundary conditions
	 */
	public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) {
		int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx);
		int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny);
		int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz);
		int xp, yp, zp;
		try {
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			int xs = x - leni / 2;
			int ys = y - lenj / 2;
			int zs = z - lenk / 2;
			for (int k = 0; k < lenk; k++) {
				zp = zs + k;
				while (zp < 0)
					zp += zperiod;
				while (zp >= nz) {
					zp = zperiod - zp;
					zp = (zp < 0 ? -zp : zp);
				}
				short[] tmp = ((short[]) data[zp]);
				for (int j = 0; j < lenj; j++) {
					yp = ys + j;
					while (yp < 0)
						yp += yperiod;
					while (yp >= ny) {
						yp = yperiod - yp;
						yp = (yp < 0 ? -yp : yp);
					}
					yp *= nx;
					for (int i = 0; i < leni; i++) {
						xp = xs + i;
						while (xp < 0)
							xp += xperiod;
						while (xp >= nx) {
							xp = xperiod - xp;
							xp = (xp < 0 ? -xp : xp);
						}
						buffer[i][j][k] = (double) (tmp[xp + yp] & 0xFFFF);
					}
				}
			}
		}
		catch (Exception e) {
			throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z);
		}
	}

	// ------------------------------------------------------------------
	//
	// putBounded section
	//
	// ------------------------------------------------------------------

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (short) (buffer[i] & 0xFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (short) (buffer[i] & 0xFFFF);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (short) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in X axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedX(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int offset = (x + iinf) + (y) * nx;
			int leni = buffer.length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (short) (buffer[i]);
				offset++;
			}
		}
		catch (Exception e) {
			throw_put("X", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (short) (buffer[i] & 0xFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (short) (buffer[i] & 0xFFFF);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (short) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Y axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedY(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int offset = (x) + (y + iinf) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			short[] tmp = (short[]) data[z];
			for (int i = iinf; i < isup; i++) {
				tmp[offset] = (short) (buffer[i]);
				offset += nx;
			}
		}
		catch (Exception e) {
			throw_put("Y", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, byte[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((short[]) data[k])[offset] = (short) (buffer[i] & 0xFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, short[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((short[]) data[k])[offset] = (short) (buffer[i] & 0xFFFF);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, float[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((short[]) data[k])[offset] = (short) (buffer[i]);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in Z axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 1D array to put into the imageware
	 */
	public void putBoundedZ(int x, int y, int z, double[] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (z < 0 ? -z : 0);
			int k = z + iinf;
			int offset = (x) + (y) * nx;
			int leni = buffer.length;
			if (x < 0)
				return;
			if (y < 0)
				return;
			if (z + leni < 0)
				return;
			int isup = (z + leni >= nz ? nz - z : leni);
			for (int i = iinf; i < isup; i++) {
				((short[]) data[k])[offset] = (short) (buffer[i]);
				k++;
			}
		}
		catch (Exception e) {
			throw_put("Z", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			short[] tmp = (short[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (short) (buffer[i][j] & 0xFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			short[] tmp = (short[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (short) (buffer[i][j] & 0xFFFF);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			short[] tmp = (short[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (short) (buffer[i][j]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XY axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedXY(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			short[] tmp = (short[]) data[z];
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y + j) * nx;
				for (int i = iinf; i < isup; i++) {
					tmp[offset] = (short) (buffer[i][j]);
					offset++;
				}
			}
		}
		catch (Exception e) {
			throw_put("XY", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((short[]) data[k])[offset] = (short) (buffer[i][j] & 0xFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((short[]) data[k])[offset] = (short) (buffer[i][j] & 0xFFFF);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((short[]) data[k])[offset] = (short) (buffer[i][j]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedXZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x + leni < 0)
				return;
			if (y < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = (x + iinf) + (y) * nx;
				for (int i = iinf; i < isup; i++) {
					((short[]) data[k])[offset] = (short) (buffer[i][j]);
					offset++;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("YZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, byte[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((short[]) data[k])[offset] = (short) (buffer[i][j] & 0xFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, short[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((short[]) data[k])[offset] = (short) (buffer[i][j] & 0xFFFF);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, float[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((short[]) data[k])[offset] = (short) (buffer[i][j]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 2D array to put into the imageware
	 */
	public void putBoundedYZ(int x, int y, int z, double[][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (y < 0 ? -y : 0);
			int jinf = (z < 0 ? -z : 0);
			int k = z + jinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			if (x < 0)
				return;
			if (y + leni < 0)
				return;
			if (z + lenj < 0)
				return;
			int isup = (y + leni >= ny ? ny - y : leni);
			int jsup = (z + lenj >= nz ? nz - z : lenj);
			for (int j = jinf; j < jsup; j++) {
				offset = x + (y + iinf) * nx;
				for (int i = iinf; i < isup; i++) {
					((short[]) data[k])[offset] = (short) (buffer[i][j]);
					offset += nx;
				}
				k++;
			}
		}
		catch (Exception e) {
			throw_put("XZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            byte 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				short[] tmp = (short[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (short) (buffer[i][j][k] & 0xFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            short 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, short[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				short[] tmp = (short[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (short) (buffer[i][j][k] & 0xFFFF);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            float 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, float[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				short[] tmp = (short[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (short) (buffer[i][j][k]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

	/**
	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy
	 * only the bounded area, intersection between the array and the imageware.
	 * 
	 * @param x
	 *            X starting position to put the buffer
	 * @param y
	 *            Y starting position to put the buffer
	 * @param z
	 *            Z starting position to put the buffer
	 * @param buffer
	 *            double 3D array to put into the imageware
	 */
	public void putBoundedXYZ(int x, int y, int z, double[][][] buffer) {
		try {
			if (x >= nx)
				return;
			if (y >= ny)
				return;
			if (z >= nz)
				return;
			int iinf = (x < 0 ? -x : 0);
			int jinf = (y < 0 ? -y : 0);
			int kinf = (z < 0 ? -z : 0);
			int ko = z + kinf;
			int offset = 0;
			int leni = buffer.length;
			int lenj = buffer[0].length;
			int lenk = buffer[0][0].length;
			if (x + leni < 0)
				return;
			if (y + lenj < 0)
				return;
			if (z + lenk < 0)
				return;
			int isup = (x + leni >= nx ? nx - x : leni);
			int jsup = (y + lenj >= ny ? ny - y : lenj);
			int ksup = (z + lenk >= nz ? nz - z : lenk);
			for (int k = kinf; k < ksup; k++) {
				short[] tmp = (short[]) data[ko];
				for (int j = jinf; j < jsup; j++) {
					offset = (x + iinf) + (y + j) * nx;
					for (int i = iinf; i < isup; i++) {
						tmp[offset] = (short) (buffer[i][j][k]);
						offset++;
					}
				}
				ko++;
			}
		}
		catch (Exception e) {
			throw_put("XYZ", "Bounded check", buffer, x, y, z);
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/ShortBuffer.java b/src/bilib/src/imageware/ShortBuffer.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4d24f033e7c9fef643323d7d4c8f1a981fcbd9c
--- /dev/null
+++ b/src/bilib/src/imageware/ShortBuffer.java
@@ -0,0 +1,2768 @@
+package imageware;
+
+import ij.ImageStack;
+import ij.process.ByteProcessor;
+import ij.process.ColorProcessor;
+import ij.process.FloatProcessor;
+import ij.process.ImageProcessor;
+import ij.process.ShortProcessor;
+
+import java.awt.Image;
+import java.awt.image.ImageObserver;
+import java.awt.image.PixelGrabber;
+
+/**
+ * Class ShortBuffer.
+ * 
+ * 
+ * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
+ *         Lausanne, Lausanne, Switzerland
+ */
+
+public class ShortBuffer implements Buffer {
+
+	protected Object[]	data	= null;
+	protected int		nx		= 0;
+	protected int		ny		= 0;
+	protected int		nz		= 0;
+	protected int		nxy		= 0;
+
+	/**
+	 * Constructor of a empty 3D short buffer.
+	 * 
+	 * @param nx
+	 *            size of the 3D buffer in the X axis
+	 * @param ny
+	 *            size of the 3D buffer in the Y axis
+	 * @param nz
+	 *            size of the 3D buffer in the Z axis
+	 */
+	protected ShortBuffer(int nx, int ny, int nz) {
+		this.nx = nx;
+		this.ny = ny;
+		this.nz = nz;
+		if (nx <= 0 || ny <= 0 || nz <= 0)
+			throw_constructor(nx, ny, nz);
+		allocate();
+	}
+
+	/**
+	 * Constructor of a short buffer from a object Image of Java.
+	 * 
+	 * @param image
+	 *            source to build a new imageware
+	 */
+	protected ShortBuffer(Image image, int mode) {
+		if (image == null) {
+			throw_constructor();
+		}
+		ImageObserver observer = null;
+		this.nx = image.getWidth(observer);
+		this.ny = image.getHeight(observer);
+		this.nz = 1;
+		this.nxy = nx * ny;
+		byte[] pixels = new byte[nxy];
+		PixelGrabber pg = new PixelGrabber(image, 0, 0, nx, ny, false);
+		try {
+			pg.grabPixels();
+			pixels = (byte[]) (pg.getPixels());
+		}
+		catch (Exception e) {
+			throw_constructor();
+		}
+		allocate();
+		for (int k = 0; k < nxy; k++)
+			((short[]) data[0])[k] = (short) (pixels[k] & 0xFF);
+	}
+
+	/**
+	 * Constructor of a short buffer from a ImageStack.
+	 * 
+	 * New data are allocated if the mode is CREATE, the imageware use the data
+	 * of ImageJ if the mode is WRAP.
+	 * 
+	 * @param stack
+	 *            source to build a new imageware
+	 * @param mode
+	 *            WRAP or CREATE
+	 */
+	protected ShortBuffer(ImageStack stack, int mode) {
+		if (stack == null) {
+			throw_constructor();
+		}
+		this.nx = stack.getWidth();
+		this.ny = stack.getHeight();
+		this.nz = stack.getSize();
+		this.nxy = nx * ny;
+		switch (mode) {
+		case ImageWare.WRAP:
+			this.data = stack.getImageArray();
+			break;
+		case ImageWare.CREATE:
+			allocate();
+			ImageProcessor ip = stack.getProcessor(1);
+			if (ip instanceof ByteProcessor) {
+				Object[] vol = stack.getImageArray();
+				for (int z = 0; z < nz; z++) {
+					byte[] slice = (byte[]) vol[z];
+					for (int k = 0; k < nxy; k++) {
+						((short[]) data[z])[k] = (short) (slice[k] & 0xFF);
+					}
+				}
+			}
+			else if (ip instanceof ShortProcessor) {
+				Object[] vol = stack.getImageArray();
+				for (int z = 0; z < nz; z++) {
+					short[] slice = (short[]) vol[z];
+					for (int k = 0; k < nxy; k++) {
+						((short[]) data[z])[k] = (short) (slice[k] & 0xFFFF);
+					}
+				}
+			}
+			else if (ip instanceof FloatProcessor) {
+				Object[] vol = stack.getImageArray();
+				for (int z = 0; z < nz; z++) {
+					float[] slice = (float[]) vol[z];
+					for (int k = 0; k < nxy; k++) {
+						((short[]) data[z])[k] = (short) slice[k];
+					}
+				}
+			}
+			else if (ip instanceof ColorProcessor) {
+				double r, g, b;
+				int c;
+				ColorProcessor cp;
+				int[] pixels;
+				for (int z = 0; z < nz; z++) {
+					cp = (ColorProcessor) stack.getProcessor(z + 1);
+					pixels = (int[]) cp.getPixels();
+					for (int k = 0; k < nxy; k++) {
+						c = pixels[k];
+						r = (double) ((c & 0xFF0000) >> 16);
+						g = (double) ((c & 0xFF00) >> 8);
+						b = (double) ((c & 0xFF));
+						((short[]) data[z])[k] = (short) ((r + g + b) / 3.0);
+					}
+				}
+			}
+			else {
+				throw_constructor();
+			}
+			break;
+		default:
+			throw_constructor();
+			break;
+		}
+	}
+
+	/**
+	 * Constructor of a short buffer from a specific color channel of
+	 * ImageStack.
+	 * 
+	 * New data are always allocated. If it is a gray image the imageware is
+	 * created and fill up with data of the source ImageStack. If it is a color
+	 * image only the selected channel is used to create this imageware.
+	 * 
+	 * @param stack
+	 *            source to build a new imageware
+	 * @param channel
+	 *            RED, GREEN or BLUE
+	 */
+	protected ShortBuffer(ImageStack stack, byte channel) {
+		if (stack == null) {
+			throw_constructor();
+		}
+		this.nx = stack.getWidth();
+		this.ny = stack.getHeight();
+		this.nz = stack.getSize();
+		this.nxy = nx * ny;
+		allocate();
+		ImageProcessor ip = stack.getProcessor(1);
+		if (ip instanceof ByteProcessor) {
+			Object[] vol = stack.getImageArray();
+			for (int z = 0; z < nz; z++) {
+				byte[] slice = (byte[]) vol[z];
+				for (int k = 0; k < nxy; k++) {
+					((short[]) data[z])[k] = (short) (slice[k] & 0xFF);
+				}
+			}
+		}
+		else if (ip instanceof ShortProcessor) {
+			Object[] vol = stack.getImageArray();
+			for (int z = 0; z < nz; z++) {
+				short[] slice = (short[]) vol[z];
+				for (int k = 0; k < nxy; k++) {
+					((short[]) data[z])[k] = (short) (slice[k] & 0xFFFF);
+				}
+			}
+		}
+		else if (ip instanceof FloatProcessor) {
+			Object[] vol = stack.getImageArray();
+			for (int z = 0; z < nz; z++) {
+				float[] slice = (float[]) vol[z];
+				for (int k = 0; k < nxy; k++) {
+					((short[]) data[z])[k] = (short) slice[k];
+				}
+			}
+		}
+		else if (ip instanceof ColorProcessor) {
+			ColorProcessor cp;
+			int[] pixels;
+			for (int z = 0; z < nz; z++) {
+				cp = (ColorProcessor) stack.getProcessor(z + 1);
+				pixels = (int[]) cp.getPixels();
+				switch (channel) {
+				case ImageWare.RED:
+					for (int k = 0; k < nxy; k++) {
+						((short[]) data[z])[k] = (short) ((pixels[k] & 0xFF0000) >> 16);
+					}
+					break;
+				case ImageWare.GREEN:
+					for (int k = 0; k < nxy; k++) {
+						((short[]) data[z])[k] = (short) ((pixels[k] & 0xFF00) >> 8);
+					}
+					break;
+				case ImageWare.BLUE:
+					for (int k = 0; k < nxy; k++) {
+						((short[]) data[z])[k] = (short) (pixels[k] & 0xFF);
+					}
+					break;
+				default:
+					throw_constructor();
+				}
+			}
+		}
+		else {
+			throw_constructor();
+		}
+	}
+
+	/**
+	 * Constructor of a short buffer from a byte array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(byte[] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = 1;
+		this.nz = 1;
+		allocate();
+		putX(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a byte array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(byte[][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = 1;
+		allocate();
+		putXY(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a byte array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(byte[][][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = array[0][0].length;
+		allocate();
+		putXYZ(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a short array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(short[] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = 1;
+		this.nz = 1;
+		allocate();
+		putX(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a short array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(short[][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = 1;
+		allocate();
+		putXY(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a short array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(short[][][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = array[0][0].length;
+		allocate();
+		putXYZ(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a float array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(float[] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = 1;
+		this.nz = 1;
+		allocate();
+		putX(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a float array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(float[][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = 1;
+		allocate();
+		putXY(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a float array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(float[][][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = array[0][0].length;
+		allocate();
+		putXYZ(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a double array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(double[] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = 1;
+		this.nz = 1;
+		allocate();
+		putX(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a double array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(double[][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = 1;
+		allocate();
+		putXY(0, 0, 0, array);
+	}
+
+	/**
+	 * Constructor of a short buffer from a double array.
+	 * 
+	 * @param array
+	 *            source to build this new imageware
+	 */
+	protected ShortBuffer(double[][][] array, int mode) {
+		if (array == null) {
+			throw_constructor();
+		}
+		this.nx = array.length;
+		this.ny = array[0].length;
+		this.nz = array[0][0].length;
+		allocate();
+		putXYZ(0, 0, 0, array);
+	}
+
+	/**
+	 * Return the type of this imageware.
+	 * 
+	 * @return the type of this imageware
+	 */
+	public int getType() {
+		return ImageWare.SHORT;
+	}
+
+	/**
+	 * Return the type of this imageware in a string format.
+	 * 
+	 * @return the type of this imageware translated in a string format
+	 */
+	public String getTypeToString() {
+		return "Short";
+	}
+
+	/**
+	 * Return the number of dimension of this imageware (1, 2 or 3).
+	 * 
+	 * @return the number of dimension of this imageware
+	 */
+	public int getDimension() {
+		int dims = 0;
+		dims += (nx > 1 ? 1 : 0);
+		dims += (ny > 1 ? 1 : 0);
+		dims += (nz > 1 ? 1 : 0);
+		return dims;
+	}
+
+	/**
+	 * Return the size of the imageware int[0] : x, int[1] : y, int[2] : z.
+	 * 
+	 * @return an array given the size of the imageware
+	 */
+	public int[] getSize() {
+		int[] size = { nx, ny, nz };
+		return size;
+	}
+
+	/**
+	 * Return the size in the X axis.
+	 * 
+	 * @return the size in the X axis
+	 */
+	public int getSizeX() {
+		return nx;
+	}
+
+	/**
+	 * Return the size in the Y axis.
+	 * 
+	 * @return the size in the Y axis
+	 */
+	public int getSizeY() {
+		return ny;
+	}
+
+	/**
+	 * Return the size in the Z axis.
+	 * 
+	 * @return the size in the Z axis
+	 */
+	public int getSizeZ() {
+		return nz;
+	}
+
+	/**
+	 * Return the size in the X axis.
+	 * 
+	 * @return the size in the X axis
+	 */
+	public int getWidth() {
+		return nx;
+	}
+
+	/**
+	 * Return the size in the Y axis.
+	 * 
+	 * @return the size in the Y axis
+	 */
+	public int getHeight() {
+		return ny;
+	}
+
+	/**
+	 * Return the size in the Z axis.
+	 * 
+	 * @return the size in the Z axis
+	 */
+	public int getDepth() {
+		return nz;
+	}
+
+	/**
+	 * Return the number of pixels in the imageware.
+	 * 
+	 * @return number of pixels in the imageware
+	 */
+	public int getTotalSize() {
+		return nxy * nz;
+	}
+
+	/**
+	 * Return true is this imageware has the same size the imageware given as
+	 * parameter.
+	 * 
+	 * @param imageware
+	 *            imageware to be compared
+	 * @return true if the imageware of the same size than this imageware
+	 */
+	public boolean isSameSize(ImageWare imageware) {
+		if (nx != imageware.getSizeX())
+			return false;
+		if (ny != imageware.getSizeY())
+			return false;
+		if (nz != imageware.getSizeZ())
+			return false;
+		return true;
+	}
+
+	// ------------------------------------------------------------------
+	//
+	// put Section
+	//
+	// ------------------------------------------------------------------
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putX(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		double buf[] = new double[bnx];
+		buffer.getX(0, 0, 0, buf);
+		putX(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putY(int x, int y, int z, ImageWare buffer) {
+		int bny = buffer.getSizeY();
+		double buf[] = new double[bny];
+		buffer.getY(0, 0, 0, buf);
+		putY(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, ImageWare buffer) {
+		int bnz = buffer.getSizeZ();
+		double buf[] = new double[bnz];
+		buffer.getZ(0, 0, 0, buf);
+		putZ(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bny = buffer.getSizeY();
+		double buf[][] = new double[bnx][bny];
+		buffer.getXY(0, 0, 0, buf);
+		putXY(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bnz = buffer.getSizeZ();
+		double buf[][] = new double[bnx][bnz];
+		buffer.getXZ(0, 0, 0, buf);
+		putXZ(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, ImageWare buffer) {
+		int bny = buffer.getSizeY();
+		int bnz = buffer.getSizeZ();
+		double buf[][] = new double[bny][bnz];
+		buffer.getYZ(0, 0, 0, buf);
+		putYZ(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            ImageWare object to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bny = buffer.getSizeY();
+		int bnz = buffer.getSizeZ();
+		double buf[][][] = new double[bnx][bny][bnz];
+		buffer.getXYZ(0, 0, 0, buf);
+		putXYZ(x, y, z, buf);
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 1D array to put into the imageware
+	 */
+	public void putX(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (short) (buffer[i] & 0xFF);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 1D array to put into the imageware
+	 */
+	public void putX(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+
+			System.arraycopy(buffer, 0, tmp, offset, leni);
+		}
+		catch (Exception e) {
+			throw_put("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 1D array to put into the imageware
+	 */
+	public void putX(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (short) (buffer[i]);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 1D array to put into the imageware
+	 */
+	public void putX(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (short) (buffer[i]);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 1D array to put into the imageware
+	 */
+	public void putY(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (short) (buffer[i] & 0xFF);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 1D array to put into the imageware
+	 */
+	public void putY(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (short) (buffer[i] & 0xFFFF);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 1D array to put into the imageware
+	 */
+	public void putY(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (short) (buffer[i]);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 1D array to put into the imageware
+	 */
+	public void putY(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				tmp[offset] = (short) (buffer[i]);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            bybytete 1D array to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				((short[]) data[z])[offset] = (short) (buffer[i] & 0xFF);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Z", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byshortte 1D array to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				((short[]) data[z])[offset] = (short) (buffer[i] & 0xFFFF);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Z", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byfloatte 1D array to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				((short[]) data[z])[offset] = (short) (buffer[i]);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Z", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            bydoublete 1D array to put into the imageware
+	 */
+	public void putZ(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				((short[]) data[z])[offset] = (short) (buffer[i]);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_put("Z", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 2D array to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			short[] tmp = (short[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					tmp[offset] = (short) (buffer[i][j] & 0xFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 2D array to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			short[] tmp = (short[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					tmp[offset] = (short) (buffer[i][j] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 2D array to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			short[] tmp = (short[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					tmp[offset] = (short) (buffer[i][j]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 2D array to put into the imageware
+	 */
+	public void putXY(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			short[] tmp = (short[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					tmp[offset] = (short) (buffer[i][j]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 2D array to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + j * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					((short[]) data[z])[offset] = (short) (buffer[i][j] & 0xFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 2D array to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + j * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					((short[]) data[z])[offset] = (short) (buffer[i][j] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 2D array to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + j * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					((short[]) data[z])[offset] = (short) (buffer[i][j]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 2D array to put into the imageware
+	 */
+	public void putXZ(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + j * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					((short[]) data[z])[offset] = (short) (buffer[i][j]);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 2D array to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
+				for (int i = 0; i < leni; i++, offset += nx) {
+					((short[]) data[z])[offset] = (short) (buffer[i][j] & 0xFF);
+				}
+		}
+		catch (Exception e) {
+			throw_put("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 2D array to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
+				for (int i = 0; i < leni; i++, offset += nx) {
+					((short[]) data[z])[offset] = (short) (buffer[i][j] & 0xFFFF);
+				}
+		}
+		catch (Exception e) {
+			throw_put("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 2D array to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
+				for (int i = 0; i < leni; i++, offset += nx) {
+					((short[]) data[z])[offset] = (short) (buffer[i][j]);
+				}
+		}
+		catch (Exception e) {
+			throw_put("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 2D array to put into the imageware
+	 */
+	public void putYZ(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y))
+				for (int i = 0; i < leni; i++, offset += nx) {
+					((short[]) data[z])[offset] = (short) (buffer[i][j]);
+				}
+		}
+		catch (Exception e) {
+			throw_put("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            byte 3D array to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, byte[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				short[] tmp = (short[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						tmp[offset] = (short) (buffer[i][j][k] & 0xFF);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            short 3D array to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, short[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				short[] tmp = (short[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						tmp[offset] = (short) (buffer[i][j][k] & 0xFFFF);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            float 3D array to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, float[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				short[] tmp = (short[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						tmp[offset] = (short) (buffer[i][j][k]);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Put an array into the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to put the buffer
+	 * @param y
+	 *            Y starting position to put the buffer
+	 * @param z
+	 *            Z starting position to put the buffer
+	 * @param buffer
+	 *            double 3D array to put into the imageware
+	 */
+	public void putXYZ(int x, int y, int z, double[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				short[] tmp = (short[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						tmp[offset] = (short) (buffer[i][j][k]);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_put("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	// ------------------------------------------------------------------
+	//
+	// get Section
+	//
+	// ------------------------------------------------------------------
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getX(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		double buf[] = new double[bnx];
+		getX(x, y, z, buf);
+		buffer.putX(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getY(int x, int y, int z, ImageWare buffer) {
+		int bny = buffer.getSizeY();
+		double buf[] = new double[bny];
+		getY(x, y, z, buf);
+		buffer.putY(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, ImageWare buffer) {
+		int bnz = buffer.getSizeZ();
+		double buf[] = new double[bnz];
+		getZ(x, y, z, buf);
+		buffer.putZ(0, 0, 0, buf);
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bny = buffer.getSizeY();
+		double buf[][] = new double[bnx][bny];
+		getXY(x, y, z, buf);
+		buffer.putXY(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bnz = buffer.getSizeZ();
+		double buf[][] = new double[bnx][bnz];
+		getXZ(x, y, z, buf);
+		buffer.putXZ(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, ImageWare buffer) {
+		int bny = buffer.getSizeY();
+		int bnz = buffer.getSizeZ();
+		double buf[][] = new double[bny][bnz];
+		getYZ(x, y, z, buf);
+		buffer.putYZ(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            ImageWare object to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, ImageWare buffer) {
+		int bnx = buffer.getSizeX();
+		int bny = buffer.getSizeY();
+		int bnz = buffer.getSizeZ();
+		double buf[][][] = new double[bnx][bny][bnz];
+		getXYZ(x, y, z, buf);
+		buffer.putXYZ(0, 0, 0, buf);
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 1D array to get into the imageware
+	 */
+	public void getX(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (byte) (tmp[offset] & 0xffff);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 1D array to get into the imageware
+	 */
+	public void getX(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+
+			System.arraycopy(tmp, offset, buffer, 0, leni);
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 1D array to get into the imageware
+	 */
+	public void getX(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (float) (tmp[offset] & 0xffff);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in X axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 1D array to get into the imageware
+	 */
+	public void getX(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (double) (tmp[offset] & 0xffff);
+				offset++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 1D array to get into the imageware
+	 */
+	public void getY(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (byte) (tmp[offset] & 0xFFFF);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 1D array to get into the imageware
+	 */
+	public void getY(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (short) (tmp[offset] & 0xFFFF);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 1D array to get into the imageware
+	 */
+	public void getY(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (float) (tmp[offset] & 0xFFFF);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Y axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 1D array to get into the imageware
+	 */
+	public void getY(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			short[] tmp = (short[]) data[z];
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (double) (tmp[offset] & 0xFFFF);
+				offset += nx;
+			}
+		}
+		catch (Exception e) {
+			throw_get("X", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 1D array to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, byte[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (byte) (((short[]) data[z])[offset] & 0xFFFF);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 1D array to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, short[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (short) (((short[]) data[z])[offset] & 0xFFFF);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 1D array to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, float[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (float) (((short[]) data[z])[offset] & 0xFFFF);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in Z axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 1D array to get into the imageware
+	 */
+	public void getZ(int x, int y, int z, double[] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			for (int i = 0; i < leni; i++) {
+				buffer[i] = (double) (((short[]) data[z])[offset] & 0xFFFF);
+				z++;
+			}
+		}
+		catch (Exception e) {
+			throw_get("Y", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 2D array to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			short[] tmp = (short[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (byte) (tmp[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 2D array to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			short[] tmp = (short[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (short) (tmp[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 2D array to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			short[] tmp = (short[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (float) (tmp[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * get an array into the imageware at the position (x,y,z) in XY axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 2D array to get into the imageware
+	 */
+	public void getXY(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			short[] tmp = (short[]) data[z];
+			for (int j = 0; j < lenj; j++) {
+				offset = x + (y + j) * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (double) (tmp[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XY", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 2D array to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + y * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (byte) (((short[]) data[z])[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 2D array to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + y * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (short) (((short[]) data[z])[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 2D array to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + y * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (float) (((short[]) data[z])[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 2D array to get into the imageware
+	 */
+	public void getXZ(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++) {
+				offset = x + y * nx;
+				for (int i = 0; i < leni; i++, offset++) {
+					buffer[i][j] = (double) (((short[]) data[z])[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 2D array to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, byte[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
+				for (int i = 0; i < leni; i++, offset += nx) {
+					buffer[i][j] = (byte) (((short[]) data[z])[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 2D array to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, short[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
+				for (int i = 0; i < leni; i++, offset += nx) {
+					buffer[i][j] = (short) (((short[]) data[z])[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 2D array to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, float[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
+				for (int i = 0; i < leni; i++, offset += nx) {
+					buffer[i][j] = (float) (((short[]) data[z])[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in YZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 2D array to get into the datase
+	 */
+	public void getYZ(int x, int y, int z, double[][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) {
+				for (int i = 0; i < leni; i++, offset += nx) {
+					buffer[i][j] = (double) (((short[]) data[z])[offset] & 0xFFFF);
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("YZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            byte 3D array to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, byte[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				short[] tmp = (short[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						buffer[i][j][k] = (byte) (tmp[offset] & 0xFFFF);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            short 3D array to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, short[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				short[] tmp = (short[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						buffer[i][j][k] = (short) (tmp[offset] & 0xFFFF);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            float 3D array to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, float[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				short[] tmp = (short[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						buffer[i][j][k] = (float) (tmp[offset] & 0xFFFF);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	/**
+	 * Get an array from the imageware at the position (x,y,z) in XYZ axis. No
+	 * check are performed if the array is outside of the imageware.
+	 * 
+	 * @param x
+	 *            X starting position to get the buffer
+	 * @param y
+	 *            Y starting position to get the buffer
+	 * @param z
+	 *            Z starting position to get the buffer
+	 * @param buffer
+	 *            double 3D array to get into the imageware
+	 */
+	public void getXYZ(int x, int y, int z, double[][][] buffer) {
+		try {
+			int offset = x + y * nx;
+			int leni = buffer.length;
+			int lenj = buffer[0].length;
+			int lenk = buffer[0][0].length;
+			for (int k = 0; k < lenk; k++, z++) {
+				short[] tmp = (short[]) data[z];
+				for (int j = 0; j < lenj; j++) {
+					offset = x + (j + y) * nx;
+					for (int i = 0; i < leni; i++, offset++) {
+						buffer[i][j][k] = (double) (tmp[offset] & 0xFFFF);
+					}
+				}
+			}
+		}
+		catch (Exception e) {
+			throw_get("XYZ", "No check", buffer, x, y, z);
+		}
+	}
+
+	// ------------------------------------------------------------------
+	//
+	// Private Section
+	//
+	// ------------------------------------------------------------------
+
+	/**
+	 * Prepare a complete error message from the errors coming the constructors.
+	 */
+	protected void throw_constructor() {
+		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a short imageware.\n"
+				+ "-------------------------------------------------------\n");
+	}
+
+	/**
+	 * Prepare a complete error message from the errors coming the constructors.
+	 */
+	protected void throw_constructor(int nx, int ny, int nz) {
+		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a short imageware " + nx + "," + ny
+				+ "," + nz + "].\n" + "-------------------------------------------------------\n");
+	}
+
+	/**
+	 * Prepare a complete error message from the errors coming the get routines.
+	 */
+	protected void throw_get(String direction, String border, Object buffer, int x, int y, int z) {
+		int leni = 0;
+		int lenj = 0;
+		int lenk = 0;
+		String type = " unknown type";
+		if (buffer instanceof byte[]) {
+			leni = ((byte[]) buffer).length;
+			type = " 1D byte";
+		}
+		else if (buffer instanceof short[]) {
+			leni = ((short[]) buffer).length;
+			type = " 1D short";
+		}
+		else if (buffer instanceof float[]) {
+			leni = ((float[]) buffer).length;
+			type = " 1D float";
+		}
+		else if (buffer instanceof double[]) {
+			leni = ((double[]) buffer).length;
+			type = " 1D double";
+		}
+		else if (buffer instanceof byte[][]) {
+			leni = ((byte[][]) buffer).length;
+			lenj = ((byte[][]) buffer)[0].length;
+			type = " 2D byte";
+		}
+		else if (buffer instanceof short[][]) {
+			leni = ((short[][]) buffer).length;
+			lenj = ((short[][]) buffer)[0].length;
+			type = " 2D short";
+		}
+		else if (buffer instanceof float[][]) {
+			leni = ((float[][]) buffer).length;
+			lenj = ((float[][]) buffer)[0].length;
+			type = " 2D float";
+		}
+		else if (buffer instanceof double[][]) {
+			leni = ((double[][]) buffer).length;
+			lenj = ((double[][]) buffer)[0].length;
+			type = " 2D double";
+		}
+		else if (buffer instanceof byte[][][]) {
+			leni = ((byte[][][]) buffer).length;
+			lenj = ((byte[][][]) buffer)[0].length;
+			lenk = ((byte[][][]) buffer)[0][0].length;
+			type = " 3D byte";
+		}
+		else if (buffer instanceof short[][][]) {
+			leni = ((short[][][]) buffer).length;
+			lenj = ((short[][][]) buffer)[0].length;
+			lenk = ((short[][][]) buffer)[0][0].length;
+			type = " 3D short";
+		}
+		else if (buffer instanceof float[][][]) {
+			leni = ((float[][][]) buffer).length;
+			lenj = ((float[][][]) buffer)[0].length;
+			lenk = ((float[][][]) buffer)[0][0].length;
+			type = " 3D float";
+		}
+		else if (buffer instanceof double[][][]) {
+			leni = ((double[][][]) buffer).length;
+			lenj = ((double[][][]) buffer)[0].length;
+			lenk = ((double[][][]) buffer)[0][0].length;
+			type = " 3D double";
+		}
+		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get a" + type + " buffer ["
+				+ (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "from the short imageware [" + nx + "," + ny + "," + nz + "]\n"
+				+ "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n");
+	}
+
+	/**
+	 * Prepare a complete error message from the errors coming the put routines.
+	 */
+	protected void throw_put(String direction, String border, Object buffer, int x, int y, int z) {
+		int leni = 0;
+		int lenj = 0;
+		int lenk = 0;
+		String type = " unknown type";
+		if (buffer instanceof byte[]) {
+			leni = ((byte[]) buffer).length;
+			type = " 1D byte";
+		}
+		else if (buffer instanceof short[]) {
+			leni = ((short[]) buffer).length;
+			type = " 1D short";
+		}
+		else if (buffer instanceof float[]) {
+			leni = ((float[]) buffer).length;
+			type = " 1D float";
+		}
+		else if (buffer instanceof double[]) {
+			leni = ((double[]) buffer).length;
+			type = " 1D double";
+		}
+		else if (buffer instanceof byte[][]) {
+			leni = ((byte[][]) buffer).length;
+			lenj = ((byte[][]) buffer)[0].length;
+			type = " 2D byte";
+		}
+		else if (buffer instanceof short[][]) {
+			leni = ((short[][]) buffer).length;
+			lenj = ((short[][]) buffer)[0].length;
+			type = " 2D short";
+		}
+		else if (buffer instanceof float[][]) {
+			leni = ((float[][]) buffer).length;
+			lenj = ((float[][]) buffer)[0].length;
+			type = " 2D float";
+		}
+		else if (buffer instanceof double[][]) {
+			leni = ((double[][]) buffer).length;
+			lenj = ((double[][]) buffer)[0].length;
+			type = " 2D double";
+		}
+		else if (buffer instanceof byte[][][]) {
+			leni = ((byte[][][]) buffer).length;
+			lenj = ((byte[][][]) buffer)[0].length;
+			lenk = ((byte[][][]) buffer)[0][0].length;
+			type = " 3D byte";
+		}
+		else if (buffer instanceof short[][][]) {
+			leni = ((short[][][]) buffer).length;
+			lenj = ((short[][][]) buffer)[0].length;
+			lenk = ((short[][][]) buffer)[0][0].length;
+			type = " 3D short";
+		}
+		else if (buffer instanceof float[][][]) {
+			leni = ((float[][][]) buffer).length;
+			lenj = ((float[][][]) buffer)[0].length;
+			lenk = ((float[][][]) buffer)[0][0].length;
+			type = " 3D float";
+		}
+		else if (buffer instanceof double[][][]) {
+			leni = ((double[][][]) buffer).length;
+			lenj = ((double[][][]) buffer)[0].length;
+			lenk = ((double[][][]) buffer)[0][0].length;
+			type = " 3D double";
+		}
+		throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a" + type + " buffer ["
+				+ (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "into the short imageware [" + nx + "," + ny + "," + nz + "]\n"
+				+ "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n");
+	}
+
+	// ------------------------------------------------------------------
+	//
+	// Get slice fast and direct access Section
+	//
+	// ------------------------------------------------------------------
+
+	/**
+	 * Get a reference of the whole volume data.
+	 * 
+	 * @return a reference of the data of this imageware
+	 */
+	public Object[] getVolume() {
+		return data;
+	}
+
+	/**
+	 * Get a specific slice, fast and direct access, but only for byte
+	 * imageware.
+	 * 
+	 * @param z
+	 *            number of the requested slice
+	 * @return a reference of the data of one slice of this imageware
+	 */
+	public byte[] getSliceByte(int z) {
+		return null;
+	}
+
+	/**
+	 * Get a specific slice, fast and direct access, but only for short
+	 * imageware.
+	 * 
+	 * @param z
+	 *            number of the requested slice
+	 * @return a reference of the data of one slice of this imageware
+	 */
+	public short[] getSliceShort(int z) {
+		return (short[]) data[z];
+	}
+
+	/**
+	 * Get a specific slice, fast and direct access, but only for float
+	 * imageware.
+	 * 
+	 * @param z
+	 *            number of the requested slice
+	 * @return a reference of the data of one slice of this imageware
+	 */
+	public float[] getSliceFloat(int z) {
+		return null;
+	}
+
+	/**
+	 * Get a specific slice, fast and direct access, but only for double
+	 * imageware.
+	 * 
+	 * @param z
+	 *            number of the requested slice
+	 * @return a reference of the data of one slice of this imageware
+	 */
+	public double[] getSliceDouble(int z) {
+		return null;
+	}
+
+	/**
+	 * Allocate a buffer of size [nx,ny,nz].
+	 */
+	private void allocate() {
+		try {
+			this.data = new Object[nz];
+			this.nxy = nx * ny;
+			for (int z = 0; z < nz; z++)
+				this.data[z] = new short[nxy];
+		}
+		catch (Exception e) {
+			throw_constructor(nx, ny, nz);
+		}
+	}
+
+} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/ShortPointwise.java b/src/bilib/src/imageware/ShortPointwise.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b6da121721170c2945ecef13078eb62546e2456
--- /dev/null
+++ b/src/bilib/src/imageware/ShortPointwise.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;
import ij.process.ShortProcessor;

import java.awt.Image;
import java.util.Random;

/**
 * Class ShortPointwise.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class ShortPointwise extends ShortAccess implements Pointwise {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected ShortPointwise(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected ShortPointwise(Image image, int mode) {
		super(image, mode);
	}

	protected ShortPointwise(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected ShortPointwise(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected ShortPointwise(byte[] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(byte[][] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(short[] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(short[][] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(short[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(float[] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(float[][] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(float[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(double[] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(double[][] array, int mode) {
		super(array, mode);
	}

	protected ShortPointwise(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Fill this imageware with a constant value.
	 * 
	 * @param value
	 *            the constant value
	 */
	public void fillConstant(double value) {
		short typedValue = (short) value;
		short[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++)
				slice[k] = typedValue;
		}
	}

	/**
	 * Fill this imageware with ramp.
	 */
	public void fillRamp() {
		int off = 0;
		short[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++)
				slice[k] = (short) (off + k);
			off += nxy;
		}
	}

	/**
	 * Generate a gaussian noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void fillGaussianNoise(double amplitude) {
		Random rnd = new Random();
		short[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) ((rnd.nextGaussian()) * amplitude);
			}
		}
	}

	/**
	 * Generate a uniform noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void fillUniformNoise(double amplitude) {
		Random rnd = new Random();
		short[] slice = null;
		amplitude *= 2.0;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) ((rnd.nextDouble() - 0.5) * amplitude);
			}
		}
	}

	/**
	 * Generate a salt and pepper noise.
	 * 
	 * @param amplitudeSalt
	 *            amplitude of the salt noise
	 * @param amplitudePepper
	 *            amplitude of the pepper noise
	 * @param percentageSalt
	 *            percentage of the salt noise
	 * @param percentagePepper
	 *            percentage of the pepper noise
	 */
	public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) {
		Random rnd = new Random();
		int index, z;
		if (percentageSalt > 0) {
			double nbSalt = nxy * nz / percentageSalt;
			for (int k = 0; k < nbSalt; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((short[]) data[z])[index] = (short) (rnd.nextDouble() * amplitudeSalt);
			}
		}
		if (percentagePepper > 0) {
			double nbPepper = nxy * nz / percentagePepper;
			for (int k = 0; k < nbPepper; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((short[]) data[z])[index] = (short) (-rnd.nextDouble() * amplitudeSalt);
			}
		}
	}

	/**
	 * Build an ImageStack of ImageJ.
	 */
	public ImageStack buildImageStack() {
		ImageStack imagestack = new ImageStack(nx, ny);
		for (int z = 0; z < nz; z++) {
			ShortProcessor ip = new ShortProcessor(nx, ny);
			short pix[] = (short[]) ip.getPixels();
			for (int k = 0; k < nxy; k++)
				pix[k] = (short) (((short[]) data[z])[k]);
			imagestack.addSlice("" + z, ip);
		}
		return imagestack;
	}

	/**
	 * Invert the pixel intensity.
	 */
	public void invert() {
		double max = -Double.MAX_VALUE;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFFFF) > max)
					max = slice[k] & 0xFFFF;
			}
		}
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) (max - ((double) (slice[k] & 0xFFFF)));
			}
		}
	}

	/**
	 * Negate the pixel intensity.
	 */
	public void negate() {
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) (-((double) (slice[k] & 0xFFFF)));
			}
		}
	}

	/**
	 * Clip the pixel intensity into [0..255].
	 */
	public void clip() {
		clip(0.0, 255.0);
	}

	/**
	 * Clip the pixel intensity into [minLevel..maxLevel].
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void clip(double minLevel, double maxLevel) {
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			short value;
			short min = (short) minLevel;
			short max = (short) maxLevel;
			for (int k = 0; k < nxy; k++) {
				value = (short) (slice[k] & 0xFFFF);
				if (value < min)
					slice[k] = min;
				if (value > max)
					slice[k] = max;
			}
		}
	}

	/**
	 * Rescale the pixel intensity into [0..255].
	 */
	public void rescale() {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFFFF) > maxImage)
					maxImage = slice[k] & 0xFFFF;
				if ((slice[k] & 0xFFFF) < minImage)
					minImage = slice[k] & 0xFFFF;
			}
		}
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = 128.0;
		}
		else {
			a = 255.0 / (maxImage - minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) (a * (((double) (slice[k] & 0xFFFF)) - minImage));
			}
		}
	}

	/**
	 * Rescale the pixel intensity into [minLevel..maxLevel].
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void rescale(double minLevel, double maxLevel) {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFFFF) > maxImage)
					maxImage = slice[k] & 0xFFFF;
				if ((slice[k] & 0xFFFF) < minImage)
					minImage = slice[k] & 0xFFFF;
			}
		}
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = (maxLevel - minLevel) / 2.0;
		}
		else {
			a = (maxLevel - minLevel) / (maxImage - minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) (a * (((double) (slice[k] & 0xFFFF)) - minImage) + minLevel);
			}
		}
	}

	/**
	 * Rescale the pixel intensity with a linear curve passing through
	 * (maxLevel-minLevel)/2 at the 0 input intensity.
	 * 
	 * @param minLevel
	 *            double value given the threshold
	 * @param maxLevel
	 *            double value given the threshold
	 */
	public void rescaleCenter(double minLevel, double maxLevel) {
		double maxImage = -Double.MAX_VALUE;
		double minImage = Double.MAX_VALUE;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFFFF) > maxImage)
					maxImage = slice[k] & 0xFFFF;
				if ((slice[k] & 0xFFFF) < minImage)
					minImage = slice[k] & 0xFFFF;
			}
		}
		double center = (maxLevel + minLevel) / 2.0;
		double a;
		if (minImage - maxImage == 0) {
			a = 1.0;
			minImage = (maxLevel - minLevel) / 2.0;
		}
		else {
			if (Math.abs(maxImage) > Math.abs(minImage))
				a = (maxLevel - center) / Math.abs(maxImage);
			else
				a = (center - minLevel) / Math.abs(minImage);
		}
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) (a * (((double) (slice[k] & 0xFFFF)) - minImage) + center);
			}
		}
	}

	/**
	 * Compute the absolute value of this imageware.
	 */
	public void abs() {
	}

	/**
	 * Compute the log of this imageware.
	 */
	public void log() {
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) Math.log(slice[k]);
			}
		}
	}

	/**
	 * Compute the exponential of this imageware.
	 */
	public void exp() {
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) Math.exp(slice[k]);
			}
		}
	}

	/**
	 * Compute the square root of this imageware.
	 */
	public void sqrt() {
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) Math.sqrt(slice[k]);
			}
		}
	}

	/**
	 * Compute the square of this imageware.
	 */
	public void sqr() {
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] *= slice[k];
			}
		}
	}

	/**
	 * Compute the power of a of this imageware.
	 * 
	 * @param a
	 *            exponent
	 */
	public void pow(double a) {
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = (short) Math.pow(slice[k], a);
			}
		}
	}

	/**
	 * Add a constant value to this imageware.
	 */
	public void add(double constant) {
		short cst = (short) constant;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += cst;
			}
		}
	}

	/**
	 * Multiply a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void multiply(double constant) {
		short cst = (short) constant;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] *= cst;
			}
		}
	}

	/**
	 * Subtract a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void subtract(double constant) {
		short cst = (short) constant;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] -= cst;
			}
		}
	}

	/**
	 * Divide by a constant value to this imageware.
	 * 
	 * @param constant
	 *            the constant value
	 */
	public void divide(double constant) {
		if (constant == 0.0)
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the constant is 0.\n"
					+ "-------------------------------------------------------\n");
		short cst = (short) constant;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] /= cst;
			}
		}
	}

	/**
	 * Threshold a imageware in two levels 0 and 255.
	 * 
	 * All the pixels values strictly greater than 'thresholdValue' and are set
	 * to 0. The remaining values are set to 255.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void threshold(double thresholdValue) {
		threshold(thresholdValue, 0.0, 255.0);
	}

	/**
	 * Threshold a imageware in two levels minLevel and maxLevel.
	 * 
	 * All the pixels values strictly greater than 'thresholdValue' and are set
	 * to maxLevel. The remaining values are set to minLevel.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 * @param minLevel
	 *            double value given the minimum level
	 * @param maxLevel
	 *            double value given the maximum level
	 */
	public void threshold(double thresholdValue, double minLevel, double maxLevel) {
		short low = (short) (minLevel);
		short high = (short) (maxLevel);
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] = ((double) (slice[k] & 0xFFFF) > thresholdValue ? high : low);
			}
		}
	}

	/**
	 * Apply a soft thresholding.
	 * 
	 * All the pixels values strictly greater than '-thresholdValue' and stricty
	 * lower than 'thresholdValue' set to 0. The remaining positive values are
	 * reduced by 'thresholdvalue'; the remaining negative values are augmented
	 * by 'thresholdValue'.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void thresholdSoft(double thresholdValue) {
		short zero = (short) (0.0);
		double pixel;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				pixel = (double) (slice[k] & 0xFFFF);
				slice[k] = (pixel <= -thresholdValue ? (short) (pixel + thresholdValue) : (pixel > thresholdValue ? (short) (pixel - thresholdValue) : zero));
			}
		}
	}

	/**
	 * Apply a hard thresholding.
	 * 
	 * All the pixels values strictly greater than '-thresholdValue' and stricty
	 * lower than 'thresholdValue' are set to 0. The remaining values are
	 * unchanged.
	 * 
	 * @param thresholdValue
	 *            double value given the threshold
	 */
	public void thresholdHard(double thresholdValue) {
		short zero = (short) (0.0);
		double pixel;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				pixel = (double) (slice[k] & 0xFFFF);
				if (pixel > -thresholdValue && pixel < thresholdValue)
					slice[k] = zero;
			}
		}
	}

	/**
	 * Add a gaussian noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void addGaussianNoise(double amplitude) {
		Random rnd = new Random();
		short[] slice = null;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += (short) ((rnd.nextGaussian()) * amplitude);
			}
		}
	}

	/**
	 * Add a uniform noise with a range [-amplitude..amplitude].
	 * 
	 * @param amplitude
	 *            amplitude of the noise
	 */
	public void addUniformNoise(double amplitude) {
		Random rnd = new Random();
		short[] slice = null;
		amplitude *= 2.0;
		for (int z = 0; z < nz; z++) {
			slice = (short[]) data[z];
			for (int k = 0; k < nxy; k++) {
				slice[k] += (short) ((rnd.nextDouble() - 0.5) * amplitude);
			}
		}
	}

	/**
	 * Add a salt and pepper noise.
	 * 
	 * @param amplitudeSalt
	 *            amplitude of the salt noise
	 * @param amplitudePepper
	 *            amplitude of the pepper noise
	 * @param percentageSalt
	 *            percentage of the salt noise
	 * @param percentagePepper
	 *            percentage of the pepper noise
	 */
	public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) {
		Random rnd = new Random();
		int index, z;
		if (percentageSalt > 0) {
			double nbSalt = nxy * nz / percentageSalt;
			for (int k = 0; k < nbSalt; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((short[]) data[z])[index] += (short) (rnd.nextDouble() * amplitudeSalt);
			}
		}
		if (percentagePepper > 0) {
			double nbPepper = nxy * nz / percentagePepper;
			for (int k = 0; k < nbPepper; k++) {
				index = (int) (rnd.nextDouble() * nxy);
				z = (int) (rnd.nextDouble() * nz);
				((short[]) data[z])[index] -= (short) (rnd.nextDouble() * amplitudeSalt);
			}
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/ShortProcess.java b/src/bilib/src/imageware/ShortProcess.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a627c5b8b518385649cbe8630094c7dc7441e26
--- /dev/null
+++ b/src/bilib/src/imageware/ShortProcess.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class ShortProcess.
 * 
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class ShortProcess extends ShortPointwise implements Process {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected ShortProcess(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected ShortProcess(Image image, int mode) {
		super(image, mode);
	}

	protected ShortProcess(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected ShortProcess(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected ShortProcess(byte[] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(byte[][] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(short[] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(short[][] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(short[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(float[] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(float[][] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(float[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(double[] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(double[][] array, int mode) {
		super(array, mode);
	}

	protected ShortProcess(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Apply a separable gaussian smoothing over the image with the same
	 * strengthness in all directions. To have a smmothing effect the
	 * strengthness should be strictly greater than 0 and the size in the
	 * considered directions should be greater strictly than 1.
	 * 
	 * @param sigma
	 *            Strengthness of the smoothing
	 */
	public void smoothGaussian(double sigma) {
		smoothGaussian(sigma, sigma, sigma);
	}

	/**
	 * Apply a separablegaussian smoothing over the image with an independant
	 * strengthness in the different directions. To have a smmothing effect the
	 * strengthness should be strictly greater than 0 and the size in the
	 * considered directions should be greater strictly than 1.
	 * 
	 * @param sigmaX
	 *            Strengthness of the smoothing in X axis
	 * @param sigmaY
	 *            Strengthness of the smoothing in X axis
	 * @param sigmaZ
	 *            Strengthness of the smoothing in X axis
	 */
	public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ) {
		int n = 3;
		double N = (double) n;
		double poles[] = new double[n];

		if (nx > 1 && sigmaX > 0.0) {
			double s2 = sigmaX * sigmaX;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[nx];
			for (int z = 0; z < nz; z++) {
				for (int y = 0; y < ny; y++) {
					getX(0, y, z, line);
					putX(0, y, z, Convolver.convolveIIR(line, poles));
				}
			}
		}

		if (ny > 1 && sigmaY > 0.0) {
			double s2 = sigmaY * sigmaY;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[ny];
			for (int x = 0; x < nx; x++) {
				for (int z = 0; z < nz; z++) {
					getY(x, 0, z, line);
					putY(x, 0, z, Convolver.convolveIIR(line, poles));
				}
			}
		}

		if (nz > 1 && sigmaZ > 0.0) {
			double s2 = sigmaZ * sigmaZ;
			double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2);
			poles[0] = poles[1] = poles[2] = alpha;
			double line[] = new double[nz];
			for (int y = 0; y < ny; y++) {
				for (int x = 0; x < nx; x++) {
					getZ(x, y, 0, line);
					putZ(x, y, 0, Convolver.convolveIIR(line, poles));
				}
			}
		}
	}

	/**
	 * Get the maximum of this imageware and a imageware.
	 * 
	 * @param imageware
	 *            imageware to max
	 */
	public void max(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to get the maximum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					if (((short[]) data[z])[k] < (short) tmp[k])
						((short[]) data[z])[k] = (short) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					if (((short[]) data[z])[k] < (short) tmp[k])
						((short[]) data[z])[k] = (short) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					if (((short[]) data[z])[k] < (short) tmp[k])
						((short[]) data[z])[k] = (short) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					if (((short[]) data[z])[k] < (short) tmp[k])
						((short[]) data[z])[k] = (short) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Get the minimum of this imageware and a imageware.
	 * 
	 * @param imageware
	 *            imageware to min
	 */
	public void min(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to get the minimum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					if (((short[]) data[z])[k] > (short) tmp[k])
						((short[]) data[z])[k] = (short) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					if (((short[]) data[z])[k] > (short) tmp[k])
						((short[]) data[z])[k] = (short) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					if (((short[]) data[z])[k] > (short) tmp[k])
						((short[]) data[z])[k] = (short) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					if (((short[]) data[z])[k] > (short) tmp[k])
						((short[]) data[z])[k] = (short) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Add a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to add
	 */
	public void add(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to add because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] += (short) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] += (short) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] += (short) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] += (short) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Multiply a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to multiply
	 */
	public void multiply(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to multiply because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] *= (short) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] *= (short) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] *= (short) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] *= (short) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Subtract a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to subtract
	 */
	public void subtract(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to subtract because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] -= (short) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] -= (short) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] -= (short) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] -= (short) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Divide a imageware to the current imageware.
	 * 
	 * @param imageware
	 *            imageware to divide
	 */
	public void divide(ImageWare imageware) {
		if (!isSameSize(imageware)) {
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n"
					+ "Unable to divide because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + ","
					+ imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n");
		}
		switch (imageware.getType()) {
		case ImageWare.BYTE:
			for (int z = 0; z < nz; z++) {
				byte[] tmp = ((ByteSet) imageware).getSliceByte(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] /= (short) tmp[k];
				}
			}
			break;
		case ImageWare.SHORT:
			for (int z = 0; z < nz; z++) {
				short[] tmp = ((ShortSet) imageware).getSliceShort(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] /= (short) tmp[k];
				}
			}
			break;
		case ImageWare.FLOAT:
			for (int z = 0; z < nz; z++) {
				float[] tmp = ((FloatSet) imageware).getSliceFloat(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] /= (short) tmp[k];
				}
			}
			break;
		case ImageWare.DOUBLE:
			for (int z = 0; z < nz; z++) {
				double[] tmp = ((DoubleSet) imageware).getSliceDouble(z);
				for (int k = 0; k < nxy; k++) {
					((short[]) data[z])[k] /= (short) tmp[k];
				}
			}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/imageware/ShortSet.java b/src/bilib/src/imageware/ShortSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e6012218e165a730d2ba859e3b5aaaa3bf98f7d
--- /dev/null
+++ b/src/bilib/src/imageware/ShortSet.java
@@ -0,0 +1 @@
+package imageware;

import ij.ImageStack;

import java.awt.Image;

/**
 * Class ShortSet.
 * 
 * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de
 *         Lausanne, Lausanne, Switzerland
 */

public class ShortSet extends ShortProcess implements ImageWare {

	// ------------------------------------------------------------------
	//
	// Constructors section
	//
	// ------------------------------------------------------------------
	protected ShortSet(int nx, int ny, int nz) {
		super(nx, ny, nz);
	}

	protected ShortSet(Image image, int mode) {
		super(image, mode);
	}

	protected ShortSet(ImageStack stack, int mode) {
		super(stack, mode);
	}

	protected ShortSet(ImageStack stack, byte chan) {
		super(stack, chan);
	}

	protected ShortSet(byte[] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(byte[][] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(byte[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(short[] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(short[][] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(short[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(float[] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(float[][] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(float[][][] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(double[] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(double[][] array, int mode) {
		super(array, mode);
	}

	protected ShortSet(double[][][] array, int mode) {
		super(array, mode);
	}

	/**
	 * Duplicate the imageware.
	 * 
	 * Create a new imageware with the same size, same type and same data than
	 * the calling one.
	 * 
	 * @return a duplicated version of this imageware
	 */
	public ImageWare duplicate() {
		ImageWare out = new ShortSet(nx, ny, nz);
		short[] outdata;
		for (int z = 0; z < nz; z++) {
			outdata = (short[]) (((ShortSet) out).data[z]);
			System.arraycopy(data[z], 0, outdata, 0, nxy);
		}
		return out;
	}

	/**
	 * Replicate the imageware.
	 * 
	 * Create a new imageware with the same size, same type than the calling
	 * one. The data are not copied.
	 * 
	 * @return a replicated version of this imageware
	 */
	public ImageWare replicate() {
		return new ShortSet(nx, ny, nz);
	}

	/**
	 * Replicate the imageware.
	 * 
	 * Create a new imageware with the same size and a specified type than the
	 * calling one. The data are not copied.
	 * 
	 * @param type
	 *            requested type
	 * @return a replicated version of this imageware
	 */
	public ImageWare replicate(int type) {
		switch (type) {
		case ImageWare.BYTE:
			return new ByteSet(nx, ny, nz);
		case ImageWare.SHORT:
			return new ShortSet(nx, ny, nz);
		case ImageWare.FLOAT:
			return new FloatSet(nx, ny, nz);
		case ImageWare.DOUBLE:
			return new DoubleSet(nx, ny, nz);
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n"
					+ "-------------------------------------------------------\n");
		}
	}

	/**
	 * Copy all the data of source in the current imageware. The source should
	 * have the same size and same type than the calling one.
	 * 
	 * @param source
	 *            a source imageware
	 */
	public void copy(ImageWare source) {
		if (nx != source.getSizeX())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ nx + " != " + source.getSizeX() + ").\n" + "-------------------------------------------------------\n");
		if (ny != source.getSizeY())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ ny + " != " + source.getSizeY() + ").\n" + "-------------------------------------------------------\n");
		if (nz != source.getSizeZ())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size ("
					+ nz + " != " + source.getSizeZ() + ").\n" + "-------------------------------------------------------\n");
		if (getType() != source.getType())
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same type ("
					+ getType() + " != " + source.getType() + ").\n" + "-------------------------------------------------------\n");
		short[] src;
		for (int z = 0; z < nz; z++) {
			src = (short[]) (((ShortSet) source).data[z]);
			System.arraycopy(src, 0, data[z], 0, nxy);
		}
	}

	/**
	 * convert the imageware in a specified type.
	 * 
	 * Create a new imageware with the same size and converted data than the
	 * calling one.
	 * 
	 * @param type
	 *            indicates the type of the output
	 * @return a converted version of this imageware
	 */
	public ImageWare convert(int type) {
		if (type == ImageWare.SHORT)
			return duplicate();
		ImageWare out = null;
		switch (type) {
		case ImageWare.BYTE: {
			short[] slice;
			out = new ByteSet(nx, ny, nz);
			byte[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((short[]) data[z]);
				outslice = ((byte[]) ((ByteSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (byte) (slice[k] & 0xFFFF);
				}
			}
		}
			break;
		case ImageWare.SHORT: {
			short[] slice;
			out = new ShortSet(nx, ny, nz);
			short[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((short[]) data[z]);
				outslice = ((short[]) ((ShortSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (short) (slice[k] & 0xFFFF);
				}
			}
		}
			break;
		case ImageWare.FLOAT: {
			short[] slice;
			out = new FloatSet(nx, ny, nz);
			float[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((short[]) data[z]);
				outslice = ((float[]) ((FloatSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (float) (slice[k] & 0xFFFF);
				}
			}
		}
			break;
		case ImageWare.DOUBLE: {
			short[] slice;
			out = new DoubleSet(nx, ny, nz);
			double[] outslice;
			for (int z = 0; z < nz; z++) {
				slice = ((short[]) data[z]);
				outslice = ((double[]) ((DoubleSet) out).data[z]);
				for (int k = 0; k < nxy; k++) {
					outslice[k] = (double) (slice[k] & 0xFFFF);
				}
			}
		}
			break;
		default:
			throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n"
					+ "-------------------------------------------------------\n");
		}
		return out;
	}

	/**
	 * Print information of this ImageWare object.
	 */
	public void printInfo() {
		System.out.println("ImageWare object information");
		System.out.println("Dimension: " + getDimension());
		System.out.println("Size: [" + nx + ", " + ny + ", " + nz + "]");
		System.out.println("TotalSize: " + getTotalSize());
		System.out.println("Type: " + getTypeToString());
		System.out.println("Maximun: " + getMaximum());
		System.out.println("Minimun: " + getMinimum());
		System.out.println("Mean: " + getMean());
		System.out.println("Norm1: " + getNorm1());
		System.out.println("Norm2: " + getNorm2());
		System.out.println("Total: " + getTotal());
		System.out.println("");
	}

	/**
	 * Show this ImageWare object.
	 */
	public void show() {
		String title = getTypeToString();
		switch (getDimension()) {
		case 1:
			title += " line";
			break;
		case 2:
			title += " image";
			break;
		case 3:
			title += " volume";
			break;
		}
		Display.show(title, this);
		// ImagePlus imp = new ImagePlus(title, buildImageStack());
		// imp.show();
	}

	/**
	 * Show the data in ImagePlus object with a specify title.
	 * 
	 * @param title
	 *            a string given the title of the window
	 */
	public void show(String title) {
		Display.show(title, this);
		// ImagePlus imp = new ImagePlus(title, buildImageStack());
		// imp.show();
	}

	/**
	 * Return the minimum value of this imageware.
	 * 
	 * @return the min value of this imageware
	 */
	public double getMinimum() {
		double min = Double.MAX_VALUE;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((short[]) data[z]);
			for (int k = 0; k < nxy; k++)
				if ((slice[k] & 0xFFFF) < min)
					min = slice[k] & 0xFFFF;
		}
		return min;
	}

	/**
	 * Return the maximum value of this imageware.
	 * 
	 * @return the max value of this imageware
	 */
	public double getMaximum() {
		double max = -Double.MAX_VALUE;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((short[]) data[z]);
			for (int k = 0; k < nxy; k++)
				if ((slice[k] & 0xFFFF) > max)
					max = slice[k] & 0xFFFF;
		}
		return max;
	}

	/**
	 * Return the mean value of this imageware.
	 * 
	 * @return the mean value of this imageware
	 */
	public double getMean() {
		return getTotal() / (nz * nxy);
	}

	/**
	 * Return the norm value of order 1.
	 * 
	 * @return the norm value of this imageware in L1 sense
	 */
	public double getNorm1() {
		double norm = 0.0;
		double value = 0;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((short[]) data[z]);
			for (int k = 0; k < nxy; k++) {
				value = (double) (slice[k] & 0xFFFF);
				norm += (value > 0.0 ? value : -value);
			}
		}
		return norm;
	}

	/**
	 * Return the norm value of order 2.
	 * 
	 * @return the norm value of this imageware in L2 sense
	 */
	public double getNorm2() {
		double norm = 0.0;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((short[]) data[z]);
			for (int k = 0; k < nxy; k++)
				norm += (slice[k] & 0xFFFF) * (slice[k] & 0xFFFF);
		}
		return norm;
	}

	/**
	 * Return the sum of all pixel in this imageware.
	 * 
	 * @return the total sum of all pixel in this imageware
	 */
	public double getTotal() {
		double total = 0.0;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((short[]) data[z]);
			for (int k = 0; k < nxy; k++)
				total += slice[k] & 0xFFFF;
		}
		return total;
	}

	/**
	 * Return the the minumum [0] and the maximum [1] value of this imageware.
	 * Faster routine than call one getMinimum() and then one getMaximum().
	 * 
	 * @return an array of two values, the min and the max values of the images
	 */
	public double[] getMinMax() {
		double max = -Double.MAX_VALUE;
		double min = Double.MAX_VALUE;
		short[] slice;
		for (int z = 0; z < nz; z++) {
			slice = ((short[]) data[z]);
			for (int k = 0; k < nxy; k++) {
				if ((slice[k] & 0xFFFF) > max)
					max = slice[k] & 0xFFFF;
				if ((slice[k] & 0xFFFF) < min)
					min = slice[k] & 0xFFFF;
			}
		}
		double minmax[] = { min, max };
		return minmax;
	}

} // end of class
\ No newline at end of file
diff --git a/src/bilib/src/jama/CholeskyDecomposition.java b/src/bilib/src/jama/CholeskyDecomposition.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9befbe2a5893aed7356e360f8760595554ea0dc
--- /dev/null
+++ b/src/bilib/src/jama/CholeskyDecomposition.java
@@ -0,0 +1,195 @@
+package jama;
+
+/**
+ * Cholesky Decomposition.
+ * <P>
+ * For a symmetric, positive definite matrix A, the Cholesky decomposition is an
+ * lower triangular matrix L so that A = L*L'.
+ * <P>
+ * If the matrix is not symmetric or positive definite, the constructor returns
+ * a partial decomposition and sets an internal flag that may be queried by the
+ * isSPD() method.
+ */
+
+public class CholeskyDecomposition implements java.io.Serializable {
+
+	/*
+	 * ------------------------ Class variables ------------------------
+	 */
+
+	/**
+	 * Array for internal storage of decomposition.
+	 * 
+	 * @serial internal array storage.
+	 */
+	private double[][]	L;
+
+	/**
+	 * Row and column dimension (square matrix).
+	 * 
+	 * @serial matrix dimension.
+	 */
+	private int			n;
+
+	/**
+	 * Symmetric and positive definite flag.
+	 * 
+	 * @serial is symmetric and positive definite flag.
+	 */
+	private boolean		isspd;
+
+	/*
+	 * ------------------------ Constructor ------------------------
+	 */
+
+	/**
+	 * Cholesky algorithm for symmetric and positive definite matrix.
+	 * 
+	 * @param Arg
+	 *            Square, symmetric matrix.
+	 */
+
+	public CholeskyDecomposition(Matrix Arg) {
+
+		// Initialize.
+		double[][] A = Arg.getArray();
+		n = Arg.getRowDimension();
+		L = new double[n][n];
+		isspd = (Arg.getColumnDimension() == n);
+		// Main loop.
+		for (int j = 0; j < n; j++) {
+			double[] Lrowj = L[j];
+			double d = 0.0;
+			for (int k = 0; k < j; k++) {
+				double[] Lrowk = L[k];
+				double s = 0.0;
+				for (int i = 0; i < k; i++) {
+					s += Lrowk[i] * Lrowj[i];
+				}
+				Lrowj[k] = s = (A[j][k] - s) / L[k][k];
+				d = d + s * s;
+				isspd = isspd & (A[k][j] == A[j][k]);
+			}
+			d = A[j][j] - d;
+			isspd = isspd & (d > 0.0);
+			L[j][j] = Math.sqrt(Math.max(d, 0.0));
+			for (int k = j + 1; k < n; k++) {
+				L[j][k] = 0.0;
+			}
+		}
+	}
+
+	/*
+	 * ------------------------ Temporary, experimental code.
+	 * ------------------------ *\
+	 * 
+	 * \** Right Triangular Cholesky Decomposition. <P> For a symmetric,
+	 * positive definite matrix A, the Right Cholesky decomposition is an upper
+	 * triangular matrix R so that A = R'*R. This constructor computes R with
+	 * the Fortran inspired column oriented algorithm used in LINPACK and
+	 * MATLAB. In Java, we suspect a row oriented, lower triangular
+	 * decomposition is faster. We have temporarily included this constructor
+	 * here until timing experiments confirm this suspicion.\
+	 * 
+	 * \** Array for internal storage of right triangular decomposition. **\
+	 * private transient double[][] R;
+	 * 
+	 * \** Cholesky algorithm for symmetric and positive definite matrix.
+	 * 
+	 * @param A Square, symmetric matrix.
+	 * 
+	 * @param rightflag Actual value ignored.
+	 * 
+	 * @return Structure to access R and isspd flag.\
+	 * 
+	 * public CholeskyDecomposition (Matrix Arg, int rightflag) { // Initialize.
+	 * double[][] A = Arg.getArray(); n = Arg.getColumnDimension(); R = new
+	 * double[n][n]; isspd = (Arg.getColumnDimension() == n); // Main loop. for
+	 * (int j = 0; j < n; j++) { double d = 0.0; for (int k = 0; k < j; k++) {
+	 * double s = A[k][j]; for (int i = 0; i < k; i++) { s = s -
+	 * R[i][k]*R[i][j]; } R[k][j] = s = s/R[k][k]; d = d + s*s; isspd = isspd &
+	 * (A[k][j] == A[j][k]); } d = A[j][j] - d; isspd = isspd & (d > 0.0);
+	 * R[j][j] = Math.sqrt(Math.max(d,0.0)); for (int k = j+1; k < n; k++) {
+	 * R[k][j] = 0.0; } } }
+	 * 
+	 * \** Return upper triangular factor.
+	 * 
+	 * @return R\
+	 * 
+	 * public Matrix getR () { return new Matrix(R,n,n); }
+	 * 
+	 * \* ------------------------ End of temporary code.
+	 * ------------------------
+	 */
+
+	/*
+	 * ------------------------ Public Methods ------------------------
+	 */
+
+	/**
+	 * Is the matrix symmetric and positive definite?
+	 * 
+	 * @return true if A is symmetric and positive definite.
+	 */
+
+	public boolean isSPD() {
+		return isspd;
+	}
+
+	/**
+	 * Return triangular factor.
+	 * 
+	 * @return L
+	 */
+
+	public Matrix getL() {
+		return new Matrix(L, n, n);
+	}
+
+	/**
+	 * Solve A*X = B
+	 * 
+	 * @param B
+	 *            A Matrix with as many rows as A and any number of columns.
+	 * @return X so that L*L'*X = B
+	 * @exception IllegalArgumentException
+	 *                Matrix row dimensions must agree.
+	 * @exception RuntimeException
+	 *                Matrix is not symmetric positive definite.
+	 */
+
+	public Matrix solve(Matrix B) {
+		if (B.getRowDimension() != n) {
+			throw new IllegalArgumentException("Matrix row dimensions must agree.");
+		}
+		if (!isspd) {
+			throw new RuntimeException("Matrix is not symmetric positive definite.");
+		}
+
+		// Copy right hand side.
+		double[][] X = B.getArrayCopy();
+		int nx = B.getColumnDimension();
+
+		// Solve L*Y = B;
+		for (int k = 0; k < n; k++) {
+			for (int j = 0; j < nx; j++) {
+				for (int i = 0; i < k; i++) {
+					X[k][j] -= X[i][j] * L[k][i];
+				}
+				X[k][j] /= L[k][k];
+			}
+		}
+
+		// Solve L'*X = Y;
+		for (int k = n - 1; k >= 0; k--) {
+			for (int j = 0; j < nx; j++) {
+				for (int i = k + 1; i < n; i++) {
+					X[k][j] -= X[i][j] * L[i][k];
+				}
+				X[k][j] /= L[k][k];
+			}
+		}
+
+		return new Matrix(X, n, nx);
+	}
+}
diff --git a/src/bilib/src/jama/EigenvalueDecomposition.java b/src/bilib/src/jama/EigenvalueDecomposition.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f5dff00f920caac461a2c088e90f93498ae2a3a
--- /dev/null
+++ b/src/bilib/src/jama/EigenvalueDecomposition.java
@@ -0,0 +1,990 @@
+package jama;
+
+/**
+ * Eigenvalues and eigenvectors of a real matrix.
+ * <P>
+ * If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is diagonal
+ * and the eigenvector matrix V is orthogonal. I.e. A =
+ * V.times(D.times(V.transpose())) and V.times(V.transpose()) equals the
+ * identity matrix.
+ * <P>
+ * If A is not symmetric, then the eigenvalue matrix D is block diagonal with
+ * the real eigenvalues in 1-by-1 blocks and any complex eigenvalues, lambda +
+ * i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The columns of V represent
+ * the eigenvectors in the sense that A*V = V*D, i.e. A.times(V) equals
+ * V.times(D). The matrix V may be badly conditioned, or even singular, so the
+ * validity of the equation A = V*D*inverse(V) depends upon V.cond().
+ **/
+
+public class EigenvalueDecomposition implements java.io.Serializable {
+
+	/*
+	 * ------------------------ Class variables ------------------------
+	 */
+
+	/**
+	 * Row and column dimension (square matrix).
+	 * 
+	 * @serial matrix dimension.
+	 */
+	private int		n;
+
+	/**
+	 * Symmetry flag.
+	 * 
+	 * @serial internal symmetry flag.
+	 */
+	private boolean	issymmetric;
+
+	/**
+	 * Arrays for internal storage of eigenvalues.
+	 * 
+	 * @serial internal storage of eigenvalues.
+	 */
+	private double[]	d, e;
+
+	/**
+	 * Array for internal storage of eigenvectors.
+	 * 
+	 * @serial internal storage of eigenvectors.
+	 */
+	private double[][]	V;
+
+	/**
+	 * Array for internal storage of nonsymmetric Hessenberg form.
+	 * 
+	 * @serial internal storage of nonsymmetric Hessenberg form.
+	 */
+	private double[][]	H;
+
+	/**
+	 * Working storage for nonsymmetric algorithm.
+	 * 
+	 * @serial working storage for nonsymmetric algorithm.
+	 */
+	private double[]	ort;
+
+	/*
+	 * ------------------------ Private Methods ------------------------
+	 */
+
+	// Symmetric Householder reduction to tridiagonal form.
+
+	private void tred2() {
+
+		// This is derived from the Algol procedures tred2 by
+		// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+		// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+		// Fortran subroutine in EISPACK.
+
+		for (int j = 0; j < n; j++) {
+			d[j] = V[n - 1][j];
+		}
+
+		// Householder reduction to tridiagonal form.
+
+		for (int i = n - 1; i > 0; i--) {
+
+			// Scale to avoid under/overflow.
+
+			double scale = 0.0;
+			double h = 0.0;
+			for (int k = 0; k < i; k++) {
+				scale = scale + Math.abs(d[k]);
+			}
+			if (scale == 0.0) {
+				e[i] = d[i - 1];
+				for (int j = 0; j < i; j++) {
+					d[j] = V[i - 1][j];
+					V[i][j] = 0.0;
+					V[j][i] = 0.0;
+				}
+			}
+			else {
+
+				// Generate Householder vector.
+
+				for (int k = 0; k < i; k++) {
+					d[k] /= scale;
+					h += d[k] * d[k];
+				}
+				double f = d[i - 1];
+				double g = Math.sqrt(h);
+				if (f > 0) {
+					g = -g;
+				}
+				e[i] = scale * g;
+				h = h - f * g;
+				d[i - 1] = f - g;
+				for (int j = 0; j < i; j++) {
+					e[j] = 0.0;
+				}
+
+				// Apply similarity transformation to remaining columns.
+
+				for (int j = 0; j < i; j++) {
+					f = d[j];
+					V[j][i] = f;
+					g = e[j] + V[j][j] * f;
+					for (int k = j + 1; k <= i - 1; k++) {
+						g += V[k][j] * d[k];
+						e[k] += V[k][j] * f;
+					}
+					e[j] = g;
+				}
+				f = 0.0;
+				for (int j = 0; j < i; j++) {
+					e[j] /= h;
+					f += e[j] * d[j];
+				}
+				double hh = f / (h + h);
+				for (int j = 0; j < i; j++) {
+					e[j] -= hh * d[j];
+				}
+				for (int j = 0; j < i; j++) {
+					f = d[j];
+					g = e[j];
+					for (int k = j; k <= i - 1; k++) {
+						V[k][j] -= (f * e[k] + g * d[k]);
+					}
+					d[j] = V[i - 1][j];
+					V[i][j] = 0.0;
+				}
+			}
+			d[i] = h;
+		}
+
+		// Accumulate transformations.
+
+		for (int i = 0; i < n - 1; i++) {
+			V[n - 1][i] = V[i][i];
+			V[i][i] = 1.0;
+			double h = d[i + 1];
+			if (h != 0.0) {
+				for (int k = 0; k <= i; k++) {
+					d[k] = V[k][i + 1] / h;
+				}
+				for (int j = 0; j <= i; j++) {
+					double g = 0.0;
+					for (int k = 0; k <= i; k++) {
+						g += V[k][i + 1] * V[k][j];
+					}
+					for (int k = 0; k <= i; k++) {
+						V[k][j] -= g * d[k];
+					}
+				}
+			}
+			for (int k = 0; k <= i; k++) {
+				V[k][i + 1] = 0.0;
+			}
+		}
+		for (int j = 0; j < n; j++) {
+			d[j] = V[n - 1][j];
+			V[n - 1][j] = 0.0;
+		}
+		V[n - 1][n - 1] = 1.0;
+		e[0] = 0.0;
+	}
+
+	// Symmetric tridiagonal QL algorithm.
+
+	private void tql2() {
+
+		// This is derived from the Algol procedures tql2, by
+		// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+		// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+		// Fortran subroutine in EISPACK.
+
+		for (int i = 1; i < n; i++) {
+			e[i - 1] = e[i];
+		}
+		e[n - 1] = 0.0;
+
+		double f = 0.0;
+		double tst1 = 0.0;
+		double eps = Math.pow(2.0, -52.0);
+		for (int l = 0; l < n; l++) {
+
+			// Find small subdiagonal element
+
+			tst1 = Math.max(tst1, Math.abs(d[l]) + Math.abs(e[l]));
+			int m = l;
+			while (m < n) {
+				if (Math.abs(e[m]) <= eps * tst1) {
+					break;
+				}
+				m++;
+			}
+
+			// If m == l, d[l] is an eigenvalue,
+			// otherwise, iterate.
+
+			if (m > l) {
+				int iter = 0;
+				do {
+					iter = iter + 1; // (Could check iteration count here.)
+
+					// Compute implicit shift
+
+					double g = d[l];
+					double p = (d[l + 1] - g) / (2.0 * e[l]);
+					double r = Maths.hypot(p, 1.0);
+					if (p < 0) {
+						r = -r;
+					}
+					d[l] = e[l] / (p + r);
+					d[l + 1] = e[l] * (p + r);
+					double dl1 = d[l + 1];
+					double h = g - d[l];
+					for (int i = l + 2; i < n; i++) {
+						d[i] -= h;
+					}
+					f = f + h;
+
+					// Implicit QL transformation.
+
+					p = d[m];
+					double c = 1.0;
+					double c2 = c;
+					double c3 = c;
+					double el1 = e[l + 1];
+					double s = 0.0;
+					double s2 = 0.0;
+					for (int i = m - 1; i >= l; i--) {
+						c3 = c2;
+						c2 = c;
+						s2 = s;
+						g = c * e[i];
+						h = c * p;
+						r = Maths.hypot(p, e[i]);
+						e[i + 1] = s * r;
+						s = e[i] / r;
+						c = p / r;
+						p = c * d[i] - s * g;
+						d[i + 1] = h + s * (c * g + s * d[i]);
+
+						// Accumulate transformation.
+
+						for (int k = 0; k < n; k++) {
+							h = V[k][i + 1];
+							V[k][i + 1] = s * V[k][i] + c * h;
+							V[k][i] = c * V[k][i] - s * h;
+						}
+					}
+					p = -s * s2 * c3 * el1 * e[l] / dl1;
+					e[l] = s * p;
+					d[l] = c * p;
+
+					// Check for convergence.
+
+				}
+				while (Math.abs(e[l]) > eps * tst1);
+			}
+			d[l] = d[l] + f;
+			e[l] = 0.0;
+		}
+
+		// Sort eigenvalues and corresponding vectors.
+
+		for (int i = 0; i < n - 1; i++) {
+			int k = i;
+			double p = d[i];
+			for (int j = i + 1; j < n; j++) {
+				if (d[j] < p) {
+					k = j;
+					p = d[j];
+				}
+			}
+			if (k != i) {
+				d[k] = d[i];
+				d[i] = p;
+				for (int j = 0; j < n; j++) {
+					p = V[j][i];
+					V[j][i] = V[j][k];
+					V[j][k] = p;
+				}
+			}
+		}
+	}
+
+	// Nonsymmetric reduction to Hessenberg form.
+
+	private void orthes() {
+
+		// This is derived from the Algol procedures orthes and ortran,
+		// by Martin and Wilkinson, Handbook for Auto. Comp.,
+		// Vol.ii-Linear Algebra, and the corresponding
+		// Fortran subroutines in EISPACK.
+
+		int low = 0;
+		int high = n - 1;
+
+		for (int m = low + 1; m <= high - 1; m++) {
+
+			// Scale column.
+
+			double scale = 0.0;
+			for (int i = m; i <= high; i++) {
+				scale = scale + Math.abs(H[i][m - 1]);
+			}
+			if (scale != 0.0) {
+
+				// Compute Householder transformation.
+
+				double h = 0.0;
+				for (int i = high; i >= m; i--) {
+					ort[i] = H[i][m - 1] / scale;
+					h += ort[i] * ort[i];
+				}
+				double g = Math.sqrt(h);
+				if (ort[m] > 0) {
+					g = -g;
+				}
+				h = h - ort[m] * g;
+				ort[m] = ort[m] - g;
+
+				// Apply Householder similarity transformation
+				// H = (I-u*u'/h)*H*(I-u*u')/h)
+
+				for (int j = m; j < n; j++) {
+					double f = 0.0;
+					for (int i = high; i >= m; i--) {
+						f += ort[i] * H[i][j];
+					}
+					f = f / h;
+					for (int i = m; i <= high; i++) {
+						H[i][j] -= f * ort[i];
+					}
+				}
+
+				for (int i = 0; i <= high; i++) {
+					double f = 0.0;
+					for (int j = high; j >= m; j--) {
+						f += ort[j] * H[i][j];
+					}
+					f = f / h;
+					for (int j = m; j <= high; j++) {
+						H[i][j] -= f * ort[j];
+					}
+				}
+				ort[m] = scale * ort[m];
+				H[m][m - 1] = scale * g;
+			}
+		}
+
+		// Accumulate transformations (Algol's ortran).
+
+		for (int i = 0; i < n; i++) {
+			for (int j = 0; j < n; j++) {
+				V[i][j] = (i == j ? 1.0 : 0.0);
+			}
+		}
+
+		for (int m = high - 1; m >= low + 1; m--) {
+			if (H[m][m - 1] != 0.0) {
+				for (int i = m + 1; i <= high; i++) {
+					ort[i] = H[i][m - 1];
+				}
+				for (int j = m; j <= high; j++) {
+					double g = 0.0;
+					for (int i = m; i <= high; i++) {
+						g += ort[i] * V[i][j];
+					}
+					// Double division avoids possible underflow
+					g = (g / ort[m]) / H[m][m - 1];
+					for (int i = m; i <= high; i++) {
+						V[i][j] += g * ort[i];
+					}
+				}
+			}
+		}
+	}
+
+	// Complex scalar division.
+
+	private transient double	cdivr, cdivi;
+
+	private void cdiv(double xr, double xi, double yr, double yi) {
+		double r, d;
+		if (Math.abs(yr) > Math.abs(yi)) {
+			r = yi / yr;
+			d = yr + r * yi;
+			cdivr = (xr + r * xi) / d;
+			cdivi = (xi - r * xr) / d;
+		}
+		else {
+			r = yr / yi;
+			d = yi + r * yr;
+			cdivr = (r * xr + xi) / d;
+			cdivi = (r * xi - xr) / d;
+		}
+	}
+
+	// Nonsymmetric reduction from Hessenberg to real Schur form.
+
+	private void hqr2() {
+
+		// This is derived from the Algol procedure hqr2,
+		// by Martin and Wilkinson, Handbook for Auto. Comp.,
+		// Vol.ii-Linear Algebra, and the corresponding
+		// Fortran subroutine in EISPACK.
+
+		// Initialize
+
+		int nn = this.n;
+		int n = nn - 1;
+		int low = 0;
+		int high = nn - 1;
+		double eps = Math.pow(2.0, -52.0);
+		double exshift = 0.0;
+		double p = 0, q = 0, r = 0, s = 0, z = 0, t, w, x, y;
+
+		// Store roots isolated by balanc and compute matrix norm
+
+		double norm = 0.0;
+		for (int i = 0; i < nn; i++) {
+			if (i < low | i > high) {
+				d[i] = H[i][i];
+				e[i] = 0.0;
+			}
+			for (int j = Math.max(i - 1, 0); j < nn; j++) {
+				norm = norm + Math.abs(H[i][j]);
+			}
+		}
+
+		// Outer loop over eigenvalue index
+
+		int iter = 0;
+		while (n >= low) {
+
+			// Look for single small sub-diagonal element
+
+			int l = n;
+			while (l > low) {
+				s = Math.abs(H[l - 1][l - 1]) + Math.abs(H[l][l]);
+				if (s == 0.0) {
+					s = norm;
+				}
+				if (Math.abs(H[l][l - 1]) < eps * s) {
+					break;
+				}
+				l--;
+			}
+
+			// Check for convergence
+			// One root found
+
+			if (l == n) {
+				H[n][n] = H[n][n] + exshift;
+				d[n] = H[n][n];
+				e[n] = 0.0;
+				n--;
+				iter = 0;
+
+				// Two roots found
+
+			}
+			else if (l == n - 1) {
+				w = H[n][n - 1] * H[n - 1][n];
+				p = (H[n - 1][n - 1] - H[n][n]) / 2.0;
+				q = p * p + w;
+				z = Math.sqrt(Math.abs(q));
+				H[n][n] = H[n][n] + exshift;
+				H[n - 1][n - 1] = H[n - 1][n - 1] + exshift;
+				x = H[n][n];
+
+				// Real pair
+
+				if (q >= 0) {
+					if (p >= 0) {
+						z = p + z;
+					}
+					else {
+						z = p - z;
+					}
+					d[n - 1] = x + z;
+					d[n] = d[n - 1];
+					if (z != 0.0) {
+						d[n] = x - w / z;
+					}
+					e[n - 1] = 0.0;
+					e[n] = 0.0;
+					x = H[n][n - 1];
+					s = Math.abs(x) + Math.abs(z);
+					p = x / s;
+					q = z / s;
+					r = Math.sqrt(p * p + q * q);
+					p = p / r;
+					q = q / r;
+
+					// Row modification
+
+					for (int j = n - 1; j < nn; j++) {
+						z = H[n - 1][j];
+						H[n - 1][j] = q * z + p * H[n][j];
+						H[n][j] = q * H[n][j] - p * z;
+					}
+
+					// Column modification
+
+					for (int i = 0; i <= n; i++) {
+						z = H[i][n - 1];
+						H[i][n - 1] = q * z + p * H[i][n];
+						H[i][n] = q * H[i][n] - p * z;
+					}
+
+					// Accumulate transformations
+
+					for (int i = low; i <= high; i++) {
+						z = V[i][n - 1];
+						V[i][n - 1] = q * z + p * V[i][n];
+						V[i][n] = q * V[i][n] - p * z;
+					}
+
+					// Complex pair
+
+				}
+				else {
+					d[n - 1] = x + p;
+					d[n] = x + p;
+					e[n - 1] = z;
+					e[n] = -z;
+				}
+				n = n - 2;
+				iter = 0;
+
+				// No convergence yet
+
+			}
+			else {
+
+				// Form shift
+
+				x = H[n][n];
+				y = 0.0;
+				w = 0.0;
+				if (l < n) {
+					y = H[n - 1][n - 1];
+					w = H[n][n - 1] * H[n - 1][n];
+				}
+
+				// Wilkinson's original ad hoc shift
+
+				if (iter == 10) {
+					exshift += x;
+					for (int i = low; i <= n; i++) {
+						H[i][i] -= x;
+					}
+					s = Math.abs(H[n][n - 1]) + Math.abs(H[n - 1][n - 2]);
+					x = y = 0.75 * s;
+					w = -0.4375 * s * s;
+				}
+
+				// MATLAB's new ad hoc shift
+
+				if (iter == 30) {
+					s = (y - x) / 2.0;
+					s = s * s + w;
+					if (s > 0) {
+						s = Math.sqrt(s);
+						if (y < x) {
+							s = -s;
+						}
+						s = x - w / ((y - x) / 2.0 + s);
+						for (int i = low; i <= n; i++) {
+							H[i][i] -= s;
+						}
+						exshift += s;
+						x = y = w = 0.964;
+					}
+				}
+
+				iter = iter + 1; // (Could check iteration count here.)
+
+				// Look for two consecutive small sub-diagonal elements
+
+				int m = n - 2;
+				while (m >= l) {
+					z = H[m][m];
+					r = x - z;
+					s = y - z;
+					p = (r * s - w) / H[m + 1][m] + H[m][m + 1];
+					q = H[m + 1][m + 1] - z - r - s;
+					r = H[m + 2][m + 1];
+					s = Math.abs(p) + Math.abs(q) + Math.abs(r);
+					p = p / s;
+					q = q / s;
+					r = r / s;
+					if (m == l) {
+						break;
+					}
+					if (Math.abs(H[m][m - 1]) * (Math.abs(q) + Math.abs(r)) < eps * (Math.abs(p) * (Math.abs(H[m - 1][m - 1]) + Math.abs(z) + Math.abs(H[m + 1][m + 1])))) {
+						break;
+					}
+					m--;
+				}
+
+				for (int i = m + 2; i <= n; i++) {
+					H[i][i - 2] = 0.0;
+					if (i > m + 2) {
+						H[i][i - 3] = 0.0;
+					}
+				}
+
+				// Double QR step involving rows l:n and columns m:n
+
+				for (int k = m; k <= n - 1; k++) {
+					boolean notlast = (k != n - 1);
+					if (k != m) {
+						p = H[k][k - 1];
+						q = H[k + 1][k - 1];
+						r = (notlast ? H[k + 2][k - 1] : 0.0);
+						x = Math.abs(p) + Math.abs(q) + Math.abs(r);
+						if (x != 0.0) {
+							p = p / x;
+							q = q / x;
+							r = r / x;
+						}
+					}
+					if (x == 0.0) {
+						break;
+					}
+					s = Math.sqrt(p * p + q * q + r * r);
+					if (p < 0) {
+						s = -s;
+					}
+					if (s != 0) {
+						if (k != m) {
+							H[k][k - 1] = -s * x;
+						}
+						else if (l != m) {
+							H[k][k - 1] = -H[k][k - 1];
+						}
+						p = p + s;
+						x = p / s;
+						y = q / s;
+						z = r / s;
+						q = q / p;
+						r = r / p;
+
+						// Row modification
+
+						for (int j = k; j < nn; j++) {
+							p = H[k][j] + q * H[k + 1][j];
+							if (notlast) {
+								p = p + r * H[k + 2][j];
+								H[k + 2][j] = H[k + 2][j] - p * z;
+							}
+							H[k][j] = H[k][j] - p * x;
+							H[k + 1][j] = H[k + 1][j] - p * y;
+						}
+
+						// Column modification
+
+						for (int i = 0; i <= Math.min(n, k + 3); i++) {
+							p = x * H[i][k] + y * H[i][k + 1];
+							if (notlast) {
+								p = p + z * H[i][k + 2];
+								H[i][k + 2] = H[i][k + 2] - p * r;
+							}
+							H[i][k] = H[i][k] - p;
+							H[i][k + 1] = H[i][k + 1] - p * q;
+						}
+
+						// Accumulate transformations
+
+						for (int i = low; i <= high; i++) {
+							p = x * V[i][k] + y * V[i][k + 1];
+							if (notlast) {
+								p = p + z * V[i][k + 2];
+								V[i][k + 2] = V[i][k + 2] - p * r;
+							}
+							V[i][k] = V[i][k] - p;
+							V[i][k + 1] = V[i][k + 1] - p * q;
+						}
+					} // (s != 0)
+				} // k loop
+			} // check convergence
+		} // while (n >= low)
+
+		// Backsubstitute to find vectors of upper triangular form
+
+		if (norm == 0.0) {
+			return;
+		}
+
+		for (n = nn - 1; n >= 0; n--) {
+			p = d[n];
+			q = e[n];
+
+			// Real vector
+
+			if (q == 0) {
+				int l = n;
+				H[n][n] = 1.0;
+				for (int i = n - 1; i >= 0; i--) {
+					w = H[i][i] - p;
+					r = 0.0;
+					for (int j = l; j <= n; j++) {
+						r = r + H[i][j] * H[j][n];
+					}
+					if (e[i] < 0.0) {
+						z = w;
+						s = r;
+					}
+					else {
+						l = i;
+						if (e[i] == 0.0) {
+							if (w != 0.0) {
+								H[i][n] = -r / w;
+							}
+							else {
+								H[i][n] = -r / (eps * norm);
+							}
+
+							// Solve real equations
+
+						}
+						else {
+							x = H[i][i + 1];
+							y = H[i + 1][i];
+							q = (d[i] - p) * (d[i] - p) + e[i] * e[i];
+							t = (x * s - z * r) / q;
+							H[i][n] = t;
+							if (Math.abs(x) > Math.abs(z)) {
+								H[i + 1][n] = (-r - w * t) / x;
+							}
+							else {
+								H[i + 1][n] = (-s - y * t) / z;
+							}
+						}
+
+						// Overflow control
+
+						t = Math.abs(H[i][n]);
+						if ((eps * t) * t > 1) {
+							for (int j = i; j <= n; j++) {
+								H[j][n] = H[j][n] / t;
+							}
+						}
+					}
+				}
+
+				// Complex vector
+
+			}
+			else if (q < 0) {
+				int l = n - 1;
+
+				// Last vector component imaginary so matrix is triangular
+
+				if (Math.abs(H[n][n - 1]) > Math.abs(H[n - 1][n])) {
+					H[n - 1][n - 1] = q / H[n][n - 1];
+					H[n - 1][n] = -(H[n][n] - p) / H[n][n - 1];
+				}
+				else {
+					cdiv(0.0, -H[n - 1][n], H[n - 1][n - 1] - p, q);
+					H[n - 1][n - 1] = cdivr;
+					H[n - 1][n] = cdivi;
+				}
+				H[n][n - 1] = 0.0;
+				H[n][n] = 1.0;
+				for (int i = n - 2; i >= 0; i--) {
+					double ra, sa, vr, vi;
+					ra = 0.0;
+					sa = 0.0;
+					for (int j = l; j <= n; j++) {
+						ra = ra + H[i][j] * H[j][n - 1];
+						sa = sa + H[i][j] * H[j][n];
+					}
+					w = H[i][i] - p;
+
+					if (e[i] < 0.0) {
+						z = w;
+						r = ra;
+						s = sa;
+					}
+					else {
+						l = i;
+						if (e[i] == 0) {
+							cdiv(-ra, -sa, w, q);
+							H[i][n - 1] = cdivr;
+							H[i][n] = cdivi;
+						}
+						else {
+
+							// Solve complex equations
+
+							x = H[i][i + 1];
+							y = H[i + 1][i];
+							vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q;
+							vi = (d[i] - p) * 2.0 * q;
+							if (vr == 0.0 & vi == 0.0) {
+								vr = eps * norm * (Math.abs(w) + Math.abs(q) + Math.abs(x) + Math.abs(y) + Math.abs(z));
+							}
+							cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi);
+							H[i][n - 1] = cdivr;
+							H[i][n] = cdivi;
+							if (Math.abs(x) > (Math.abs(z) + Math.abs(q))) {
+								H[i + 1][n - 1] = (-ra - w * H[i][n - 1] + q * H[i][n]) / x;
+								H[i + 1][n] = (-sa - w * H[i][n] - q * H[i][n - 1]) / x;
+							}
+							else {
+								cdiv(-r - y * H[i][n - 1], -s - y * H[i][n], z, q);
+								H[i + 1][n - 1] = cdivr;
+								H[i + 1][n] = cdivi;
+							}
+						}
+
+						// Overflow control
+
+						t = Math.max(Math.abs(H[i][n - 1]), Math.abs(H[i][n]));
+						if ((eps * t) * t > 1) {
+							for (int j = i; j <= n; j++) {
+								H[j][n - 1] = H[j][n - 1] / t;
+								H[j][n] = H[j][n] / t;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		// Vectors of isolated roots
+
+		for (int i = 0; i < nn; i++) {
+			if (i < low | i > high) {
+				for (int j = i; j < nn; j++) {
+					V[i][j] = H[i][j];
+				}
+			}
+		}
+
+		// Back transformation to get eigenvectors of original matrix
+
+		for (int j = nn - 1; j >= low; j--) {
+			for (int i = low; i <= high; i++) {
+				z = 0.0;
+				for (int k = low; k <= Math.min(j, high); k++) {
+					z = z + V[i][k] * H[k][j];
+				}
+				V[i][j] = z;
+			}
+		}
+	}
+
+	/*
+	 * ------------------------ Constructor ------------------------
+	 */
+
+	/**
+	 * Check for symmetry, then construct the eigenvalue decomposition
+	 * 
+	 * @param Arg
+	 *            Square matrix
+	 */
+
+	public EigenvalueDecomposition(Matrix Arg) {
+		double[][] A = Arg.getArray();
+		n = Arg.getColumnDimension();
+		V = new double[n][n];
+		d = new double[n];
+		e = new double[n];
+
+		issymmetric = true;
+		for (int j = 0; (j < n) & issymmetric; j++) {
+			for (int i = 0; (i < n) & issymmetric; i++) {
+				issymmetric = (A[i][j] == A[j][i]);
+			}
+		}
+
+		if (issymmetric) {
+			for (int i = 0; i < n; i++) {
+				for (int j = 0; j < n; j++) {
+					V[i][j] = A[i][j];
+				}
+			}
+
+			// Tridiagonalize.
+			tred2();
+
+			// Diagonalize.
+			tql2();
+
+		}
+		else {
+			H = new double[n][n];
+			ort = new double[n];
+
+			for (int j = 0; j < n; j++) {
+				for (int i = 0; i < n; i++) {
+					H[i][j] = A[i][j];
+				}
+			}
+
+			// Reduce to Hessenberg form.
+			orthes();
+
+			// Reduce Hessenberg to real Schur form.
+			hqr2();
+		}
+	}
+
+	/*
+	 * ------------------------ Public Methods ------------------------
+	 */
+
+	/**
+	 * Return the eigenvector matrix
+	 * 
+	 * @return V
+	 */
+
+	public Matrix getV() {
+		return new Matrix(V, n, n);
+	}
+
+	/**
+	 * Return the real parts of the eigenvalues
+	 * 
+	 * @return real(diag(D))
+	 */
+
+	public double[] getRealEigenvalues() {
+		return d;
+	}
+
+	/**
+	 * Return the imaginary parts of the eigenvalues
+	 * 
+	 * @return imag(diag(D))
+	 */
+
+	public double[] getImagEigenvalues() {
+		return e;
+	}
+
+	/**
+	 * Return the block diagonal eigenvalue matrix
+	 * 
+	 * @return D
+	 */
+
+	public Matrix getD() {
+		Matrix X = new Matrix(n, n);
+		double[][] D = X.getArray();
+		for (int i = 0; i < n; i++) {
+			for (int j = 0; j < n; j++) {
+				D[i][j] = 0.0;
+			}
+			D[i][i] = d[i];
+			if (e[i] > 0) {
+				D[i][i + 1] = e[i];
+			}
+			else if (e[i] < 0) {
+				D[i][i - 1] = e[i];
+			}
+		}
+		return X;
+	}
+}
diff --git a/src/bilib/src/jama/LUDecomposition.java b/src/bilib/src/jama/LUDecomposition.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc18e7bb28e845909f5e48bf6858d9ec11f7da4f
--- /dev/null
+++ b/src/bilib/src/jama/LUDecomposition.java
@@ -0,0 +1,318 @@
+package jama;
+
+/**
+ * LU Decomposition.
+ * <P>
+ * For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n unit
+ * lower triangular matrix L, an n-by-n upper triangular matrix U, and a
+ * permutation vector piv of length m so that A(piv,:) = L*U. If m < n, then L
+ * is m-by-m and U is m-by-n.
+ * <P>
+ * The LU decompostion with pivoting always exists, even if the matrix is
+ * singular, so the constructor will never fail. The primary use of the LU
+ * decomposition is in the solution of square systems of simultaneous linear
+ * equations. This will fail if isNonsingular() returns false.
+ */
+
+public class LUDecomposition implements java.io.Serializable {
+
+	/*
+	 * ------------------------ Class variables ------------------------
+	 */
+
+	/**
+	 * Array for internal storage of decomposition.
+	 * 
+	 * @serial internal array storage.
+	 */
+	private double[][]	LU;
+
+	/**
+	 * Row and column dimensions, and pivot sign.
+	 * 
+	 * @serial column dimension.
+	 * @serial row dimension.
+	 * @serial pivot sign.
+	 */
+	private int			m, n, pivsign;
+
+	/**
+	 * Internal storage of pivot vector.
+	 * 
+	 * @serial pivot vector.
+	 */
+	private int[]		piv;
+
+	/*
+	 * ------------------------ Constructor ------------------------
+	 */
+
+	/**
+	 * LU Decomposition
+	 * 
+	 * @param A
+	 *            Rectangular matrix
+	 */
+
+	public LUDecomposition(Matrix A) {
+
+		// Use a "left-looking", dot-product, Crout/Doolittle algorithm.
+
+		LU = A.getArrayCopy();
+		m = A.getRowDimension();
+		n = A.getColumnDimension();
+		piv = new int[m];
+		for (int i = 0; i < m; i++) {
+			piv[i] = i;
+		}
+		pivsign = 1;
+		double[] LUrowi;
+		double[] LUcolj = new double[m];
+
+		// Outer loop.
+
+		for (int j = 0; j < n; j++) {
+
+			// Make a copy of the j-th column to localize references.
+
+			for (int i = 0; i < m; i++) {
+				LUcolj[i] = LU[i][j];
+			}
+
+			// Apply previous transformations.
+
+			for (int i = 0; i < m; i++) {
+				LUrowi = LU[i];
+
+				// Most of the time is spent in the following dot product.
+
+				int kmax = Math.min(i, j);
+				double s = 0.0;
+				for (int k = 0; k < kmax; k++) {
+					s += LUrowi[k] * LUcolj[k];
+				}
+
+				LUrowi[j] = LUcolj[i] -= s;
+			}
+
+			// Find pivot and exchange if necessary.
+
+			int p = j;
+			for (int i = j + 1; i < m; i++) {
+				if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) {
+					p = i;
+				}
+			}
+			if (p != j) {
+				for (int k = 0; k < n; k++) {
+					double t = LU[p][k];
+					LU[p][k] = LU[j][k];
+					LU[j][k] = t;
+				}
+				int k = piv[p];
+				piv[p] = piv[j];
+				piv[j] = k;
+				pivsign = -pivsign;
+			}
+
+			// Compute multipliers.
+
+			if (j < m & LU[j][j] != 0.0) {
+				for (int i = j + 1; i < m; i++) {
+					LU[i][j] /= LU[j][j];
+				}
+			}
+		}
+	}
+
+	/*
+	 * ------------------------ Temporary, experimental code.
+	 * ------------------------ *\
+	 * 
+	 * \** LU Decomposition, computed by Gaussian elimination. <P> This
+	 * constructor computes L and U with the "daxpy"-based elimination algorithm
+	 * used in LINPACK and MATLAB. In Java, we suspect the dot-product, Crout
+	 * algorithm will be faster. We have temporarily included this constructor
+	 * until timing experiments confirm this suspicion. <P>
+	 * 
+	 * @param A Rectangular matrix
+	 * 
+	 * @param linpackflag Use Gaussian elimination. Actual value ignored.
+	 * 
+	 * @return Structure to access L, U and piv.\
+	 * 
+	 * public LUDecomposition (Matrix A, int linpackflag) { // Initialize. LU =
+	 * A.getArrayCopy(); m = A.getRowDimension(); n = A.getColumnDimension();
+	 * piv = new int[m]; for (int i = 0; i < m; i++) { piv[i] = i; } pivsign =
+	 * 1; // Main loop. for (int k = 0; k < n; k++) { // Find pivot. int p = k;
+	 * for (int i = k+1; i < m; i++) { if (Math.abs(LU[i][k]) >
+	 * Math.abs(LU[p][k])) { p = i; } } // Exchange if necessary. if (p != k) {
+	 * for (int j = 0; j < n; j++) { double t = LU[p][j]; LU[p][j] = LU[k][j];
+	 * LU[k][j] = t; } int t = piv[p]; piv[p] = piv[k]; piv[k] = t; pivsign =
+	 * -pivsign; } // Compute multipliers and eliminate k-th column. if
+	 * (LU[k][k] != 0.0) { for (int i = k+1; i < m; i++) { LU[i][k] /= LU[k][k];
+	 * for (int j = k+1; j < n; j++) { LU[i][j] -= LU[i][k]*LU[k][j]; } } } } }
+	 * 
+	 * \* ------------------------ End of temporary code.
+	 * ------------------------
+	 */
+
+	/*
+	 * ------------------------ Public Methods ------------------------
+	 */
+
+	/**
+	 * Is the matrix nonsingular?
+	 * 
+	 * @return true if U, and hence A, is nonsingular.
+	 */
+
+	public boolean isNonsingular() {
+		for (int j = 0; j < n; j++) {
+			if (LU[j][j] == 0)
+				return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Return lower triangular factor
+	 * 
+	 * @return L
+	 */
+
+	public Matrix getL() {
+		Matrix X = new Matrix(m, n);
+		double[][] L = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				if (i > j) {
+					L[i][j] = LU[i][j];
+				}
+				else if (i == j) {
+					L[i][j] = 1.0;
+				}
+				else {
+					L[i][j] = 0.0;
+				}
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Return upper triangular factor
+	 * 
+	 * @return U
+	 */
+
+	public Matrix getU() {
+		Matrix X = new Matrix(n, n);
+		double[][] U = X.getArray();
+		for (int i = 0; i < n; i++) {
+			for (int j = 0; j < n; j++) {
+				if (i <= j) {
+					U[i][j] = LU[i][j];
+				}
+				else {
+					U[i][j] = 0.0;
+				}
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Return pivot permutation vector
+	 * 
+	 * @return piv
+	 */
+
+	public int[] getPivot() {
+		int[] p = new int[m];
+		for (int i = 0; i < m; i++) {
+			p[i] = piv[i];
+		}
+		return p;
+	}
+
+	/**
+	 * Return pivot permutation vector as a one-dimensional double array
+	 * 
+	 * @return (double) piv
+	 */
+
+	public double[] getDoublePivot() {
+		double[] vals = new double[m];
+		for (int i = 0; i < m; i++) {
+			vals[i] = (double) piv[i];
+		}
+		return vals;
+	}
+
+	/**
+	 * Determinant
+	 * 
+	 * @return det(A)
+	 * @exception IllegalArgumentException
+	 *                Matrix must be square
+	 */
+
+	public double det() {
+		if (m != n) {
+			throw new IllegalArgumentException("Matrix must be square.");
+		}
+		double d = (double) pivsign;
+		for (int j = 0; j < n; j++) {
+			d *= LU[j][j];
+		}
+		return d;
+	}
+
+	/**
+	 * Solve A*X = B
+	 * 
+	 * @param B
+	 *            A Matrix with as many rows as A and any number of columns.
+	 * @return X so that L*U*X = B(piv,:)
+	 * @exception IllegalArgumentException
+	 *                Matrix row dimensions must agree.
+	 * @exception RuntimeException
+	 *                Matrix is singular.
+	 */
+
+	public Matrix solve(Matrix B) {
+		if (B.getRowDimension() != m) {
+			throw new IllegalArgumentException("Matrix row dimensions must agree.");
+		}
+		if (!this.isNonsingular()) {
+			throw new RuntimeException("Matrix is singular.");
+		}
+
+		// Copy right hand side with pivoting
+		int nx = B.getColumnDimension();
+		Matrix Xmat = B.getMatrix(piv, 0, nx - 1);
+		double[][] X = Xmat.getArray();
+
+		// Solve L*Y = B(piv,:)
+		for (int k = 0; k < n; k++) {
+			for (int i = k + 1; i < n; i++) {
+				for (int j = 0; j < nx; j++) {
+					X[i][j] -= X[k][j] * LU[i][k];
+				}
+			}
+		}
+		// Solve U*X = Y;
+		for (int k = n - 1; k >= 0; k--) {
+			for (int j = 0; j < nx; j++) {
+				X[k][j] /= LU[k][k];
+			}
+			for (int i = 0; i < k; i++) {
+				for (int j = 0; j < nx; j++) {
+					X[i][j] -= X[k][j] * LU[i][k];
+				}
+			}
+		}
+		return Xmat;
+	}
+}
diff --git a/src/bilib/src/jama/Maths.java b/src/bilib/src/jama/Maths.java
new file mode 100644
index 0000000000000000000000000000000000000000..f58071da8485d23e199f0c873ecba58bb1a39e46
--- /dev/null
+++ b/src/bilib/src/jama/Maths.java
@@ -0,0 +1 @@
+package jama;

public class Maths {

	/** sqrt(a^2 + b^2) without under/overflow. **/

	public static double hypot(double a, double b) {

		double r;

		if (Math.abs(a) > Math.abs(b)) {

			r = b / a;

			r = Math.abs(a) * Math.sqrt(1 + r * r);

		}
		else if (b != 0) {

			r = a / b;

			r = Math.abs(b) * Math.sqrt(1 + r * r);

		}
		else {

			r = 0.0;

		}

		return r;

	}

}
\ No newline at end of file
diff --git a/src/bilib/src/jama/Matrix.java b/src/bilib/src/jama/Matrix.java
new file mode 100644
index 0000000000000000000000000000000000000000..c42f4ff518fc65cd27efb50ce7561a0d049a9af5
--- /dev/null
+++ b/src/bilib/src/jama/Matrix.java
@@ -0,0 +1,1276 @@
+package jama;
+
+import java.io.BufferedReader;
+import java.io.PrintWriter;
+import java.io.StreamTokenizer;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+/**
+ * Jama = Java Matrix class.
+ * <P>
+ * The Java Matrix Class provides the fundamental operations of numerical linear
+ * algebra. Various constructors create Matrices from two dimensional arrays of
+ * double precision floating point numbers. Various "gets" and "sets" provide
+ * access to submatrices and matrix elements. Several methods implement basic
+ * matrix arithmetic, including matrix addition and multiplication, matrix
+ * norms, and element-by-element array operations. Methods for reading and
+ * printing matrices are also included. All the operations in this version of
+ * the Matrix Class involve real matrices. Complex matrices may be handled in a
+ * future version.
+ * <P>
+ * Five fundamental matrix decompositions, which consist of pairs or triples of
+ * matrices, permutation vectors, and the like, produce results in five
+ * decomposition classes. These decompositions are accessed by the Matrix class
+ * to compute solutions of simultaneous linear equations, determinants, inverses
+ * and other matrix functions. The five decompositions are:
+ * <P>
+ * <UL>
+ * <LI>Cholesky Decomposition of symmetric, positive definite matrices.
+ * <LI>LU Decomposition of rectangular matrices.
+ * <LI>QR Decomposition of rectangular matrices.
+ * <LI>Singular Value Decomposition of rectangular matrices.
+ * <LI>Eigenvalue Decomposition of both symmetric and nonsymmetric square
+ * matrices.
+ * </UL>
+ * <DL>
+ * <DT><B>Example of use:</B></DT>
+ * <P>
+ * <DD>Solve a linear system A x = b and compute the residual norm, ||b - A x||.
+ * <P>
+ * 
+ * <PRE>
+ * double[][]	vals	= { { 1., 2., 3 }, { 4., 5., 6. }, { 7., 8., 10. } };
+ * 																			Matrix	A	= new Matrix(vals);
+ * 																											Matrix	b	= Matrix.random(3, 1);
+ * 																																				Matrix	x	= A.solve(b);
+ * 																																											Matrix	r	= A.times(
+ * 																																																x)
+ * 																																																.minus(b);
+ * 																																																			double	rnorm	= r.normInf();
+ * </PRE>
+ * 
+ * </DD>
+ * </DL>
+ * 
+ * @author The MathWorks, Inc. and the National Institute of Standards and
+ *         Technology.
+ * @version 5 August 1998
+ */
+
+public class Matrix implements Cloneable, java.io.Serializable {
+
+	/*
+	 * ------------------------ Class variables ------------------------
+	 */
+
+	/**
+	 * Array for internal storage of elements.
+	 * 
+	 * @serial internal array storage.
+	 */
+	private double[][]	A;
+
+	/**
+	 * Row and column dimensions.
+	 * 
+	 * @serial row dimension.
+	 * @serial column dimension.
+	 */
+	private int			m, n;
+
+	/*
+	 * ------------------------ Constructors ------------------------
+	 */
+
+	/**
+	 * Construct an m-by-n matrix of zeros.
+	 * 
+	 * @param m
+	 *            Number of rows.
+	 * @param n
+	 *            Number of colums.
+	 */
+
+	public Matrix(int m, int n) {
+		this.m = m;
+		this.n = n;
+		A = new double[m][n];
+	}
+
+	/**
+	 * Construct an m-by-n constant matrix.
+	 * 
+	 * @param m
+	 *            Number of rows.
+	 * @param n
+	 *            Number of colums.
+	 * @param s
+	 *            Fill the matrix with this scalar value.
+	 */
+
+	public Matrix(int m, int n, double s) {
+		this.m = m;
+		this.n = n;
+		A = new double[m][n];
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				A[i][j] = s;
+			}
+		}
+	}
+
+	/**
+	 * Construct a matrix from a 2-D array.
+	 * 
+	 * @param A
+	 *            Two-dimensional array of doubles.
+	 * @exception IllegalArgumentException
+	 *                All rows must have the same length
+	 * @see #constructWithCopy
+	 */
+
+	public Matrix(double[][] A) {
+		m = A.length;
+		n = A[0].length;
+		for (int i = 0; i < m; i++) {
+			if (A[i].length != n) {
+				throw new IllegalArgumentException("All rows must have the same length.");
+			}
+		}
+		this.A = A;
+	}
+
+	/**
+	 * Construct a matrix quickly without checking arguments.
+	 * 
+	 * @param A
+	 *            Two-dimensional array of doubles.
+	 * @param m
+	 *            Number of rows.
+	 * @param n
+	 *            Number of colums.
+	 */
+
+	public Matrix(double[][] A, int m, int n) {
+		this.A = A;
+		this.m = m;
+		this.n = n;
+	}
+
+	/**
+	 * Construct a matrix from a one-dimensional packed array
+	 * 
+	 * @param vals
+	 *            One-dimensional array of doubles, packed by columns (ala
+	 *            Fortran).
+	 * @param m
+	 *            Number of rows.
+	 * @exception IllegalArgumentException
+	 *                Array length must be a multiple of m.
+	 */
+
+	public Matrix(double vals[], int m) {
+		this.m = m;
+		n = (m != 0 ? vals.length / m : 0);
+		if (m * n != vals.length) {
+			throw new IllegalArgumentException("Array length must be a multiple of m.");
+		}
+		A = new double[m][n];
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				A[i][j] = vals[i + j * m];
+			}
+		}
+	}
+
+	/*
+	 * ------------------------ Public Methods ------------------------
+	 */
+
+	/**
+	 * Construct a matrix from a copy of a 2-D array.
+	 * 
+	 * @param A
+	 *            Two-dimensional array of doubles.
+	 * @exception IllegalArgumentException
+	 *                All rows must have the same length
+	 */
+
+	public static Matrix constructWithCopy(double[][] A) {
+		int m = A.length;
+		int n = A[0].length;
+		Matrix X = new Matrix(m, n);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			if (A[i].length != n) {
+				throw new IllegalArgumentException("All rows must have the same length.");
+			}
+			for (int j = 0; j < n; j++) {
+				C[i][j] = A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Make a deep copy of a matrix
+	 */
+
+	public Matrix copy() {
+		Matrix X = new Matrix(m, n);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[i][j] = A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Clone the Matrix object.
+	 */
+
+	public Object clone() {
+		return this.copy();
+	}
+
+	/**
+	 * Access the internal two-dimensional array.
+	 * 
+	 * @return Pointer to the two-dimensional array of matrix elements.
+	 */
+
+	public double[][] getArray() {
+		return A;
+	}
+
+	/**
+	 * Copy the internal two-dimensional array.
+	 * 
+	 * @return Two-dimensional array copy of matrix elements.
+	 */
+
+	public double[][] getArrayCopy() {
+		double[][] C = new double[m][n];
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[i][j] = A[i][j];
+			}
+		}
+		return C;
+	}
+
+	/**
+	 * Make a one-dimensional column packed copy of the internal array.
+	 * 
+	 * @return Matrix elements packed in a one-dimensional array by columns.
+	 */
+
+	public double[] getColumnPackedCopy() {
+		double[] vals = new double[m * n];
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				vals[i + j * m] = A[i][j];
+			}
+		}
+		return vals;
+	}
+
+	/**
+	 * Make a one-dimensional row packed copy of the internal array.
+	 * 
+	 * @return Matrix elements packed in a one-dimensional array by rows.
+	 */
+
+	public double[] getRowPackedCopy() {
+		double[] vals = new double[m * n];
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				vals[i * n + j] = A[i][j];
+			}
+		}
+		return vals;
+	}
+
+	/**
+	 * Get row dimension.
+	 * 
+	 * @return m, the number of rows.
+	 */
+
+	public int getRowDimension() {
+		return m;
+	}
+
+	/**
+	 * Get column dimension.
+	 * 
+	 * @return n, the number of columns.
+	 */
+
+	public int getColumnDimension() {
+		return n;
+	}
+
+	/**
+	 * Get a single element.
+	 * 
+	 * @param i
+	 *            Row index.
+	 * @param j
+	 *            Column index.
+	 * @return A(i,j)
+	 * @exception ArrayIndexOutOfBoundsException
+	 */
+
+	public double get(int i, int j) {
+		return A[i][j];
+	}
+
+	/**
+	 * Get a submatrix.
+	 * 
+	 * @param i0
+	 *            Initial row index
+	 * @param i1
+	 *            Final row index
+	 * @param j0
+	 *            Initial column index
+	 * @param j1
+	 *            Final column index
+	 * @return A(i0:i1,j0:j1)
+	 * @exception ArrayIndexOutOfBoundsException
+	 *                Submatrix indices
+	 */
+
+	public Matrix getMatrix(int i0, int i1, int j0, int j1) {
+		Matrix X = new Matrix(i1 - i0 + 1, j1 - j0 + 1);
+		double[][] B = X.getArray();
+		try {
+			for (int i = i0; i <= i1; i++) {
+				for (int j = j0; j <= j1; j++) {
+					B[i - i0][j - j0] = A[i][j];
+				}
+			}
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+		}
+		return X;
+	}
+
+	/**
+	 * Get a submatrix.
+	 * 
+	 * @param r
+	 *            Array of row indices.
+	 * @param c
+	 *            Array of column indices.
+	 * @return A(r(:),c(:))
+	 * @exception ArrayIndexOutOfBoundsException
+	 *                Submatrix indices
+	 */
+
+	public Matrix getMatrix(int[] r, int[] c) {
+		Matrix X = new Matrix(r.length, c.length);
+		double[][] B = X.getArray();
+		try {
+			for (int i = 0; i < r.length; i++) {
+				for (int j = 0; j < c.length; j++) {
+					B[i][j] = A[r[i]][c[j]];
+				}
+			}
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+		}
+		return X;
+	}
+
+	/**
+	 * Get a submatrix.
+	 * 
+	 * @param i0
+	 *            Initial row index
+	 * @param i1
+	 *            Final row index
+	 * @param c
+	 *            Array of column indices.
+	 * @return A(i0:i1,c(:))
+	 * @exception ArrayIndexOutOfBoundsException
+	 *                Submatrix indices
+	 */
+
+	public Matrix getMatrix(int i0, int i1, int[] c) {
+		Matrix X = new Matrix(i1 - i0 + 1, c.length);
+		double[][] B = X.getArray();
+		try {
+			for (int i = i0; i <= i1; i++) {
+				for (int j = 0; j < c.length; j++) {
+					B[i - i0][j] = A[i][c[j]];
+				}
+			}
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+		}
+		return X;
+	}
+
+	/**
+	 * Get a submatrix.
+	 * 
+	 * @param r
+	 *            Array of row indices.
+	 * @param j0
+	 *            Initial column index
+	 * @param j1
+	 *            Final column index
+	 * @return A(r(:),j0:j1)
+	 * @exception ArrayIndexOutOfBoundsException
+	 *                Submatrix indices
+	 */
+
+	public Matrix getMatrix(int[] r, int j0, int j1) {
+		Matrix X = new Matrix(r.length, j1 - j0 + 1);
+		double[][] B = X.getArray();
+		try {
+			for (int i = 0; i < r.length; i++) {
+				for (int j = j0; j <= j1; j++) {
+					B[i][j - j0] = A[r[i]][j];
+				}
+			}
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+		}
+		return X;
+	}
+
+	/**
+	 * Set a single element.
+	 * 
+	 * @param i
+	 *            Row index.
+	 * @param j
+	 *            Column index.
+	 * @param s
+	 *            A(i,j).
+	 * @exception ArrayIndexOutOfBoundsException
+	 */
+
+	public void set(int i, int j, double s) {
+		A[i][j] = s;
+	}
+
+	/**
+	 * Set a submatrix.
+	 * 
+	 * @param i0
+	 *            Initial row index
+	 * @param i1
+	 *            Final row index
+	 * @param j0
+	 *            Initial column index
+	 * @param j1
+	 *            Final column index
+	 * @param X
+	 *            A(i0:i1,j0:j1)
+	 * @exception ArrayIndexOutOfBoundsException
+	 *                Submatrix indices
+	 */
+
+	public void setMatrix(int i0, int i1, int j0, int j1, Matrix X) {
+		try {
+			for (int i = i0; i <= i1; i++) {
+				for (int j = j0; j <= j1; j++) {
+					A[i][j] = X.get(i - i0, j - j0);
+				}
+			}
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+		}
+	}
+
+	/**
+	 * Set a submatrix.
+	 * 
+	 * @param r
+	 *            Array of row indices.
+	 * @param c
+	 *            Array of column indices.
+	 * @param X
+	 *            A(r(:),c(:))
+	 * @exception ArrayIndexOutOfBoundsException
+	 *                Submatrix indices
+	 */
+
+	public void setMatrix(int[] r, int[] c, Matrix X) {
+		try {
+			for (int i = 0; i < r.length; i++) {
+				for (int j = 0; j < c.length; j++) {
+					A[r[i]][c[j]] = X.get(i, j);
+				}
+			}
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+		}
+	}
+
+	/**
+	 * Set a submatrix.
+	 * 
+	 * @param r
+	 *            Array of row indices.
+	 * @param j0
+	 *            Initial column index
+	 * @param j1
+	 *            Final column index
+	 * @param X
+	 *            A(r(:),j0:j1)
+	 * @exception ArrayIndexOutOfBoundsException
+	 *                Submatrix indices
+	 */
+
+	public void setMatrix(int[] r, int j0, int j1, Matrix X) {
+		try {
+			for (int i = 0; i < r.length; i++) {
+				for (int j = j0; j <= j1; j++) {
+					A[r[i]][j] = X.get(i, j - j0);
+				}
+			}
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+		}
+	}
+
+	/**
+	 * Set a submatrix.
+	 * 
+	 * @param i0
+	 *            Initial row index
+	 * @param i1
+	 *            Final row index
+	 * @param c
+	 *            Array of column indices.
+	 * @param X
+	 *            A(i0:i1,c(:))
+	 * @exception ArrayIndexOutOfBoundsException
+	 *                Submatrix indices
+	 */
+
+	public void setMatrix(int i0, int i1, int[] c, Matrix X) {
+		try {
+			for (int i = i0; i <= i1; i++) {
+				for (int j = 0; j < c.length; j++) {
+					A[i][c[j]] = X.get(i - i0, j);
+				}
+			}
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+		}
+	}
+
+	/**
+	 * Matrix transpose.
+	 * 
+	 * @return A'
+	 */
+
+	public Matrix transpose() {
+		Matrix X = new Matrix(n, m);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[j][i] = A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * One norm
+	 * 
+	 * @return maximum column sum.
+	 */
+
+	public double norm1() {
+		double f = 0;
+		for (int j = 0; j < n; j++) {
+			double s = 0;
+			for (int i = 0; i < m; i++) {
+				s += Math.abs(A[i][j]);
+			}
+			f = Math.max(f, s);
+		}
+		return f;
+	}
+
+	/**
+	 * Two norm
+	 * 
+	 * @return maximum singular value.
+	 */
+
+	public double norm2() {
+		return (new SingularValueDecomposition(this).norm2());
+	}
+
+	/**
+	 * Infinity norm
+	 * 
+	 * @return maximum row sum.
+	 */
+
+	public double normInf() {
+		double f = 0;
+		for (int i = 0; i < m; i++) {
+			double s = 0;
+			for (int j = 0; j < n; j++) {
+				s += Math.abs(A[i][j]);
+			}
+			f = Math.max(f, s);
+		}
+		return f;
+	}
+
+	/**
+	 * Frobenius norm
+	 * 
+	 * @return sqrt of sum of squares of all elements.
+	 */
+
+	public double normF() {
+		double f = 0;
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				f = Maths.hypot(f, A[i][j]);
+			}
+		}
+		return f;
+	}
+
+	/**
+	 * Unary minus
+	 * 
+	 * @return -A
+	 */
+
+	public Matrix uminus() {
+		Matrix X = new Matrix(m, n);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[i][j] = -A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * C = A + B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A + B
+	 */
+
+	public Matrix plus(Matrix B) {
+		checkMatrixDimensions(B);
+		Matrix X = new Matrix(m, n);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[i][j] = A[i][j] + B.A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * A = A + B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A + B
+	 */
+
+	public Matrix plusEquals(Matrix B) {
+		checkMatrixDimensions(B);
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				A[i][j] = A[i][j] + B.A[i][j];
+			}
+		}
+		return this;
+	}
+
+	/**
+	 * C = A - B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A - B
+	 */
+
+	public Matrix minus(Matrix B) {
+		checkMatrixDimensions(B);
+		Matrix X = new Matrix(m, n);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[i][j] = A[i][j] - B.A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * A = A - B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A - B
+	 */
+
+	public Matrix minusEquals(Matrix B) {
+		checkMatrixDimensions(B);
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				A[i][j] = A[i][j] - B.A[i][j];
+			}
+		}
+		return this;
+	}
+
+	/**
+	 * Element-by-element multiplication, C = A.*B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A.*B
+	 */
+
+	public Matrix arrayTimes(Matrix B) {
+		checkMatrixDimensions(B);
+		Matrix X = new Matrix(m, n);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[i][j] = A[i][j] * B.A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Element-by-element multiplication in place, A = A.*B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A.*B
+	 */
+
+	public Matrix arrayTimesEquals(Matrix B) {
+		checkMatrixDimensions(B);
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				A[i][j] = A[i][j] * B.A[i][j];
+			}
+		}
+		return this;
+	}
+
+	/**
+	 * Element-by-element right division, C = A./B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A./B
+	 */
+
+	public Matrix arrayRightDivide(Matrix B) {
+		checkMatrixDimensions(B);
+		Matrix X = new Matrix(m, n);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[i][j] = A[i][j] / B.A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Element-by-element right division in place, A = A./B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A./B
+	 */
+
+	public Matrix arrayRightDivideEquals(Matrix B) {
+		checkMatrixDimensions(B);
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				A[i][j] = A[i][j] / B.A[i][j];
+			}
+		}
+		return this;
+	}
+
+	/**
+	 * Element-by-element left division, C = A.\B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A.\B
+	 */
+
+	public Matrix arrayLeftDivide(Matrix B) {
+		checkMatrixDimensions(B);
+		Matrix X = new Matrix(m, n);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[i][j] = B.A[i][j] / A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Element-by-element left division in place, A = A.\B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return A.\B
+	 */
+
+	public Matrix arrayLeftDivideEquals(Matrix B) {
+		checkMatrixDimensions(B);
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				A[i][j] = B.A[i][j] / A[i][j];
+			}
+		}
+		return this;
+	}
+
+	/**
+	 * Multiply a matrix by a scalar, C = s*A
+	 * 
+	 * @param s
+	 *            scalar
+	 * @return s*A
+	 */
+
+	public Matrix times(double s) {
+		Matrix X = new Matrix(m, n);
+		double[][] C = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				C[i][j] = s * A[i][j];
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Multiply a matrix by a scalar in place, A = s*A
+	 * 
+	 * @param s
+	 *            scalar
+	 * @return replace A by s*A
+	 */
+
+	public Matrix timesEquals(double s) {
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				A[i][j] = s * A[i][j];
+			}
+		}
+		return this;
+	}
+
+	/**
+	 * Linear algebraic matrix multiplication, A * B
+	 * 
+	 * @param B
+	 *            another matrix
+	 * @return Matrix product, A * B
+	 * @exception IllegalArgumentException
+	 *                Matrix inner dimensions must agree.
+	 */
+
+	public Matrix times(Matrix B) {
+		if (B.m != n) {
+			throw new IllegalArgumentException("Matrix inner dimensions must agree.");
+		}
+		Matrix X = new Matrix(m, B.n);
+		double[][] C = X.getArray();
+		double[] Bcolj = new double[n];
+		for (int j = 0; j < B.n; j++) {
+			for (int k = 0; k < n; k++) {
+				Bcolj[k] = B.A[k][j];
+			}
+			for (int i = 0; i < m; i++) {
+				double[] Arowi = A[i];
+				double s = 0;
+				for (int k = 0; k < n; k++) {
+					s += Arowi[k] * Bcolj[k];
+				}
+				C[i][j] = s;
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * LU Decomposition
+	 * 
+	 * @return LUDecomposition
+	 * @see LUDecomposition
+	 */
+
+	public LUDecomposition lu() {
+		return new LUDecomposition(this);
+	}
+
+	/**
+	 * QR Decomposition
+	 * 
+	 * @return QRDecomposition
+	 * @see QRDecomposition
+	 */
+
+	public QRDecomposition qr() {
+		return new QRDecomposition(this);
+	}
+
+	/**
+	 * Cholesky Decomposition
+	 * 
+	 * @return CholeskyDecomposition
+	 * @see CholeskyDecomposition
+	 */
+
+	public CholeskyDecomposition chol() {
+		return new CholeskyDecomposition(this);
+	}
+
+	/**
+	 * Singular Value Decomposition
+	 * 
+	 * @return SingularValueDecomposition
+	 * @see SingularValueDecomposition
+	 */
+
+	public SingularValueDecomposition svd() {
+		return new SingularValueDecomposition(this);
+	}
+
+	/**
+	 * Eigenvalue Decomposition
+	 * 
+	 * @return EigenvalueDecomposition
+	 * @see EigenvalueDecomposition
+	 */
+
+	public EigenvalueDecomposition eig() {
+		return new EigenvalueDecomposition(this);
+	}
+
+	/**
+	 * Solve A*X = B
+	 * 
+	 * @param B
+	 *            right hand side
+	 * @return solution if A is square, least squares solution otherwise
+	 */
+
+	public Matrix solve(Matrix B) {
+		return (m == n ? (new LUDecomposition(this)).solve(B) : (new QRDecomposition(this)).solve(B));
+	}
+
+	/**
+	 * Solve X*A = B, which is also A'*X' = B'
+	 * 
+	 * @param B
+	 *            right hand side
+	 * @return solution if A is square, least squares solution otherwise.
+	 */
+
+	public Matrix solveTranspose(Matrix B) {
+		return transpose().solve(B.transpose());
+	}
+
+	/**
+	 * Matrix inverse or pseudoinverse
+	 * 
+	 * @return inverse(A) if A is square, pseudoinverse otherwise.
+	 */
+
+	public Matrix inverse() {
+		return solve(identity(m, m));
+	}
+
+	/**
+	 * Matrix determinant
+	 * 
+	 * @return determinant
+	 */
+
+	public double det() {
+		return new LUDecomposition(this).det();
+	}
+
+	/**
+	 * Matrix rank
+	 * 
+	 * @return effective numerical rank, obtained from SVD.
+	 */
+
+	public int rank() {
+		return new SingularValueDecomposition(this).rank();
+	}
+
+	/**
+	 * Matrix condition (2 norm)
+	 * 
+	 * @return ratio of largest to smallest singular value.
+	 */
+
+	public double cond() {
+		return new SingularValueDecomposition(this).cond();
+	}
+
+	/**
+	 * Matrix trace.
+	 * 
+	 * @return sum of the diagonal elements.
+	 */
+
+	public double trace() {
+		double t = 0;
+		for (int i = 0; i < Math.min(m, n); i++) {
+			t += A[i][i];
+		}
+		return t;
+	}
+
+	/**
+	 * Generate matrix with random elements
+	 * 
+	 * @param m
+	 *            Number of rows.
+	 * @param n
+	 *            Number of colums.
+	 * @return An m-by-n matrix with uniformly distributed random elements.
+	 */
+
+	public static Matrix random(int m, int n) {
+		Matrix A = new Matrix(m, n);
+		double[][] X = A.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				X[i][j] = Math.random();
+			}
+		}
+		return A;
+	}
+
+	/**
+	 * Generate identity matrix
+	 * 
+	 * @param m
+	 *            Number of rows.
+	 * @param n
+	 *            Number of colums.
+	 * @return An m-by-n matrix with ones on the diagonal and zeros elsewhere.
+	 */
+
+	public static Matrix identity(int m, int n) {
+		Matrix A = new Matrix(m, n);
+		double[][] X = A.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				X[i][j] = (i == j ? 1.0 : 0.0);
+			}
+		}
+		return A;
+	}
+
+	/**
+	 * Print the matrix to stdout. Line the elements up in columns with a
+	 * Fortran-like 'Fw.d' style format.
+	 * 
+	 * @param w
+	 *            Column width.
+	 * @param d
+	 *            Number of digits after the decimal.
+	 */
+
+	public void print(int w, int d) {
+		print(new PrintWriter(System.out, true), w, d);
+	}
+
+	/**
+	 * Print the matrix to the output stream. Line the elements up in columns
+	 * with a Fortran-like 'Fw.d' style format.
+	 * 
+	 * @param output
+	 *            Output stream.
+	 * @param w
+	 *            Column width.
+	 * @param d
+	 *            Number of digits after the decimal.
+	 */
+
+	public void print(PrintWriter output, int w, int d) {
+		DecimalFormat format = new DecimalFormat();
+		format.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
+		format.setMinimumIntegerDigits(1);
+		format.setMaximumFractionDigits(d);
+		format.setMinimumFractionDigits(d);
+		format.setGroupingUsed(false);
+		print(output, format, w + 2);
+	}
+
+	/**
+	 * Print the matrix to stdout. Line the elements up in columns. Use the
+	 * format object, and right justify within columns of width characters. Note
+	 * that is the matrix is to be read back in, you probably will want to use a
+	 * NumberFormat that is set to US Locale.
+	 * 
+	 * @param format
+	 *            A Formatting object for individual elements.
+	 * @param width
+	 *            Field width for each column.
+	 * @see java.text.DecimalFormat#setDecimalFormatSymbols
+	 */
+
+	public void print(NumberFormat format, int width) {
+		print(new PrintWriter(System.out, true), format, width);
+	}
+
+	// DecimalFormat is a little disappointing coming from Fortran or C's
+	// printf.
+	// Since it doesn't pad on the left, the elements will come out different
+	// widths. Consequently, we'll pass the desired column width in as an
+	// argument and do the extra padding ourselves.
+
+	/**
+	 * Print the matrix to the output stream. Line the elements up in columns.
+	 * Use the format object, and right justify within columns of width
+	 * characters. Note that is the matrix is to be read back in, you probably
+	 * will want to use a NumberFormat that is set to US Locale.
+	 * 
+	 * @param output
+	 *            the output stream.
+	 * @param format
+	 *            A formatting object to format the matrix elements
+	 * @param width
+	 *            Column width.
+	 * @see java.text.DecimalFormat#setDecimalFormatSymbols
+	 */
+
+	public void print(PrintWriter output, NumberFormat format, int width) {
+		output.println(); // start on new line.
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				String s = format.format(A[i][j]); // format the number
+				int padding = Math.max(1, width - s.length()); // At _least_ 1
+																// space
+				for (int k = 0; k < padding; k++)
+					output.print(' ');
+				output.print(s);
+			}
+			output.println();
+		}
+		output.println(); // end with blank line.
+	}
+
+	/**
+	 * Read a matrix from a stream. The format is the same the print method, so
+	 * printed matrices can be read back in (provided they were printed using US
+	 * Locale). Elements are separated by whitespace, all the elements for each
+	 * row appear on a single line, the last row is followed by a blank line.
+	 * 
+	 * @param input
+	 *            the input stream.
+	 */
+
+	public static Matrix read(BufferedReader input) throws java.io.IOException {
+		StreamTokenizer tokenizer = new StreamTokenizer(input);
+
+		// Although StreamTokenizer will parse numbers, it doesn't recognize
+		// scientific notation (E or D); however, Double.valueOf does.
+		// The strategy here is to disable StreamTokenizer's number parsing.
+		// We'll only get whitespace delimited words, EOL's and EOF's.
+		// These words should all be numbers, for Double.valueOf to parse.
+
+		tokenizer.resetSyntax();
+		tokenizer.wordChars(0, 255);
+		tokenizer.whitespaceChars(0, ' ');
+		tokenizer.eolIsSignificant(true);
+		java.util.Vector v = new java.util.Vector();
+
+		// Ignore initial empty lines
+		while (tokenizer.nextToken() == StreamTokenizer.TT_EOL)
+			;
+		if (tokenizer.ttype == StreamTokenizer.TT_EOF)
+			throw new java.io.IOException("Unexpected EOF on matrix read.");
+		do {
+			v.addElement(Double.valueOf(tokenizer.sval)); // Read & store 1st
+															// row.
+		}
+		while (tokenizer.nextToken() == StreamTokenizer.TT_WORD);
+
+		int n = v.size(); // Now we've got the number of columns!
+		double row[] = new double[n];
+		for (int j = 0; j < n; j++)
+			// extract the elements of the 1st row.
+			row[j] = ((Double) v.elementAt(j)).doubleValue();
+		v.removeAllElements();
+		v.addElement(row); // Start storing rows instead of columns.
+		while (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
+			// While non-empty lines
+			v.addElement(row = new double[n]);
+			int j = 0;
+			do {
+				if (j >= n)
+					throw new java.io.IOException("Row " + v.size() + " is too long.");
+				row[j++] = Double.valueOf(tokenizer.sval).doubleValue();
+			}
+			while (tokenizer.nextToken() == StreamTokenizer.TT_WORD);
+			if (j < n)
+				throw new java.io.IOException("Row " + v.size() + " is too short.");
+		}
+		int m = v.size(); // Now we've got the number of rows.
+		double[][] A = new double[m][];
+		v.copyInto(A); // copy the rows out of the vector
+		return new Matrix(A);
+	}
+
+	/*
+	 * ------------------------ Private Methods ------------------------
+	 */
+
+	/** Check if size(A) == size(B) **/
+
+	private void checkMatrixDimensions(Matrix B) {
+		if (B.m != m || B.n != n) {
+			throw new IllegalArgumentException("Matrix dimensions must agree.");
+		}
+	}
+
+}
diff --git a/src/bilib/src/jama/QRDecomposition.java b/src/bilib/src/jama/QRDecomposition.java
new file mode 100644
index 0000000000000000000000000000000000000000..bfa3ef5b5db33a380861e81d56c1e32f6ffd9b95
--- /dev/null
+++ b/src/bilib/src/jama/QRDecomposition.java
@@ -0,0 +1,240 @@
+package jama;
+
+/**
+ * QR Decomposition.
+ * <P>
+ * For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n
+ * orthogonal matrix Q and an n-by-n upper triangular matrix R so that A = Q*R.
+ * <P>
+ * The QR decompostion always exists, even if the matrix does not have full
+ * rank, so the constructor will never fail. The primary use of the QR
+ * decomposition is in the least squares solution of nonsquare systems of
+ * simultaneous linear equations. This will fail if isFullRank() returns false.
+ */
+
+public class QRDecomposition implements java.io.Serializable {
+
+	/*
+	 * ------------------------ Class variables ------------------------
+	 */
+
+	/**
+	 * Array for internal storage of decomposition.
+	 * 
+	 * @serial internal array storage.
+	 */
+	private double[][]	QR;
+
+	/**
+	 * Row and column dimensions.
+	 * 
+	 * @serial column dimension.
+	 * @serial row dimension.
+	 */
+	private int			m, n;
+
+	/**
+	 * Array for internal storage of diagonal of R.
+	 * 
+	 * @serial diagonal of R.
+	 */
+	private double[]	Rdiag;
+
+	/*
+	 * ------------------------ Constructor ------------------------
+	 */
+
+	/**
+	 * QR Decomposition, computed by Householder reflections.
+	 * 
+	 * @param A
+	 *            Rectangular matrix
+	 */
+
+	public QRDecomposition(Matrix A) {
+		// Initialize.
+		QR = A.getArrayCopy();
+		m = A.getRowDimension();
+		n = A.getColumnDimension();
+		Rdiag = new double[n];
+
+		// Main loop.
+		for (int k = 0; k < n; k++) {
+			// Compute 2-norm of k-th column without under/overflow.
+			double nrm = 0;
+			for (int i = k; i < m; i++) {
+				nrm = Maths.hypot(nrm, QR[i][k]);
+			}
+
+			if (nrm != 0.0) {
+				// Form k-th Householder vector.
+				if (QR[k][k] < 0) {
+					nrm = -nrm;
+				}
+				for (int i = k; i < m; i++) {
+					QR[i][k] /= nrm;
+				}
+				QR[k][k] += 1.0;
+
+				// Apply transformation to remaining columns.
+				for (int j = k + 1; j < n; j++) {
+					double s = 0.0;
+					for (int i = k; i < m; i++) {
+						s += QR[i][k] * QR[i][j];
+					}
+					s = -s / QR[k][k];
+					for (int i = k; i < m; i++) {
+						QR[i][j] += s * QR[i][k];
+					}
+				}
+			}
+			Rdiag[k] = -nrm;
+		}
+	}
+
+	/*
+	 * ------------------------ Public Methods ------------------------
+	 */
+
+	/**
+	 * Is the matrix full rank?
+	 * 
+	 * @return true if R, and hence A, has full rank.
+	 */
+
+	public boolean isFullRank() {
+		for (int j = 0; j < n; j++) {
+			if (Rdiag[j] == 0)
+				return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Return the Householder vectors
+	 * 
+	 * @return Lower trapezoidal matrix whose columns define the reflections
+	 */
+
+	public Matrix getH() {
+		Matrix X = new Matrix(m, n);
+		double[][] H = X.getArray();
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				if (i >= j) {
+					H[i][j] = QR[i][j];
+				}
+				else {
+					H[i][j] = 0.0;
+				}
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Return the upper triangular factor
+	 * 
+	 * @return R
+	 */
+
+	public Matrix getR() {
+		Matrix X = new Matrix(n, n);
+		double[][] R = X.getArray();
+		for (int i = 0; i < n; i++) {
+			for (int j = 0; j < n; j++) {
+				if (i < j) {
+					R[i][j] = QR[i][j];
+				}
+				else if (i == j) {
+					R[i][j] = Rdiag[i];
+				}
+				else {
+					R[i][j] = 0.0;
+				}
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Generate and return the (economy-sized) orthogonal factor
+	 * 
+	 * @return Q
+	 */
+
+	public Matrix getQ() {
+		Matrix X = new Matrix(m, n);
+		double[][] Q = X.getArray();
+		for (int k = n - 1; k >= 0; k--) {
+			for (int i = 0; i < m; i++) {
+				Q[i][k] = 0.0;
+			}
+			Q[k][k] = 1.0;
+			for (int j = k; j < n; j++) {
+				if (QR[k][k] != 0) {
+					double s = 0.0;
+					for (int i = k; i < m; i++) {
+						s += QR[i][k] * Q[i][j];
+					}
+					s = -s / QR[k][k];
+					for (int i = k; i < m; i++) {
+						Q[i][j] += s * QR[i][k];
+					}
+				}
+			}
+		}
+		return X;
+	}
+
+	/**
+	 * Least squares solution of A*X = B
+	 * 
+	 * @param B
+	 *            A Matrix with as many rows as A and any number of columns.
+	 * @return X that minimizes the two norm of Q*R*X-B.
+	 * @exception IllegalArgumentException
+	 *                Matrix row dimensions must agree.
+	 * @exception RuntimeException
+	 *                Matrix is rank deficient.
+	 */
+
+	public Matrix solve(Matrix B) {
+		if (B.getRowDimension() != m) {
+			throw new IllegalArgumentException("Matrix row dimensions must agree.");
+		}
+		if (!this.isFullRank()) {
+			throw new RuntimeException("Matrix is rank deficient.");
+		}
+
+		// Copy right hand side
+		int nx = B.getColumnDimension();
+		double[][] X = B.getArrayCopy();
+
+		// Compute Y = transpose(Q)*B
+		for (int k = 0; k < n; k++) {
+			for (int j = 0; j < nx; j++) {
+				double s = 0.0;
+				for (int i = k; i < m; i++) {
+					s += QR[i][k] * X[i][j];
+				}
+				s = -s / QR[k][k];
+				for (int i = k; i < m; i++) {
+					X[i][j] += s * QR[i][k];
+				}
+			}
+		}
+		// Solve R*X = Y;
+		for (int k = n - 1; k >= 0; k--) {
+			for (int j = 0; j < nx; j++) {
+				X[k][j] /= Rdiag[k];
+			}
+			for (int i = 0; i < k; i++) {
+				for (int j = 0; j < nx; j++) {
+					X[i][j] -= X[k][j] * QR[i][k];
+				}
+			}
+		}
+		return (new Matrix(X, n, nx).getMatrix(0, n - 1, 0, nx - 1));
+	}
+}
diff --git a/src/bilib/src/jama/SingularValueDecomposition.java b/src/bilib/src/jama/SingularValueDecomposition.java
new file mode 100644
index 0000000000000000000000000000000000000000..267c123e7d702da773be4e9322b4bd1ce40dc52e
--- /dev/null
+++ b/src/bilib/src/jama/SingularValueDecomposition.java
@@ -0,0 +1,573 @@
+package jama;
+
+/**
+ * Singular Value Decomposition.
+ * <P>
+ * For an m-by-n matrix A with m >= n, the singular value decomposition is an
+ * m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and an n-by-n
+ * orthogonal matrix V so that A = U*S*V'.
+ * <P>
+ * The singular values, sigma[k] = S[k][k], are ordered so that sigma[0] >=
+ * sigma[1] >= ... >= sigma[n-1].
+ * <P>
+ * The singular value decompostion always exists, so the constructor will never
+ * fail. The matrix condition number and the effective numerical rank can be
+ * computed from this decomposition.
+ */
+
+public class SingularValueDecomposition implements java.io.Serializable {
+
+	/*
+	 * ------------------------ Class variables ------------------------
+	 */
+
+	/**
+	 * Arrays for internal storage of U and V.
+	 * 
+	 * @serial internal storage of U.
+	 * @serial internal storage of V.
+	 */
+	private double[][]	U, V;
+
+	/**
+	 * Array for internal storage of singular values.
+	 * 
+	 * @serial internal storage of singular values.
+	 */
+	private double[]	s;
+
+	/**
+	 * Row and column dimensions.
+	 * 
+	 * @serial row dimension.
+	 * @serial column dimension.
+	 */
+	private int			m, n;
+
+	/*
+	 * ------------------------ Constructor ------------------------
+	 */
+
+	/**
+	 * Construct the singular value decomposition
+	 * 
+	 * @param Arg
+	 *            Rectangular matrix
+	 */
+
+	public SingularValueDecomposition(Matrix Arg) {
+
+		// Derived from LINPACK code.
+		// Initialize.
+		double[][] A = Arg.getArrayCopy();
+		m = Arg.getRowDimension();
+		n = Arg.getColumnDimension();
+
+		/*
+		 * Apparently the failing cases are only a proper subset of (m<n), so
+		 * let's not throw error. Correct fix to come later? if (m<n) { throw
+		 * new IllegalArgumentException("Jama SVD only works for m >= n"); }
+		 */
+		int nu = Math.min(m, n);
+		s = new double[Math.min(m + 1, n)];
+		U = new double[m][nu];
+		V = new double[n][n];
+		double[] e = new double[n];
+		double[] work = new double[m];
+		boolean wantu = true;
+		boolean wantv = true;
+
+		// Reduce A to bidiagonal form, storing the diagonal elements
+		// in s and the super-diagonal elements in e.
+
+		int nct = Math.min(m - 1, n);
+		int nrt = Math.max(0, Math.min(n - 2, m));
+		for (int k = 0; k < Math.max(nct, nrt); k++) {
+			if (k < nct) {
+
+				// Compute the transformation for the k-th column and
+				// place the k-th diagonal in s[k].
+				// Compute 2-norm of k-th column without under/overflow.
+				s[k] = 0;
+				for (int i = k; i < m; i++) {
+					s[k] = Maths.hypot(s[k], A[i][k]);
+				}
+				if (s[k] != 0.0) {
+					if (A[k][k] < 0.0) {
+						s[k] = -s[k];
+					}
+					for (int i = k; i < m; i++) {
+						A[i][k] /= s[k];
+					}
+					A[k][k] += 1.0;
+				}
+				s[k] = -s[k];
+			}
+			for (int j = k + 1; j < n; j++) {
+				if ((k < nct) & (s[k] != 0.0)) {
+
+					// Apply the transformation.
+
+					double t = 0;
+					for (int i = k; i < m; i++) {
+						t += A[i][k] * A[i][j];
+					}
+					t = -t / A[k][k];
+					for (int i = k; i < m; i++) {
+						A[i][j] += t * A[i][k];
+					}
+				}
+
+				// Place the k-th row of A into e for the
+				// subsequent calculation of the row transformation.
+
+				e[j] = A[k][j];
+			}
+			if (wantu & (k < nct)) {
+
+				// Place the transformation in U for subsequent back
+				// multiplication.
+
+				for (int i = k; i < m; i++) {
+					U[i][k] = A[i][k];
+				}
+			}
+			if (k < nrt) {
+
+				// Compute the k-th row transformation and place the
+				// k-th super-diagonal in e[k].
+				// Compute 2-norm without under/overflow.
+				e[k] = 0;
+				for (int i = k + 1; i < n; i++) {
+					e[k] = Maths.hypot(e[k], e[i]);
+				}
+				if (e[k] != 0.0) {
+					if (e[k + 1] < 0.0) {
+						e[k] = -e[k];
+					}
+					for (int i = k + 1; i < n; i++) {
+						e[i] /= e[k];
+					}
+					e[k + 1] += 1.0;
+				}
+				e[k] = -e[k];
+				if ((k + 1 < m) & (e[k] != 0.0)) {
+
+					// Apply the transformation.
+
+					for (int i = k + 1; i < m; i++) {
+						work[i] = 0.0;
+					}
+					for (int j = k + 1; j < n; j++) {
+						for (int i = k + 1; i < m; i++) {
+							work[i] += e[j] * A[i][j];
+						}
+					}
+					for (int j = k + 1; j < n; j++) {
+						double t = -e[j] / e[k + 1];
+						for (int i = k + 1; i < m; i++) {
+							A[i][j] += t * work[i];
+						}
+					}
+				}
+				if (wantv) {
+
+					// Place the transformation in V for subsequent
+					// back multiplication.
+
+					for (int i = k + 1; i < n; i++) {
+						V[i][k] = e[i];
+					}
+				}
+			}
+		}
+
+		// Set up the final bidiagonal matrix or order p.
+
+		int p = Math.min(n, m + 1);
+		if (nct < n) {
+			s[nct] = A[nct][nct];
+		}
+		if (m < p) {
+			s[p - 1] = 0.0;
+		}
+		if (nrt + 1 < p) {
+			e[nrt] = A[nrt][p - 1];
+		}
+		e[p - 1] = 0.0;
+
+		// If required, generate U.
+
+		if (wantu) {
+			for (int j = nct; j < nu; j++) {
+				for (int i = 0; i < m; i++) {
+					U[i][j] = 0.0;
+				}
+				U[j][j] = 1.0;
+			}
+			for (int k = nct - 1; k >= 0; k--) {
+				if (s[k] != 0.0) {
+					for (int j = k + 1; j < nu; j++) {
+						double t = 0;
+						for (int i = k; i < m; i++) {
+							t += U[i][k] * U[i][j];
+						}
+						t = -t / U[k][k];
+						for (int i = k; i < m; i++) {
+							U[i][j] += t * U[i][k];
+						}
+					}
+					for (int i = k; i < m; i++) {
+						U[i][k] = -U[i][k];
+					}
+					U[k][k] = 1.0 + U[k][k];
+					for (int i = 0; i < k - 1; i++) {
+						U[i][k] = 0.0;
+					}
+				}
+				else {
+					for (int i = 0; i < m; i++) {
+						U[i][k] = 0.0;
+					}
+					U[k][k] = 1.0;
+				}
+			}
+		}
+
+		// If required, generate V.
+
+		if (wantv) {
+			for (int k = n - 1; k >= 0; k--) {
+				if ((k < nrt) & (e[k] != 0.0)) {
+					for (int j = k + 1; j < nu; j++) {
+						double t = 0;
+						for (int i = k + 1; i < n; i++) {
+							t += V[i][k] * V[i][j];
+						}
+						t = -t / V[k + 1][k];
+						for (int i = k + 1; i < n; i++) {
+							V[i][j] += t * V[i][k];
+						}
+					}
+				}
+				for (int i = 0; i < n; i++) {
+					V[i][k] = 0.0;
+				}
+				V[k][k] = 1.0;
+			}
+		}
+
+		// Main iteration loop for the singular values.
+
+		int pp = p - 1;
+		int iter = 0;
+		double eps = Math.pow(2.0, -52.0);
+		double tiny = Math.pow(2.0, -966.0);
+		while (p > 0) {
+			int k, kase;
+
+			// Here is where a test for too many iterations would go.
+
+			// This section of the program inspects for
+			// negligible elements in the s and e arrays. On
+			// completion the variables kase and k are set as follows.
+
+			// kase = 1 if s(p) and e[k-1] are negligible and k<p
+			// kase = 2 if s(k) is negligible and k<p
+			// kase = 3 if e[k-1] is negligible, k<p, and
+			// s(k), ..., s(p) are not negligible (qr step).
+			// kase = 4 if e(p-1) is negligible (convergence).
+
+			for (k = p - 2; k >= -1; k--) {
+				if (k == -1) {
+					break;
+				}
+				if (Math.abs(e[k]) <= tiny + eps * (Math.abs(s[k]) + Math.abs(s[k + 1]))) {
+					e[k] = 0.0;
+					break;
+				}
+			}
+			if (k == p - 2) {
+				kase = 4;
+			}
+			else {
+				int ks;
+				for (ks = p - 1; ks >= k; ks--) {
+					if (ks == k) {
+						break;
+					}
+					double t = (ks != p ? Math.abs(e[ks]) : 0.) + (ks != k + 1 ? Math.abs(e[ks - 1]) : 0.);
+					if (Math.abs(s[ks]) <= tiny + eps * t) {
+						s[ks] = 0.0;
+						break;
+					}
+				}
+				if (ks == k) {
+					kase = 3;
+				}
+				else if (ks == p - 1) {
+					kase = 1;
+				}
+				else {
+					kase = 2;
+					k = ks;
+				}
+			}
+			k++;
+
+			// Perform the task indicated by kase.
+
+			switch (kase) {
+
+			// Deflate negligible s(p).
+
+			case 1: {
+				double f = e[p - 2];
+				e[p - 2] = 0.0;
+				for (int j = p - 2; j >= k; j--) {
+					double t = Maths.hypot(s[j], f);
+					double cs = s[j] / t;
+					double sn = f / t;
+					s[j] = t;
+					if (j != k) {
+						f = -sn * e[j - 1];
+						e[j - 1] = cs * e[j - 1];
+					}
+					if (wantv) {
+						for (int i = 0; i < n; i++) {
+							t = cs * V[i][j] + sn * V[i][p - 1];
+							V[i][p - 1] = -sn * V[i][j] + cs * V[i][p - 1];
+							V[i][j] = t;
+						}
+					}
+				}
+			}
+				break;
+
+			// Split at negligible s(k).
+
+			case 2: {
+				double f = e[k - 1];
+				e[k - 1] = 0.0;
+				for (int j = k; j < p; j++) {
+					double t = Maths.hypot(s[j], f);
+					double cs = s[j] / t;
+					double sn = f / t;
+					s[j] = t;
+					f = -sn * e[j];
+					e[j] = cs * e[j];
+					if (wantu) {
+						for (int i = 0; i < m; i++) {
+							t = cs * U[i][j] + sn * U[i][k - 1];
+							U[i][k - 1] = -sn * U[i][j] + cs * U[i][k - 1];
+							U[i][j] = t;
+						}
+					}
+				}
+			}
+				break;
+
+			// Perform one qr step.
+
+			case 3: {
+
+				// Calculate the shift.
+
+				double scale = Math.max(Math.max(Math.max(Math.max(Math.abs(s[p - 1]), Math.abs(s[p - 2])), Math.abs(e[p - 2])), Math.abs(s[k])), Math.abs(e[k]));
+				double sp = s[p - 1] / scale;
+				double spm1 = s[p - 2] / scale;
+				double epm1 = e[p - 2] / scale;
+				double sk = s[k] / scale;
+				double ek = e[k] / scale;
+				double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0;
+				double c = (sp * epm1) * (sp * epm1);
+				double shift = 0.0;
+				if ((b != 0.0) | (c != 0.0)) {
+					shift = Math.sqrt(b * b + c);
+					if (b < 0.0) {
+						shift = -shift;
+					}
+					shift = c / (b + shift);
+				}
+				double f = (sk + sp) * (sk - sp) + shift;
+				double g = sk * ek;
+
+				// Chase zeros.
+
+				for (int j = k; j < p - 1; j++) {
+					double t = Maths.hypot(f, g);
+					double cs = f / t;
+					double sn = g / t;
+					if (j != k) {
+						e[j - 1] = t;
+					}
+					f = cs * s[j] + sn * e[j];
+					e[j] = cs * e[j] - sn * s[j];
+					g = sn * s[j + 1];
+					s[j + 1] = cs * s[j + 1];
+					if (wantv) {
+						for (int i = 0; i < n; i++) {
+							t = cs * V[i][j] + sn * V[i][j + 1];
+							V[i][j + 1] = -sn * V[i][j] + cs * V[i][j + 1];
+							V[i][j] = t;
+						}
+					}
+					t = Maths.hypot(f, g);
+					cs = f / t;
+					sn = g / t;
+					s[j] = t;
+					f = cs * e[j] + sn * s[j + 1];
+					s[j + 1] = -sn * e[j] + cs * s[j + 1];
+					g = sn * e[j + 1];
+					e[j + 1] = cs * e[j + 1];
+					if (wantu && (j < m - 1)) {
+						for (int i = 0; i < m; i++) {
+							t = cs * U[i][j] + sn * U[i][j + 1];
+							U[i][j + 1] = -sn * U[i][j] + cs * U[i][j + 1];
+							U[i][j] = t;
+						}
+					}
+				}
+				e[p - 2] = f;
+				iter = iter + 1;
+			}
+				break;
+
+			// Convergence.
+
+			case 4: {
+
+				// Make the singular values positive.
+
+				if (s[k] <= 0.0) {
+					s[k] = (s[k] < 0.0 ? -s[k] : 0.0);
+					if (wantv) {
+						for (int i = 0; i <= pp; i++) {
+							V[i][k] = -V[i][k];
+						}
+					}
+				}
+
+				// Order the singular values.
+
+				while (k < pp) {
+					if (s[k] >= s[k + 1]) {
+						break;
+					}
+					double t = s[k];
+					s[k] = s[k + 1];
+					s[k + 1] = t;
+					if (wantv && (k < n - 1)) {
+						for (int i = 0; i < n; i++) {
+							t = V[i][k + 1];
+							V[i][k + 1] = V[i][k];
+							V[i][k] = t;
+						}
+					}
+					if (wantu && (k < m - 1)) {
+						for (int i = 0; i < m; i++) {
+							t = U[i][k + 1];
+							U[i][k + 1] = U[i][k];
+							U[i][k] = t;
+						}
+					}
+					k++;
+				}
+				iter = 0;
+				p--;
+			}
+				break;
+			}
+		}
+	}
+
+	/*
+	 * ------------------------ Public Methods ------------------------
+	 */
+
+	/**
+	 * Return the left singular vectors
+	 * 
+	 * @return U
+	 */
+
+	public Matrix getU() {
+		return new Matrix(U, m, Math.min(m + 1, n));
+	}
+
+	/**
+	 * Return the right singular vectors
+	 * 
+	 * @return V
+	 */
+
+	public Matrix getV() {
+		return new Matrix(V, n, n);
+	}
+
+	/**
+	 * Return the one-dimensional array of singular values
+	 * 
+	 * @return diagonal of S.
+	 */
+
+	public double[] getSingularValues() {
+		return s;
+	}
+
+	/**
+	 * Return the diagonal matrix of singular values
+	 * 
+	 * @return S
+	 */
+
+	public Matrix getS() {
+		Matrix X = new Matrix(n, n);
+		double[][] S = X.getArray();
+		for (int i = 0; i < n; i++) {
+			for (int j = 0; j < n; j++) {
+				S[i][j] = 0.0;
+			}
+			S[i][i] = this.s[i];
+		}
+		return X;
+	}
+
+	/**
+	 * Two norm
+	 * 
+	 * @return max(S)
+	 */
+
+	public double norm2() {
+		return s[0];
+	}
+
+	/**
+	 * Two norm condition number
+	 * 
+	 * @return max(S)/min(S)
+	 */
+
+	public double cond() {
+		return s[0] / s[Math.min(m, n) - 1];
+	}
+
+	/**
+	 * Effective numerical matrix rank
+	 * 
+	 * @return Number of nonnegligible singular values.
+	 */
+
+	public int rank() {
+		double eps = Math.pow(2.0, -52.0);
+		double tol = Math.max(m, n) * s[0] * eps;
+		int r = 0;
+		for (int i = 0; i < s.length; i++) {
+			if (s[i] > tol) {
+				r++;
+			}
+		}
+		return r;
+	}
+}
diff --git a/src/bilib/src/levenbergmarquardt/Cholesky.java b/src/bilib/src/levenbergmarquardt/Cholesky.java
new file mode 100644
index 0000000000000000000000000000000000000000..79ed3c31b248b464744a316636c8c2ee3970136e
--- /dev/null
+++ b/src/bilib/src/levenbergmarquardt/Cholesky.java
@@ -0,0 +1,111 @@
+package levenbergmarquardt;
+
+/**
+ * 
+ * <p>
+ * Title: Cholesky Decomposition
+ * </p>
+ * <p>
+ * Description: Performs a Cholesky decomposition of a matrix and solve a linear
+ * system using this decomposition. Ported to Java from the Numerical Recipes in
+ * C. Press, Teukolsky, Vetterling,and Flannery. 2nd edition. Cambridge
+ * University Press, 1992.
+ * </p>
+ */
+
+public class Cholesky {
+
+	/**
+	 * Given a positive-definite symmetric matrix A[1..n][1..n], this method
+	 * constructs its Cholesky decomposition, A = L � L' . On input, only the
+	 * upper triangle of a need be given; it is not modified. The Cholesky
+	 * factor L is returned in the lower triangle of a, except for its diagonal
+	 * elements which are returned in p[1..n].
+	 * 
+	 * @param A
+	 *            double[][] Input matrix.
+	 * @param p
+	 *            double[]
+	 * @return boolean Returns false if the decomposition is not possible.
+	 */
+	public static boolean decomp(double[][] A, double[] p) {
+		int n = A.length;
+		int i, j, k;
+		double sum;
+		for (i = 0; i < n; i++) {
+			for (j = 0; j < n; j++) {
+				sum = A[i][j];
+				for (k = i - 1; k >= 0; k--) {
+					sum -= (A[i][k] * A[j][k]);
+				}
+				if (i == j) {
+					if (sum <= 0.) {
+						return false; // not positive definite
+					}
+					p[i] = Math.sqrt(sum);
+				}
+				else {
+					A[j][i] = sum / p[i];
+				}
+			}
+		}
+		return true;
+	} // decomp
+
+	/**
+	 * Solves a the linear system Ax=b.
+	 * 
+	 * @param A
+	 *            double[][] Is the result of decomp(A)
+	 * @param p
+	 *            double[] The resulting diagonal vector.
+	 * @param b
+	 *            double[]
+	 * @param x
+	 *            double[]
+	 */
+	private static void solve(double[][] A, double[] p, double[] b, double[] x) {
+		int n = A.length;
+		int i, k;
+		double sum;
+		// Solve L � y = b, storing y in x.
+		for (i = 0; i < n; i++) {
+			sum = b[i];
+			for (k = i - 1; k >= 0; k--) {
+				sum -= (A[i][k] * x[k]);
+			}
+			x[i] = sum / p[i];
+		}
+
+		// Solve L' � x = y.
+		for (i = n - 1; i >= 0; i--) {
+			sum = x[i];
+			for (k = i + 1; k < n; k++) {
+				sum -= (A[k][i] * x[k]);
+			}
+			x[i] = sum / p[i];
+		}
+	} // solve
+
+	/**
+	 * Solves the linear system Ax=b.
+	 * 
+	 * @param A
+	 *            double[][]
+	 * @param x
+	 *            double[]
+	 * @param b
+	 *            double[]
+	 * @return boolean returns false if the system can not be solved using
+	 *         Cholesky decomposition.
+	 */
+	public static boolean solve(double[][] A, double[] x, double[] b) {
+		double[] p = new double[A.length];
+		if (!decomp(A, p)) {
+			return false;
+		}
+		solve(A, p, b, x);
+		return true;
+	} // solve
+
+} // Cholesky
diff --git a/src/bilib/src/levenbergmarquardt/Function.java b/src/bilib/src/levenbergmarquardt/Function.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ade3c6e4c7505c2ea8a88f07751062b09f4bf2a
--- /dev/null
+++ b/src/bilib/src/levenbergmarquardt/Function.java
@@ -0,0 +1,32 @@
+package levenbergmarquardt;
+
+/**
+ */
+public interface Function {
+
+	/**
+	 * Evaluates the model at point x (may be mulidimensional).
+	 * 
+	 * @param x
+	 *            double[] Point where we evaluate the model function.
+	 * @param a
+	 *            double[] Model estimators.
+	 * @return double
+	 */
+	public abstract double eval(double[] x, double[] a);
+
+	/**
+	 * Returns the kth component of the gradient df(x,a)/da_k
+	 * 
+	 * @param x
+	 *            double[]
+	 * @param a
+	 *            double[]
+	 * @param ak
+	 *            int
+	 * @return double
+	 */
+	public abstract double grad(double[] x, double[] a, int ak);
+
+	public abstract void setDebug(boolean debug);
+}
diff --git a/src/bilib/src/levenbergmarquardt/LevenbergMarquardt.java b/src/bilib/src/levenbergmarquardt/LevenbergMarquardt.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d32bab26a929919dbae0dde1119958b35aff405
--- /dev/null
+++ b/src/bilib/src/levenbergmarquardt/LevenbergMarquardt.java
@@ -0,0 +1,297 @@
+package levenbergmarquardt;
+
+/**
+ * 
+ * <p>
+ * Title: Levenberg-Marquardt
+ * </p>
+ * <p>
+ * Description: Perfoms data fitting to a non linear model using the
+ * Levenberg-Marquadrt method. Ported to Java from the Numerical Recipes in C.
+ * Press, Teukolsky, Vetterling,and Flannery. 2nd edition. Cambridge University
+ * Press, 1992.
+ * </p>
+ */
+
+public class LevenbergMarquardt {
+
+	private Function	f;
+	private double		lambdaInitial	= 0.0001;
+	private int			itmax			= 1000;
+	private boolean		print			= false;
+	private int			iter;
+	private double		tolerance		= 0.001;
+
+	/**
+	 * Levenberg-Marquardt constructor. Supply a Function object, f, that
+	 * evaluates the fitting function y, and its derivatives dyda[1..ma] with
+	 * respect to the fitting parameters a at x. On the first call provide an
+	 * initial guess for the parameters a, and set alamda to some small value,
+	 * e.g. alambda=0.001. If a step succeeds chisq becomes smaller and alamda
+	 * decreases by a factor of 10. If a step fails alamda grows by a factor of
+	 * 10.
+	 * 
+	 * @param f
+	 *            Function
+	 * @param lambdaInitial
+	 *            double
+	 * @param itmax
+	 *            int
+	 */
+	public LevenbergMarquardt(Function f, double lambdaInitial, int itmax, boolean print) {
+		this.f = f;
+		this.lambdaInitial = lambdaInitial;
+		this.itmax = itmax;
+		this.print = print;
+		if (print) {
+			System.out.print("CONSTRUCTOR \tlambda:" + lambdaInitial + " max iterations: " + itmax);
+		}
+	}
+
+	public LevenbergMarquardt(Function f, int itmax, double tolerance) {
+		this.f = f;
+		this.itmax = itmax;
+		this.tolerance = tolerance;
+	}
+
+	public LevenbergMarquardt(Function f, int itmax) {
+		this.f = f;
+		this.itmax = itmax;
+	}
+
+	public LevenbergMarquardt(Function f, boolean print) {
+		this.f = f;
+		this.print = print;
+	}
+
+	public LevenbergMarquardt(Function f) {
+		this.f = f;
+	}
+
+	public void setPrint(boolean print) {
+		this.print = print;
+	}
+
+	/**
+	 * Levenberg-Marquardt method, attempting to reduce the value chi2 of a fit
+	 * between a set of data points x[1..ndata], y[1..ndata] with individual
+	 * standard deviations sig[1..ndata], and a nonlinear function dependent on
+	 * ma coefficients a[1..ma]. The input array ia[1..ma] indicates by true,
+	 * entries those components of a that should be fitted for, and by false,
+	 * entries those components that should be held fixed at their input values.
+	 * The program returns current best-fit values for the parameters a[1..ma],
+	 * and chi2 = chisq.
+	 * 
+	 * @param x
+	 *            double[]
+	 * @param y
+	 *            double[]
+	 * @param sig
+	 *            double[]
+	 * @param a
+	 *            double[]
+	 * @return double
+	 * 
+	 */
+	public double minimize(double x[], double y[], double sig[], double a[]) {
+		iter = 0;
+		double lambda = lambdaInitial;
+
+		boolean ia[] = new boolean[a.length];
+		for (int i = 0; i < a.length; i++)
+			ia[i] = true;
+
+		int rep = 0;
+		boolean done = false;
+		double eps = 0;
+		int mfit = 0;
+		int j, k, l;
+		int ma = a.length;
+		double ochisq = 0, chisq;
+
+		double[][] covar = new double[ma][ma];
+		double[][] alpha = new double[ma][ma];
+		double[] beta = new double[ma];
+		double[] atry = new double[ma];
+		double[] da = new double[ma];
+
+		double[] oneda;
+
+		// initialization
+		for (mfit = 0, j = 0; j < ma; j++) {
+			if (ia[j]) {
+				mfit++;
+			}
+		}
+		oneda = new double[mfit];
+
+		chisq = mrqcof(x, y, sig, a, ia, alpha, beta);
+		ochisq = chisq;
+		for (j = 0; j < ma; j++) {
+			atry[j] = a[j];
+		}
+
+		do {
+			// Alter linearized fitting matrix, by augmenting diagonal elements.
+			for (j = 0; j < mfit; j++) {
+				for (k = 0; k < mfit; k++) {
+					covar[j][k] = alpha[j][k];
+				}
+				covar[j][j] = alpha[j][j] * (1.0 + lambda);
+				oneda[j] = beta[j];
+			}
+
+			Cholesky.solve(covar, oneda, oneda); // Matrix solution.
+
+			for (j = 0; j < mfit; j++) {
+				da[j] = oneda[j];
+			}
+
+			for (j = 0, l = 0; l < ma; l++) {
+				if (ia[l]) {
+					atry[l] = a[l] + da[j++];
+				}
+			}
+			chisq = mrqcof(x, y, sig, atry, ia, covar, da);
+			eps = Math.abs(chisq - ochisq);
+			if (print) {
+				System.out.print("#" + iter + "\t chi:" + Math.round(Math.sqrt(chisq) * 1000) / 1000.0 + " \tlambda:" + lambda + " eps:" + eps);
+				for (int i = 0; i < a.length; i++)
+					System.out.print("\t a[" + i + "]=" + atry[i]);
+				System.out.println(";");
+			}
+			if (chisq < ochisq) {
+				// Success, accept the new solution.
+				lambda *= 0.1;
+				ochisq = chisq;
+				for (j = 0; j < mfit; j++) {
+					for (k = 0; k < mfit; k++) {
+						alpha[j][k] = covar[j][k];
+					}
+					beta[j] = da[j];
+				}
+				for (l = 0; l < ma; l++) {
+					a[l] = atry[l];
+				}
+			}
+			else {
+				// Failure, increase alamda and return.
+				lambda *= 10.0;
+				chisq = ochisq;
+			}
+			iter++;
+			if (eps > tolerance) {
+				rep = 0;
+			}
+			else {
+				rep++;
+				if (rep == 4) {
+					done = true;
+				}
+			}
+
+		}
+		while (iter < itmax && !done);
+		if (print)
+			System.out.println("Final iter" + iter + "\t rep:" + rep + " \tdone:" + done + " eps:" + eps + " tolerance:" + tolerance);
+
+		return Math.sqrt(chisq);
+	}
+
+	/**
+	 * Return the number of iterations after minimization.
+	 * 
+	 * @return number of iteration
+	 */
+	public int getIteration() {
+		return iter;
+	}
+
+	/**
+	 * Used by mrqmin to evaluate the linearized fitting matrix alpha, and
+	 * vector beta as in "NR in C"(15.5.8), and calculate chi2.
+	 * 
+	 * @param x
+	 *            double[]
+	 * @param y
+	 *            double[]
+	 * @param sig
+	 *            double[]
+	 * @param a
+	 *            double[]
+	 * @param ia
+	 *            boolean[]
+	 * @param alpha
+	 *            double[][]
+	 * @param beta
+	 *            double[]
+	 * @param f
+	 *            LMfunc
+	 * @return double
+	 */
+	private double mrqcof(double x[], double y[], double sig[], double a[], boolean ia[], double alpha[][], double beta[]) {
+
+		int ndata = x.length;
+		int ma = a.length;
+		double chisq;
+		int i, j, k, l, m, mfit = 0;
+		double ymod, wt, sig2i, dy;
+		double[] dyda = new double[ma];
+
+		for (j = 0; j < ma; j++) {
+			if (ia[j]) {
+				mfit++;
+			}
+		}
+
+		// Initialize(symmetric) alpha, beta.
+		for (j = 0; j < mfit; j++) {
+			for (k = 0; k <= j; k++) {
+				alpha[j][k] = 0;
+			}
+			beta[j] = 0;
+		}
+
+		chisq = 0;
+
+		// Summation loop over all data.
+		for (i = 0; i < ndata; i++) {
+			double[] xi = new double[1];
+			xi[0] = x[i];
+			ymod = f.eval(xi, a);
+			for (k = 0; k < a.length; k++) {
+				dyda[k] = f.grad(xi, a, k);
+			}
+
+			/*
+			 * if (print) { System.out.print("D" + iter); for(int p=0;
+			 * p<dyda.length; p++) System.out.print("\t da["+p+"]=" + dyda[p]);
+			 * System.out.println(";"); }
+			 */
+			sig2i = 1.0 / (sig[i] * sig[i]);
+			dy = y[i] - ymod;
+			for (j = 0, l = 0; l < ma; l++) {
+				if (ia[l]) {
+					wt = dyda[l] * sig2i;
+					for (k = 0, m = 0; m <= l; m++) {
+						if (ia[m]) {
+							alpha[j][k++] += wt * dyda[m];
+						}
+					}
+					beta[j] += dy * wt;
+					j++;
+				}
+			}
+			chisq += dy * dy * sig2i; // And find chi2.
+		}
+
+		// Fill in the symmetric side of alpha
+		for (j = 1; j < mfit; j++) {
+			for (k = 0; k < j; k++) {
+				alpha[k][j] = alpha[j][k];
+			}
+		}
+		return chisq;
+	}
+
+}
diff --git a/src/bilib/src/polyharmonicwavelets/Autocorrelation.java b/src/bilib/src/polyharmonicwavelets/Autocorrelation.java
new file mode 100644
index 0000000000000000000000000000000000000000..2bbedf25ef80c5b5519714ed1e5992574d9ecbfa
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/Autocorrelation.java
@@ -0,0 +1,291 @@
+package polyharmonicwavelets;
+
+//
+//  Autocorrelation.java
+//  PolyharmonicWavelets
+//
+//  Created by Biomedical Imaging Group on 2/13/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+// 
+
+import java.util.*;
+import java.text.DecimalFormat;
+
+/**
+ * This class computes the autocorrelation of the polyharmonic B-spline
+ * function. Two methods can be used, the Gamma function method [1] or the
+ * iterative algorithm [2]. <br>
+ * References: <br>
+ * [1] Yan Barbotin semmester project <br>
+ * [2] T. Blu, D. Van De Ville, M. Unser, ''Numerical methods for the
+ * computation of wavelet correlation sequences,'' SIAM Numerical Analysis. <br>
+ * [3] Matlab documentation
+ * 
+ * @author Katarina Balac, EPFL.
+ */
+
+public class Autocorrelation {
+
+	/**
+	 * Returns the Fourier domain autocorrelation of any scaling function given
+	 * the squared modulus of the refinement filter. <br>
+	 * Reference: T. Blu, D. Van De Ville, M. Unser, ''Numerical methods for the
+	 * computation of wavelet correlation sequences,'' SIAM Numerical Analysis.
+	 * 
+	 * @param HH
+	 *            the squared modulus of the refinement filter.
+	 * @return the autocorreltion of scaling function.
+	 */
+
+	public static ComplexImage autocorrIterative(ComplexImage HH) {
+		int nx = HH.nx;
+		int ny = HH.ny;
+		int lx = nx / 2;
+		int ly = ny / 2;
+		int lyx = ly * lx;
+		ComplexImage A0 = new ComplexImage(lx, ly, true);
+		ComplexImage Af = new ComplexImage(lx, ly);
+		ComplexImage Afe = new ComplexImage(lx + 1, ly + 1);
+		ComplexImage Ad = new ComplexImage(lx, ly, true);
+		ComplexImage Aq = new ComplexImage(lx, ly, true);
+		ComplexImage A1 = new ComplexImage(lx, ly, true);
+		ComplexImage At = new ComplexImage(nx, ny, true); // Does not change
+															// size
+		ComplexImage Ai = new ComplexImage(nx, ny);
+		for (int i = 0; i < lx * ly; i++) {
+			A0.real[i] = 1.0;
+		}
+		final double crit = 0.00000001; // stop criterion
+		double improvement;
+		int lx2 = lx / 2;
+		int lx231 = 3 * lx2 - 1;
+		int ly2 = ly / 2;
+		int ly231 = 3 * ly2 - 1;
+		int ly32 = 3 * ly / 2;
+		int k1 = nx * 3 * ly / 2 + lx / 2;
+		int k3 = nx * ly / 2 + 3 * lx / 2;
+		int k2 = k3 - 1;
+		int k4 = nx * (ly32 - 1) + lx / 2;
+		int count = 0;
+		int maxit = 100;
+		do {
+			count++;
+			Af.copyImageContent(A0);
+			Af.iFFT2D();
+			Af.shift();
+			for (int x = 0; x < lx; x++) {
+				Af.real[x] /= 2.0;
+				Af.imag[x] /= 2.0;
+			}
+			for (int y = 0; y < lyx; y += lx) {
+				Af.real[y] /= 2.0;
+				Af.imag[y] /= 2.0;
+			}
+			Afe.extend(Af);
+			Ai.putZeros();
+			Ai.putSubimage(lx2, ly2, Afe);
+			Ai.shift();
+			Ai.FFT2D(); // Ai is real
+			// recursion
+			A1.putZeros();
+			At.copyImageContent(HH);
+			At.multiply(Ai);
+			Aq.getSubimageContent(0, Aq.nx - 1, 0, Aq.ny - 1, At);
+			A1.add(Aq);
+			Aq.getSubimageContent(0, Aq.nx - 1, ly, ly + Aq.ny - 1, At);
+			A1.add(Aq);
+			Aq.getSubimageContent(lx, lx + Aq.nx - 1, 0, Aq.ny - 1, At);
+			A1.add(Aq);
+			Aq.getSubimageContent(lx, lx + Aq.nx - 1, ly, ly + Aq.ny - 1, At);
+			A1.add(Aq);
+			Ad.copyImageContent(A1);
+			Ad.subtract(A0);
+			improvement = Ad.meanModulus();
+			A0 = A1.copyImage();
+		}
+		while ((improvement > crit) && (count < maxit));
+		System.out.println("The autocoorelation has been computed in " + count + " iterations.");
+		if (count == maxit) {
+			System.out.println("The autocorrelation does not converge!");
+		}
+		return A0;
+	}
+
+	/**
+	 * Returns the autocorrelation of the polyharmonic B-spline function given
+	 * the squared modulus of its localisation. <br>
+	 * Reference: Yan Barbotin semmester project
+	 * 
+	 * @param loc
+	 *            the polyharmonic B-spline localisation
+	 * @param order
+	 *            the order of polyharmonic B-spline
+	 * @return the polyharmonic B-spline autocorrelation
+	 */
+
+	public static ComplexImage autocorrGamma(ComplexImage loc, double order) {
+		final double PI = Math.PI;
+		final double PI2 = 2.0 * PI;
+		double[][] d = { { 0.0, 1.0 }, { 0.0, 2.0 }, { 1.0, 0.0 }, { 1.0, 1.0 }, { 1.0, 2.0 }, { 2.0, 0.0 }, { 2.0, 1.0 }, { -1.0, 0.0 }, { -1.0, 1.0 }, { -1.0, 2.0 }, { -2.0, 0.0 },
+				{ -2.0, 1.0 }, { 0.0, -1.0 }, { 0.0, -2.0 }, { 1.0, -1.0 }, { 1.0, -2.0 }, { 2.0, -1.0 }, { -1.0, -1.0 }, { -1.0, -2.0 }, { -2.0, -1.0 }, { 0.0, 0.0 } }; // 21
+																																											// pair
+		int nx = loc.nx;
+		int ny = loc.ny;
+		int nxy = nx * ny;
+		ComplexImage ac = new ComplexImage(nx, ny, true);
+		GammaFunction gm = new GammaFunction();
+		double gammanorm = Math.exp(gm.lnGamma(order));
+		for (int kx = 0, nx2 = nx / 2; kx <= nx2; kx++) {
+			for (int ky = 0, ny2 = ny / 2; ky <= ny2; ky++) {
+				int kynx = ky * nx;
+				if (ac.real[kynx + kx] == 0.0) {
+					int kxny = kx * ny;
+					double x = (double) kx / (double) nx;
+					double y = (double) ky / (double) ny;
+					double res = 1.0 / (order - 1.0);
+					for (int i = 0; i < 21; i++) {
+						double sqn = PI * ((x - d[i][0]) * (x - d[i][0]) + (y - d[i][1]) * (y - d[i][1]));
+						res += gm.incompleteGammaQ(order, sqn) * gammanorm / Math.pow(sqn, order);
+						sqn = PI * (d[i][0] * d[i][0] + d[i][1] * d[i][1]);
+						if (sqn > 0.0) {
+							res += incompleteGammaGeneral(sqn, 1.0 - order) * Math.cos(PI2 * (d[i][0] * x + d[i][1] * y)) / Math.pow(sqn, 1.0 - order);
+						}
+					}
+					ac.real[kynx + kx] = res;
+					if (kx > 0) {
+						ac.real[kynx + nx - kx] = res;
+					}
+					if (ky > 0) {
+						ac.real[nxy - kynx + kx] = res;
+					}
+					if ((kx > 0) && (ky > 0)) {
+						ac.real[nxy - kynx + nx - kx] = res;
+					}
+					if ((((kynx / ny) * ny) == kynx) && (((kxny / nx) * nx) == kxny)) {
+						int kx1 = ky * nx / ny;
+						int ky1 = kx * ny / nx;
+						kynx = ky1 * nx;
+						kxny = kx1 * ny;
+						ac.real[kynx + kx1] = res;
+						if (kx1 > 0) {
+							ac.real[kynx + nx - kx1] = res;
+						}
+						if (ky1 > 0) {
+							ac.real[nxy - kynx + kx1] = res;
+						}
+						if ((kx1 > 0) && (ky1 > 0)) {
+							ac.real[nxy - kynx + nx - kx1] = res;
+						}
+					}
+				}
+			}
+		}
+		ac.multiply(Math.pow(PI, order) / (gammanorm * Math.pow(PI2, 2.0 * order)));
+		ac.multiply(loc);
+		ac.real[0] = 1.0;
+		return ac;
+	}
+
+	/*
+	 * Solves the incomplete gamma function even for negative a, regularised
+	 * integral from x to infinity. Not normalised. x has to be positive.
+	 * Reference: Yan Barbotin semmester project
+	 */
+
+	private static double incompleteGammaGeneral(double x, double a) {
+		double res = 0;
+		GammaFunction gm = new GammaFunction();
+		if (a < 0) {
+			double a0 = a;
+			int iter = 0;
+			while (a < 0) {
+				a += 1.0;
+				iter++;
+			}
+			if (a == 0.0) {
+				res = expInt(x);
+			}
+			else {
+				res = gm.incompleteGammaQ(a, x) * Math.exp(gm.lnGamma(a));
+			}
+			res *= Math.exp(x - a * Math.log(x));
+			for (int k = 1; k <= iter; k++) {
+				res = (x * res - 1.0) / (a - (double) k);
+			}
+			res *= Math.exp(a0 * Math.log(x) - x);
+		}
+		else {
+			if (a == 0.0) {
+				res = expInt(x);
+			}
+			else {
+				res = gm.incompleteGammaQ(a, x) * Math.exp(gm.lnGamma(a));
+			}
+		}
+		return res;
+	}
+
+	/*
+	 * Computes the exponential integral for a real positive argument x. Copied
+	 * from Matlab.
+	 */
+
+	private static double expInt(double x) {
+		double[] p = { -3.602693626336023e-09, -4.819538452140960e-07, -2.569498322115933e-05, -6.973790859534190e-04, -1.019573529845792e-02, -7.811863559248197e-02, -3.012432892762715e-01,
+				-7.773807325735529e-01, 8.267661952366478e+00 };
+		double d = 0.0;
+		double y = 0.0;
+		for (int j = 0; j < 9; j++) {
+			d *= x;
+			d += p[j];
+		}
+		if (d > 0.0) {
+			double egamma = 0.57721566490153286061;
+			y = -egamma - Math.log(x);
+			double term = x;
+			double pterm = x;
+			double eps = 0.00000000000000000000000000001;
+			for (double j = 2.0; Math.abs(term) > eps; j += 1.0) {
+				y += term;
+				pterm = -x * pterm / j;
+				term = pterm / j;
+			}
+		}
+		else {
+			double n = 1.0;
+			double am2 = 0.0;
+			double bm2 = 1.0;
+			double am1 = 1.0;
+			double bm1 = x;
+			double f = am1 / bm1;
+			double oldf = f + 100.0;
+			double j = 2.0;
+			double eps = 0.000000000000000000000000001;
+			while (Math.abs(f - oldf) > eps) {
+				double alpha = n - 1.0 + j / 2.0;
+				double a = am1 + alpha * am2;
+				double b = bm1 + alpha * bm2;
+				am2 = am1 / b;
+				bm2 = bm1 / b;
+				am1 = a / b;
+				bm1 = 1.0;
+				oldf = f;
+				f = am1;
+				j += 1.0;
+				alpha = (j - 1.0) / 2.0;
+				double beta = x;
+				a = beta * am1 + alpha * am2;
+				b = beta * bm1 + alpha * bm2;
+				am2 = am1 / b;
+				bm2 = bm1 / b;
+				am1 = a / b;
+				bm1 = 1.0;
+				oldf = f;
+				f = am1;
+				j += 1.0;
+			}
+			y = Math.exp(-x) * f;
+		}
+		return y;
+	}
+}
diff --git a/src/bilib/src/polyharmonicwavelets/CoeffProcessing.java b/src/bilib/src/polyharmonicwavelets/CoeffProcessing.java
new file mode 100644
index 0000000000000000000000000000000000000000..3229509f8650fedd4b40814c63c9370e6aed13b0
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/CoeffProcessing.java
@@ -0,0 +1,371 @@
+package polyharmonicwavelets;
+
+//
+//  CoeffProcessing.java
+//  PolyharmonicWavelets
+//
+//  Created by Biomedical Imaging Group on 2/13/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+import java.util.*;
+import ij.*;
+import java.text.DecimalFormat;
+import ij.text.*;
+
+/**
+ * This class is used to proccess the wavelet transform coefficients.
+ * 
+ * @author Katarina Balac, EPFL.
+ */
+
+final public class CoeffProcessing {
+
+	/**
+	 * Do your own processing.
+	 * 
+	 * @param param
+	 *            the transform parameters
+	 * @param transform
+	 *            the array of transform coefficients
+	 */
+
+	static final public void doMyProcessing(Parameters param, ComplexImage[] transform) {
+		if (param.redundancy == param.PYRAMID) {
+			for (int j = 0; j < param.J; j++) {
+				// ...................
+				// process highpass transform[j]
+				// ....................
+			}
+			// ...................
+			// process lowpass transform[J]
+			// ....................
+		}
+		else { // nonredundant
+			if (param.lattice == param.DYADIC) {
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				dx /= 2;
+				dy /= 2;
+				for (int j = 0; j < param.J; j++) {
+					ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+					// ...................
+					// process highpass subband
+					// ....................
+					transform[0].putSubimage(0, dy, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+					// ...................
+					// process highpass subband
+					// ....................
+					transform[0].putSubimage(dx, 0, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1);
+					// ...................
+					// process highpass subband
+					// ....................
+					transform[0].putSubimage(dx, dy, subband);
+					dx /= 2;
+					dy /= 2;
+				}
+				ComplexImage subband = transform[0].getSubimage(0, dx - 1, 0, dy - 1);
+				// ...................
+				// process lowpass subband
+				// ....................
+				transform[0].putSubimage(0, 0, subband);
+			}
+			else { // quincunx
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				dx /= 2;
+				for (int j = 0; j < param.J; j++) {
+					if (j % 2 == 0) {
+						ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+						// ...................
+						// process highpass subband
+						// ....................
+						transform[0].putSubimage(dx, 0, subband);
+						dy /= 2;
+					}
+					else {
+						dy /= 2;
+						ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+						// ...................
+						// process highpass subband
+						// ....................
+						transform[0].putSubimage(0, dy, subband);
+						dx /= 2;
+					}
+					ComplexImage subband = transform[0].getSubimage(0, dx - 1, 0, dy - 1);
+					// ...................
+					// process lowpass subband
+					// ....................
+					transform[0].putSubimage(0, 0, subband);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Implements hard threshold on transform coefficients.
+	 * 
+	 * @param param
+	 *            the transform parameters
+	 * @param transform
+	 *            the array of transform coefficients
+	 * @param t
+	 *            the threshold
+	 */
+
+	static final public void doHardThreshold(Parameters param, ComplexImage[] transform, double t) {
+		if (param.redundancy == param.PYRAMID) {
+			for (int j = 0; j < param.J; j++) {
+				transform[j].hardThreshold(t);
+			}
+		}
+		else { // nonredundant
+			if (param.lattice == param.DYADIC) {
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				dx /= 2;
+				dy /= 2;
+				for (int j = 0; j < param.J; j++) {
+					ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+					subband.hardThreshold(t);
+					transform[0].putSubimage(0, dy, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+					subband.hardThreshold(t);
+					transform[0].putSubimage(dx, 0, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1);
+					subband.hardThreshold(t);
+					transform[0].putSubimage(dx, dy, subband);
+					dx /= 2;
+					dy /= 2;
+				}
+			}
+			else { // quincunx
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				for (int j = 0; j < param.J; j++) {
+					if (j % 2 == 0) {
+						dx /= 2;
+						ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+						subband.hardThreshold(t);
+						transform[0].putSubimage(dx, 0, subband);
+					}
+					else {
+						dy /= 2;
+						ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+						subband.hardThreshold(t);
+						transform[0].putSubimage(0, dy, subband);
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Implements soft threshold on transform coefficients.
+	 * 
+	 * @param param
+	 *            the transform parameters
+	 * @param transform
+	 *            the array of transform coefficients
+	 * @param t
+	 *            the threshold
+	 */
+
+	static final public void doSoftThreshold(Parameters param, ComplexImage[] transform, double t) {
+		if (param.redundancy == param.PYRAMID) {
+			for (int j = 0; j < param.J; j++) {
+				transform[j].softThreshold(t);
+			}
+		}
+		else { // nonredundant
+			if (param.lattice == param.DYADIC) {
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				dx /= 2;
+				dy /= 2;
+				for (int j = 0; j < param.J; j++) {
+					ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+					subband.softThreshold(t);
+					transform[0].putSubimage(0, dy, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+					subband.softThreshold(t);
+					transform[0].putSubimage(dx, 0, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1);
+					subband.softThreshold(t);
+					transform[0].putSubimage(dx, dy, subband);
+					dx /= 2;
+					dy /= 2;
+				}
+			}
+			else { // quincunx
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				for (int j = 0; j < param.J; j++) {
+					if (j % 2 == 0) {
+						dx /= 2;
+						ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+						subband.softThreshold(t);
+						transform[0].putSubimage(dx, 0, subband);
+					}
+					else {
+						dy /= 2;
+						ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+						subband.softThreshold(t);
+						transform[0].putSubimage(0, dy, subband);
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Applies the soft threshold to each subband where threshold is computed
+	 * from the image features.
+	 * 
+	 * @param param
+	 *            the transform parameters
+	 * @param transform
+	 *            the array of transform coefficients.
+	 */
+
+	static final public void softThresholdAdaptive(Parameters param, ComplexImage[] transform) { // take
+																									// care
+																									// of
+																									// redundant!
+		int J = param.J;
+		double[] scale = { 1.0, 0.6, 0.34, 0.26, 0.23, 0.21, 0.20, 0.20 };
+		double[] threshold = new double[J];
+		double coef = 1.0;
+		double c = 0.1 * coef;
+		ComplexImage firstsubband = transform[0].copyImage();
+		int nx = firstsubband.nx;
+		int ny = firstsubband.ny;
+		if (param.redundancy == param.BASIS) {
+			if (param.lattice == param.DYADIC) {
+				firstsubband = firstsubband.getSubimage(nx / 2, nx - 1, ny / 2, ny - 1);
+			}
+			else {
+				firstsubband = firstsubband.getSubimage(nx / 2, nx - 1, 0, ny - 1);
+			}
+		}
+		threshold[0] = firstsubband.deviation() * c;
+		if (J > 8) {
+			for (int k = 1; k < 8; k++) {
+				threshold[k] = threshold[0] * scale[k];
+			}
+			for (int k = 8; k < J; k++) {
+				threshold[k] = threshold[8];
+			}
+		}
+		else {
+			for (int k = 1; k < J; k++) {
+				threshold[k] = threshold[0] * scale[k];
+			}
+		}
+		if (param.redundancy == param.PYRAMID) {
+			for (int j = 0; j < param.J; j++) {
+				transform[j].softThreshold(threshold[j]);
+			}
+		}
+		else { // nonredundant
+			if (param.lattice == param.DYADIC) {
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				dx /= 2;
+				dy /= 2;
+				for (int j = 0; j < param.J; j++) {
+					ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+					subband.softThreshold(threshold[j]);
+					transform[0].putSubimage(0, dy, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+					subband.softThreshold(threshold[j]);
+					transform[0].putSubimage(dx, 0, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1);
+					subband.softThreshold(threshold[j]);
+					transform[0].putSubimage(dx, dy, subband);
+					dx /= 2;
+					dy /= 2;
+				}
+			}
+			else { // quincunx
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				for (int j = 0; j < param.J; j++) {
+					if (j % 2 == 0) {
+						dx /= 2;
+						ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+						subband.softThreshold(threshold[j]);
+						transform[0].putSubimage(dx, 0, subband);
+					}
+					else {
+						dy /= 2;
+						ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+						subband.softThreshold(threshold[j]);
+						transform[0].putSubimage(0, dy, subband);
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Multiplies the coefficients in all highpass subbands with the given
+	 * enhancement parameter.
+	 * 
+	 * @param param
+	 *            the transform parameters
+	 * @param transform
+	 *            the array of transform coefficients
+	 * @param t
+	 *            the enhancement parameter
+	 */
+
+	static final public void doEnhancement(Parameters param, ComplexImage[] transform, double t) {
+		if (param.redundancy == param.PYRAMID) {
+			for (int j = 0; j < param.J; j++) {
+				transform[j].multiply(t);
+			}
+		}
+		else { // nonredundant
+			if (param.lattice == param.DYADIC) {
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				dx /= 2;
+				dy /= 2;
+				for (int j = 0; j < param.J; j++) {
+					ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+					subband.multiply(t);
+					transform[0].putSubimage(0, dy, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+					subband.multiply(t);
+					transform[0].putSubimage(dx, 0, subband);
+					subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1);
+					subband.multiply(t);
+					transform[0].putSubimage(dx, dy, subband);
+					dx /= 2;
+					dy /= 2;
+				}
+			}
+			else { // quincunx
+				int dx = transform[0].nx;
+				int dy = transform[0].ny;
+				for (int j = 0; j < param.J; j++) {
+					if (j % 2 == 0) {
+						dx /= 2;
+						ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1);
+						subband.multiply(t);
+						transform[0].putSubimage(dx, 0, subband);
+					}
+					else {
+						dy /= 2;
+						ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1);
+						subband.multiply(t);
+						transform[0].putSubimage(0, dy, subband);
+					}
+				}
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/bilib/src/polyharmonicwavelets/ComplexImage.java b/src/bilib/src/polyharmonicwavelets/ComplexImage.java
new file mode 100644
index 0000000000000000000000000000000000000000..a33473bed1465bd7deb5e7c5ec64822750b6b370
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/ComplexImage.java
@@ -0,0 +1,2706 @@
+//
+//  ComplexImage.java
+//  PolyharmonicWavelets
+//
+//  Created by Biomedical Imaging Group on 2/13/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+package polyharmonicwavelets;
+
+import java.util.*;
+import java.awt.*;
+import java.awt.event.*;
+import ij.process.*;
+import ij.*;
+import java.text.DecimalFormat;
+import ij.plugin.filter.PlugInFilter;
+import ij.text.*;
+
+/**
+ * A ComplexImage object is a two-dimensional array of complex numbers.
+ * 
+ * @author Katarina Balac, EPFL.
+ */
+
+public class ComplexImage {
+
+	/**
+	 * The real part of the ComplexImage object.
+	 */
+	public double			real[];
+
+	/**
+	 * The imaginary part of the ComplexImage object.
+	 */
+	public double			imag[];
+
+	/**
+	 * The number of columns in the ComplexImage object.
+	 */
+	public int				nx;
+
+	/**
+	 * The number of rows in the ComplexImage object.
+	 */
+	public int				ny;
+
+	/**
+	 * The total number of elements in the ComplexImage object.
+	 */
+	public int				nxy;
+
+	private final double	PI2		= 2.0 * Math.PI;
+	private final double	sqrt2	= Math.sqrt(2.0);
+
+	/**
+	 * Creates a ComplexImage object from an ImagePlus image.
+	 * 
+	 * @param imp
+	 *            the ImagePlus image used to create the ComplexImage object
+	 */
+
+	public ComplexImage(ImagePlus imp) {
+		nx = imp.getWidth();
+		ny = imp.getHeight();
+		nxy = nx * ny;
+		real = new double[nxy];
+		imag = new double[nxy];
+		if (imp.getType() == ImagePlus.GRAY8) {
+			byte[] pixels = (byte[]) imp.getProcessor().getPixels();
+			for (int k = 0; k < nx * ny; k++)
+				real[k] = (double) (pixels[k] & 0x00FF);
+		}
+		if (imp.getType() == ImagePlus.GRAY16) {
+			short[] pixels = (short[]) imp.getProcessor().getPixels();
+			for (int k = 0; k < nx * ny; k++)
+				real[k] = (double) (pixels[k]);
+		}
+		if (imp.getType() == ImagePlus.GRAY32) {
+			float[] pixels = (float[]) imp.getProcessor().getPixels();
+			for (int k = 0; k < nx * ny; k++)
+				real[k] = (double) (pixels[k]);
+		}
+	}
+
+	/**
+	 * Creates a Compleximage object of a given size filed with zeros.
+	 * 
+	 * @param sizex
+	 *            the number of columns in the ComplexImage
+	 * @param sizey
+	 *            the number of rows in the ComplexImage
+	 */
+
+	public ComplexImage(int sizex, int sizey) {
+		nx = sizex;
+		ny = sizey;
+		nxy = nx * ny;
+		real = new double[nxy];
+		imag = new double[nxy];
+	}
+
+	/**
+	 * Creates a Compleximage object of a given size filed with zeros, enables
+	 * not to reserve space for the imaginary part.
+	 * 
+	 * @param sizex
+	 *            the number of columns in the ComplexImage
+	 * @param sizey
+	 *            the number of rows in the ComplexImage
+	 * @param r
+	 *            if true the image is real, the imaginary part is set to null
+	 */
+
+	public ComplexImage(int sizex, int sizey, boolean r) {
+		nx = sizex;
+		ny = sizey;
+		nxy = nx * ny;
+		real = new double[nxy];
+		imag = null;
+		if (!r) {
+			imag = new double[nxy];
+		}
+	}
+
+	/**
+	 * If necessary, this ComplexImage is cropped by keeping its central part so
+	 * that the quincunx wavelet analysis can be performed.
+	 * 
+	 * @param J
+	 *            number of decomposition levels
+	 */
+
+	public void cropQuincunx(int J) {
+		int ind = 1;
+		int lx = nx;
+		int ly = ny;
+		for (int j = 0; j < J; j++) {
+			if (ind == 1) {
+				lx /= 2;
+			}
+			else {
+				ly /= 2;
+			}
+			ind = 1 - ind;
+		}
+		ind = 1 - ind;
+		for (int j = 0; j < J; j++) {
+			if (ind == 1) {
+				lx *= 2;
+			}
+			else {
+				ly *= 2;
+			}
+			ind = 1 - ind;
+		}
+		if (!((lx == nx) && (ly == ny))) {
+			System.out.println("Image has been cropped");
+			int dx = (nx - lx) / 2;
+			int dy = (ny - ly) / 2;
+			ComplexImage temp = getSubimage(dx, lx - 1 + dx, dy, ly - 1 + dy);
+			nx = temp.nx;
+			ny = temp.ny;
+			nxy = temp.nxy;
+			System.arraycopy(temp.real, 0, real, 0, nxy);
+			System.arraycopy(temp.imag, 0, imag, 0, nxy);
+			showReal("Cropped image");
+		}
+	}
+
+	/**
+	 * If necessary, this ComplexImage is cropped by keeping its central part so
+	 * that the dyadic wavelet analysis can be performed.
+	 * 
+	 * @param J
+	 *            number of decomposition levels
+	 */
+
+	public void cropDyadic(int J) {
+		int lx = nx;
+		int ly = ny;
+		for (int j = 0; j < J; j++) {
+			lx /= 2;
+			ly /= 2;
+		}
+
+		for (int j = 0; j < J; j++) {
+			lx *= 2;
+			ly *= 2;
+		}
+		if (!((lx == nx) && (ly == ny))) {
+			System.out.println("Image has been cropped");
+			int dx = (nx - lx) / 2;
+			int dy = (ny - ly) / 2;
+			ComplexImage temp = getSubimage(dx, lx - 1 + dx, dy, ly - 1 + dy);
+			nx = temp.nx;
+			ny = temp.ny;
+			nxy = temp.nxy;
+			System.arraycopy(temp.real, 0, real, 0, nxy);
+			System.arraycopy(temp.imag, 0, imag, 0, nxy);
+			showReal("Cropped image");
+		}
+	}
+
+	/**
+	 * Returns the maximum number of wavelet iterations for this ComplexImage.
+	 * 
+	 * @param type
+	 *            the transform lattice, "Quincunx" or "Dyadic"
+	 */
+
+	public int noIterations(String type) {
+		int noit = 0;
+		int dx = nx;
+		int dy = ny;
+		if (type == "Dyadic") {
+			while ((dx % 2 == 0) && (dy % 2 == 0)) {
+				noit++;
+				dx /= 2;
+				dy /= 2;
+			}
+		}
+		if (type == "Quincunx") {
+			while ((dx % 2 == 0) && (dy % 2 == 0)) {
+				noit += 2;
+				dx /= 2;
+				dy /= 2;
+			}
+			if (dx % 2 == 0) {
+				noit++;
+			}
+		}
+		return noit;
+	}
+
+	/**
+	 * Crops this ComplexImage to a given size by keeping its central part.
+	 * 
+	 * @param lx
+	 *            number of columns in the cropped ComplexImage
+	 * @param ly
+	 *            number of rows in the cropped ComplexImage
+	 */
+
+	public void croptoSize(int lx, int ly) {
+		if (!((lx == nx) && (ly == ny))) {
+			int dx = (nx - lx) / 2;
+			int dy = (ny - ly) / 2;
+			ComplexImage temp = getSubimage(dx, lx - 1 + dx, dy, ly - 1 + dy);
+			nx = temp.nx;
+			ny = temp.ny;
+			nxy = temp.nxy;
+			System.arraycopy(temp.real, 0, real, 0, nxy);
+			System.arraycopy(temp.imag, 0, imag, 0, nxy);
+		}
+	}
+
+	/**
+	 * Fills the real part of this squared ComplexImage with the zoneplate
+	 * image.
+	 */
+
+	public void zone() {
+		int size = nx;
+		int size2 = size / 2;
+		double c = Math.PI / sqrt2 / (double) nx;
+		int ind = 0;
+		for (int y1 = -size2; y1 < size2; y1++) {
+			for (int x1 = -size2; x1 < size2; x1++) {
+				real[ind++] = (1.0 + Math.cos(c * (double) (y1 * y1 + x1 * x1))) * 127.5;
+			}
+		}
+	}
+
+	/**
+	 * Fills the real part of this ComplexImage with random values of Gaussian
+	 * distribution N(0,1).
+	 */
+
+	public void makeNoise() {
+		Random random = new Random();
+		for (int k = 0; k < nxy; k++) {
+			real[k] = random.nextGaussian();
+		}
+	}
+
+	/**
+	 * Fills the real part of this ComplexImage with the Gaussian shaped spots.
+	 */
+
+	public void makeGaussianSpots() {
+		Random random = new Random();
+		int N = random.nextInt(12) + 9; // Number of spots, between 10 and 20
+		// int N=1;
+		int[] X0 = new int[N];
+		int[] Y0 = new int[N];
+		double[] sigma = new double[N];
+		for (int k = 0; k < N; k++) {
+			X0[k] = random.nextInt(nx - 60) + 30;
+			Y0[k] = random.nextInt(ny - 60) + 30;
+			sigma[k] = random.nextDouble() * 5.0 + 1.0; // sigma between 1 and 6
+		}
+		for (int k = 0; k < N; k++) {
+			double s2 = 2.0 * sigma[k] * sigma[k];
+			// double s1=1.0/(s2*Math.PI);
+			double s1 = 1.0 / (2.0 * Math.PI);
+			for (int y = -30; y < 30; y++) {
+				int dy = nx * y;
+				for (int x = -30; x < 30; x++) {
+					int p0 = Y0[k] * nx + X0[k];
+					double s = s1 * Math.exp(-(double) (x * x + y * y) / s2);
+					real[p0 + dy + x] += s;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Displays the real part of this ComplexImage on the screen.
+	 * 
+	 * @param text
+	 *            name of the image to display
+	 */
+
+	public void showReal(String text) {
+		float re[][] = new float[nx][ny];
+		int index;
+		for (int k = 0; k < nx; k++)
+			for (int l = 0; l < ny; l++) {
+				index = l * nx + k;
+				re[k][l] = (float) real[index];
+			}
+		FloatProcessor fp = new FloatProcessor(re);
+		ImagePlus imp = new ImagePlus(text, fp);
+		imp.show();
+	}
+
+	/**
+	 * Displays the imaginary part of this ComplexImage on the screen.
+	 * 
+	 * @param text
+	 *            name of the image to display
+	 */
+
+	public void showImag(String text) {
+		float imaginary[][] = new float[nx][ny];
+		int index;
+		for (int k = 0; k < nx; k++)
+			for (int l = 0; l < ny; l++) {
+				index = l * nx + k;
+				imaginary[k][l] = (float) imag[index];
+			}
+		FloatProcessor fp = new FloatProcessor(imaginary);
+		ImagePlus imp = new ImagePlus(text, fp);
+		imp.show();
+	}
+
+	/**
+	 * Displays the modulus of this ComplexImage on the screen.
+	 * 
+	 * @param text
+	 *            name of the image to display
+	 */
+
+	public void showModulus(String text) {
+		float mod[][] = new float[nx][ny];
+		int index;
+		for (int k = 0; k < nx; k++)
+			if (imag == null) {
+				for (int l = 0; l < ny; l++) {
+					index = l * nx + k;
+					mod[k][l] = (float) Math.abs(real[index]);
+				}
+			}
+			else {
+				for (int l = 0; l < ny; l++) {
+					index = l * nx + k;
+					mod[k][l] = (float) Math.sqrt(real[index] * real[index] + imag[index] * imag[index]);
+				}
+			}
+		FloatProcessor fp = new FloatProcessor(mod);
+		ImagePlus imp = new ImagePlus(text, fp);
+		imp.show();
+	}
+
+	/**
+	 * Displays the real and the imaginary part of this ComplexImage as a stack.
+	 * 
+	 * @param text
+	 *            name of the stack to be displayed
+	 */
+
+	public void displayComplexStack(String text) {
+		double color = -255.0;
+		ComplexImage[] out = new ComplexImage[2];
+		ImageStack stack = new ImageStack(nx, ny);
+		float ima[][] = new float[nx][ny];
+		for (int k = 0; k < nx; k++) {
+			for (int l = 0; l < ny; l++) {
+				int index = l * nx + k;
+				ima[k][l] = (float) real[index];
+			}
+		}
+		FloatProcessor fp = new FloatProcessor(ima);
+		fp.setValue(color);
+		stack.addSlice("Real part", fp);
+		for (int k = 0; k < nx; k++) {
+			for (int l = 0; l < ny; l++) {
+				int index = l * nx + k;
+				ima[k][l] = (float) imag[index];
+			}
+		}
+		fp = new FloatProcessor(ima);
+		fp.setValue(color);
+		stack.addSlice("Imaginary part", fp);
+		ImagePlus imp = new ImagePlus(text, stack);
+		imp.show();
+	}
+
+	/**
+	 * Displays the real parts of ComplexImages in an array as a stack.
+	 * 
+	 * @param array
+	 *            the aray of images to be displayed
+	 * @param text
+	 *            name of the stack to be displayed
+	 */
+
+	public static void displayStack(ComplexImage[] array, String text) {
+		int nx = array[0].nx;
+		int ny = array[0].ny;
+		ImageStack stack = new ImageStack(nx, ny);
+		float ima[][] = new float[nx][ny];
+		for (int j = 0; j < array.length; j++) {
+			float im[][] = new float[nx][ny];
+			int index;
+			for (int k = 0; k < nx; k++) {
+				for (int l = 0; l < ny; l++) {
+					index = l * nx + k;
+					ima[k][l] = (float) array[j].real[index];
+				}
+			}
+			FloatProcessor fp = new FloatProcessor(ima);
+			stack.addSlice("j", fp);
+		}
+		ImagePlus imp = new ImagePlus(text, stack);
+		imp.show();
+	}
+
+	/**
+	 * Displays the imaginary parts of ComplexImages in an array as a stack.
+	 * 
+	 * @param array
+	 *            the aray of images to be displayed
+	 * @param text
+	 *            name of the stack to be displayed
+	 */
+
+	public static void displayStackImag(ComplexImage[] array, String text) {
+		int nx = array[0].nx;
+		int ny = array[0].ny;
+		ImageStack stack = new ImageStack(nx, ny);
+		float ima[][] = new float[nx][ny];
+		for (int j = 0; j < array.length; j++) {
+			float im[][] = new float[nx][ny];
+			int index;
+			for (int k = 0; k < nx; k++)
+				for (int l = 0; l < ny; l++) {
+					index = l * nx + k;
+					ima[k][l] = (float) array[j].imag[index];
+				}
+			FloatProcessor fp = new FloatProcessor(ima);
+			stack.addSlice("j", fp);
+		}
+		ImagePlus imp = new ImagePlus(text, stack);
+		imp.show();
+	}
+
+	/**
+	 * Displays this ComplexImage values on the screen.
+	 * 
+	 * @param text
+	 *            name of the image to be displayed
+	 */
+
+	public void displayValues(String text) {
+		DecimalFormat decimalFormat = new DecimalFormat();
+		decimalFormat.applyPattern("0.0000");
+		System.out.println(" ");
+		System.out.println(text);
+		System.out.println("sizenxnx=" + nx);
+		System.out.println("sizeny=" + ny);
+		for (int i = 0; i < ny; i++) {
+			for (int j = 0; j < nx; j++) {
+				System.out.print("  " + decimalFormat.format(real[i * nx + j]) + "+" + decimalFormat.format(imag[i * nx + j]) + "i");
+			}
+			System.out.println(" ");
+		}
+	}
+
+	/**
+	 * Sets both the real and the imaginary part of this ComplexImage object to
+	 * zero.
+	 */
+
+	public void putZeros() {
+		if (imag == null) {
+			for (int i = 0; i < nxy; i++) {
+				real[i] = 0.0;
+			}
+		}
+		else {
+			for (int i = 0; i < nxy; i++) {
+				real[i] = imag[i] = 0.0;
+			}
+		}
+	}
+
+	/**
+	 * Sets the real part of this ComplexImage object to zero.
+	 */
+
+	public void setRealtoZero() {
+		for (int k = 0; k < nxy; k++) {
+			real[k] = 0.0;
+		}
+	}
+
+	/**
+	 * Sets the imaginary part of this ComplexImage object to zero.
+	 */
+
+	public void setImagtoZero() {
+		for (int k = 0; k < nxy; k++) {
+			imag[k] = 0.0;
+		}
+	}
+
+	/**
+	 * Sets all the values in this ComplexImage to a given constant.
+	 * 
+	 * @param r
+	 *            - the real part of the ComplexImage to be set
+	 * @param i
+	 *            - the imaginary part of the ComplexImage to be set
+	 */
+
+	public void settoConstant(double r, double i) {
+		for (int k = 0; k < nxy; k++) {
+			real[k] = r;
+			imag[k] = i;
+		}
+	}
+
+	/**
+	 * Sets the real part of this ComplexImage to a given constant.
+	 * 
+	 * @param r
+	 *            - the value to be set
+	 */
+
+	public void settoConstant(double r) {
+		for (int k = 0; k < nxy; k++) {
+			real[k] = r;
+		}
+	}
+
+	/**
+	 * Creates a new ComplexImage object containing the chosen row of this
+	 * ComplexImage.
+	 * 
+	 * @param y
+	 *            the number of row to be copied
+	 * @return ComplexImage containing the chosen row
+	 */
+
+	public ComplexImage getRow(int y) {
+		ComplexImage row = new ComplexImage(nx, 1, imag == null);
+		System.arraycopy(real, y * nx, row.real, 0, nx);
+		if (!(imag == null)) {
+			System.arraycopy(imag, y * nx, row.imag, 0, nx);
+		}
+		return row;
+	}
+
+	/**
+	 * Copies the content of the chosen row of image to this ComplexImage.
+	 * 
+	 * @param image
+	 *            the ComplexImage to copy from
+	 * @param y
+	 *            the number of row to be copied
+	 */
+
+	public void getRowContent(int y, ComplexImage image) {
+		nx = image.nx;
+		ny = 1;
+		int s = y * nx;
+		System.arraycopy(image.real, s, real, 0, image.nx);
+		if ((!(imag == null)) && (!(image.imag == null))) {
+			System.arraycopy(image.imag, s, imag, 0, image.nx);
+		}
+	}
+
+	/**
+	 * Copies an 1D array row to y-th row of this ComplexImage.
+	 * 
+	 * @param y
+	 *            number of row to modify
+	 * @param row
+	 *            ComplexImage with one row containing the row to be placed
+	 */
+
+	public void putRow(int y, ComplexImage row) {
+		System.arraycopy(row.real, 0, real, y * nx, nx);
+		if ((!(imag == null)) && (!(row.imag == null))) {
+			System.arraycopy(row.imag, 0, imag, y * nx, nx);
+		}
+	}
+
+	/**
+	 * Creates a new ComplexImage object containing the chosen column of this
+	 * ComplexImage.
+	 * 
+	 * @param x
+	 *            number of column to be copied
+	 * @return ComplexImage containing the chosen column of this ComplexImage
+	 */
+
+	public ComplexImage getColumn(int x) {
+		ComplexImage column = new ComplexImage(ny, 1, imag == null);
+		if (imag == null) {
+			for (int y = 0, n = x; y < ny; y++) {
+				column.real[y] = real[n];
+				n += nx;
+			}
+		}
+		else {
+			for (int y = 0, n = x; y < ny; y++) {
+				column.real[y] = real[n];
+				column.imag[y] = imag[n];
+				n += nx;
+			}
+		}
+		return column;
+	}
+
+	/**
+	 * Copies the content of the chosen column of image to this ComplexImage.
+	 * 
+	 * @param image
+	 *            the ComplexImage to copy from
+	 * @param x
+	 *            the number of column to be copied
+	 */
+
+	public void getColumnContent(int x, ComplexImage image) {
+		nx = 1;
+		ny = image.ny;
+		if ((image.imag == null) || (imag == null)) {
+			for (int y = 0, n = x; y < image.ny; y++) {
+				real[y] = image.real[n];
+				n += image.nx;
+			}
+		}
+		else {
+			for (int y = 0, n = x; y < image.ny; y++) {
+				real[y] = image.real[n];
+				imag[y] = image.imag[n];
+				n += image.nx;
+			}
+		}
+	}
+
+	/**
+	 * Copies an 1D array column to x-th column of this ComplexImage.
+	 * 
+	 * @param x
+	 *            number of column to modify
+	 * @param column
+	 *            ComplexImage with one column containing the column to be
+	 *            placed
+	 */
+
+	public void putColumn(int x, ComplexImage column) {
+		if ((!(imag == null)) && (!(column.imag == null))) {
+			for (int y = 0, n = x; y < ny; y++) {
+				real[n] = column.real[y];
+				imag[n] = column.imag[y];
+				n += nx;
+			}
+		}
+		else {
+			for (int y = 0, n = x; y < ny; y++) {
+				real[n] = column.real[y];
+				n += nx;
+			}
+		}
+	}
+
+	/**
+	 * Creates a new ComplexImage object containing the subimage of this
+	 * ComplexImage.
+	 * 
+	 * @param x1
+	 *            starting x coordinate within this ComplexImage
+	 * @param x2
+	 *            end x coordinate within this ComplexImage
+	 * @param y1
+	 *            starting y coordinate within this ComplexImage
+	 * @param y2
+	 *            end y coordinate within this ComplexImage
+	 * @return the subimage
+	 */
+
+	public ComplexImage getSubimage(int x1, int x2, int y1, int y2) {
+		ComplexImage sub = new ComplexImage(x2 - x1 + 1, y2 - y1 + 1, imag == null);
+		int d = x1 + y1 * nx;
+		if (imag == null) {
+			for (int y = 0; y < sub.ny; y++) {
+				System.arraycopy(real, d + y * nx, sub.real, y * sub.nx, sub.nx);
+			}
+
+		}
+		else {
+			for (int y = 0; y < sub.ny; y++) {
+				System.arraycopy(real, d + y * nx, sub.real, y * sub.nx, sub.nx);
+				System.arraycopy(imag, d + y * nx, sub.imag, y * sub.nx, sub.nx);
+			}
+		}
+		return (sub);
+	}
+
+	/**
+	 * Copies the content of a subimage of image to this ComplexImage. Gives the
+	 * same result as getSubimage, but does not create a new object. Total
+	 * number of pixels assigned to this ComplexImage when created has to be
+	 * sufficient for the subimage.
+	 * 
+	 * @param x1
+	 *            starting x coordinate within image
+	 * @param x2
+	 *            end x coordinate within image
+	 * @param y1
+	 *            starting y coordinate within image
+	 * @param y2
+	 *            end y coordinate within image
+	 * @param image
+	 *            the image to copy
+	 */
+
+	public void getSubimageContent(int x1, int x2, int y1, int y2, ComplexImage image) {
+		int d = x1 + y1 * image.nx;
+		nx = x2 - x1 + 1;
+		ny = y2 - y1 + 1;
+		nxy = nx * ny;
+		if ((imag == null) || (image.imag == null)) {
+			for (int y = 0; y < ny; y++) {
+				System.arraycopy(image.real, d + y * image.nx, real, y * nx, nx);
+			}
+		}
+		else {
+			for (int y = 0; y < ny; y++) {
+				System.arraycopy(image.real, d + y * image.nx, real, y * nx, nx);
+				System.arraycopy(image.imag, d + y * image.nx, imag, y * nx, nx);
+			}
+		}
+	}
+
+	/**
+	 * Copies the ComplexImage sub to the position in this ComplexImage given by
+	 * its upper left corner.
+	 * 
+	 * @param sub
+	 *            the image to copy
+	 * @param x1
+	 *            starting x coordinate
+	 * @param y1
+	 *            starting y coordinate
+	 */
+
+	public void putSubimage(int x1, int y1, ComplexImage sub) {
+		int k;
+		int k1;
+		int d = x1 + y1 * nx;
+		if (sub.imag == null) {
+			for (int y = 0; y < sub.ny; y++) {
+				System.arraycopy(sub.real, y * sub.nx, real, d + y * nx, sub.nx);
+			}
+		}
+		else {
+			for (int y = 0; y < sub.ny; y++) {
+				System.arraycopy(sub.real, y * sub.nx, real, d + y * nx, sub.nx);
+				System.arraycopy(sub.imag, y * sub.nx, imag, d + y * nx, sub.nx);
+			}
+		}
+	}
+
+	/**
+	 * Creates a ComplexImage object as a copy of this ComplexImage.
+	 * 
+	 * @return the copy of this ComplexImage
+	 */
+
+	public ComplexImage copyImage() {
+		ComplexImage temp = null;
+		if (imag == null) {
+			temp = new ComplexImage(nx, ny, true);
+			System.arraycopy(real, 0, temp.real, 0, nxy);
+		}
+		else {
+			temp = new ComplexImage(nx, ny);
+			System.arraycopy(real, 0, temp.real, 0, nxy);
+			System.arraycopy(imag, 0, temp.imag, 0, nxy);
+		}
+		return temp;
+	}
+
+	/**
+	 * Copies the content of ComplexImage original to this ComplexImage. Gives
+	 * the same result as copyImage, but does not create a new object. Total
+	 * number of pixels assigned to this ComplexImage when created has to be
+	 * sufficient for the original image.
+	 * 
+	 * @param original
+	 *            the ComplexImage to copy
+	 */
+
+	public void copyImageContent(ComplexImage original) {
+		nx = original.nx;
+		ny = original.ny;
+		nxy = original.nxy;
+		if ((original.imag == null) || (imag == null)) {
+			System.arraycopy(original.real, 0, real, 0, nxy);
+		}
+		else {
+			System.arraycopy(original.real, 0, real, 0, nxy);
+			System.arraycopy(original.imag, 0, imag, 0, nxy);
+		}
+	}
+
+	/**
+	 * Computes a square modulus of this ComplexImage. Puts a square modulus in
+	 * the real part of this ComplexImage and zeros in the imaginary part if it
+	 * exists.
+	 */
+
+	public void squareModulus() {
+		if (imag == null) {
+			for (int i = 0; i < nxy; i++) {
+				real[i] = real[i] * real[i];
+			}
+		}
+		else {
+			for (int i = 0; i < nxy; i++) {
+				real[i] = real[i] * real[i] + imag[i] * imag[i];
+				imag[i] = 0.0;
+			}
+		}
+	}
+
+	/**
+	 * Computes a modulus of this ComplexImage. Puts a modulus in the real part
+	 * of this ComplexImage and zeros in the imaginary part.
+	 */
+
+	public void modulus() {
+		for (int k = 0; k < nxy; k++) {
+			real[k] = Math.sqrt(real[k] * real[k] + imag[k] * imag[k]);
+			imag[k] = 0.0;
+		}
+	}
+
+	/**
+	 * Computes the phase of this ComplexImage. Puts a phase in the real part of
+	 * this ComplexImage and zeros in the imaginary part.
+	 */
+
+	public void phase() {
+		for (int k = 0; k < nxy; k++) {
+			real[k] = Math.atan(imag[k] / real[k]);
+			if (real[k] < 0.0) {
+				real[k] += Math.PI;
+			}
+			imag[k] = 0.0;
+		}
+	}
+
+	/**
+	 * Adds a ComplexImage object of the same dimensions to this ComplexImage.
+	 * 
+	 * @param im
+	 *            the ComplexImage to add
+	 */
+
+	public void add(ComplexImage im) {
+		if (!(im.imag == null)) { // im is complex
+			if (imag == null) {
+				imag = new double[nxy];
+			}
+			for (int k = 0; k < nxy; k++) {
+				real[k] += im.real[k];
+				imag[k] += im.imag[k];
+			}
+		}
+		else { // im is real
+			for (int k = 0; k < nxy; k++) {
+				real[k] += im.real[k];
+			}
+		}
+	}
+
+	/**
+	 * Adds a real constant to every element in this ComplexImage.
+	 * 
+	 * @param d
+	 *            the constant to add
+	 */
+
+	public void add(double d) {
+		for (int k = 0; k < nxy; k++) {
+			real[k] += d;
+		}
+	}
+
+	/**
+	 * Substracts a ComplexImage of the same dimensions from this ComplexImage.
+	 * 
+	 * @param im
+	 *            the ComplexImage to be substracted
+	 */
+
+	public void subtract(ComplexImage im) {
+		if (!(im.imag == null)) { // im is complex
+			if (imag == null) {
+				imag = new double[nxy];
+			}
+			for (int k = 0; k < nxy; k++) {
+				real[k] -= im.real[k];
+				imag[k] -= im.imag[k];
+			}
+		}
+		else { // im is real
+			for (int k = 0; k < nxy; k++) {
+				real[k] -= im.real[k];
+			}
+		}
+	}
+
+	/**
+	 * Performs the complex conjugate on this ComplexImage.
+	 */
+
+	public void conj() {
+		if (!(imag == null)) {
+			for (int k = 0; k < nxy; k++) {
+				imag[k] = -imag[k];
+			}
+		}
+	}
+
+	/**
+	 * Multiplies each element in this ComplexImage with a given constant.
+	 * 
+	 * @param constant
+	 *            the multiplication constant
+	 */
+
+	public void multiply(double constant) {
+		if (imag == null) {
+			for (int k = 0; k < nxy; k++) {
+				real[k] *= constant;
+			}
+		}
+		else {
+			for (int k = 0; k < nxy; k++) {
+				real[k] *= constant;
+				imag[k] *= constant;
+			}
+		}
+	}
+
+	/**
+	 * Multiplies this complex image by a complex constant re+i*im.
+	 * 
+	 * @param re
+	 *            real part of the complex constant
+	 * @param im
+	 *            imaginary part of the complex constant
+	 */
+
+	public void multiply(double re, double im) {
+		for (int k = 0; k < nxy; k++) {
+			double r = real[k];
+			real[k] = re * real[k] - im * imag[k];
+			imag[k] = re * imag[k] + im * r;
+		}
+	}
+
+	/**
+	 * Multiplies this ComplexImage with another ComplexImage of same
+	 * dimensions, pointwise.
+	 * 
+	 * @param im
+	 *            the ComplexImage to multiply with
+	 */
+
+	public void multiply(ComplexImage im) {
+		if (!(im.imag == null)) { // im is complex
+			if (imag == null) {
+				imag = new double[nxy];
+			}
+			for (int k = 0; k < nxy; k++) {
+				double re = real[k];
+				real[k] = im.real[k] * re - im.imag[k] * imag[k];
+				imag[k] = im.real[k] * imag[k] + im.imag[k] * re;
+			}
+		}
+		else { // im is real
+			if (imag == null) {
+				for (int k = 0; k < nxy; k++) {
+					real[k] *= im.real[k];
+				}
+			}
+			else {
+				for (int k = 0; k < nxy; k++) {
+					real[k] *= im.real[k];
+					imag[k] *= im.real[k];
+				}
+			}
+		}
+	}
+
+	/**
+	 * Multiplies this ComplexImage by ComplexImage obtained by taking every
+	 * l-th sample of circulary extended ComplexImage im.
+	 * 
+	 * @param im
+	 *            the ComplexImage to multiply with
+	 * @param l
+	 *            subsampling factor for im
+	 */
+
+	public void multiplyCircular(ComplexImage im, int l) {
+		int l2 = l * l;
+		int k;
+		int t;
+		double ima;
+		double re;
+		int l2y;
+		if (!(im.imag == null)) { // im is complex
+			if (imag == null) {
+				imag = new double[nxy];
+			}
+			for (int y = 0; y < ny; y++) {
+				l2y = ((l * y) % im.ny) * im.nx;
+				for (int x = 0; x < nx; x++) { // x,y-coordinates in the image
+					k = nx * y + x;
+					t = l2y + ((l * x) % im.nx);
+					re = real[k];
+					ima = imag[k];
+					real[k] = im.real[t] * re - im.imag[t] * ima;
+					imag[k] = im.real[t] * ima + im.imag[t] * re;
+				}
+			}
+		}
+		else { // im is real
+			if (imag == null) {
+				for (int y = 0; y < ny; y++) {
+					l2y = ((l * y) % im.ny) * im.nx;
+					for (int x = 0; x < nx; x++) { // x,y-coordinates in the
+													// image
+						k = nx * y + x;
+						t = l2y + ((l * x) % im.nx);
+						real[k] *= im.real[t];
+					}
+				}
+			}
+			else {
+				for (int y = 0; y < ny; y++) {
+					l2y = ((l * y) % im.ny) * im.nx;
+					for (int x = 0; x < nx; x++) { // x,y-coordinates in the
+													// image
+						k = nx * y + x;
+						t = l2y + ((l * x) % im.nx);
+						re = real[k];
+						ima = imag[k];
+						real[k] *= im.real[t];
+						imag[k] *= im.real[t];
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Multiplies this ComplexImage pointwise with ComplexImage obtained by
+	 * taking every l-th sample of another ComplexImage im along each dimension.
+	 * Dimensions of im have to be bigger or equal to dimensions of the
+	 * ComplexImage multiplied by l.
+	 * 
+	 * @param im
+	 *            the ComplexImage to multiply with
+	 * @param l
+	 *            subsampling factor for im
+	 */
+
+	public void multiply(ComplexImage im, int l) {
+		if (l == 1) {
+			multiply(im);
+		}
+		else {
+			int l2 = l * l;
+			double ima;
+			double re;
+			int d = l * im.nx;
+			if (!(im.imag == null)) { // im is complex
+				if (imag == null) {
+					imag = new double[nxy];
+				}
+				for (int y = 0, l2y = 0; y < ny; y++, l2y += d) {
+					for (int t = l2y, k = nx * y, end = nx * y + nx; k < end; t += l, k++) { // x,y-coordinates
+																								// in
+																								// the
+																								// image
+						re = real[k];
+						real[k] = im.real[t] * re - im.imag[t] * imag[k];
+						imag[k] = im.real[t] * imag[k] + im.imag[t] * re;
+					}
+				}
+			}
+			else { // im is real
+				if (imag == null) {
+					for (int y = 0, l2y = 0; y < ny; y++, l2y += d) {
+						for (int t = l2y, k = nx * y, end = nx * y + nx; k < end; t += l, k++) { // x,y-coordinates
+																									// in
+																									// the
+																									// image
+							real[k] *= im.real[t];
+						}
+					}
+				}
+				else {
+					for (int y = 0, l2y = 0; y < ny; y++, l2y += d) {
+						for (int t = l2y, k = nx * y, end = nx * y + nx; k < end; t += l, k++) { // x,y-coordinates
+																									// in
+																									// the
+																									// image
+							real[k] *= im.real[t];
+							imag[k] *= im.real[t];
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Divides this ComplexImage by another ComplexImage im of same dimensions,
+	 * pointwise. Where both real and imaginary part of im are smaller then
+	 * 10^-30 result is given by cr+i*cim.
+	 * 
+	 * @param im
+	 *            the ComplexImage to divide by
+	 * @param cr
+	 *            the real part of the result if division by zero
+	 * @param cim
+	 *            the imaginary part of the result if division by zero
+	 */
+
+	public void divide(ComplexImage im, double cr, double cim) {
+		double ima;
+		double rea;
+		double eps = 1E-30;
+		if (!(im.imag == null)) { // im is complex
+			if (imag == null) {
+				imag = new double[nxy];
+			}
+			for (int k = 0; k < nxy; k++) {
+				if ((Math.abs(im.real[k]) < eps) && (Math.abs(im.imag[k]) < eps)) {
+					real[k] = cr;
+					imag[k] = cim;
+				}
+				else {
+					rea = real[k];
+					ima = imag[k];
+					real[k] = (rea * im.real[k] + ima * im.imag[k]) / (im.real[k] * im.real[k] + im.imag[k] * im.imag[k]);
+					imag[k] = (ima * im.real[k] - rea * im.imag[k]) / (im.real[k] * im.real[k] + im.imag[k] * im.imag[k]);
+				}
+			}
+		}
+		else { // im is real
+			if (imag == null) { // divide real by real
+				for (int k = 0; k < nxy; k++) {
+					if ((Math.abs(im.real[k]) < eps)) {
+						real[k] = cr;
+					}
+					else {
+						real[k] /= im.real[k];
+					}
+				}
+			}
+			else { // divide complex by real
+				for (int k = 0; k < nxy; k++) {
+					if ((Math.abs(im.real[k]) < eps)) {
+						real[k] = cr;
+						imag[k] = cim;
+					}
+					else {
+						real[k] /= im.real[k];
+						imag[k] /= im.real[k];
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Divides this ComplexImage by another ComplexImage im of the same
+	 * dimensions, pointwise. Does not check for division by zero.
+	 * 
+	 * @param im
+	 *            the ComplexImage to divide by
+	 */
+
+	public void divide(ComplexImage im) {
+		if (!(im.imag == null)) { // im is complex
+			if (imag == null) {
+				imag = new double[nxy];
+			}
+			for (int k = 0; k < nxy; k++) {
+				double rea = real[k];
+				double d = (im.real[k] * im.real[k] + im.imag[k] * im.imag[k]);
+				real[k] = (rea * im.real[k] + imag[k] * im.imag[k]) / d;
+				imag[k] = (imag[k] * im.real[k] - rea * im.imag[k]) / d;
+			}
+		}
+		else { // im is real
+			if (imag == null) { // divide real by real
+				for (int k = 0; k < nxy; k++) {
+					real[k] /= im.real[k];
+				}
+			}
+			else { // divide complex by real
+				for (int k = 0; k < nxy; k++) {
+					real[k] /= im.real[k];
+					imag[k] /= im.real[k];
+				}
+			}
+		}
+	}
+
+	/**
+	 * Computes the square root of each element of the real part of this
+	 * ComplexImage and puts it in the real part of this ComplexImage.
+	 */
+
+	public void rootReal() {
+		for (int k = 0; k < nxy; k++) {
+			real[k] = Math.sqrt(real[k]);
+		}
+	}
+
+	/**
+	 * Returns the median of the real part of this ComplexImage.
+	 * 
+	 * @return median of the real part of this ComplexImage
+	 */
+
+	public double median() {
+		ComplexImage temp = copyImage();
+		java.util.Arrays.sort(temp.real);
+		double m = temp.real[nxy / 2];
+		return m;
+	}
+
+	/**
+	 * Returns the median absolute deviation of the real part of this
+	 * ComplexImage.
+	 * 
+	 * @return median absolute deviation of the real part of this ComplexImage
+	 */
+
+	public double mad() {
+		double m = median();
+		ComplexImage temp = copyImage();
+		temp.add(-m);
+		for (int x = 0; x < nxy; x++) {
+			temp.real[x] = Math.abs(temp.real[x]);
+		}
+		double md = temp.median();
+		return md;
+	}
+
+	/**
+	 * Rises each element of the real part of this ComplexImage to the power of
+	 * exp.
+	 * 
+	 * @param exp
+	 *            exponent
+	 */
+
+	public void powerReal(double exp) {
+		for (int i = 0; i < nxy; i++) {
+			real[i] = Math.pow(real[i], exp);
+		}
+	}
+
+	/**
+	 * Returns the mean value of the real part of this ComplexImage.
+	 * 
+	 * @return the mean of the real part of this ComplexImage
+	 */
+
+	public double meanReal() {
+		double d = 0.0;
+		for (int k = 0; k < nxy; k++) {
+			d += real[k];
+		}
+		d /= nxy;
+		return d;
+	}
+
+	/**
+	 * Returns the mean of the real part of this ComplexImage inside the mask
+	 * which is defined by real(ma)=val.
+	 * 
+	 * @param ma
+	 *            mask
+	 * @param val
+	 *            defines the mask foreground
+	 * @return the mean value of real part of this ComplexImage inside the mask
+	 */
+
+	public double meanMask(int[] ma, int val) {
+		double cnt = 0;
+		double s = 0;
+		for (int k = 0; k < nxy; k++) {
+			if (ma[k] == val) {
+				cnt += 1.0;
+				s += real[k];
+			}
+		}
+		s /= cnt;
+		return s;
+	}
+
+	/**
+	 * Returns the mean value of this ComplexImage modulus.
+	 * 
+	 * @return mean of this ComplexImage modulus
+	 */
+
+	public double meanModulus() {
+		double d = 0.0;
+		if (imag == null) {
+			for (int k = 0; k < nxy; k++) {
+				d += Math.abs(real[k]);
+			}
+		}
+		else {
+			for (int k = 0; k < nxy; k++) {
+				d += Math.pow(real[k] * real[k] + imag[k] * imag[k], 0.5);
+			}
+		}
+		d /= (nxy);
+		return d;
+	}
+
+	/**
+	 * Computes the sum of the real part of this ComplexImage.
+	 * 
+	 * @return the sum of the real part of this ComplexImage
+	 */
+
+	public double sumReal() {
+		double d = 0.0;
+		for (int k = 0; k < nxy; k++) {
+			d += real[k];
+		}
+		return d;
+	}
+
+	/**
+	 * Returns the maximum absolute value of the real part of this ComplexImage.
+	 * 
+	 * @return the maximum absolute value of the real part of this ComplexImage
+	 */
+
+	public double maxAbsReal() {
+		double max = real[0];
+		double min = real[0];
+		for (int k = 0; k < nxy; k++) {
+			if (real[k] > max) {
+				max = real[k];
+			}
+			else {
+				if (real[k] < min) {
+					min = real[k];
+				}
+			}
+		}
+		max = Math.abs(max);
+		min = Math.abs(min);
+		if (min > max) {
+			max = min;
+		}
+		return max;
+	}
+
+	/**
+	 * Returns the maximum value of the real part of this ComplexImage.
+	 * 
+	 * @return the maximum value of the real part of this ComplexImage
+	 */
+
+	public double max() {
+		double m = real[0];
+		for (int k = 0; k < nxy; k++) {
+			if (real[k] > m) {
+				m = real[k];
+			}
+		}
+		return m;
+	}
+
+	/**
+	 * Returns the minimum value of the real part of this ComplexImage.
+	 * 
+	 * @return the minimum value of the real part of this ComplexImage
+	 */
+
+	public double min() {
+		double m = real[0];
+		for (int k = 0; k < nxy; k++) {
+			if (real[k] < m) {
+				m = real[k];
+			}
+		}
+		return m;
+	}
+
+	/**
+	 * Returns the standard deviation of the real part of this ComplexImage.
+	 * 
+	 * @return the standard deviation of the real part of this ComplexImage
+	 */
+
+	public double deviation() {
+		double m = meanReal();
+		double s = 0;
+		for (int k = 0; k < nxy; k++) {
+			double d = real[k] - m;
+			s += (d * d);
+		}
+		s /= nxy;
+		s = Math.sqrt(s);
+		return s;
+	}
+
+	/**
+	 * Performs the 2D Fast Fourier Transform on this ComplexImage.
+	 */
+
+	public void FFT2D() {
+		// perform FFT1D for each row
+		FFT1D FFTrow = new FFT1D(nx);
+		ComplexImage row = new ComplexImage(nx, 1);
+		for (int y = 0; y < ny; y++) {
+			row.getRowContent(y, this);
+			FFTrow.transform(row.real, row.imag, nx, 0);
+			putRow(y, row);
+		}
+		// perform FFT1D for each column
+		ComplexImage column = new ComplexImage(1, ny);
+		FFT1D FFTcolumn = new FFT1D(ny);
+		for (int x = 0; x < nx; x++) {
+			column.getColumnContent(x, this);
+			FFTcolumn.transform(column.real, column.imag, ny, 0);
+			putColumn(x, column);
+		}
+	}
+
+	/**
+	 * Performs the 2D inverse Fast Fourier Transform on this ComplexImage.
+	 */
+
+	public void iFFT2D() {
+		// perform iFFT1D for each row
+		ComplexImage row = new ComplexImage(nx, 1);
+		FFT1D FFTrow = new FFT1D(nx);
+		for (int y = 0; y < ny; y++) {
+			row.getRowContent(y, this);
+			FFTrow.inverse(row.real, row.imag, nx, 0);
+			putRow(y, row);
+		}
+		// perform iFFT1D for each column
+		ComplexImage column = new ComplexImage(1, ny);
+		FFT1D FFTcolumn = new FFT1D(ny);
+		for (int x = 0; x < nx; x++) {
+			column.getColumnContent(x, this);
+			FFTcolumn.inverse(column.real, column.imag, ny, 0);
+			putColumn(x, column);
+		}
+	}
+
+	/**
+	 * Exchanges quadrants of this ComplexImage 1 <-> 3, 2 <-> 4. The quadrants
+	 * are defined as follows: <br>
+	 * 1 | 2 <br>
+	 * ----- <br>
+	 * 4 | 3 <br>
+	 */
+
+	public void shift() {
+		double p;
+		int q1;
+		int q2;
+		int q3;
+		int q4;
+		int nxy2 = nxy / 2;
+		int nx2 = nx / 2;
+		if (imag == null) {
+			for (int y = 0; y < ny / 2; y++) {
+				int ynx = nx * y;
+				for (int x = 0; x < nx2; x++) {
+					q1 = ynx + x;
+					q2 = q1 + nx2;
+					q4 = q1 + nxy2;
+					q3 = q4 + nx2;
+					p = real[q1];
+					real[q1] = real[q3];
+					real[q3] = p;
+					p = real[q2];
+					real[q2] = real[q4];
+					real[q4] = p;
+				}
+			}
+		}
+		else {
+			for (int y = 0; y < ny / 2; y++) {
+				int ynx = nx * y;
+				for (int x = 0; x < nx2; x++) {
+					q1 = ynx + x;
+					q2 = q1 + nx2;
+					q4 = q1 + nxy2;
+					q3 = q4 + nx2;
+					p = real[q1];
+					real[q1] = real[q3];
+					real[q3] = p;
+					p = real[q2];
+					real[q2] = real[q4];
+					real[q4] = p;
+					p = imag[q1];
+					imag[q1] = imag[q3];
+					imag[q3] = p;
+					p = imag[q2];
+					imag[q2] = imag[q4];
+					imag[q4] = p;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Exchanges quadrants of this ComplexImage 1 <-> 2, 3 <-> 4. The quadrants
+	 * are defined as follows: <br>
+	 * 1 | 2 <br>
+	 * ----- <br>
+	 * 4 | 3 <br>
+	 */
+
+	public void shiftX() {
+		double p;
+		int q1;
+		int q2;
+		int nx2 = nx / 2;
+		if (imag == null) {
+			for (int y = 0; y < ny; y++) {
+				int ynx = nx * y;
+				for (int x = 0; x < nx2; x++) {
+					q1 = ynx + x;
+					q2 = q1 + nx2;
+					p = real[q1];
+					real[q1] = real[q2];
+					real[q2] = p;
+				}
+			}
+		}
+		else {
+			for (int y = 0; y < ny; y++) {
+				int ynx = nx * y;
+				for (int x = 0; x < nx2; x++) {
+					q1 = ynx + x;
+					q2 = q1 + nx2;
+					p = real[q1];
+					real[q1] = real[q2];
+					real[q2] = p;
+					p = imag[q1];
+					imag[q1] = imag[q2];
+					imag[q2] = p;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Exchanges quadrants of this ComplexImage 1 <-> 4, 2 <-> 3. The quadrants
+	 * are defined as follows: <br>
+	 * 1 | 2 <br>
+	 * ----- <br>
+	 * 4 | 3 <br>
+	 */
+
+	public void shiftY() {
+		final int halfim = nxy / 2;
+		double p;
+		if (imag == null) {
+			for (int k = 0; k < halfim; k++) {
+				p = real[k];
+				real[k] = real[k + halfim];
+				real[k + halfim] = p;
+			}
+		}
+		else {
+			for (int k = 0; k < halfim; k++) {
+				p = real[k];
+				real[k] = real[k + halfim];
+				real[k + halfim] = p;
+				p = imag[k];
+				imag[k] = imag[k + halfim];
+				imag[k + halfim] = p;
+			}
+		}
+	}
+
+	/**
+	 * Shifts this ComplexImage right by shiftx and down by shifty.
+	 * 
+	 * @param shiftx
+	 *            shift to the right
+	 * @param shifty
+	 *            shift down
+	 */
+
+	public ComplexImage circShift(int shiftx, int shifty) {
+		while (shiftx < 0) {
+			shiftx = nx + shiftx;
+		}
+		while (shifty < 0) {
+			shifty = ny + shifty;
+		}
+		while (shiftx > (nx - 1)) {
+			shiftx = shiftx - nx;
+		}
+		while (shifty > (ny - 1)) {
+			shifty = shifty - ny;
+		}
+		ComplexImage res = new ComplexImage(nx, ny);
+		ComplexImage temp = getSubimage(0, nx - shiftx - 1, 0, ny - shifty - 1);
+		res.putSubimage(shiftx, shifty, temp);
+		temp = getSubimage(nx - shiftx, nx - 1, ny - shifty, ny - 1);
+		res.putSubimage(0, 0, temp);
+		temp = getSubimage(0, nx - shiftx - 1, ny - shifty, ny - 1);
+		res.putSubimage(shiftx, 0, temp);
+		temp = getSubimage(nx - shiftx, nx - 1, 0, ny - shifty - 1);
+		res.putSubimage(0, shifty, temp);
+		return res;
+	}
+
+	/**
+	 * Performs quincunx downsampling followed by upsampling in Fourier domain
+	 * on this ComplexImage. Sums the first quadrant with the third and the
+	 * second with the forth. <br>
+	 * 1 | 2 <br>
+	 * ----- <br>
+	 * 4 | 3 <br>
+	 */
+
+	public void quincunxDownUp() {
+		int nx2 = nx / 2;
+		int ny2 = ny / 2;
+		int nxy2 = nxy / 2;
+		for (int y = 0; y < nxy2; y += nx) {
+			for (int q1 = y, end = q1 + nx2; q1 < end; q1++) {
+				int q2 = q1 + nx2;
+				int q4 = q1 + nxy2;
+				int q3 = q4 + nx2;
+				double r = real[q1] + real[q3];
+				double im = imag[q1] + imag[q3];
+				real[q1] = real[q3] = r;
+				imag[q1] = imag[q3] = im;
+				r = real[q2] + real[q4];
+				im = imag[q2] + imag[q4];
+				real[q2] = real[q4] = r;
+				imag[q2] = imag[q4] = im;
+			}
+		}
+	}
+
+	/**
+	 * Down and upsampling in Y direction Sums the first quadrant with the forth
+	 * and the third with the third. <br>
+	 * 1 | 2 <br>
+	 * ----- <br>
+	 * 4 | 3 <br>
+	 */
+
+	public void downUpY() {
+		int nxy2 = nxy / 2;
+		for (int q1 = 0; q1 < nxy2; q1++) {
+			int q2 = q1 + nxy2;
+			double r = real[q1] + real[q2];
+			double im = imag[q1] + imag[q2];
+			real[q1] = real[q2] = r;
+			imag[q1] = imag[q2] = im;
+		}
+	}
+
+	/**
+	 * Performs dyadic downsampling followed by upsampling in Fourier domain on
+	 * this ComplexImage. Sums the four quadrants pointwise and places the sum
+	 * in each quadrant.
+	 */
+
+	public void dyadicDownUp() {
+		int nx2 = nx / 2;
+		int ny2 = ny / 2;
+		int nxy2 = nxy / 2;
+		for (int y = 0; y < nxy2; y += nx) {
+			for (int q1 = y, end = q1 + nx2; q1 < end; q1++) {
+				int q2 = q1 + nx2;
+				int q3 = q1 + nxy2;
+				int q4 = q3 + nx2;
+				double r = real[q1] + real[q2] + real[q3] + real[q4];
+				real[q1] = real[q2] = real[q3] = real[q4] = r;
+				double im = imag[q1] + imag[q2] + imag[q3] + imag[q4];
+				imag[q1] = imag[q2] = imag[q3] = imag[q4] = im;
+			}
+		}
+	}
+
+	/**
+	 * Sums the four quadrants of this ComplexImage pointwise and reduces its
+	 * size by 2 along each dimension.
+	 */
+
+	public void dyadicDownUpCrop() {
+		int nx2 = nx / 2;
+		int ny2 = ny / 2;
+		int nxy2 = nxy / 2;
+		int ind = 0;
+		for (int y = 0; y < nxy2; y += nx) {
+			for (int q1 = y, end = q1 + nx2; q1 < end; q1++) {
+				int q2 = q1 + nx2;
+				int q3 = q1 + nxy2;
+				int q4 = q3 + nx2;
+				double r = real[q1] + real[q2] + real[q3] + real[q4];
+				real[ind] = r;
+				double im = imag[q1] + imag[q2] + imag[q3] + imag[q4];
+				imag[ind++] = im;
+			}
+		}
+		nx = nx2;
+		ny = ny2;
+		nxy /= 4;
+	}
+
+	/**
+	 * Sums the lower half of this ComplexImage to the upper half, pointwise.
+	 * Keeps only the upper half.
+	 */
+
+	public void dyadicDownY() {
+		ComplexImage temp = getSubimage(0, nx - 1, 0, ny / 2 - 1);
+		ComplexImage temp1 = getSubimage(0, nx - 1, ny / 2, ny - 1);
+		temp1.add(temp);
+		putSubimage(0, 0, temp1);
+		ny = ny / 2;
+		nxy /= 2;
+	}
+
+	/**
+	 * Subsamples this ComplexImage by 2x2, reducing its size by 2 along each
+	 * dimension.
+	 */
+
+	public void decimateCrop() {
+		int nx2 = nx / 2;
+		int ny2 = ny / 2;
+		if (imag == null) {
+			for (int y = 0; y < ny2; y++) {
+				for (int x = 0, k = 2 * nx * y, k1 = nx2 * y; x < nx2; x++, k += 2, k1++) {
+					real[k1] = real[k];
+				}
+			}
+		}
+		else {
+			for (int y = 0; y < ny2; y++) {
+				for (int x = 0, k = 2 * nx * y, k1 = nx2 * y; x < nx2; x++, k += 2, k1++) {
+					real[k1] = real[k];
+					imag[k1] = imag[k];
+				}
+			}
+		}
+		nx /= 2;
+		ny /= 2;
+		nxy /= 4;
+	}
+
+	/**
+	 * Subsamples this ComplexImage by 2x2, circularily, while keeping its size
+	 * unchanged.
+	 */
+
+	public void decimate() {
+		ComplexImage temp = copyImage();
+		if (imag == null) {
+			for (int l = 0, y = 0; l < nxy; l += nx, y += 2) {
+				if (y >= ny) {
+					y -= ny;
+				}
+				for (int k = l, x = 0; k < l + nx; k++, x += 2) {
+					if (x >= nx) {
+						x -= nx;
+					}
+					int k1 = y * nx + x;
+					real[k] = temp.real[k1];
+				}
+			}
+		}
+		else {
+			for (int l = 0, y = 0; l < nxy; l += nx, y += 2) {
+				if (y >= ny) {
+					y -= ny;
+				}
+				for (int k = l, x = 0; k < l + nx; k++, x += 2) {
+					if (x >= nx) {
+						x -= nx;
+					}
+					int k1 = y * nx + x;
+					real[k] = temp.real[k1];
+					imag[k] = temp.imag[k1];
+				}
+			}
+		}
+	}
+
+	/**
+	 * Substitutes the 2k-th and the 2k+1-th column of this ComplexImage by
+	 * their sum, for each integer k. The number of columns of the ComplexImage
+	 * is reduced by two.
+	 */
+
+	public void fold() {
+		int nxy2 = nxy / 2;
+		for (int k = 0; k < nxy2; k++) {
+			real[k] = real[2 * k] + real[2 * k + 1];
+			imag[k] = imag[2 * k] + imag[2 * k + 1];
+		}
+		nx /= 2;
+		nxy /= 2;
+	}
+
+	/**
+	 * Substitutes each column of this ComplexImage by two columns. Each of them
+	 * contains the even (odd) samples from the initial column and the odd
+	 * (even) positions contain zeros. The number of columns of the ComplexImage
+	 * is increased by two.
+	 */
+
+	public void unfold() {
+		double[] real1 = new double[2 * nxy];
+		double[] imag1 = new double[2 * nxy];
+		for (int k = 0; k < 2 * nxy; k++)
+			real1[k] = imag1[k] = 0;
+		for (int k = 0; k < nx * ny; k++) {
+			if (k % (2 * nx) < nx) { // odd row
+				real1[2 * k] = real[k];
+				imag1[2 * k] = imag[k];
+			}
+			else {
+				real1[2 * k + 1] = real[k];
+				imag1[2 * k + 1] = imag[k];
+			}
+		}
+		real = real1;
+		imag = imag1;
+		nx = nx * 2;
+		nxy *= 2;
+	}
+
+	/**
+	 * Extends this ComplexImage circularily to double its size along each
+	 * direction. <br>
+	 */
+
+	public void dyadicUpsample() {
+		int index;
+		int q1;
+		int q2;
+		int q3;
+		int q4;
+		int nx2 = 2 * nx;
+		int nxy2 = 2 * nxy;
+		double r;
+		double i;
+		if (real.length < 4 * nxy) {
+			double[] re = new double[4 * nxy];
+			System.arraycopy(real, 0, re, 0, nxy);
+			real = re;
+		}
+		if (imag.length < 4 * nxy) {
+			double[] im = new double[4 * nxy];
+			System.arraycopy(imag, 0, im, 0, nxy);
+			imag = im;
+		}
+		for (int y = ny - 1; y >= 0; y--) {
+			int ynx = y * nx;
+			int ynx2 = 2 * ynx;
+			for (int x = nx - 1; x >= 0; x--) {
+				index = ynx + x;
+				r = real[index];
+				i = imag[index];
+				q1 = ynx2 + x;
+				real[q1] = r; // First quadrant
+				imag[q1] = i;
+				q2 = q1 + nx;
+				real[q2] = r; // Second quadrant
+				imag[q2] = i;
+				q3 = q1 + nxy2;
+				real[q3] = r; // Third quadrant
+				imag[q3] = i;
+				q4 = q3 + nx;
+				real[q4] = r; // Forth quadrant
+				imag[q4] = i;
+			}
+		}
+		nx *= 2;
+		ny *= 2;
+		nxy *= 4;
+	}
+
+	/**
+	 * Multiplies this ComplexImage by e^ix, with x=2*pi*kx/nx, where kx is the
+	 * column index.
+	 */
+
+	public void modulatePlusX() {
+		if (imag == null) {
+			imag = new double[nxy];
+		}
+		int index;
+		double x;
+		double c;
+		double s;
+		double re;
+		double im;
+		for (int k = 1; k < nx; k++) {
+			x = PI2 * (double) k / (double) nx;
+			c = Math.cos(x);
+			s = Math.sin(x);
+			for (int l = 0; l < ny; l++) {
+				index = l * nx + k;
+				re = real[index];
+				im = imag[index];
+				real[index] = c * re - s * im;
+				imag[index] = s * re + c * im;
+			}
+		}
+	}
+
+	/**
+	 * Multiplies this ComplexImage by e^-ix, with x=2*pi*kx/nx, where kx is the
+	 * column index.
+	 */
+
+	public void modulateMinusX() {
+		if (imag == null) {
+			imag = new double[nxy];
+		}
+		int index;
+		double x;
+		double c;
+		double s;
+		double re;
+		double im;
+		for (int k = 1; k < nx; k++) {
+			x = (double) k * PI2 / (double) nx;
+			c = Math.cos(x);
+			s = Math.sin(x);
+			for (int l = 0; l < ny; l++) {
+				index = l * nx + k;
+				re = real[index];
+				im = imag[index];
+				real[index] = c * re + s * im;
+				imag[index] = c * im - s * re;
+			}
+		}
+	}
+
+	/**
+	 * Multiplies this ComplexImage by e^iy, with y=2*pi*ky/ny, where ky is the
+	 * row index.
+	 */
+
+	public void modulatePlusY() {
+		if (imag == null) {
+			imag = new double[nxy];
+		}
+		int index;
+		double y;
+		double c;
+		double s;
+		double re;
+		double im;
+		int knx;
+		for (int k = 1; k < ny; k++) {
+			y = PI2 * (double) k / (double) ny;
+			c = Math.cos(y);
+			s = Math.sin(y);
+			knx = k * nx;
+			for (int l = 0; l < nx; l++) {
+				index = knx + l;
+				re = real[index];
+				im = imag[index];
+				real[index] = c * re - s * im;
+				imag[index] = s * re + c * im;
+			}
+		}
+	}
+
+	/**
+	 * Multiplies this ComplexImage by e^-iy, with y=2*pi*ky/ny, where ky is the
+	 * row index.
+	 */
+
+	public void modulateMinusY() {
+		if (imag == null) {
+			imag = new double[nxy];
+		}
+		int index;
+		double y;
+		double c;
+		double s;
+		double re;
+		double im;
+		int knx;
+		for (int k = 1; k < ny; k++) {
+			y = PI2 * (double) k / (double) ny;
+			c = Math.cos(y);
+			s = Math.sin(y);
+			knx = k * nx;
+			for (int l = 0; l < nx; l++) {
+				index = knx + l;
+				re = real[index];
+				im = imag[index];
+				real[index] = c * re + s * im;
+				imag[index] = c * im - s * re;
+			}
+		}
+	}
+
+	/**
+	 * Multiplies this ComplexImage by e^i(x+y), with x=2*pi*kx/nx, where kx is
+	 * the column index, and y=2*pi*ky/ny, where ky is the row index.
+	 */
+
+	public void modulatePlusQuincunx() {
+		if (imag == null) {
+			imag = new double[nxy];
+		}
+		int index;
+		double x;
+		double c;
+		double s;
+		double re;
+		double im;
+		for (int k = 0; k < nx; k++) {
+			for (int l = 0; l < ny; l++) {
+				x = PI2 * ((double) k / (double) nx + (double) l / (double) ny);
+				c = Math.cos(x);
+				s = Math.sin(x);
+				index = l * nx + k;
+				re = real[index];
+				im = imag[index];
+				real[index] = c * re - s * im;
+				imag[index] = s * re + c * im;
+			}
+		}
+	}
+
+	/**
+	 * Multiplies this ComplexImage by e^-i(x+y), with x=2*pi*kx/nx, where kx is
+	 * the column index, and y=2*pi*ky/ny, where ky is the row index.
+	 */
+
+	public void modulateMinusQuincunx() {
+		if (imag == null) {
+			imag = new double[nxy];
+		}
+		int index;
+		double x;
+		double c;
+		double s;
+		double re;
+		double im;
+		for (int k = 0; k < nx; k++) {
+			for (int l = 0; l < ny; l++) {
+				x = PI2 * ((double) k / (double) nx + (double) l / (double) ny);
+				c = Math.cos(x);
+				s = Math.sin(x);
+				index = l * nx + k;
+				re = real[index];
+				im = imag[index];
+				real[index] = c * re + s * im;
+				imag[index] = c * im - s * re;
+			}
+		}
+	}
+
+	/**
+	 * Returns this ComplexImage rotated by 45 degrees clockwise. Only the
+	 * ComplexImage samples on the quincunx grid are kept, the ComplexImage is
+	 * rotated so that the remaining samples are all in the same region, and the
+	 * rest of the output ComplexImage is filled by the background. Size of
+	 * rotated image is bigger or equal to image size before rotation
+	 * 
+	 * @param back
+	 *            the background color to be used.
+	 * @return the rotated ComplexImage
+	 */
+
+	public ComplexImage rotate(double back) {
+		double s1 = 0;
+		double s0 = 0;
+		int k = 0;
+		int i = 1;
+		for (int y = 0; y < nxy; y += nx) {
+			for (int ind = y + k, end = y + nx; ind < end; ind += 2) {
+				s0 += Math.abs(real[ind]);
+				s1 += Math.abs(real[ind + i]);
+			}
+			k = 1 - k;
+			i = -i;
+		}
+		k = (s0 < s1) ? 1 : 0;
+		int s = (nx + ny) / 2;
+		ComplexImage rot = new ComplexImage(s, s);
+		rot.settoConstant(back, back);
+		for (int y = 0; y < ny; y++) {
+			for (int x = k, ind = y * nx + k; x < nx; x += 2, ind += 2) {
+				int x1 = (x - y + ny - 1) / 2;
+				int y1 = (x + y) / 2;
+				rot.real[rot.nx * y1 + x1] = real[ind];
+				rot.imag[rot.nx * y1 + x1] = imag[ind];
+			}
+			k = 1 - k;
+		}
+		return rot;
+	}
+
+	/**
+	 * Rotates this ComplexImage back, anticlockwise by 45 degrees. After
+	 * applying the rotate operation followed by the unrotate, only the samples
+	 * on the quincunx grid are kept.
+	 * 
+	 * @param kx
+	 *            the number of rows in the unrotated ComplexImage
+	 * @param ky
+	 *            the number of columns in the unrotated ComplexImage
+	 */
+
+	public void unrotate(int kx, int ky) {
+		int kxy = kx * ky;
+		int nx1 = nx;
+		int ny1 = ny;
+		double[] re = new double[kxy];
+		double[] im = new double[kxy];
+		nx = kx;
+		ny = ky;
+		nxy = nx * ny;
+		int k = 0;
+		for (int y = 0; y < ky; y++) {
+			for (int x = k, ind = kx * y + k; x < kx; x += 2, ind += 2) {
+				int x1 = (x - y + ky - 1) / 2;
+				int y1 = (x + y) / 2;
+				re[ind] = real[nx1 * y1 + x1];
+				im[ind] = imag[nx1 * y1 + x1];
+			}
+			k = 1 - k;
+		}
+		System.arraycopy(re, 0, real, 0, kxy);
+		System.arraycopy(im, 0, imag, 0, kxy);
+	}
+
+	/**
+	 * Places the ComplexImage original extended by its first row and first
+	 * column in this ComplexImage.
+	 * 
+	 * @param original
+	 *            the image to extend
+	 */
+
+	public void extend(ComplexImage original) {
+		putSubimage(0, 0, original);
+		for (int x = 0; x < original.nx; x++) {
+			real[(original.nx + 1) * original.ny + x] = real[x];
+		}
+		for (int y = 0; y < original.ny + 1; y++) {
+			real[(original.nx + 1) * y + original.nx] = real[(original.nx + 1) * y];
+		}
+	}
+
+	/**
+	 * Normalizes the modulus of this ComplexImage so that all values are in the
+	 * range (0,250). Does not modify the background given by the value 255.0 in
+	 * the real part.
+	 */
+
+	public void stretch() {
+		double max = 250.0;
+		double back = 255.0;
+		ComplexImage mod = copyImage();
+		if (imag == null) {
+			double maximage = mod.maxAbsReal();
+			double sp = max / maximage;
+			for (int i = 0; i < nxy; i++) {
+				if (real[i] != back) {
+					real[i] *= sp;
+				}
+			}
+		}
+		else {
+			mod.modulus();
+			double maximage = mod.max();
+			double sp = max / maximage;
+			for (int i = 0; i < nxy; i++) {
+				if (real[i] != back) {
+					real[i] *= sp;
+					imag[i] *= sp;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Stretches each subband of the nonredundant quincunx transform in this
+	 * ComplexImage so that all values are between -250 and 250.
+	 * 
+	 * @param J
+	 *            number of decomposition levels
+	 */
+
+	void displayQuincunxNonredundant(int J) {
+		int dx = nx;
+		int dy = ny;
+		for (int j = 1; j <= J; j++) {
+			ComplexImage sub;
+			if (j % 2 == 1) { // Odd iteration
+				sub = getSubimage(dx / 2, dx - 1, 0, dy - 1);
+				sub.stretch();
+				putSubimage(dx / 2, 0, sub);
+				dx /= 2;
+			}
+			else {
+				sub = getSubimage(0, dx - 1, dy / 2, dy - 1);
+				sub.stretch();
+				putSubimage(0, dy / 2, sub);
+				dy /= 2;
+			}
+		}
+	}
+
+	/**
+	 * Puts a frame of the given color around the real part of this
+	 * ComplexImage.
+	 * 
+	 * @param color
+	 *            the frame color
+	 */
+
+	public void frame(double color) {
+		for (int k = 0, nxy1 = nxy - 1; k < nx; k++) {
+			real[k] = real[nxy1 - k] = color;
+		}
+		for (int k = 1; k < ny; k++) {
+			real[k * nx] = real[k * nx - 1] = color;
+		}
+	}
+
+	/**
+	 * Puts the frames of given colors around both the real and the complex part
+	 * of this ComplexImage.
+	 * 
+	 * @param colorr
+	 *            the frame color for the real part
+	 * @param colori
+	 *            the frame color for the complex part
+	 */
+
+	public void frame(double colorr, double colori) {
+		for (int k = 0, nxy1 = nxy - 1; k < nx; k++) {
+			real[k] = real[nxy1 - k] = colorr;
+			imag[k] = imag[nxy1 - k] = colori;
+		}
+		for (int k = 1; k < ny; k++) {
+			real[k * nx] = real[k * nx - 1] = colorr;
+			imag[k * nx] = imag[k * nx - 1] = colori;
+		}
+	}
+
+	/**
+	 * Extends this ComplexImage by adding zero rows and columns around the
+	 * edges.
+	 */
+
+	public void extendWithZeros() {
+		ComplexImage temp = new ComplexImage(nx + 2, ny + 2);
+		ComplexImage temp1 = copyImage();
+		temp.putSubimage(1, 1, temp1);
+		real = temp.real;
+		imag = temp.imag;
+		nx += 2;
+		ny += 2;
+		nxy = nx * ny;
+	}
+
+	/**
+	 * Removes the first and the last row and coulumn of this ComplexImage.
+	 */
+
+	public void reduce() {
+		ComplexImage temp = getSubimage(1, nx - 2, 1, ny - 2);
+		real = temp.real;
+		imag = temp.imag;
+		nx -= 2;
+		ny -= 2;
+		nxy = nx * ny;
+	}
+
+	/**
+	 * Sets this ComplexImage value to 1 where it is different from 0.
+	 */
+
+	public void createMap() {
+		for (int i = 0; i < nxy; i++) {
+			if (!(real[i] == 0.0)) {
+				real[i] = 1.0;
+			}
+		}
+	}
+
+	/**
+	 * Implements soft threshold on this ComplexImage. Where the modulus of the
+	 * ComplexImage is bigger then t it is reduced by t while the phase of the
+	 * ComplexImage remains unchanged. Elsewhere, the value is set to zero.
+	 * 
+	 * @param t
+	 *            threshold
+	 */
+
+	public void softThreshold(double t) {
+		double t2 = t * t;
+		for (int k = 0; k < nxy; k++) {
+			if ((real[k] * real[k] + imag[k] * imag[k]) < t2) {
+				real[k] = imag[k] = 0;
+			}
+			else {
+				double r = real[k];
+				double im = imag[k];
+				double m = Math.sqrt(r * r + im * im);
+				real[k] -= r / m;
+				imag[k] -= im / m;
+			}
+		}
+	}
+
+	/**
+	 * Implements hard threshold on this ComplexImage. Where the modulus of the
+	 * ComplexImage is smaler then t the value it is set to zero.
+	 * 
+	 * @param t
+	 *            the threshold
+	 */
+
+	public void hardThreshold(double t) {
+		double t2 = t * t;
+		for (int k = 0; k < nxy; k++) {
+			if ((real[k] * real[k] + imag[k] * imag[k]) < t2) {
+				real[k] = imag[k] = 0.0;
+			}
+		}
+	}
+
+	/**
+	 * Sets the real part of this ComplexImage to max where it is higher then
+	 * max and to min where it is smaller then min.
+	 * 
+	 * @param min
+	 *            the lower limit
+	 * @param max
+	 *            the upper limit
+	 */
+
+	public void limitImage(double min, double max) {
+		for (int i = 0; i < nxy; i++) {
+			if (real[i] > max) {
+				real[i] = max;
+			}
+			else {
+				if (real[i] < min) {
+					real[i] = min;
+				}
+			}
+		}
+	}
+
+	/*
+	 * Searches for neighbouring pixels to include in the zerocrossings map
+	 */
+
+	private void search8(int x, int y) {
+		if ((x < nx) && (x >= 0) && (y < ny) && (y >= 0)) {
+			int i = y * nx + x;
+			if (real[i] == 1.0) {
+				real[i] = 3.0;
+				search8(x + 1, y);
+				search8(x - 1, y);
+				search8(x, y + 1);
+				search8(x, y - 1);
+				search8(x + 1, y + 1);
+				search8(x - 1, y - 1);
+				search8(x - 1, y + 1);
+				search8(x + 1, y - 1);
+			}
+		}
+	}
+
+	/*
+	 * Creates the zerocrossing map from the primary map
+	 */
+
+	private void zeroCrossHysteresis8() {
+		for (int y = 0; y < ny; y++) {
+			int i = y * nx;
+			for (int x = 0; x < nx; x++, i++) {
+				if (real[i] == 2.0) {
+					search8(x + 1, y);
+					search8(x - 1, y);
+					search8(x, y + 1);
+					search8(x, y - 1);
+					search8(x + 1, y + 1);
+					search8(x - 1, y - 1);
+					search8(x - 1, y + 1);
+					search8(x + 1, y - 1);
+				}
+			}
+		}
+		for (int x = 0; x < nxy; x++) {
+			if (real[x] < 1.5) {
+				real[x] = 0.0;
+			}
+			else {
+				real[x] = 1.0;
+			}
+		}
+	}
+
+	/**
+	 * Performs Canny edge detection on this ComplexImage and keeps only the
+	 * pixels that belong to the edge map. The edge of the image is included in
+	 * the map.
+	 * 
+	 * @param Tl
+	 *            the lower threshold
+	 * @param Th
+	 *            the higher threshold
+	 * @return the percentage of pixels that belong to the edge map, excluding
+	 *         the edge
+	 */
+
+	public double canny(double Tl, double Th) {
+		ComplexImage mod = copyImage();
+		mod.modulus();
+		// determin directions
+		int[] dir = new int[nxy]; // Directions of gradient 0,1,2,3
+		for (int x = 0; x < nxy; x++) {
+			if (((imag[x] <= 0.0) && (real[x] > -imag[x])) || ((imag[x] >= 0.0) && (real[x] < -imag[x]))) {
+				dir[x] = 0;
+			}
+			if (((real[x] > 0.0) && (-imag[x] >= real[x])) || ((real[x] < 0.0) && (real[x] >= -imag[x]))) {
+				dir[x] = 1;
+			}
+			if (((real[x] <= 0.0) && (real[x] > imag[x])) || ((real[x] >= 0.0) && (real[x] < imag[x]))) {
+				dir[x] = 2;
+			}
+			if (((imag[x] < 0.0) && (real[x] <= imag[x])) || ((imag[x] > 0.0) && (real[x] >= imag[x]))) {
+				dir[x] = 3;
+			}
+		}
+		// Nonmaxima supression
+		ComplexImage mod1 = mod.copyImage();
+		int[][] neighbours = { { nx, nx - 1, -nx, -nx + 1 }, { -1, nx - 1, 1, -nx + 1 }, { -1, -nx - 1, 1, nx + 1 }, { -nx, -nx - 1, nx, nx + 1 } };
+		// exclude edges
+		for (int x = 0, x1 = nxy - 1; x < nx; x++, x1--) {
+			mod1.real[x] = mod1.real[x1] = 0.0;
+		}
+		for (int x = 0, x1 = nxy - 1; x < nxy; x += nx, x1 -= nx) {
+			mod1.real[x] = mod1.real[x1] = 0.0;
+		}
+		for (int x = nx + 1; x < nxy - nx - 1; x++) {
+			double d = Math.abs(imag[x] / real[x]);
+			if (d > 1.0) {
+				d = 1.0 / d;
+			}
+			double ss1 = mod.real[x + neighbours[dir[x]][0]] * (1.0 - d) + mod.real[x + neighbours[dir[x]][1]] * d;
+			double ss2 = mod.real[x + neighbours[dir[x]][2]] * (1.0 - d) + mod.real[x + neighbours[dir[x]][3]] * d;
+			if (!((mod.real[x] > ss1) && (mod.real[x] > ss2))) {
+				mod1.real[x] = 0.0;
+			}
+		}
+		// Hysteresis
+		// Form a map
+		ComplexImage map = new ComplexImage(nx, ny);
+		for (int x = 0; x < nxy; x++) {
+			if (mod1.real[x] > Th) {
+				map.real[x] = 2.0;
+			}
+			else {
+				if (mod1.real[x] > Tl) {
+					map.real[x] = 1.0;
+				}
+			}
+		}
+		map.zeroCrossHysteresis8();
+		double pr = map.sumReal() / map.nxy;
+		map.frame(1.0);
+		multiply(map);
+		return pr;
+	}
+
+	/**
+	 * Differentiates the real part of this ComplexImage along x.
+	 */
+
+	public void derivativeX() {
+		ComplexImage temp = new ComplexImage(nx, ny);
+		System.arraycopy(real, 0, temp.real, 0, nxy);
+		temp.FFT2D();
+		for (int k = 0; k < nx; k++) {
+			double x = PI2 * (double) k / (double) nx;
+			if (x > Math.PI) {
+				x -= PI2;
+			}
+			for (int l = 0; l < ny; l++) {
+				int index = l * nx + k;
+				temp.real[index] *= x;
+				temp.imag[index] *= x;
+			}
+		}
+		temp.conj();
+		System.arraycopy(temp.real, 0, imag, 0, nxy);
+		System.arraycopy(temp.imag, 0, real, 0, nxy);
+		iFFT2D();
+	}
+
+	/**
+	 * Differentiates the real part of this ComplexImage along y.
+	 */
+
+	public void derivativeY() {
+		ComplexImage temp = new ComplexImage(nx, ny);
+		System.arraycopy(real, 0, temp.real, 0, nxy);
+		temp.FFT2D();
+		for (int k = 0; k < ny; k++) {
+			double y = PI2 * (double) k / (double) ny;
+			if (y > Math.PI) {
+				y -= PI2;
+			}
+			for (int l = 0; l < nx; l++) {
+				int index = k * nx + l;
+				temp.real[index] *= y;
+				temp.imag[index] *= y;
+			}
+		}
+		temp.conj();
+		System.arraycopy(temp.real, 0, imag, 0, nxy);
+		System.arraycopy(temp.imag, 0, real, 0, nxy);
+		iFFT2D();
+	}
+
+	/**
+	 * Smooth the real part of this ComplexImage. The value of each pixel is
+	 * computed as the mean value of its 3x3 neighbourhood.
+	 */
+
+	public void smooth() {
+		// Define neighbour
+		int[] d = { 1, -1, nx, -nx, nx + 1, nx - 1, -nx + 1, -nx - 1, 0 };
+		double[] w1 = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; // additional
+																		// weights
+																		// that
+																		// depend
+																		// on
+																		// position
+		ComplexImage temp = copyImage();
+		for (int i = nx + 1; i < nxy - nx - 1; i++) {
+			double m = 0.0;
+			double s = 0.0;
+			for (int k = 0; k < 9; k++) {
+				double w = w1[k];
+				s += w; // normalization
+				m += (temp.real[i + d[k]] * w);
+			}
+			m /= s;
+			real[i] = m;
+		}
+	}
+
+	/**
+	 * Smooth the real part of this ComplexImage using the given weights. The
+	 * value of each pixel is computed as the mean value of its weighted 3x3
+	 * neighbourhood.
+	 * 
+	 * @param weights
+	 *            the weights, a Compleximage of the same size as this
+	 *            ComplexImage
+	 */
+
+	public void smooth(ComplexImage weights) {
+		// Define neighbour
+		int[] d = { 1, -1, nx, -nx, nx + 1, nx - 1, -nx + 1, -nx - 1, 0 };
+		double[] w1 = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; // additional
+																		// weights
+																		// that
+																		// depend
+																		// on
+																		// position
+		ComplexImage temp = copyImage();
+		for (int i = nx + 1; i < nxy - nx - 1; i++) {
+			double m = 0.0;
+			double s = 0.0;
+			for (int k = 0; k < 9; k++) {
+				double w = weights.real[i + d[k]] * w1[k];
+				s += w; // normalization
+				double t = temp.real[i + d[k]];
+				m += (t * w);
+			}
+			m /= s;
+			real[i] = m;
+		}
+	}
+
+	/**
+	 * Computes the first parameter of Riesz transform of this ComplexImage.
+	 */
+
+	public void riesz1() {
+		FFT2D();
+		ComplexImage mult1 = new ComplexImage(nx, ny);
+		for (int y = 0; y < ny; y++) {
+			double omy = 2.0 * Math.PI * (double) y / (double) ny - Math.PI;
+			for (int x = 0; x < nx; x++) {
+				double omx = 2.0 * Math.PI * (double) x / (double) nx - Math.PI;
+				mult1.real[nx * y + x] = 1.0 / Math.sqrt(omx * omx + omy * omy);
+				mult1.real[nx * y + x] *= omx;
+			}
+		}
+		mult1.shift();
+		mult1.real[0] = 1.0;
+		multiply(mult1);
+		iFFT2D();
+		double[] temparray = new double[nxy];
+		System.arraycopy(real, 0, temparray, 0, nxy);
+		System.arraycopy(imag, 0, real, 0, nxy);
+		System.arraycopy(temparray, 0, imag, 0, nxy);
+		conj();
+	}
+
+	/**
+	 * Computes the second parameter of Riesz transform of this ComplexImage.
+	 */
+
+	public void riesz2() {
+		FFT2D();
+		ComplexImage mult1 = new ComplexImage(nx, ny);
+		for (int y = 0; y < ny; y++) {
+			double omy = 2.0 * Math.PI * (double) y / (double) ny - Math.PI;
+			for (int x = 0; x < nx; x++) {
+				double omx = 2.0 * Math.PI * (double) x / (double) nx - Math.PI;
+				mult1.real[nx * y + x] = 1.0 / Math.sqrt(omx * omx + omy * omy);
+				mult1.real[nx * y + x] *= omy;
+			}
+		}
+		mult1.shift();
+		mult1.real[0] = 1.0;
+		multiply(mult1);
+		double[] temparray = new double[nxy];
+		System.arraycopy(real, 0, temparray, 0, nxy);
+		System.arraycopy(imag, 0, real, 0, nxy);
+		System.arraycopy(temparray, 0, imag, 0, nxy);
+		conj();
+		iFFT2D();
+	}
+}
diff --git a/src/bilib/src/polyharmonicwavelets/DyadicFilters.java b/src/bilib/src/polyharmonicwavelets/DyadicFilters.java
new file mode 100644
index 0000000000000000000000000000000000000000..36b7f36f62dc7723487e1a9d0c9e5d5d7a210cb2
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/DyadicFilters.java
@@ -0,0 +1,497 @@
+package polyharmonicwavelets;
+
+//
+//  DyadicFilters.java
+//  PolyharmonicWavelets
+//
+//  Created by Biomedical Imaging Group on 2/13/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+import java.util.*;
+import ij.*;
+import java.text.DecimalFormat;
+
+/**
+ * This class computes the filters for the dyadic wavelet transform.
+ * 
+ * @author Katarina Balac, EPFL.
+ */
+
+public class DyadicFilters {
+
+	/**
+	 * The analysis filters. FA[0] the lowpass analysis filter FA[1], FA[2],
+	 * FA[3] the highpass analysis filters
+	 */
+	public ComplexImage[]	FA;
+
+	/**
+	 * The synthesis filters. FS[0] the lowpass synthesis filter FS[1], FS[2],
+	 * FS[3] the highpass synthesis filters
+	 */
+	public ComplexImage[]	FS;
+
+	/**
+	 * Pyramid synthesis filters
+	 */
+	public ComplexImage[]	FP;
+
+	/**
+	 * The prefilter.
+	 */
+	public ComplexImage		P;
+
+	/**
+	 * The autocorrelation.
+	 */
+
+	public ComplexImage		ac;
+
+	private Parameters		param;						// Parameters for the
+														// transform
+	private int				nx;						// Size of filters
+	private int				ny;
+	private final double	PI		= Math.PI;
+	private final double	PI2		= 2.0 * PI;
+	private final double	sqrt2	= Math.sqrt(2.0);
+
+	/**
+	 * Constructor, creates a filters object and reserves the memory space for
+	 * all the analysis and synthesis filters that will be needed depending on
+	 * parameters.
+	 * 
+	 * @param par
+	 *            the wavelet transform parameters
+	 * @param sizex
+	 *            the number of columns in the image to transform
+	 * @param sizey
+	 *            the number of rows in the image to transform
+	 */
+
+	public DyadicFilters(Parameters par, int sizex, int sizey) {
+		param = par;
+		nx = sizex;
+		ny = sizey;
+		FA = new ComplexImage[4];
+		FA[0] = new ComplexImage(nx, ny, param.N == 0);
+		FA[1] = new ComplexImage(nx, ny, (!(param.redundancy == param.BASIS) && (param.N == 0)));
+		if (param.redundancy == param.BASIS) {
+			FA[2] = new ComplexImage(nx, ny);
+			FA[3] = new ComplexImage(nx, ny);
+		}
+		FS = new ComplexImage[4];
+		if (!(param.analysesonly)) {
+			FS = new ComplexImage[4];
+			FS[0] = new ComplexImage(nx, ny, param.N == 0);
+			for (int i = 1; i < 4; i++) {
+				FS[i] = new ComplexImage(nx, ny);
+			}
+			if (param.redundancy == param.PYRAMID) {
+				FP = new ComplexImage[3];
+				for (int i = 0; i < 3; i++) {
+					FP[i] = new ComplexImage(nx, ny);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Sets the wavelet transform parameters to desired value.
+	 * 
+	 * @param param
+	 *            the parameters to set
+	 */
+
+	public void setParameters(Parameters param) {
+		this.param = param;
+	}
+
+	/*
+	 * Calculates numenator of scaling function, localization support is [ minx
+	 * : (maxx-minx)/sizex : maxx-(maxx-minx)/sizex, miny : (maxy-miny)/sizey :
+	 * maxy-(maxy-miny)/sizey ] output is of size [sizex, sizey] and defined
+	 * on[minx...maxx-eps,miny...maxy-eps]
+	 */
+
+	private ComplexImage multiplicator(int sizex, int sizey, double minx, double miny, double maxx, double maxy, double gama, int N) {
+		ComplexImage result = new ComplexImage(sizex, sizey, N == 0);
+		double gama2 = gama / 2.0;
+		final double d83 = 8.0 / 3.0;
+		final double d23 = 2.0 / 3.0;
+		double epsx = (maxx - minx) / (4.0 * (double) sizex);
+		double epsy = (maxy - miny) / (4.0 * (double) sizey);
+		double rx = (maxx - minx) / (double) sizex;
+		double ry = (maxy - miny) / (double) sizey;
+		double[] sxarr = new double[sizex];
+		double[] x1arr = new double[sizex];
+		double x = minx;
+		for (int kx = 0; kx < sizex; kx++, x += rx) {
+			sxarr[kx] = Math.sin(x / 2) * Math.sin(x / 2);
+			double x1 = x;
+			while (x1 >= Math.PI - epsx)
+				x1 = x1 - PI2;
+			while (x1 < -Math.PI - epsx)
+				x1 = x1 + PI2;
+			x1arr[kx] = x1;
+		}
+		double y = miny;
+		for (int ky = 0; ky < sizey; ky++, y += ry) {
+			int kxy = ky * sizex;
+			double sy = Math.sin(y / 2.0);
+			sy = sy * sy;
+			double y1 = y;
+			while (y1 >= Math.PI - epsy)
+				y1 = y1 - PI2;
+			while (y1 < -Math.PI - epsy)
+				y1 = y1 + PI2;
+			double y11 = y1;
+			for (int kx = 0, index = kxy; kx < sizex; kx++, index++) {
+				y1 = y11;
+				double x1 = x1arr[kx];
+				final double sx = sxarr[kx];
+				double a = 1.0;
+				if (param.type == param.ISOTROPIC) { // Isotropic
+					a = 4.0 * (sx + sy) - d83 * (sx * sy);
+				}
+				if (param.type == param.CHANGESIGMA) {
+					final double sigma2 = param.s2;
+					final double b = -16.0 / sigma2;
+					final double c = 24.0 / (sigma2 * sigma2) - 16.0 / (3.0 * sigma2);
+					final double d = 8.0 / (sigma2 * sigma2) + 32.0 / 45.0 - 16.0 / (3.0 * sigma2);
+					final double e = 4.0 / 3.0 - 8.0 / sigma2;
+					a = 4.0 * (sx + sy) + b * (sx * sy) + c * (sx * sx * sy + sy * sy * sx) + d * (sx * sx * sx + sy * sy * sy) + e * (sx * sx + sy * sy);
+				}
+				double re = Math.pow(a, gama2);
+				double im = 0.0;
+				if (N > 0) {
+					boolean xpi = ((x1 < -Math.PI + epsx) && (x1 > -Math.PI - epsx));
+					boolean ypi = ((y1 < -Math.PI + epsy) && (y1 > -Math.PI - epsy));
+					boolean x0 = ((x1 < epsx) && (x1 > -epsx));
+					boolean y0 = ((y1 < epsy) && (y1 > -epsy));
+					if (!(x0 && y0)) {
+						double x1p = x1;
+						double y1p = y1;
+						if (xpi && !y0 && !ypi) {
+							x1p = 0.0;
+						}
+						if (ypi && !x0 && !xpi) {
+							y1p = 0.0;
+						}
+						x1 = x1p;
+						y1 = y1p;
+					}
+					for (int i = 0; i < N; i++) {
+						double re1 = re * x1 - im * y1;
+						double im1 = re * y1 + im * x1;
+						re = re1;
+						im = im1;
+					}
+					double t = Math.pow(x1 * x1 + y1 * y1, (double) N / 2.0);
+					if (t == 0.0) {
+						result.real[index] = 0.0;
+						result.imag[index] = 0.0;
+					}
+					else {
+						result.real[index] = re / t;
+						result.imag[index] = im / t;
+					}
+				}
+				else {
+					result.real[index] = re;
+				}
+			}
+		}
+		return result;
+	}
+
+	/*
+	 * Calculates denominator of scaling function support is [ 0 : maxx/sizex :
+	 * maxx-maxx/sizex, 0 : maxy/sizey : maxy-maxy/sizey ] output is of size
+	 * [sizex, sizey] and defined on[0...maxx-eps,0...maxy-eps]
+	 */
+
+	private ComplexImage denominator(int sizex, int sizey, double minx, double miny, double maxx, double maxy, int N) {
+		ComplexImage result = new ComplexImage(sizex, sizey);
+		double gamaN2;
+		gamaN2 = (param.order - N) / 2.0;
+		for (int ky = 0; ky < sizey; ky++) {
+			int kxy = ky * sizex;
+			double y = miny + (double) ky * (maxy - miny) / (double) sizey;
+			for (int kx = 0, index = kxy; kx < sizex; kx++, index++) {
+				double x = minx + (double) kx * (maxx - minx) / (double) sizex;
+				double re = Math.pow(x * x + y * y, gamaN2);
+				double im = 0.0;
+				if (N > 0) {
+					for (int i = 0; i < N; i++) {
+						double re1 = re * x - im * y;
+						double im1 = re * y + im * x;
+						re = re1;
+						im = im1;
+					}
+					result.real[index] = re;
+					result.imag[index] = im;
+				}
+				else {
+					result.real[index] = re;
+				}
+			}
+		}
+		return result;
+	}
+
+	/*
+	 * Computes the prefilter P
+	 */
+
+	private void calculatePrefilter() {
+		P = multiplicator(nx, ny, -PI, -PI, PI, PI, param.order, 0);
+		ComplexImage d = denominator(nx, ny, -PI, -PI, PI, PI, 0);
+		P.divide(d, 1.0, 0.0);
+		P.shift();
+		if (param.flavor == param.DUALOPERATOR) {
+			P.divide(ac);
+		}
+	}
+
+	/*
+	 * Calculate filters for pyramid synthesis
+	 */
+
+	private void pyramidSynthesisFilters() {
+		FA[1].multiply(1 / sqrt2);
+		ComplexImage[] Ge = FP;
+		Ge[0].copyImageContent(FA[1]);
+		Ge[1].copyImageContent(FA[1]);
+		Ge[2].copyImageContent(FA[1]);
+		Ge[0].multiply(FS[1]);
+		Ge[1].multiply(FS[2]);
+		Ge[2].multiply(FS[3]);
+		Ge[0].multiply(0.5);
+		Ge[1].multiply(0.5);
+		Ge[2].multiply(0.5);
+		ComplexImage[] Geconj = Ge;
+		Geconj[0].conj();
+		Geconj[1].conj();
+		Geconj[2].conj();
+		int nx2 = FA[1].nx / 2;
+		int ny2 = FA[1].ny / 2;
+		int nxy2 = FA[1].nxy / 2;
+		int[] d = { 0, nx2, nxy2, nx2 + nxy2 };
+		double[] mr = new double[9];
+		double[] mi = new double[9];
+		for (int ky = 0, km = 0; ky < nxy2; ky += FA[1].nx) {
+			for (int kx = ky, end = ky + nx2; kx < end; kx++, km++) {
+				for (int i = 0; i < 9; i++) {
+					mr[i] = mi[i] = 0.0;
+				}
+				double inr0 = 0.0;
+				double inr1 = 0.0;
+				double inr2 = 0.0;
+				double inr4 = 0.0;
+				double inr5 = 0.0;
+				double inr8 = 0.0;
+				double inri = 0.0;
+				double ini1 = 0.0;
+				double ini2 = 0.0;
+				double ini4 = 0.0;
+				double ini5 = 0.0;
+				double ini8 = 0.0;
+				for (int l = 0; l < 4; l++) {
+					int k = kx + d[l];
+					inr0 += Geconj[0].real[k] * Geconj[0].real[k] + Geconj[0].imag[k] * Geconj[0].imag[k];
+					inr4 += Geconj[1].real[k] * Geconj[1].real[k] + Geconj[1].imag[k] * Geconj[1].imag[k];
+					inr8 += Geconj[2].real[k] * Geconj[2].real[k] + Geconj[2].imag[k] * Geconj[2].imag[k];
+					inr1 += Geconj[0].real[k] * Geconj[1].real[k] + Geconj[0].imag[k] * Geconj[1].imag[k];
+					ini1 += (-Geconj[0].real[k] * Geconj[1].imag[k] + Geconj[0].imag[k] * Geconj[1].real[k]);
+					inr2 += Geconj[0].real[k] * Geconj[2].real[k] + Geconj[0].imag[k] * Geconj[2].imag[k];
+					ini2 += (-Geconj[0].real[k] * Geconj[2].imag[k] + Geconj[0].imag[k] * Geconj[2].real[k]);
+					inr5 += Geconj[1].real[k] * Geconj[2].real[k] + Geconj[1].imag[k] * Geconj[2].imag[k];
+					ini5 += (-Geconj[1].real[k] * Geconj[2].imag[k] + Geconj[1].imag[k] * Geconj[2].real[k]);
+				}
+				// invert m
+				mr[0] = (inr4 * inr8) - (inr5 * inr5 + ini5 * ini5);
+				mr[1] = (inr2 * inr5 + ini2 * ini5) - (inr1 * inr8);
+				mi[1] = (-inr2 * ini5 + ini2 * inr5) - (ini1 * inr8);
+				mr[2] = (inr1 * inr5 - ini1 * ini5) - (inr2 * inr4);
+				mi[2] = (inr1 * ini5 + ini1 * inr5) - (ini2 * inr4);
+				double dr = mr[0] * inr0 + mr[1] * inr1 + mi[1] * ini1 + mr[2] * inr2 + mi[2] * ini2;
+				mr[3] = ((inr2 * inr5 + ini2 * ini5) - (inr1 * inr8)) / dr;
+				mi[3] = ((inr2 * ini5 - ini2 * inr5) + (ini1 * inr8)) / dr;
+				mr[4] = ((inr0 * inr8) - (inr2 * inr2 + ini2 * ini2)) / dr;
+				mr[5] = ((inr1 * inr2 + ini1 * ini2) - (inr0 * inr5)) / dr;
+				mi[5] = ((inr1 * ini2 - ini1 * inr2) - (inr0 * ini5)) / dr;
+				mr[6] = ((inr1 * inr5 - ini1 * ini5) - (inr2 * inr4)) / dr;
+				mi[6] = ((-inr1 * ini5 - ini1 * inr5) + (ini2 * inr4)) / dr;
+				mr[7] = ((inr2 * inr1 + ini2 * ini1) - (inr0 * inr5)) / dr;
+				mi[7] = ((inr2 * ini1 - ini2 * inr1) + (inr0 * ini5)) / dr;
+				mr[8] = ((inr0 * inr4) - (inr1 * inr1 + ini1 * ini1)) / dr;
+				mr[0] /= dr;
+				mr[1] /= dr;
+				mi[1] /= dr;
+				mr[2] /= dr;
+				mi[2] /= dr;
+				// end invert m
+				for (int l = 0; l < 4; l++) {
+					int k = kx + d[l];
+					double[] ger = new double[3];
+					double[] gei = new double[3];
+					for (int i = 0; i < 3; i++) {
+						ger[i] = Geconj[i].real[k];
+						gei[i] = Geconj[i].imag[k];
+					}
+					for (int i = 0; i < 3; i++) {
+						double gr = 0.0;
+						double gi = 0.0;
+						for (int j = 0; j < 3; j++) {
+							gr += ger[j] * mr[3 * i + j] - gei[j] * mi[3 * i + j];
+							gi += ger[j] * mi[3 * i + j] + gei[j] * mr[3 * i + j];
+						}
+						Geconj[i].real[k] = gr;
+						Geconj[i].imag[k] = gi;
+					}
+				}
+			}
+		}
+		FP = Geconj;
+		FA[1].multiply(sqrt2);
+	}
+
+	/**
+	 * Calculates all filters needed to perform dyadic transform with given
+	 * parameters.
+	 */
+
+	public void calculateFilters() {
+		double k = 1.0 / Math.pow(2.0, param.order);
+		ComplexImage HH = null;
+		ComplexImage L1 = null;
+		if (param.accompute == param.ITERATIVE) {
+			// Compute filter on a 2x finer grid for autocorrelation
+			ComplexImage L = multiplicator(2 * nx, 2 * ny, 0.0, 0.0, 2.0 * PI2, 2.0 * PI2, param.order, param.N); // Numerator(2*omega)
+																													// is
+																													// complex
+			L1 = multiplicator(2 * nx, 2 * ny, 0.0, 0.0, PI2, PI2, param.order, param.N); // Numerator(omega)
+																							// is
+																							// complex
+			ComplexImage HHdouble = L;
+			HHdouble.multiply(k);
+			HHdouble.divide(L1, 1.0, 0.0);
+			// compute filter on a regular grid
+			HH = HHdouble.copyImage();
+			HH.decimateCrop();
+			L1.decimateCrop();
+			// compute autocorrelation if needed
+			if (!(param.analysesonly)) {
+				HHdouble.squareModulus();
+				ac = Autocorrelation.autocorrIterative(HHdouble);
+			}
+		}
+		else {
+			ComplexImage L = multiplicator(nx, ny, 0.0, 0.0, 2.0 * PI2, 2.0 * PI2, param.order, param.N); // Numerator(2*omega)
+																											// is
+																											// complex
+			L1 = multiplicator(nx, ny, 0.0, 0.0, PI2, PI2, param.order, param.N); // Numerator(omega)
+																					// is
+																					// complex
+			HH = L;
+			HH.multiply(k);
+			HH.divide(L1, 1.0, 0.0);
+			if (!((param.analysesonly) && (param.flavor == Parameters.MARR))) {
+				ComplexImage simpleloc = multiplicator(nx, ny, 0.0, 0.0, PI2, PI2, 2 * param.order, 0);
+				ac = Autocorrelation.autocorrGamma(simpleloc, param.order);
+			}
+		}
+		calculatePrefilter();
+		if ((param.flavor == param.OPERATOR) || (param.flavor == param.DUALOPERATOR) || (param.flavor == param.MARR)) {
+			// Analysis filters
+			FA[0].copyImageContent(HH);
+			FA[0].multiply(2.0);
+			ComplexImage G = FA[1];
+			G.copyImageContent(L1);
+			if (!(param.flavor == param.MARR)) {
+				G.divide(ac);
+			}
+			G.multiply(2.0);
+			if (param.rieszfreq == 1) {
+				ComplexImage V2 = multiplicator(nx, ny, 0.0, 0.0, PI2, PI2, 2.0, 0);
+				G.multiply(V2);
+			}
+			G.conj();
+			ComplexImage R = null;
+			if (param.redundancy == param.BASIS) { // Basis is not for Marr
+				FA[2].copyImageContent(G);
+				FA[3].copyImageContent(G);
+			}
+			FA[1] = G;
+			// Compute synthesis filters
+			if (!(param.analysesonly)) {
+				// Synthesis lowpass
+				ComplexImage L1conj = L1.copyImage();
+				L1conj.conj();
+				ComplexImage H = FS[0];
+				H.copyImageContent(HH);
+				H.conj();
+				double k1 = k * 4.0;
+				H.multiply(k1);
+				ComplexImage acd = ac.copyImage();
+				acd.decimate();
+				ComplexImage Gs = ac.copyImage();
+				Gs.divide(acd);
+				Gs.multiply(0.25);
+				H.multiply(Gs);
+				FS[0] = H;
+				FS[0].multiply(2.0 / k);
+				// Synthesis highpass
+				Gs.divide(L1conj, 0.0, 0.0);
+				ComplexImage D = HH.copyImage();
+				D.squareModulus();
+				D.multiply(1.0 / k);
+				D.multiply(ac);
+				D.multiply(k1);
+				ComplexImage D1 = D.copyImage();
+				ComplexImage D2 = D.copyImage();
+				ComplexImage D12 = D.copyImage();
+				D1.shiftX();
+				D2.shiftY();
+				D12.shift();
+				D1.multiply(Gs);
+				D2.multiply(Gs);
+				D12.multiply(Gs);
+				FS[1] = D1.copyImage();
+				FS[1].add(D12);
+				FS[2] = D2.copyImage();
+				FS[2].add(D12);
+				FS[3] = D1;
+				FS[3].add(D2);
+			}
+			if (param.flavor == param.DUALOPERATOR) {
+				ComplexImage[] Ftmp = FA;
+				FA = FS;
+				FS = Ftmp;
+				FA[0].conj();
+				FA[1].conj();
+				FA[2].conj();
+				FA[3].conj();
+				FS[0].conj();
+				FS[1].conj();
+				FS[2].conj();
+				FS[3].conj();
+			}
+		}
+		if (param.redundancy == param.BASIS) { // Basis is not for Marr
+			FA[1].modulateMinusX();
+			FA[2].modulateMinusY();
+			FA[3].modulateMinusQuincunx();
+		}
+		if (!(param.analysesonly)) {
+			FS[1].modulatePlusX();
+			FS[2].modulatePlusY();
+			FS[3].modulatePlusQuincunx();
+		}
+
+		if ((param.redundancy == param.PYRAMID) && (!(param.analysesonly))) {
+			pyramidSynthesisFilters();
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/bilib/src/polyharmonicwavelets/DyadicTransform.java b/src/bilib/src/polyharmonicwavelets/DyadicTransform.java
new file mode 100644
index 0000000000000000000000000000000000000000..70f06bf4ffc779d9e458dad34d6ba74be485cae2
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/DyadicTransform.java
@@ -0,0 +1,556 @@
+package polyharmonicwavelets;
+
+//
+//  DyadicTransform.java
+//  PolyharmonicWavelets
+//
+//  Created by Biomedical Imaging Group on 2/13/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+import java.util.*;
+import ij.*;
+import java.text.DecimalFormat;
+
+/**
+ * This class performs basis, pyramid and redundant dyadic transform.
+ * 
+ * @author Katarina Balac, EPFL.
+ */
+
+public class DyadicTransform {
+
+	// Analysis and synthesis filters
+	// FA[0]=H1
+	// FA[1]=G1
+	// FA[2]=G2
+	// FA[3]=G3
+	// similar for analysis filters
+	// H - lowpass filters
+	// G - highpass filters
+
+	private ComplexImage[]	FA;
+	private ComplexImage[]	FS;
+	private ComplexImage[]	FP;
+	private ComplexImage	P;
+	private int				J;							// number of iterations
+	private Parameters		param;
+	private final int		nx;
+	private final int		ny;
+	private final double	PI2		= 2.0 * Math.PI;
+	private final double	sqrt2	= Math.sqrt(2.0);
+	private DyadicFilters	filters;
+
+	/**
+	 * Creates a DyadicTransform object.
+	 * 
+	 * @param filt
+	 *            the filters used for the dyadic transform
+	 * @param par
+	 *            the transform parameters
+	 */
+
+	public DyadicTransform(DyadicFilters filt, Parameters par) {
+		J = par.J;
+		param = par;
+		FA = filt.FA;
+		FS = filt.FS;
+		FP = filt.FP;
+		P = filt.P;
+		nx = FA[0].nx;
+		ny = FA[0].ny;
+		filters = filt;
+	}
+
+	/**
+	 * Performs the in-place nonredundant dyadic analysis on the input
+	 * ComplexImage.
+	 * 
+	 * @param image
+	 *            the image to transform
+	 */
+
+	public void dyadicAnalysis(ComplexImage image) {
+		ComplexImage H = FA[0].copyImage();
+		ComplexImage G1 = FA[1].copyImage();
+		ComplexImage G2 = FA[2].copyImage();
+		ComplexImage G3 = FA[3].copyImage();
+		H.multiply(0.25);
+		G1.multiply(0.25);
+		G2.multiply(0.25);
+		G3.multiply(0.25);
+		ComplexImage R = image.copyImage(); // remaining lowpass subband
+		R.FFT2D();
+		if (param.prefilter) {
+			R.multiply(P);
+		}
+		int l = 1;
+		for (int j = 1; j <= J; j++) {
+			ComplexImage Y0 = R.copyImage();
+			ComplexImage Y1 = R.copyImage();
+			ComplexImage Y2 = R.copyImage();
+			ComplexImage Y3 = R.copyImage();
+			Y0.multiply(H, l);
+			Y1.multiply(G1, l);
+			Y2.multiply(G2, l);
+			Y3.multiply(G3, l);
+			Y0.dyadicDownUpCrop();
+			Y1.dyadicDownUpCrop();
+			Y2.dyadicDownUpCrop();
+			Y3.dyadicDownUpCrop();
+			// Put highpass subbands in place
+			Y1.iFFT2D();
+			Y2.iFFT2D();
+			Y3.iFFT2D();
+			R = Y0.copyImage();
+			image.putSubimage(R.nx, 0, Y1);
+			image.putSubimage(0, R.ny, Y2);
+			image.putSubimage(R.nx, R.ny, Y3);
+			l *= 2;
+		}
+		R.iFFT2D();
+		image.putSubimage(0, 0, R);
+	}
+
+	/**
+	 * Performs the in-place nonredundant dyadic synthesis on ComplexImage.
+	 * 
+	 * @param image
+	 *            the image to transform
+	 */
+
+	public void dyadicSynthesis(ComplexImage image) {
+		ComplexImage H = FS[0];
+		ComplexImage G1 = FS[1];
+		ComplexImage G2 = FS[2];
+		ComplexImage G3 = FS[3];
+		int l = 1;
+		for (int k = 1; k < J; k++, l *= 2)
+			;
+		int cx = image.nx; // size of remaining lowpass subband
+		int cy = image.ny;
+		for (int i = 0; i < J; i++, cx /= 2, cy /= 2)
+			;
+		ComplexImage Y = new ComplexImage(image.nx, image.ny);
+		ComplexImage Z = new ComplexImage(image.nx, image.ny);
+		Y.getSubimageContent(0, cx - 1, 0, cy - 1, image); // Lowpass
+		Y.FFT2D();
+		for (int j = J; j > 0; j--) {
+			Y.dyadicUpsample();
+			Y.multiply(H, l);
+			Z.getSubimageContent(cx, 2 * cx - 1, 0, cy - 1, image);
+			Z.FFT2D();
+			Z.dyadicUpsample();
+			Z.multiply(G1, l);
+			Y.add(Z);
+			Z.getSubimageContent(0, cx - 1, cy, 2 * cy - 1, image);
+			Z.FFT2D();
+			Z.dyadicUpsample();
+			Z.multiply(G2, l);
+			Y.add(Z);
+			Z.getSubimageContent(cx, 2 * cx - 1, cy, 2 * cy - 1, image);
+			Z.FFT2D();
+			Z.dyadicUpsample();
+			Z.multiply(G3, l);
+			Y.add(Z);
+			l /= 2;
+			cx *= 2;
+			cy *= 2;
+		}
+		if (param.prefilter) {
+			Y.divide(P);
+		}
+		Y.iFFT2D();
+		image.copyImageContent(Y);
+	}
+
+	/**
+	 * Returns the result of fully redundant pyramid analysis of ComplexImage
+	 * and all the intermediary lowpass subbands.
+	 * 
+	 * @param image
+	 *            the image to transform
+	 * @return in [0][] the array of dyadic transform subbands, and in [1][] the
+	 *         intermidiary lowpass subbands
+	 */
+
+	public ComplexImage[][] dyadicAnalysesRedundantLowpass(ComplexImage image) {
+		ComplexImage[] lowpassSubbands = new ComplexImage[J + 1];
+		ComplexImage[] array = new ComplexImage[J + 1];
+		ComplexImage H = FA[0].copyImage();
+		ComplexImage G = FA[1].copyImage();
+		G.multiply(0.5 * sqrt2);
+		ComplexImage X = image.copyImage();
+		X.FFT2D();
+		if (param.prefilter) {
+			X.multiply(P);
+		}
+		for (int j = 1, l = 1; j <= J; j++) {
+			ComplexImage Yh = X.copyImage();
+			Yh.multiplyCircular(G, l);
+			// Put Yh in stack
+			Yh.iFFT2D();
+			array[j - 1] = Yh;
+			ComplexImage Yl = X;
+			Yl.multiplyCircular(H, l);
+			lowpassSubbands[j - 1] = Yl.copyImage();
+			lowpassSubbands[j - 1].iFFT2D();
+			X = Yl;
+			l *= 2;
+		}
+		// Put remaining lowpass in stack
+		X.iFFT2D();
+		array[J] = X;
+		lowpassSubbands[J] = X.copyImage();
+		G.multiply(sqrt2);
+		ComplexImage[][] out = new ComplexImage[2][];
+		out[0] = array;
+		out[1] = lowpassSubbands;
+		return out;
+	}
+
+	/**
+	 * Returns the result of fully redundant dyadic analysis of ComplexImage.
+	 * 
+	 * @param image
+	 *            the ComplexImage to transform
+	 * @return the array of dyadic transform subbands
+	 */
+
+	public ComplexImage[] dyadicAnalysesRedundant(ComplexImage image) {
+		ComplexImage[] array = new ComplexImage[J + 1];
+		ComplexImage H = FA[0].copyImage();
+		ComplexImage G = FA[1].copyImage();
+		G.multiply(0.5 * sqrt2);
+		ComplexImage X = image.copyImage();
+		X.FFT2D();
+		if (param.prefilter) {
+			X.multiply(P);
+		}
+		for (int j = 1, l = 1; j <= J; j++) {
+			ComplexImage Yh = X.copyImage();
+			Yh.multiplyCircular(G, l);
+			// Put Yh in stack
+			Yh.iFFT2D();
+			array[j - 1] = Yh;
+			ComplexImage Yl = X;
+			Yl.multiplyCircular(H, l);
+			X = Yl;
+			l *= 2;
+		}
+		// Put remaining lowpass in stack
+		X.iFFT2D();
+		array[J] = X;
+		G.multiply(sqrt2);
+		return array;
+	}
+
+	/**
+	 * Returns the result of fully redundant dyadic synthesis.
+	 * 
+	 * @param array
+	 *            subbands of dyadic pyramid transform
+	 * @return the result of synthesis
+	 */
+
+	public ComplexImage dyadicSynthesisRedundant(ComplexImage[] array) {
+		ComplexImage H = FS[0].copyImage();
+		ComplexImage G = FS[1].copyImage();
+		G.add(FS[2]);
+		G.add(FS[3]);
+		G.multiply(0.25 * sqrt2);
+		H.multiply(0.25);
+		if (param.flavor == param.MARR) {
+			G.divide(filters.ac);
+		}
+		int l = 1;
+		for (int k = 1; k < J; k++, l *= 2)
+			;
+		ComplexImage Y = array[J].copyImage(); // Lowpass
+		Y.FFT2D();
+		for (int j = J; j > 0; j--) {
+			ComplexImage Z = array[j - 1].copyImage();
+			Z.FFT2D();
+			Y.multiplyCircular(H, l);
+			Z.multiplyCircular(G, l);
+			l /= 2;
+			Y.add(Z);
+		}
+		if (param.prefilter) {
+			Y.divide(P);
+		}
+		Y.iFFT2D();
+		return Y;
+	}
+
+	/**
+	 * Returns the result of dyadic pyramid analysis of ComplexImage.
+	 * 
+	 * @param image
+	 *            the image to transform
+	 * @return the array of dyadic transform subbands
+	 */
+
+	public ComplexImage[] dyadicAnalysesPyramid(ComplexImage image) {
+		ComplexImage[] array = new ComplexImage[J + 1];
+		ComplexImage H = FA[0];
+		ComplexImage G = FA[1];
+		H.multiply(0.25);
+		G.multiply(0.5 * sqrt2);
+		ComplexImage X = image.copyImage();
+		X.FFT2D();
+		if (param.prefilter) {
+			X.multiply(P);
+		}
+		for (int j = 1, l = 1; j <= J; j++) {
+			ComplexImage Yh = X.copyImage();
+			Yh.multiply(G, l);
+			// Put Yh in stack
+			Yh.iFFT2D();
+			array[j - 1] = Yh;
+			ComplexImage Yl = X;
+			Yl.multiply(H, l);
+			l *= 2;
+			Yl.dyadicDownUpCrop();
+			X = Yl;
+		}
+		// Put remaining lowpass in stack
+		X.iFFT2D();
+		array[J] = X;
+		H.multiply(4.0);
+		G.multiply(sqrt2);
+		return array;
+	}
+
+	/**
+	 * Returns the result of dyadic pyramid analysis of ComplexImage and all the
+	 * intermediary lowpass subbands.
+	 * 
+	 * @param image
+	 *            the image to transform
+	 * @return in [0][] the array of dyadic transform subbands, and in [1][] the
+	 *         intermidiary lowpass subbands
+	 */
+
+	public ComplexImage[][] dyadicAnalysesPyramidLowpass(ComplexImage image) {
+		ComplexImage[] lowpassSubbands = new ComplexImage[J + 1];
+		ComplexImage[] array = new ComplexImage[J + 1];
+		ComplexImage H = FA[0];
+		ComplexImage G = FA[1];
+		H.multiply(0.25);
+		G.multiply(0.5 * sqrt2);
+		ComplexImage X = image.copyImage();
+		// ComplexImage Yh=new ComplexImage(image.nx,image.ny);
+		X.FFT2D();
+		if (param.prefilter) {
+			X.multiply(P);
+		}
+		for (int j = 1, l = 1; j <= J; j++) {
+			ComplexImage Yh = new ComplexImage(image.nx, image.ny);
+			Yh.copyImageContent(X);
+			Yh.multiply(G, l);
+			// Put Yh in stack
+			Yh.iFFT2D();
+			array[j - 1] = Yh;
+			ComplexImage Yl = X;
+			Yl.multiply(H, l);
+			lowpassSubbands[j - 1] = Yl.copyImage();
+			lowpassSubbands[j - 1].iFFT2D();
+			l *= 2;
+			Yl.dyadicDownUpCrop();
+			X = Yl;
+		}
+		// Put remaining lowpass in stack
+		X.iFFT2D();
+		array[J] = X;
+		lowpassSubbands[J] = X.copyImage();
+		// H.multiply(4.0);
+		// G.multiply(sqrt2);
+		ComplexImage[][] out = new ComplexImage[2][];
+		out[0] = array;
+		out[1] = lowpassSubbands;
+		return out;
+	}
+
+	/**
+	 * Returns the result of pyramid dyadic synthesis of array.
+	 * 
+	 * @param array
+	 *            subbands of dyadic pyramid transform
+	 * @return the result of synthesis
+	 */
+
+	public ComplexImage dyadicSynthesisPyramid(ComplexImage[] array) {
+		FS[1].multiply(0.5);
+		FS[2].multiply(0.5);
+		FS[3].multiply(0.5);
+		ComplexImage Gconj = FA[1].copyImage();
+		Gconj.multiply(1 / sqrt2);
+		ComplexImage[] G0 = FP;
+		// Reconstruct with calculated filters G0, FS, Gconj
+		int l = 1;
+		for (int i = 0; i < J; i++, l *= 2)
+			;
+		ComplexImage Y1 = new ComplexImage(array[0].nx, array[0].ny);
+		ComplexImage Y2 = new ComplexImage(array[0].nx, array[0].ny);
+		ComplexImage GY = new ComplexImage(array[0].nx, array[0].ny);
+		Y1.copyImageContent(array[J]);
+		Y1.FFT2D();
+		for (int j = J - 1; j >= 0; j--) {
+			Y2.copyImageContent(array[j]);
+			Y2.FFT2D();
+			l /= 2;
+			Y1.dyadicUpsample();
+			Y1.multiply(FS[0], l);
+			GY.copyImageContent(Y1);
+			GY.multiply(Gconj, l);
+			ComplexImage Y21 = Y2;
+			Y21.subtract(GY);
+			GY.copyImageContent(Y21);
+			GY.multiply(G0[1], l);
+			GY.dyadicDownUp();
+			GY.multiply(FS[2], l);
+			Y1.add(GY);
+			GY.copyImageContent(Y21);
+			GY.multiply(G0[2], l);
+			GY.dyadicDownUp();
+			GY.multiply(FS[3], l);
+			Y1.add(GY);
+			Y21.multiply(G0[0], l);
+			Y21.dyadicDownUp();
+			Y21.multiply(FS[1], l);
+			Y1.add(Y21);
+		}
+		if (param.prefilter) {
+			Y1.divide(P);
+		}
+		Y1.iFFT2D();
+		FS[1].multiply(2.0);
+		FS[2].multiply(2.0);
+		FS[3].multiply(2.0);
+		return Y1;
+	}
+
+	/**
+	 * Prepares the real dyadic pyramid transform coeffitients for being
+	 * displayed. Rescales the subbands for visualisation and puts all subbands
+	 * in one ComplexImage.
+	 * 
+	 * @param array
+	 *            subbands of dyadic pyramid transform
+	 * @param back
+	 *            background color
+	 * @param rescale
+	 *            if rescale=false there is no rescaling
+	 * @param lp
+	 *            if lp=false the lowpass subband is not displayed
+	 * @return the image to display
+	 */
+
+	public ComplexImage displayDyadicPyramidReal(ComplexImage[] array, double back, boolean rescale, boolean lp) {
+		int J1 = J;
+		if (!lp) {
+			J1 -= 1;
+		}
+		int nx = array[0].nx;
+		int ny = array[0].ny;
+		ComplexImage disp = new ComplexImage(nx, 2 * ny);
+		disp.settoConstant(back, back);
+		int x = 0;
+		int y = 0;
+		int dx = nx / 4;
+		int dy = ny;
+		for (int j = 0; j <= J1; j++) {
+			ComplexImage temp = array[j].copyImage();
+			if (rescale) {
+				temp.stretch();
+			}
+			temp.frame(back);
+			disp.putSubimage(x, y, temp);
+			x += dx;
+			y += dy;
+			dx /= 2;
+			dy /= 2;
+		}
+		return disp;
+	}
+
+	/**
+	 * Prepares the dyadic pyramid transform coeffitients for being displayed.
+	 * 
+	 * @param array
+	 *            subbands of dyadic pyramid transform
+	 * @param back
+	 *            background color
+	 * @param rescale
+	 *            if rescale=false there is no rescaling
+	 * @param lp
+	 *            if lp=false the lowpass subband is not displayed
+	 * @return the image to display
+	 */
+
+	public ComplexImage displayDyadicPyramid(ComplexImage[] array, double back, boolean rescale, boolean lp) {
+		int J1 = J;
+		if (!lp) {
+			J1 -= 1;
+		}
+		int nx = array[0].nx;
+		int ny = array[0].ny;
+		ComplexImage disp = new ComplexImage(2 * nx, 2 * ny);
+		disp.settoConstant(back, back);
+		int x = 0;
+		int y = 0;
+		int dx = nx / 2;
+		int dy = ny;
+		for (int j = 0; j <= J1; j++) {
+			ComplexImage temp = array[j].copyImage();
+			if (rescale) {
+				temp.stretch();
+			}
+			temp.frame(back, back);
+
+			disp.putSubimage(x, y, temp);
+			System.arraycopy(temp.imag, 0, temp.real, 0, temp.nxy);
+			disp.putSubimage(x + temp.nx, y, temp);
+			disp.setImagtoZero();
+			x += dx;
+			y += dy;
+			dx /= 2;
+			dy /= 2;
+		}
+		return disp;
+	}
+
+	/**
+	 * Prepares a nonredundant dyadic transform for being displayed, stretches
+	 * each subband.
+	 * 
+	 * @param image
+	 *            the basis transform coefficients
+	 * @return the image with rescaled subbands to display
+	 */
+
+	public ComplexImage displayBasis(ComplexImage image) {
+		int dx = image.nx;
+		int dy = image.ny;
+		ComplexImage out = new ComplexImage(dx, dy);
+		ComplexImage sub;
+		for (int j = 1; j <= J; j++) {
+			sub = image.getSubimage(dx / 2, dx - 1, 0, dy / 2 - 1);
+			sub.stretch();
+			out.putSubimage(dx / 2, 0, sub);
+			sub.getSubimageContent(dx / 2, dx / 2 + sub.nx - 1, dy / 2, dy / 2 + sub.ny - 1, image);
+			sub.stretch();
+			out.putSubimage(dx / 2, dy / 2, sub);
+			sub.getSubimageContent(0, sub.nx - 1, dy / 2, dy / 2 + sub.ny - 1, image);
+			sub.stretch();
+			out.putSubimage(0, dy / 2, sub);
+			dx /= 2;
+			dy /= 2;
+		}
+		sub = image.getSubimage(0, dx - 1, 0, dy - 1);
+		sub.stretch();
+		out.putSubimage(0, 0, sub);
+		return out;
+	}
+}
\ No newline at end of file
diff --git a/src/bilib/src/polyharmonicwavelets/FFT1D.java b/src/bilib/src/polyharmonicwavelets/FFT1D.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2458f78b9e8d4b5251127ded26140530d18615a
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/FFT1D.java
@@ -0,0 +1 @@
+package polyharmonicwavelets;

import ij.*;

/**
 * Performs a 1D FFT.
 * <hr>
 * <p>
 * <b>Plugin of ImageJ:</b><br>
 * Fractional Spline Wavelet<br>
 * 
 * <p>
 * <b>Authors:</b><br>
 * Gil Gaillard, Michael Liebling, Daniel Sage, Dimitri Van De Ville <a
 * href="mailto:daniel.sage@epfl.ch?subject=Fractional Spline Wavelet Plugin"
 * >daniel.sage@epfl.ch</a><br>
 * Swiss Federal Institute of Technology Lausanne, Biomedical Imaging Group,
 * CH-1015 Lausanne, Switzerland, <a
 * href="http://bigwww.epfl.ch">http://bigwww.epfl.ch</a><br>
 * 
 * <p>
 * <b>Version:</b><br>
 * April 2003<br>
 * 
 * <p>
 * <b>Copyright</b><br>
 * Copyright � 2003, Swiss Federal Institute of Technology, Lausanne,
 * Switzerland, (EPFL)<br>
 * 
 * <hr>
 * 
 * <p>
 * <b>Purpose of the class:</b><br>
 * Perform a 1D FFT.
 */

public class FFT1D {

	private boolean		radix2	= true;
	private double		Rearg[];
	private double		Imarg[];
	private double[]	yReOut;
	private double[]	yImOut;

	public FFT1D(int size) {
		int m = 1;
		int size1 = size;
		double fact;
		double arg;
		while (size1 > 2) {
			size1 /= 2;
			m++;
		}

		if ((int) Math.round(Math.pow(2, m)) == size) {
			radix2 = true;
			n = 1 << m;
			fact = 2.0 * Math.PI / (double) n;
			Imarg = new double[n];
			Rearg = new double[n];
			// compute W coefficients
			for (int i = 0; i < n; i++) {
				arg = fact * (double) i;
				Rearg[i] = Math.cos(arg);
				Imarg[i] = -Math.sin(arg);
			}
		}
		else {
			radix2 = false;
			maxPrimeFactor = /* 65537; */(int) ((double) (size + 1));
			maxPrimeFactorDiv2 = (maxPrimeFactor + 1) / 2;// (int)((double)(maxPrimeFactor+1)/2);

			twiddleRe = new double[maxPrimeFactor];
			twiddleIm = new double[maxPrimeFactor];
			trigRe = new double[maxPrimeFactor];
			trigIm = new double[maxPrimeFactor];
			zRe = new double[maxPrimeFactor];
			zIm = new double[maxPrimeFactor];
			vRe = new double[maxPrimeFactorDiv2];
			vIm = new double[maxPrimeFactorDiv2];
			wRe = new double[maxPrimeFactorDiv2];
			wIm = new double[maxPrimeFactorDiv2];
			yReOut = new double[size];
			yImOut = new double[size];
			// Math.pi = 4*Math.atan(1);
			n = size;
			sofarRadix = new int[maxFactorCount];
			actualRadix = new int[maxFactorCount];
			remainRadix = new int[maxFactorCount];
			transTableSetup(sofarRadix, actualRadix, remainRadix);
		}
	}

	/**
	 * Select the algorithm to perform the FFT1D, Cooley-Tukey or Mix.
	 * 
	 * @param Re
	 *            real part of the input signal
	 * @param Im
	 *            imaginary part of the input signal
	 * @param size
	 *            length of the FFT
	 * @param shift
	 *            set the start of the FFT
	 */
	final public void transform(double Re[], double Im[], int size, int shift) // Call
																				// this
																				// function
																				// for
																				// FFT!
	{
		n = size;
		if (radix2) {
			doFFT1D_CooleyTukey(Re, Im, size, shift);
		}
		else {
			if (shift == 0)
				doFFT_Mix(Re, Im, size);
			else
				doFFT_Mix(Re, Im, size, shift);
		}
	}

	/**
	 * Select the algorithm to perform the Inverse FFT1D, Cooley-Tukey or Mix.
	 * 
	 * @param Re
	 *            real part of the input signal
	 * @param Im
	 *            imaginary part of the input signal
	 * @param size
	 *            length of the IFFT
	 * @param shift
	 *            set the start of the IFFT
	 */
	final public void inverse(double Re[], double Im[], int size, int shift) // Call
																				// this
																				// for
																				// inverse
																				// FFT!
	{
		n = size;
		if (radix2) {
			doIFFT1D_CooleyTukey(Re, Im, size, shift);
		}
		else {
			if (shift == 0)
				doIFFT_Mix(Re, Im, size);
			else
				doIFFT_Mix(Re, Im, size, shift);
		}
	}

	/**
	 * Perform the FFT1D.
	 * 
	 * There are two algorithms, the first for power of two length is a
	 * Cooley-Tukey algorithm, the second for all size has been downloaded from
	 * the Web (Mixfft.java). These are used to transform rows or columns in the
	 * wavelet transform.
	 * 
	 * @param Re
	 *            real part of the input signal
	 * @param Im
	 *            imaginary part of the input signal
	 * @param size
	 *            length of the FFT
	 * @param shift
	 *            set the start of the FFT
	 */
	private void doFFT1D_CooleyTukey(double Re[], double Im[], int size, int shift) {

		int m = 1;
		int size1 = size;
		while (size1 > 2) {
			size1 /= 2;
			m++;
		}

		double Retmp, Imtmp;
		int i, j, k, stepsize, shifter;
		int i_j, i_j_s;

		// bit inversion
		for (i = j = shift; i < shift + n - 1; i++) {
			if (i < j) {
				Retmp = Re[i];
				Imtmp = Im[i];
				Re[i] = Re[j];
				Im[i] = Im[j];
				Re[j] = Retmp;
				Im[j] = Imtmp;
			}
			k = n >> 1;
			while (k + shift <= j) {
				j -= k;
				k /= 2;
			}
			j += k;
		}

		// Perform the FFT
		for (stepsize = 1, shifter = m - 1; stepsize < n; stepsize <<= 1, --shifter) {
			for (j = shift; j < shift + n; j += stepsize << 1) {
				for (i = 0; i < stepsize; i++) {
					i_j = i + j;
					i_j_s = i_j + stepsize;
					if (i > 0) {
						Retmp = Rearg[i << shifter] * Re[i_j_s] - Imarg[i << shifter] * Im[i_j_s];
						Im[i_j_s] = Rearg[i << shifter] * Im[i_j_s] + Imarg[i << shifter] * Re[i_j_s];
						Re[i_j_s] = Retmp;
					}
					Retmp = Re[i_j] - Re[i_j_s];
					Imtmp = Im[i_j] - Im[i_j_s];
					Re[i_j] += Re[i_j_s];
					Im[i_j] += Im[i_j_s];
					Re[i_j_s] = Retmp;
					Im[i_j_s] = Imtmp;
				}

			}
		}
	}

	/**
	 * Perform the IFFT1D.
	 * 
	 * Same algorithms as these used for the FFT.
	 * 
	 * @param Re
	 *            real part of the input signal
	 * @param Im
	 *            imaginary part of the input signal
	 * @param size
	 *            length of the IFFT
	 * @param shift
	 *            set the start of the IFFT
	 */
	private void doIFFT1D_CooleyTukey(double Re[], double Im[], final int size, int shift) {
		for (int i = shift; i < shift + size; i++) {
			Im[i] = -Im[i];
		}

		transform(Re, Im, size, shift);

		for (int i = shift; i < shift + size; i++) {
			Re[i] = Re[i] / size; /* Output is in Re */
			Im[i] = -Im[i] / size;
		}
	}

	/*
	 * fft(int n, double xRe[], double xIm[], double yRe[], double yIm[])
	 * ------------------------------------------------------------------------
	 * NOTE : This is copyrighted material, Not public domain. See below.
	 * ------------------------------------------------------------------------
	 * Input/output: int n transformation length. double xRe[] real part of
	 * input sequence. double xIm[] imaginary part of input sequence. double
	 * yRe[] real part of output sequence. double yIm[] imaginary part of output
	 * sequence.
	 * ------------------------------------------------------------------------
	 * Function: The procedure performs a fast discrete Fourier transform (FFT)
	 * of a complex sequence, x, of an arbitrary length, n. The output, y, is
	 * also a complex sequence of length n.
	 * 
	 * y[k] = sum(x[m]*exp(-i*2*Math.pi*k*m/n), m=0..(n-1)), k=0,...,(n-1)
	 * 
	 * The largest prime factor of n must be less than or equal to the constant
	 * maxPrimeFactor defined below.
	 * ------------------------------------------------------------------------
	 * Author: Jens Joergen Nielsen For non-commercial use only. Bakkehusene 54
	 * A $100 fee must be paid if used DK-2970 Hoersholm commercially. Please
	 * contact. DENMARK
	 * 
	 * E-mail : jjn@get2net.dk All rights reserved. October 2000. Homepage :
	 * http://home.get2net.dk/jjn
	 * ------------------------------------------------------------------------
	 * Implementation notes: The general idea is to factor the length of the
	 * DFT, n, into factors that are efficiently handled by the routines.
	 * 
	 * A number of short DFT's are implemented with a minimum of arithmetical
	 * operations and using (almost) straight line code resulting in very fast
	 * execution when the factors of n belong to this set. Especially radix-10
	 * is optimized.
	 * 
	 * Prime factors, that are not in the set of short DFT's are handled with
	 * direct evaluation of the DFP expression.
	 * 
	 * Please report any problems to the author. Suggestions and improvements
	 * are welcomed.
	 * ------------------------------------------------------------------------
	 * Benchmarks: The Microsoft Visual C++ comMath.piler was used with the
	 * following comMath.pile options: /nologo /Gs /G2 /W4 /AH /Ox /D "NDEBUG"
	 * /D "_DOS" /FR and the FFTBENCH test executed on a 50MHz 486DX :
	 * 
	 * Length Time [s] Accuracy [dB]
	 * 
	 * 128 0.0054 -314.8 256 0.0116 -309.8 512 0.0251 -290.8 1024 0.0567 -313.6
	 * 2048 0.1203 -306.4 4096 0.2600 -291.8 8192 0.5800 -305.1 100 0.0040
	 * -278.5 200 0.0099 -280.3 500 0.0256 -278.5 1000 0.0540 -278.5 2000 0.1294
	 * -280.6 5000 0.3300 -278.4 10000 0.7133 -278.5
	 * ------------------------------------------------------------------------
	 * The following procedures are used : factorize : factor the transformation
	 * length. transTableSetup : setup table with sofar-, actual-, and
	 * remainRadix. permute : permutation allows in-place calculations.
	 * twiddleTransf : twiddle multiplications and DFT's for one stage. initTrig
	 * : initialise sine/cosine table. fft_4 : length 4 DFT, a la Nussbaumer.
	 * fft_5 : length 5 DFT, a la Nussbaumer. fft_10 : length 10 DFT using prime
	 * factor FFT. fft_odd : length n DFT, n odd.
	 * ***********************************************************************
	 */

	private int				maxPrimeFactor;
	private int				maxPrimeFactorDiv2;
	private int				maxFactorCount	= 20;

	private int				n, nFactor;

	private final double	c3_1			= -1.5000000000000E+00; // c3_1 =
																	// cos(2*Math.pi/3)-1;
	private final double	c3_2			= 8.6602540378444E-01;	// c3_2 =
																	// sin(2*Math.pi/3);

	private final double	u5				= 1.2566370614359E+00;	// u5 =
																	// 2*Math.pi/5;
	private final double	c5_1			= -1.2500000000000E+00; // c5_1 =
																	// (cos(u5)+cos(2*u5))/2-1;
	private final double	c5_2			= 5.5901699437495E-01;	// c5_2 =
																	// (cos(u5)-cos(2*u5))/2;
	private final double	c5_3			= -9.5105651629515E-01; // c5_3 =
																	// -sin(u5);
	private final double	c5_4			= -1.5388417685876E+00; // c5_4 =
																	// -(sin(u5)+sin(2*u5));
	private final double	c5_5			= 3.6327126400268E-01;	// c5_5 =
																	// (sin(u5)-sin(2*u5));
	private final double	c8				= 7.0710678118655E-01;	// c8 =
																	// 1/sqrt(2);

	// private double Math.pi;
	private int				groupOffset, dataOffset, blockOffset, adr;
	private int				groupNo, dataNo, blockNo, twNo;
	private double			omega, tw_re, tw_im;
	private double[]		twiddleRe;
	private double[]		twiddleIm;
	private double[]		trigRe;
	private double[]		trigIm;
	private double[]		zRe;
	private double[]		zIm;
	private double[]		vRe;
	private double[]		vIm;
	private double[]		wRe;
	private double[]		wIm;
	private int[]			sofarRadix;
	private int[]			actualRadix;
	private int[]			remainRadix;

	/**
*
*/
	private void factorize(int fact[], int num) {
		int i, j, k;
		int nRadix;
		int[] radices = new int[7];
		int[] factors = new int[maxFactorCount];

		nRadix = 6;
		radices[1] = 2;
		radices[2] = 3;
		radices[3] = 4;
		radices[4] = 5;
		radices[5] = 8;
		radices[6] = 10;

		if (num == 1) {
			j = 1;
			factors[1] = 1;
		}
		else
			j = 0;
		i = nRadix;
		while ((num > 1) && (i > 0)) {
			if ((num % radices[i]) == 0) {
				num = num / radices[i];
				j = j + 1;
				factors[j] = radices[i];
			}
			else
				i = i - 1;
		}
		if (factors[j] == 2) /* substitute factors 2*8 with 4*4 */
		{
			i = j - 1;
			while ((i > 0) && (factors[i] != 8))
				i--;
			if (i > 0) {
				factors[j] = 4;
				factors[i] = 4;
			}
		}
		if (num > 1) {
			for (k = 2; k < Math.sqrt(num) + 1; k++)
				while ((num % k) == 0) {
					num = num / k;
					j = j + 1;
					factors[j] = k;
				}
			if (num > 1) {
				j = j + 1;
				factors[j] = num;
			}
		}
		for (i = 1; i <= j; i++) {
			fact[i] = factors[j - i + 1];
		}
		nFactor = j;
	}

	/**
	 * After N is factored the parameters that control the stages are generated.
	 * 
	 * @param sofar
	 *            the product of the radices so far.
	 * @param actual
	 *            : the radix handled in this stage.
	 * @param remain
	 *            : the product of the remaining radices.
	 */
	final private void transTableSetup(int sofar[], int actual[], int remain[]) {
		int i;

		factorize(actual, n);

		if (actual[1] > maxPrimeFactor) {
			System.out.println("\nPrime factor of FFT length too large : %6d" + actual[1]);
			System.out.println("\nPlease modify the value of maxPrimeFactor in mixfft.c");
		}

		remain[0] = n;
		sofar[1] = 1;
		remain[1] = n / actual[1];
		for (i = 2; i <= nFactor; i++) {
			sofar[i] = sofar[i - 1] * actual[i - 1];
			remain[i] = remain[i - 1] / actual[i];
		}
	}

	/**
	 * The sequence y is the permuted input sequence x so that the following
	 * transformations can be performed in-place, and the final result is the
	 * normal degree.
	 */

	final private void permute(int fact[], int remain[], double xRe[], double xIm[], double yRe[], double yIm[])

	{
		int i, j = 0, k;
		int[] count = new int[maxFactorCount];

		for (i = 1; i <= nFactor; i++)
			count[i] = 0;
		k = 0;

		for (i = 0; i <= n - 2; i++) {
			yRe[i] = xRe[k];
			yIm[i] = xIm[k];
			j = 1;
			k = k + remain[j];
			count[1] = count[1] + 1;
			while (count[j] >= fact[j]) {
				count[j] = 0;
				k = k - remain[j - 1] + remain[j + 1];
				j = j + 1;
				count[j] = count[j] + 1;
			}
		}
		yRe[n - 1] = xRe[n - 1];
		yIm[n - 1] = xIm[n - 1];
	}

	/*
	 * Twiddle factor multiplications and transformations are performed on a
	 * group of data. The number of multiplications with 1 are reduced by
	 * skipMath.ping the twiddle multiplication of the first stage and of the
	 * first group of the following stages.
	 */
	final private void initTrig(final int radix) {
		int i;
		double w, xre, xim;

		w = 2 * Math.PI / radix;
		trigRe[0] = 1;
		trigIm[0] = 0;
		xre = Math.cos(w);
		xim = -Math.sin(w);
		trigRe[1] = xre;
		trigIm[1] = xim;
		for (i = 2; i < radix; i++) {
			trigRe[i] = xre * trigRe[i - 1] - xim * trigIm[i - 1];
			trigIm[i] = xim * trigRe[i - 1] + xre * trigIm[i - 1];
		}
	}

	/**
*
*/
	private void fft_4(double aRe[], double aIm[]) {
		double t1_re, t1_im, t2_re, t2_im;
		double m2_re, m2_im, m3_re, m3_im;

		t1_re = aRe[0] + aRe[2];
		t1_im = aIm[0] + aIm[2];
		t2_re = aRe[1] + aRe[3];
		t2_im = aIm[1] + aIm[3];

		m2_re = aRe[0] - aRe[2];
		m2_im = aIm[0] - aIm[2];
		m3_re = aIm[1] - aIm[3];
		m3_im = aRe[3] - aRe[1];

		aRe[0] = t1_re + t2_re;
		aIm[0] = t1_im + t2_im;
		aRe[2] = t1_re - t2_re;
		aIm[2] = t1_im - t2_im;
		aRe[1] = m2_re + m3_re;
		aIm[1] = m2_im + m3_im;
		aRe[3] = m2_re - m3_re;
		aIm[3] = m2_im - m3_im;
	}

	/**
*
*/
	private void fft_5(double aRe[], double aIm[]) {
		double t1_re, t1_im, t2_re, t2_im, t3_re, t3_im;
		double t4_re, t4_im, t5_re, t5_im;
		double m2_re, m2_im, m3_re, m3_im, m4_re, m4_im;
		double m1_re, m1_im, m5_re, m5_im;
		double s1_re, s1_im, s2_re, s2_im, s3_re, s3_im;
		double s4_re, s4_im, s5_re, s5_im;

		t1_re = aRe[1] + aRe[4];
		t1_im = aIm[1] + aIm[4];
		t2_re = aRe[2] + aRe[3];
		t2_im = aIm[2] + aIm[3];
		t3_re = aRe[1] - aRe[4];
		t3_im = aIm[1] - aIm[4];
		t4_re = aRe[3] - aRe[2];
		t4_im = aIm[3] - aIm[2];
		t5_re = t1_re + t2_re;
		t5_im = t1_im + t2_im;
		aRe[0] = aRe[0] + t5_re;
		aIm[0] = aIm[0] + t5_im;
		m1_re = c5_1 * t5_re;
		m1_im = c5_1 * t5_im;
		m2_re = c5_2 * (t1_re - t2_re);
		m2_im = c5_2 * (t1_im - t2_im);

		m3_re = -c5_3 * (t3_im + t4_im);
		m3_im = c5_3 * (t3_re + t4_re);
		m4_re = -c5_4 * t4_im;
		m4_im = c5_4 * t4_re;
		m5_re = -c5_5 * t3_im;
		m5_im = c5_5 * t3_re;

		s3_re = m3_re - m4_re;
		s3_im = m3_im - m4_im;
		s5_re = m3_re + m5_re;
		s5_im = m3_im + m5_im;
		s1_re = aRe[0] + m1_re;
		s1_im = aIm[0] + m1_im;
		s2_re = s1_re + m2_re;
		s2_im = s1_im + m2_im;
		s4_re = s1_re - m2_re;
		s4_im = s1_im - m2_im;

		aRe[1] = s2_re + s3_re;
		aIm[1] = s2_im + s3_im;
		aRe[2] = s4_re + s5_re;
		aIm[2] = s4_im + s5_im;
		aRe[3] = s4_re - s5_re;
		aIm[3] = s4_im - s5_im;
		aRe[4] = s2_re - s3_re;
		aIm[4] = s2_im - s3_im;
	}

	/**
*
*/
	private void fft_8() {
		double[] aRe = new double[4];
		double[] aIm = new double[4];
		double[] bRe = new double[4];
		double[] bIm = new double[4];
		double gem;

		aRe[0] = zRe[0];
		bRe[0] = zRe[1];
		aRe[1] = zRe[2];
		bRe[1] = zRe[3];
		aRe[2] = zRe[4];
		bRe[2] = zRe[5];
		aRe[3] = zRe[6];
		bRe[3] = zRe[7];

		aIm[0] = zIm[0];
		bIm[0] = zIm[1];
		aIm[1] = zIm[2];
		bIm[1] = zIm[3];
		aIm[2] = zIm[4];
		bIm[2] = zIm[5];
		aIm[3] = zIm[6];
		bIm[3] = zIm[7];

		fft_4(aRe, aIm);
		fft_4(bRe, bIm);

		gem = c8 * (bRe[1] + bIm[1]);
		bIm[1] = c8 * (bIm[1] - bRe[1]);
		bRe[1] = gem;
		gem = bIm[2];
		bIm[2] = -bRe[2];
		bRe[2] = gem;
		gem = c8 * (bIm[3] - bRe[3]);
		bIm[3] = -c8 * (bRe[3] + bIm[3]);
		bRe[3] = gem;

		zRe[0] = aRe[0] + bRe[0];
		zRe[4] = aRe[0] - bRe[0];
		zRe[1] = aRe[1] + bRe[1];
		zRe[5] = aRe[1] - bRe[1];
		zRe[2] = aRe[2] + bRe[2];
		zRe[6] = aRe[2] - bRe[2];
		zRe[3] = aRe[3] + bRe[3];
		zRe[7] = aRe[3] - bRe[3];

		zIm[0] = aIm[0] + bIm[0];
		zIm[4] = aIm[0] - bIm[0];
		zIm[1] = aIm[1] + bIm[1];
		zIm[5] = aIm[1] - bIm[1];
		zIm[2] = aIm[2] + bIm[2];
		zIm[6] = aIm[2] - bIm[2];
		zIm[3] = aIm[3] + bIm[3];
		zIm[7] = aIm[3] - bIm[3];
	}

	/**
*
*/
	private void fft_10() {
		double[] aRe = new double[5];
		double[] aIm = new double[5];
		double[] bRe = new double[5];
		double[] bIm = new double[5];

		aRe[0] = zRe[0];
		bRe[0] = zRe[5];
		aRe[1] = zRe[2];
		bRe[1] = zRe[7];
		aRe[2] = zRe[4];
		bRe[2] = zRe[9];
		aRe[3] = zRe[6];
		bRe[3] = zRe[1];
		aRe[4] = zRe[8];
		bRe[4] = zRe[3];

		aIm[0] = zIm[0];
		bIm[0] = zIm[5];
		aIm[1] = zIm[2];
		bIm[1] = zIm[7];
		aIm[2] = zIm[4];
		bIm[2] = zIm[9];
		aIm[3] = zIm[6];
		bIm[3] = zIm[1];
		aIm[4] = zIm[8];
		bIm[4] = zIm[3];

		fft_5(aRe, aIm);
		fft_5(bRe, bIm);

		zRe[0] = aRe[0] + bRe[0];
		zRe[5] = aRe[0] - bRe[0];
		zRe[6] = aRe[1] + bRe[1];
		zRe[1] = aRe[1] - bRe[1];
		zRe[2] = aRe[2] + bRe[2];
		zRe[7] = aRe[2] - bRe[2];
		zRe[8] = aRe[3] + bRe[3];
		zRe[3] = aRe[3] - bRe[3];
		zRe[4] = aRe[4] + bRe[4];
		zRe[9] = aRe[4] - bRe[4];

		zIm[0] = aIm[0] + bIm[0];
		zIm[5] = aIm[0] - bIm[0];
		zIm[6] = aIm[1] + bIm[1];
		zIm[1] = aIm[1] - bIm[1];
		zIm[2] = aIm[2] + bIm[2];
		zIm[7] = aIm[2] - bIm[2];
		zIm[8] = aIm[3] + bIm[3];
		zIm[3] = aIm[3] - bIm[3];
		zIm[4] = aIm[4] + bIm[4];
		zIm[9] = aIm[4] - bIm[4];
	}

	/**
*
*/
	private void fft_odd(int radix) {
		double rere, reim, imre, imim;
		int i, j, k, p, max;

		p = radix;
		max = (p + 1) / 2;
		for (j = 1; j < max; j++) {
			vRe[j] = zRe[j] + zRe[p - j];
			vIm[j] = zIm[j] - zIm[p - j];
			wRe[j] = zRe[j] - zRe[p - j];
			wIm[j] = zIm[j] + zIm[p - j];
		}

		for (j = 1; j < max; j++) {
			zRe[j] = zRe[0];
			zIm[j] = zIm[0];
			zRe[p - j] = zRe[0];
			zIm[p - j] = zIm[0];
			k = j;
			for (i = 1; i < max; i++) {
				rere = trigRe[k] * vRe[i];
				imim = trigIm[k] * vIm[i];
				reim = trigRe[k] * wIm[i];
				imre = trigIm[k] * wRe[i];

				zRe[p - j] += rere + imim;
				zIm[p - j] += reim - imre;
				zRe[j] += rere - imim;
				zIm[j] += reim + imre;

				k = k + j;
				if (k >= p)
					k = k - p;
			}
		}
		for (j = 1; j < max; j++) {
			zRe[0] = zRe[0] + vRe[j];
			zIm[0] = zIm[0] + wIm[j];
		}
	}

	/**
*
*/
	final private void twiddleTransf(int sofarRadix, int radix, int remainRadix, double yRe[], double yIm[])

	{
		double cosw, sinw, gem;
		double t1_re, t1_im, t2_re, t2_im, t3_re, t3_im;
		double t4_re, t4_im, t5_re, t5_im;
		double m2_re, m2_im, m3_re, m3_im, m4_re, m4_im;
		double m1_re, m1_im, m5_re, m5_im;
		double s1_re, s1_im, s2_re, s2_im, s3_re, s3_im;
		double s4_re, s4_im, s5_re, s5_im;

		initTrig(radix);
		omega = 2 * Math.PI / (double) (sofarRadix * radix);
		cosw = Math.cos(omega);
		sinw = -Math.sin(omega);
		tw_re = 1.0;
		tw_im = 0;
		dataOffset = 0;
		groupOffset = dataOffset;
		adr = groupOffset;
		for (dataNo = 0; dataNo < sofarRadix; dataNo++) {
			if (sofarRadix > 1) {
				twiddleRe[0] = 1.0;
				twiddleIm[0] = 0.0;
				twiddleRe[1] = tw_re;
				twiddleIm[1] = tw_im;
				for (twNo = 2; twNo < radix; twNo++) {
					twiddleRe[twNo] = tw_re * twiddleRe[twNo - 1] - tw_im * twiddleIm[twNo - 1];
					twiddleIm[twNo] = tw_im * twiddleRe[twNo - 1] + tw_re * twiddleIm[twNo - 1];
				}
				gem = cosw * tw_re - sinw * tw_im;
				tw_im = sinw * tw_re + cosw * tw_im;
				tw_re = gem;
			}
			for (groupNo = 0; groupNo < remainRadix; groupNo++) {
				if ((sofarRadix > 1) && (dataNo > 0)) {
					zRe[0] = yRe[adr];
					zIm[0] = yIm[adr];
					blockNo = 1;
					do {
						adr = adr + sofarRadix;
						zRe[blockNo] = twiddleRe[blockNo] * yRe[adr] - twiddleIm[blockNo] * yIm[adr];
						zIm[blockNo] = twiddleRe[blockNo] * yIm[adr] + twiddleIm[blockNo] * yRe[adr];
						blockNo++;
					}
					while (blockNo < radix);
				}
				else
					for (blockNo = 0; blockNo < radix; blockNo++) {
						zRe[blockNo] = yRe[adr];
						zIm[blockNo] = yIm[adr];
						adr = adr + sofarRadix;
					}
				switch (radix) {
				case 2:
					gem = zRe[0] + zRe[1];
					zRe[1] = zRe[0] - zRe[1];
					zRe[0] = gem;
					gem = zIm[0] + zIm[1];
					zIm[1] = zIm[0] - zIm[1];
					zIm[0] = gem;
					break;
				case 3:
					t1_re = zRe[1] + zRe[2];
					t1_im = zIm[1] + zIm[2];
					zRe[0] = zRe[0] + t1_re;
					zIm[0] = zIm[0] + t1_im;
					m1_re = c3_1 * t1_re;
					m1_im = c3_1 * t1_im;
					m2_re = c3_2 * (zIm[1] - zIm[2]);
					m2_im = c3_2 * (zRe[2] - zRe[1]);
					s1_re = zRe[0] + m1_re;
					s1_im = zIm[0] + m1_im;
					zRe[1] = s1_re + m2_re;
					zIm[1] = s1_im + m2_im;
					zRe[2] = s1_re - m2_re;
					zIm[2] = s1_im - m2_im;
					break;
				case 4:
					t1_re = zRe[0] + zRe[2];
					t1_im = zIm[0] + zIm[2];
					t2_re = zRe[1] + zRe[3];
					t2_im = zIm[1] + zIm[3];

					m2_re = zRe[0] - zRe[2];
					m2_im = zIm[0] - zIm[2];
					m3_re = zIm[1] - zIm[3];
					m3_im = zRe[3] - zRe[1];

					zRe[0] = t1_re + t2_re;
					zIm[0] = t1_im + t2_im;
					zRe[2] = t1_re - t2_re;
					zIm[2] = t1_im - t2_im;
					zRe[1] = m2_re + m3_re;
					zIm[1] = m2_im + m3_im;
					zRe[3] = m2_re - m3_re;
					zIm[3] = m2_im - m3_im;
					break;
				case 5:
					t1_re = zRe[1] + zRe[4];
					t1_im = zIm[1] + zIm[4];
					t2_re = zRe[2] + zRe[3];
					t2_im = zIm[2] + zIm[3];
					t3_re = zRe[1] - zRe[4];
					t3_im = zIm[1] - zIm[4];
					t4_re = zRe[3] - zRe[2];
					t4_im = zIm[3] - zIm[2];
					t5_re = t1_re + t2_re;
					t5_im = t1_im + t2_im;
					zRe[0] = zRe[0] + t5_re;
					zIm[0] = zIm[0] + t5_im;
					m1_re = c5_1 * t5_re;
					m1_im = c5_1 * t5_im;
					m2_re = c5_2 * (t1_re - t2_re);
					m2_im = c5_2 * (t1_im - t2_im);

					m3_re = -c5_3 * (t3_im + t4_im);
					m3_im = c5_3 * (t3_re + t4_re);
					m4_re = -c5_4 * t4_im;
					m4_im = c5_4 * t4_re;
					m5_re = -c5_5 * t3_im;
					m5_im = c5_5 * t3_re;

					s3_re = m3_re - m4_re;
					s3_im = m3_im - m4_im;
					s5_re = m3_re + m5_re;
					s5_im = m3_im + m5_im;
					s1_re = zRe[0] + m1_re;
					s1_im = zIm[0] + m1_im;
					s2_re = s1_re + m2_re;
					s2_im = s1_im + m2_im;
					s4_re = s1_re - m2_re;
					s4_im = s1_im - m2_im;

					zRe[1] = s2_re + s3_re;
					zIm[1] = s2_im + s3_im;
					zRe[2] = s4_re + s5_re;
					zIm[2] = s4_im + s5_im;
					zRe[3] = s4_re - s5_re;
					zIm[3] = s4_im - s5_im;
					zRe[4] = s2_re - s3_re;
					zIm[4] = s2_im - s3_im;
					break;
				case 8:
					fft_8();
					break;
				case 10:
					fft_10();
					break;
				default:
					fft_odd(radix);
					break;
				}
				adr = groupOffset;
				for (blockNo = 0; blockNo < radix; blockNo++) {
					yRe[adr] = zRe[blockNo];
					yIm[adr] = zIm[blockNo];
					adr = adr + sofarRadix;
				}
				groupOffset = groupOffset + sofarRadix * radix;
				adr = groupOffset;
			}
			dataOffset = dataOffset + 1;
			groupOffset = dataOffset;
			adr = groupOffset;
		}
	}

	/*
	 * Perform the FFT for all sizes of signal.
	 */
	private void doFFT_Mix(double xRe[], double xIm[], final int size) {
		// int[] sofarRadix = new int[maxFactorCount];
		// int[] actualRadix = new int[maxFactorCount];
		// int[] remainRadix = new int[maxFactorCount];
		int count;

		// Mod

		// Math.pi = 4*Math.atan(1);
		n = size;
		transTableSetup(sofarRadix, actualRadix, remainRadix);
		permute(actualRadix, remainRadix, xRe, xIm, yReOut, yImOut);

		for (count = 1; count <= nFactor; count++)
			twiddleTransf(sofarRadix[count], actualRadix[count], remainRadix[count], yReOut, yImOut);
		// Copy results
		for (int i = 0; i < n; i++) {
			xRe[i] = yReOut[i];
			xIm[i] = yImOut[i];
		}

	}

	/*
	 * Perform the FFT for all sizes of signal, the start and the length of the
	 * FFT can be choosen to transform a part of a signal.
	 */
	private void doFFT_Mix(double xRe[], double xIm[], final int size, final int shift) {
		double[] tmp_xRe = new double[size];
		double[] tmp_xIm = new double[size];

		for (int i = 0; i < size; i++) {
			tmp_xRe[i] = xRe[i + shift];
			tmp_xIm[i] = xIm[i + shift];
		}

		doFFT_Mix(tmp_xRe, tmp_xIm, size);
		for (int i = 0; i < size; i++) {
			xRe[i + shift] = tmp_xRe[i];
			xIm[i + shift] = tmp_xIm[i];
		}
	}

	/*
	 * Perform the IFFT for all sizes of signal.
	 */
	private void doIFFT_Mix(double xRe[], double xIm[], final int size) {
		for (int i = 0; i < size; i++) {
			xIm[i] = -xIm[i];
		}

		doFFT_Mix(xRe, xIm, size);

		for (int i = 0; i < size; i++) {
			xRe[i] = xRe[i] / size;
			xIm[i] = -xIm[i] / size;
		}
	}

	/*
	 * Perform the IFFT for all sizes of signal, the start and the length of the
	 * IFFT can be choosen to transform a part of a signal.
	 */
	private void doIFFT_Mix(double xRe[], double xIm[], final int size, final int shift) {
		double[] tmp_xRe = new double[size];
		double[] tmp_xIm = new double[size];

		for (int i = 0; i < size; i++) {
			tmp_xRe[i] = xRe[i + shift];
			tmp_xIm[i] = xIm[i + shift];
		}
		doIFFT_Mix(tmp_xRe, tmp_xIm, size);
		for (int i = 0; i < size; i++) {
			xRe[i + shift] = tmp_xRe[i];
			xIm[i + shift] = tmp_xIm[i];
		}
	}

} // end of class

\ No newline at end of file
diff --git a/src/bilib/src/polyharmonicwavelets/GammaFunction.java b/src/bilib/src/polyharmonicwavelets/GammaFunction.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa2ec7e6e75d228cb259cb69a78813b1618b9324
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/GammaFunction.java
@@ -0,0 +1,208 @@
+
+//package dr.math;
+
+package polyharmonicwavelets;
+
+/**
+ * (This file is part of BEAST) Computes the Gamma function.
+ * <p>
+ * Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut
+ * <p>
+ * This file is part of BEAST. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership and licensing.
+ * <p>
+ * BEAST is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any
+ * later version.
+ * <p>
+ * BEAST 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 Lesser General Public License for more
+ * details.
+ * <p>
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with BEAST; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA<br>
+ * 
+ * @author Korbinian Strimmer
+ * 
+ */
+public class GammaFunction {
+	//
+	// Public stuff
+	//
+
+	// Gamma function
+
+	/**
+	 * log Gamma function: ln(gamma(alpha)) for alpha>0, accurate to 10 decimal
+	 * places
+	 * 
+	 * @param alpha
+	 *            argument
+	 * @return the log of the gamma function of the given alpha
+	 */
+	public static double lnGamma(double alpha) {
+		// Pike MC & Hill ID (1966) Algorithm 291: Logarithm of the gamma
+		// function.
+		// Communications of the Association for Computing Machinery, 9:684
+
+		double x = alpha, f = 0.0, z;
+
+		if (x < 7) {
+			f = 1;
+			z = x - 1;
+			while (++z < 7) {
+				f *= z;
+			}
+			x = z;
+			f = -Math.log(f);
+		}
+		z = 1 / (x * x);
+
+		return f + (x - 0.5) * Math.log(x) - x + 0.918938533204673 + (((-0.000595238095238 * z + 0.000793650793651) * z - 0.002777777777778) * z + 0.083333333333333) / x;
+	}
+
+	/**
+	 * Incomplete Gamma function Q(a,x) (a cleanroom implementation of Numerical
+	 * Recipes gammq(a,x); in Mathematica this function is called
+	 * GammaRegularized)
+	 * 
+	 * @param a
+	 *            parameter
+	 * @param x
+	 *            argument
+	 * @return function value
+	 */
+	public static double incompleteGammaQ(double a, double x) {
+		return 1.0 - incompleteGamma(x, a, lnGamma(a));
+	}
+
+	/**
+	 * Incomplete Gamma function P(a,x) = 1-Q(a,x) (a cleanroom implementation
+	 * of Numerical Recipes gammp(a,x); in Mathematica this function is
+	 * 1-GammaRegularized)
+	 * 
+	 * @param a
+	 *            parameter
+	 * @param x
+	 *            argument
+	 * @return function value
+	 */
+	public static double incompleteGammaP(double a, double x) {
+		return incompleteGamma(x, a, lnGamma(a));
+	}
+
+	/**
+	 * Incomplete Gamma function P(a,x) = 1-Q(a,x) (a cleanroom implementation
+	 * of Numerical Recipes gammp(a,x); in Mathematica this function is
+	 * 1-GammaRegularized)
+	 * 
+	 * @param a
+	 *            parameter
+	 * @param x
+	 *            argument
+	 * @param lnGammaA
+	 *            precomputed lnGamma(a)
+	 * @return function value
+	 */
+	public static double incompleteGammaP(double a, double x, double lnGammaA) {
+		return incompleteGamma(x, a, lnGammaA);
+	}
+
+	/**
+	 * Returns the incomplete gamma ratio I(x,alpha) where x is the upper limit
+	 * of the integration and alpha is the shape parameter.
+	 * 
+	 * @param x
+	 *            upper limit of integration
+	 * @param alpha
+	 *            shape parameter
+	 * @param ln_gamma_alpha
+	 *            the log gamma function for alpha
+	 * @return the incomplete gamma ratio
+	 */
+	private static double incompleteGamma(double x, double alpha, double ln_gamma_alpha) {
+		// (1) series expansion if (alpha>x || x<=1)
+		// (2) continued fraction otherwise
+		// RATNEST FORTRAN by
+		// Bhattacharjee GP (1970) The incomplete gamma integral. Applied
+		// Statistics,
+		// 19: 285-287 (AS32)
+
+		double accurate = 1e-8, overflow = 1e30;
+		double factor, gin, rn, a, b, an, dif, term;
+		double pn0, pn1, pn2, pn3, pn4, pn5;
+
+		if (x == 0.0) {
+			return 0.0;
+		}
+		// System.out.println("x="+x+" alpha="+alpha);
+		if (x < 0.0 || alpha <= 0.0) {
+			throw new IllegalArgumentException("Arguments out of bounds");
+		}
+
+		factor = Math.exp(alpha * Math.log(x) - x - ln_gamma_alpha);
+
+		if (x > 1 && x >= alpha) {
+			// continued fraction
+			a = 1 - alpha;
+			b = a + x + 1;
+			term = 0;
+			pn0 = 1;
+			pn1 = x;
+			pn2 = x + 1;
+			pn3 = x * b;
+			gin = pn2 / pn3;
+
+			do {
+				a++;
+				b += 2;
+				term++;
+				an = a * term;
+				pn4 = b * pn2 - an * pn0;
+				pn5 = b * pn3 - an * pn1;
+
+				if (pn5 != 0) {
+					rn = pn4 / pn5;
+					dif = Math.abs(gin - rn);
+					if (dif <= accurate) {
+						if (dif <= accurate * rn) {
+							break;
+						}
+					}
+
+					gin = rn;
+				}
+				pn0 = pn2;
+				pn1 = pn3;
+				pn2 = pn4;
+				pn3 = pn5;
+				if (Math.abs(pn4) >= overflow) {
+					pn0 /= overflow;
+					pn1 /= overflow;
+					pn2 /= overflow;
+					pn3 /= overflow;
+				}
+			}
+			while (true);
+			gin = 1 - factor * gin;
+		}
+		else {
+			// series expansion
+			gin = 1;
+			term = 1;
+			rn = alpha;
+			do {
+				rn++;
+				term *= x / rn;
+				gin += term;
+			}
+			while (term > accurate);
+			gin *= factor / alpha;
+		}
+		return gin;
+	}
+
+}
diff --git a/src/bilib/src/polyharmonicwavelets/Parameters.java b/src/bilib/src/polyharmonicwavelets/Parameters.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ee099fd5a7f535f82ac411700496c4be2bfea9a
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/Parameters.java
@@ -0,0 +1,139 @@
+package polyharmonicwavelets;
+
+//
+//  Parameters.java
+//  PolyharmonicWavelets
+//
+//  Created by Biomedical Imaging Group on 2/13/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+/**
+ * This class stores all the parameters neded to perform the wavelet transform.
+ */
+
+public class Parameters {
+	/**
+	 * If true, only compute the analyses filters.
+	 */
+	public boolean			analysesonly	= false;
+	/**
+	 * If rieszfreq=1 analysis wavelet filter will be multiplied by V2. Used for
+	 * Riesz transform. No need to change this, it is not a user input.
+	 */
+	public int				rieszfreq		= 0;
+	/**
+	 * Constant that defines the polyharmonic B-spline flavor.
+	 */
+	public final static int	BSPLINE			= 0;
+	/**
+	 * Constant that defines the orthogonal flavor, quincunx only.
+	 */
+	public final static int	ORTHOGONAL		= 1;
+	/**
+	 * Constant that defines the dual of the B-spline.
+	 */
+	public final static int	DUAL			= 2;
+	/**
+	 * Constant that defines the operator wavelet.
+	 */
+	public final static int	OPERATOR		= 3;
+	/**
+	 * Constant that defines the Marr wavelet.
+	 */
+	public final static int	MARR			= 7;
+	/**
+	 * Constant that defines the dual of the operator.
+	 */
+	public final static int	DUALOPERATOR	= 8;
+	/**
+	 * Defines the wavelet flavor: BSPLINE, ORTHOGONAL, DUAL, OPERATOR, MARR or
+	 * DUALOPERATOR.
+	 */
+	public int				flavor			= ORTHOGONAL;
+
+	/**
+	 * Constant that defines the basis transform.
+	 */
+	public final static int	BASIS			= 0;
+	/**
+	 * Constant that defines the fully redundant transform.
+	 */
+	public final static int	REDUNDANT		= 1;
+	/**
+	 * Constant that defines the pyramid transform.
+	 */
+	public final static int	PYRAMID			= 2;
+	/**
+	 * The redundancy, should be set to BASIS, PYRAMID, or REDUNDANT.
+	 */
+	public int				redundancy		= PYRAMID;
+	/**
+	 * Constant that defines standard isotropic polyharmonic Bspline.
+	 */
+	public final static int	ISOTROPIC		= 1;
+	/**
+	 * Constant that defines isotropic polyharmonic Bspline that allowes to
+	 * change standard deviation.
+	 */
+	public final static int	CHANGESIGMA		= 4;
+	/**
+	 * Te isotropy type.
+	 */
+	public int				type			= ISOTROPIC;
+	/**
+	 * Defines the standard deviation of gaussian if type=changesigma
+	 */
+	public double			s2				= 6.0;			// only used if
+															// type=changesigma
+	/**
+	 * Constant that defines the quincunx lattice.
+	 */
+	public final static int	QUINCUNX		= 0;
+	/**
+	 * Constant that defines the dyadic lattice.
+	 */
+	public final static int	DYADIC			= 1;
+	/**
+	 * The lattice type, set to QUINCUNX or DYADIC.
+	 */
+	public int				lattice			= DYADIC;
+
+	/**
+	 * This parameter defines whether the prefilter should be used (if true) or
+	 * not (if false).
+	 */
+	public boolean			prefilter		= true;
+
+	/**
+	 * Constant that defines the iterative method for computing the
+	 * autocorrelation.
+	 */
+	public final static int	ITERATIVE		= 0;
+	/**
+	 * Constant that defines the gamma function method for computing the
+	 * autocorrelation.
+	 */
+	public final static int	GAMMA			= 1;
+	/**
+	 * This parameter defines the autocorrelation computation method, GAMMA or
+	 * ITERATIVE.
+	 */
+	public int				accompute		= GAMMA;
+
+	/**
+	 * The B-spline order, gamma.
+	 */
+	public double			order			= 2.0;
+
+	/**
+	 * The iterate of a rotation covariant operator. Usually 0 for polyharmonic
+	 * wavelets or 1 for Marr wavelets, but other values are also possible.
+	 */
+	public int				N				= 1;
+
+	/**
+	 * The number of wavelet decomposition levels.
+	 */
+	public int				J				= 1;
+}
diff --git a/src/bilib/src/polyharmonicwavelets/QuincunxFilters.java b/src/bilib/src/polyharmonicwavelets/QuincunxFilters.java
new file mode 100644
index 0000000000000000000000000000000000000000..3992040c40e1b09042b86bdbc2b5433a6d251be5
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/QuincunxFilters.java
@@ -0,0 +1,505 @@
+package polyharmonicwavelets;
+
+//
+//  QuincunxFilters.java
+//  PolyharmonicWavelets
+//
+//  Created by Biomedical Imaging Group on 2/13/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+import java.util.*;
+import ij.*;
+import java.text.DecimalFormat;
+
+/**
+ * This class computes the filters for the quincunx wavelet transform.
+ * 
+ * @author Katarina Balac, EPFL.
+ */
+
+public class QuincunxFilters {
+
+	/**
+	 * The analysis filters. FA[0] lowpass analysis filter for odd iteration
+	 * FA[1] highpass analysis filter for odd iteration FA[2] lowpass analysis
+	 * filter for even iteration FA[3] highpass analysis filter for even
+	 * iteration
+	 */
+	public ComplexImage[]	FA;
+
+	/**
+	 * The synthesis filters. FS[0] lowpass synthesis filter for odd iteration
+	 * FS[1] highpass synthesis filter for odd iteration FS[2] lowpass synthesis
+	 * filter for even iteration FS[3] highpass synthesis filter for even
+	 * iteration
+	 */
+	public ComplexImage[]	FS;
+
+	/**
+	 * The prefilter.
+	 */
+	public ComplexImage		P;						// Prefilter
+	private Parameters		param;					// Parameters for the
+													// transform
+	private int				nx;					// Size of filters
+	private int				ny;
+	private final double	PI2	= 2.0 * Math.PI;
+	private final double	PI	= Math.PI;
+
+	/**
+	 * Constructor, creates a QuincunxFilters objectand computes all the
+	 * filters.
+	 * 
+	 * @param par
+	 *            the wavelet transform parameters
+	 * @param sizex
+	 *            the number of columns in the image to transform
+	 * @param sizey
+	 *            the number of rows in the image to transform
+	 */
+
+	public QuincunxFilters(Parameters par, int sizex, int sizey) {
+		param = par;
+		nx = sizex;
+		ny = sizey;
+		FA = new ComplexImage[4];
+		FS = new ComplexImage[4];
+		calculateFilters();
+	}
+
+	/*
+	 * Performs linear interpolation on the real part of ComplexImage ac as
+	 * ac[w]=ac[Dw], where D is the quincunx subsampling matrix and w is a two
+	 * element column vector with w1 and w2 uniformly distributed from 0 to
+	 * 2*PI, and ac[w1+2*k*PI,w2+2*n*PI]=ac[w1,w2] for all k and n integer.
+	 */
+
+	private ComplexImage interpolateQuincunxReal(ComplexImage ac) {
+		ComplexImage out = new ComplexImage(nx, ny, ac.imag == null);
+		int nx1 = nx - 1;
+		int ny1 = ny - 1;
+		for (int cy = 0; cy < ny; cy++) {
+			for (int cx = 0; cx < nx; cx++) {
+				// calculate (x+y)mod(2pi),(y-x)mod(2pi)
+				double x = ((double) cx / (double) nx) * PI2;
+				double y = ((double) cy / (double) ny) * PI2;
+				double sum = x + y;
+				double dif = y - x;
+				// Find closest integers to x,y
+				double x1 = sum * (double) nx / PI2;
+				double y1 = dif * (double) ny / PI2;
+				double fx = Math.floor(x1);
+				double fy = Math.floor(y1);
+				x1 = x1 - fx;
+				y1 = y1 - fy;
+				int kx = (int) fx;
+				int ky = (int) fy;
+				while (ky > ny1)
+					ky -= ny;
+				while (ky < 0)
+					ky += ny;
+				while (kx > nx1)
+					kx -= nx;
+				while (kx < 0)
+					kx += nx;
+				int ky1 = ky + 1;
+				while (ky1 > ny1)
+					ky1 -= ny;
+				int kx1 = kx + 1;
+				while (kx1 > nx1)
+					kx1 -= nx;
+				double a = ac.real[nx * ky1 + kx1];
+				double b = ac.real[nx * ky1 + kx];
+				double c = ac.real[nx * ky + kx];
+				double d = ac.real[nx * ky + kx1];
+				double res = y1 * (x1 * a + (1.0 - x1) * b) + (1.0 - y1) * (x1 * d + (1.0 - x1) * c);
+				out.real[cy * nx + cx] = res;
+			}
+		}
+		return out;
+	}
+
+	/*
+	 * Returns the numenator of scaling function, localization support is [ minx
+	 * : (maxx-minx)/sizex : maxx-(maxx-minx)/sizex, miny : (maxy-miny)/sizey :
+	 * maxy-(maxy-miny)/sizey ] output is of size [sizex, sizey] and defined
+	 * on[minx...maxx-eps,miny...maxy-eps] type=0, dyadic type=1, quincunx,
+	 * computes V(D^tw)
+	 */
+
+	private ComplexImage localization(int sizex, int sizey, double minx, double miny, double maxx, double maxy, double gama, int N, int type) {
+		ComplexImage result = new ComplexImage(sizex, sizey, N == 0);
+		double gama2 = gama / 2.0;
+		double epsx = PI2 / (5.0 * (double) sizex);
+		double epsy = PI2 / (5.0 * (double) sizey);
+		final double d83 = 8.0 / 3.0;
+		final double d23 = 2.0 / 3.0;
+		for (int ky = 0; ky < sizey; ky++) {
+			int kxy = ky * sizex;
+			double rx = (maxx - minx) / (double) sizex;
+			double ry = (maxy - miny) / (double) sizey;
+			for (int kx = 0, index = kxy; kx < sizex; kx++, index++) {
+				double y = miny + (double) ky * ry;
+				double x = minx + (double) kx * rx;
+				if (type == 1) { // quincunx
+					double xt = x;
+					double yt = y;
+					x = xt + yt;
+					y = yt - xt;
+				}
+				double y1 = y;
+				while (y1 >= Math.PI - epsy)
+					y1 = y1 - PI2;
+				while (y1 < -Math.PI - epsy)
+					y1 = y1 + PI2;
+				double x1 = x;
+				while (x1 >= Math.PI - epsx)
+					x1 = x1 - PI2;
+				while (x1 < -Math.PI - epsx)
+					x1 = x1 + PI2;
+				double a = 1.0;
+				// Compute modulus of localization depending on type
+				double sx = Math.sin(x / 2);
+				sx = sx * sx;
+				double sy = Math.sin(y / 2);
+				sy = sy * sy;
+				if (param.type == param.ISOTROPIC) { // Isotropic
+					a = 4.0 * (sx + sy) - d83 * (sx * sy);
+				}
+				if (param.type == param.CHANGESIGMA) {
+					final double sigma2 = param.s2;
+					final double b = -16.0 / sigma2;
+					final double c = 24.0 / (sigma2 * sigma2) - 16.0 / (3.0 * sigma2);
+					final double d = 8.0 / (sigma2 * sigma2) + 32.0 / 45.0 - 16.0 / (3.0 * sigma2);
+					final double e = 4.0 / 3.0 - 8.0 / sigma2;
+					a = 4.0 * (sx + sy) + b * (sx * sy) + c * (sx * sx * sy + sy * sy * sx) + d * (sx * sx * sx + sy * sy * sy) + e * (sx * sx + sy * sy);
+				}
+				double re = Math.pow(a, gama2);
+				double im = 0.0;
+				if (N > 0) {
+					for (int i = 0; i < N; i++) {
+						double re1 = re * x1 - im * y1;
+						double im1 = re * y1 + im * x1;
+						re = re1;
+						im = im1;
+					}
+					double t = Math.pow(x1 * x1 + y1 * y1, (double) N / 2.0);
+					if (t == 0.0) {
+						result.real[index] = 0.0;
+						result.imag[index] = 0.0;
+					}
+					else {
+						result.real[index] = re / t;
+						result.imag[index] = im / t;
+					}
+				}
+				else {
+					result.real[index] = re;
+				}
+			}
+		}
+		return result;
+	}
+
+	/*
+	 * Calculates denominator of scaling function support is [ 0 : maxx/sizex :
+	 * maxx-maxx/sizex, 0 : maxy/sizey : maxy-maxy/sizey ] output is of size
+	 * [sizex, sizey] and defined on[0...maxx-eps,0...maxy-eps]
+	 */
+
+	private ComplexImage denominator(int sizex, int sizey, double minx, double miny, double maxx, double maxy, int N) {
+		ComplexImage result = new ComplexImage(sizex, sizey);
+		double gamaN2;
+		gamaN2 = (param.order - N) / 2.0;
+		for (int ky = 0; ky < sizey; ky++) {
+			int kxy = ky * sizex;
+			double y = miny + (double) ky * (maxy - miny) / (double) sizey;
+			for (int kx = 0, index = kxy; kx < sizex; kx++, index++) {
+				double x = minx + (double) kx * (maxx - minx) / (double) sizex;
+				double re = Math.pow(x * x + y * y, gamaN2);
+				double im = 0.0;
+				if (N > 0) {
+					for (int i = 0; i < N; i++) {
+						double re1 = re * x - im * y;
+						double im1 = re * y + im * x;
+						re = re1;
+						im = im1;
+					}
+					result.real[index] = re;
+					result.imag[index] = im;
+				}
+				else {
+					result.real[index] = re;
+				}
+			}
+		}
+		return result;
+	}
+
+	/*
+	 * Computes prefilter
+	 */
+
+	private void quincunxPrefilter(ComplexImage ac) {
+		P = new ComplexImage(nx, ny, true);
+		P.settoConstant(1.0);
+		P = localization(nx, ny, -PI, -PI, PI, PI, param.order, 0, 0);
+		ComplexImage d = denominator(nx, ny, -PI, -PI, PI, PI, 0);
+		P.divide(d, 1.0, 0.0);
+		P.shift();
+		if (param.flavor == param.ORTHOGONAL) {
+			ComplexImage acsqrt = ac.copyImage();
+			acsqrt.rootReal();
+			P.divide(acsqrt);
+		}
+		if ((param.flavor == param.BSPLINE) || (param.flavor == param.DUALOPERATOR)) {
+			P.divide(ac);
+		}
+	}
+
+	/*
+	 * Computesall the filters for odd iteration B-refinement filter for the
+	 * quincunx lattice ortho-orthonormalizing factor ac - autocorrelation acD -
+	 * sampled autocorrelation it-number of iteration if it=0 computes filters
+	 * for odd iteration, if it=1 for even iteration H - analysis highpass H1 -
+	 * synthesis highpass L1 - analysis lowpass L - synthesis lowpass if even,
+	 * even iteration, else, odd iteration
+	 */
+
+	private void computeLowpassHighpass(ComplexImage B, ComplexImage ac, ComplexImage acD, ComplexImage loc, boolean even) {
+		ComplexImage L = null;
+		ComplexImage L1 = null;
+		ComplexImage H = null;
+		ComplexImage H1 = null;
+		ComplexImage ortho = acD.copyImage();
+		ortho.divide(ac);
+		final double sqrt2 = Math.sqrt(2.0);
+		B.multiply(sqrt2);
+		if (param.flavor == param.ORTHOGONAL) {
+			ComplexImage orthot = ortho;
+			orthot.rootReal();
+			L1 = B;
+			L1.divide(orthot);
+			H = L1.copyImage();
+			if (even) {
+				H.shift();
+			}
+			else {
+				H.shiftY();
+			}
+			if (!param.analysesonly) {
+				L = L1.copyImage();
+				H1 = H.copyImage();
+				H1.conj();
+			}
+			L1.conj();
+		}
+		if (param.flavor == param.DUAL) {
+			L1 = B.copyImage();
+			if (even) {
+				ac.shift();
+				B.shift();
+			}
+			else {
+				ac.shiftY();
+				B.shiftY();
+			}
+			H = B.copyImage();
+			H.conj();
+			H.multiply(ac);
+			if (!param.analysesonly) {
+				L = L1.copyImage();
+				L.divide(ortho);
+				L.conj();
+				H1 = B;
+				H1.divide(acD);
+			}
+		}
+		if (param.flavor == param.BSPLINE) {
+			L1 = B.copyImage();
+			L1.divide(ortho);
+			L1.conj();
+			if (!param.analysesonly) {
+				L = B.copyImage();
+				H = B.copyImage();
+				H.divide(acD);
+			}
+			if (even) {
+				B.shift();
+			}
+			else {
+				B.shiftY();
+			}
+			H1 = B;
+			H1.conj();
+			ac.shift();
+			H1.multiply(ac);
+		}
+		if (param.flavor == param.OPERATOR) {
+			L1 = B.copyImage();
+			ComplexImage ac0 = ac.copyImage();
+			ComplexImage loc0 = loc;
+			loc0.multiply(sqrt2);
+			if (even) {
+				ac.shift();
+				B.shift();
+			}
+			else {
+				ac.shiftY();
+				B.shiftY();
+			}
+			if (!param.analysesonly) {
+				L = L1.copyImage();
+				L.divide(ortho);
+				L.conj();
+				H1 = B;
+				H1.squareModulus();
+				H1.multiply(ac);
+				H1.multiply(ac0);
+				H1.divide(loc0, 0.0, 0.0);
+				H1.divide(acD, 0.0, 0.0);
+				H1.conj();
+			}
+			H = loc0;
+			H.conj();
+			H.divide(ac0);
+		}
+		if (param.flavor == param.DUALOPERATOR) {
+			L1 = B.copyImage();
+			if (!param.analysesonly) {
+				L = L1.copyImage();
+				L.conj();
+				H1 = loc.copyImage();
+				H1.multiply(sqrt2);
+				H1.divide(ac);
+			}
+			L1.divide(ortho);
+			ComplexImage ac0 = ac.copyImage();
+			ComplexImage loc0 = loc;
+			if (even) {
+				ac.shift();
+				ortho.shift();
+				B.shift();
+			}
+			else {
+				ac.shiftY();
+				ortho.shiftY();
+				B.shiftY();
+			}
+			H = B;
+			H.squareModulus();
+			H.multiply(1.0 / Math.sqrt(2.0));
+			H.multiply(ac);
+			H.multiply(ac0);
+			H.divide(loc0, 0.0, 0.0);
+			H.divide(acD, 0.0, 0.0);
+		}
+		if (param.flavor == param.MARR) {
+			loc.multiply(sqrt2);
+			L1 = B.copyImage();
+			ComplexImage ac0 = ac.copyImage();
+			ComplexImage loc0 = loc;
+			if (even) {
+				ac.shift();
+				B.shift();
+			}
+			else {
+				ac.shiftY();
+				B.shiftY();
+			}
+			if (!param.analysesonly) {
+				H1 = B;
+				H1.squareModulus();
+				H1.multiply(ac);
+				H1.multiply(ac0);
+				H1.divide(loc0, 0.0, 0.0);
+				H1.divide(acD);
+				H1.conj();
+				L = L1.copyImage();
+				L.divide(ortho);
+				L.conj();
+			}
+			H = loc0;
+			H.conj();
+		}
+		if (even) {
+			if (param.redundancy == param.BASIS) {
+				H.modulateMinusY();
+				if (!param.analysesonly) {
+					H1.modulatePlusY();
+				}
+			}
+			if ((param.redundancy == param.PYRAMID) && (!param.analysesonly)) {
+				H1.modulatePlusY();
+			}
+			FA[1] = H;
+			FS[1] = H1;
+			FA[0] = L1;
+			FS[0] = L;
+		}
+		else {
+			if (param.redundancy == param.BASIS) {
+				H.modulateMinusQuincunx();
+				if (!param.analysesonly) {
+					H1.modulatePlusQuincunx();
+				}
+			}
+			if ((param.redundancy == param.PYRAMID) && (!param.analysesonly)) {
+				H1.modulatePlusQuincunx();
+			}
+			FA[3] = H;
+			FS[3] = H1;
+			FA[2] = L1;
+			FS[2] = L;
+		}
+	}
+
+	/**
+	 * Computes all filters needed for the quincunx transform.
+	 */
+
+	public void calculateFilters() {
+		ComplexImage ac = null;
+		ComplexImage L = null;
+		ComplexImage LD = localization(nx, ny, 0.0, 0.0, PI2, PI2, param.order, param.N, 1);
+		double c = Math.pow(0.5, param.order / 2.0);
+		if (param.accompute == param.ITERATIVE) {
+			int nx2 = 2 * nx;
+			int ny2 = 2 * ny;
+			ComplexImage Ldouble = localization(2 * nx, 2 * ny, 0.0, 0.0, PI2, PI2, param.order, param.N, 1);
+			L = Ldouble.copyImage();
+			L.decimateCrop();
+			ComplexImage H = localization(2 * nx, 2 * ny, 0.0, 0.0, 2.0 * PI2, 2.0 * PI2, param.order, param.N, 0);
+			H.multiply(c * c);
+			H.divide(Ldouble, 1.0, 0.0);
+			ac = H;
+			ac.squareModulus();
+			ac = Autocorrelation.autocorrIterative(ac);
+		}
+		else {
+			L = localization(nx, ny, 0.0, 0.0, PI2, PI2, param.order, param.N, 0);
+			ComplexImage simpleloc = localization(nx, ny, 0.0, 0.0, PI2, PI2, 2 * param.order, 0, 0);
+			ac = Autocorrelation.autocorrGamma(simpleloc, param.order);
+		}
+		ComplexImage B = LD.copyImage();
+		B.multiply(c);
+		B.divide(L, Math.cos(0.25 * PI * (double) param.N), -Math.sin(0.25 * PI * (double) param.N));
+		// Interpolation to calculate acD
+		ComplexImage acD = interpolateQuincunxReal(ac);
+		ComplexImage loc = L;
+		quincunxPrefilter(ac);
+		ComplexImage ac0 = ac.copyImage();
+		ComplexImage loc0 = loc.copyImage();
+		computeLowpassHighpass(B, ac, acD, loc, true);
+		B = loc0;
+		B.decimate();
+		B.multiply(c);
+		B.divide(LD, Math.cos(0.25 * PI * (double) param.N), -Math.sin(0.25 * PI * (double) param.N));
+		loc = LD;
+		ac = acD;
+		acD = ac0;
+		acD.decimate();
+		computeLowpassHighpass(B, ac, acD, loc, false);
+	}
+}
diff --git a/src/bilib/src/polyharmonicwavelets/QuincunxTransform.java b/src/bilib/src/polyharmonicwavelets/QuincunxTransform.java
new file mode 100644
index 0000000000000000000000000000000000000000..f97d3eaaa3ed2be3d12f0e9f80add68b64f0b118
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/QuincunxTransform.java
@@ -0,0 +1,561 @@
+package polyharmonicwavelets;
+
+//
+//  QuincunxTransform.java
+//  PolyharmonicWavelets
+//
+//  Created by Biomedical Imaging Group on 2/13/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+import java.util.*;
+import ij.*;
+import java.text.DecimalFormat;
+import ij.text.*;
+import ij.process.*;
+import ij.plugin.filter.PlugInFilter;
+
+/**
+ * This class performs basis, pyramid and redundant quincunx transform.
+ * 
+ * @author Katarina Balac, EPFL.
+ */
+
+public class QuincunxTransform {
+
+	// Analysis and synthesis filters
+	// FA[0]=H1
+	// FA[1]=G1
+	// FA[2]=H1D
+	// FA[3]=G1D
+	// similar for analysis filters
+	// H - lowpass filters
+	// G - highpass filters
+	// H1,G1 - filters for odd iteration
+	// H1D, G1D - filters for even iteration
+	// if transform is pyramid, filters for pyramid transform are FP:
+	// FP[0]=Gls
+	// FP[1]=GlsD
+
+	private Parameters		param;
+	private ComplexImage[]	FA;
+	private ComplexImage[]	FS;
+	private ComplexImage[]	FP;
+	private ComplexImage	P;
+	private int				J;							// number of iterations
+	private final double	PI2		= 2.0 * Math.PI;
+	private final double	sqrt2	= Math.sqrt(2.0);
+
+	/**
+	 * Creates a QuincunxTransform object.
+	 * 
+	 * @param filt
+	 *            the filters used for the transform
+	 * @param par
+	 *            the transform parameters
+	 */
+
+	public QuincunxTransform(QuincunxFilters filt, Parameters par) {
+		J = par.J;
+		param = par;
+		FA = new ComplexImage[4];
+		FS = new ComplexImage[4];
+		for (int i = 0; i < 4; i++) {
+			FA[i] = filt.FA[i];
+			FS[i] = filt.FS[i];
+		}
+		P = filt.P.copyImage();
+	}
+
+	/**
+	 * Performs the in-place nonredundant quincunx analysis on the input
+	 * ComplexImage.
+	 * 
+	 * @param image
+	 *            the image to transform
+	 */
+
+	public void quincunxAnalysis(ComplexImage image) {
+		ComplexImage H1 = FA[0]; // Filters for odd iteration
+		ComplexImage G1 = FA[1];
+		H1.multiply(0.5);
+		G1.multiply(0.5);
+		ComplexImage H1Dl = FA[2].getSubimage(0, FA[2].nx / 2 - 1, 0, FA[2].ny - 1); // Filters
+																						// for
+																						// even
+																						// iteration
+		ComplexImage G1Dl = FA[3].getSubimage(0, FA[3].nx / 2 - 1, 0, FA[3].ny - 1);
+		H1Dl.multiply(0.5);
+		G1Dl.multiply(0.5);
+		ComplexImage Y1 = null; // lowpass subband
+		ComplexImage Y2 = null; // highpass subband
+		ComplexImage R = image.copyImage(); // remaining lowpass subband
+		R.FFT2D();
+		if (param.prefilter) {
+			R.multiply(P);
+		}
+		int l = 1;
+		for (int j = 1; j <= J; j++) {
+			int mj = j % 2;
+			if (mj == 1) { // odd iteration
+				// filtering: Y1=R*H1 Y2=R*G1
+				Y1 = R.copyImage();
+				Y1.multiply(H1, l);
+				Y2 = R.copyImage();
+				Y2.multiply(G1, l);
+				Y1.quincunxDownUp();
+				if (!(j == J)) { // not last itteration, leave only left half of
+									// Y1
+					Y1 = Y1.getSubimage(0, Y1.nx / 2 - 1, 0, Y1.ny - 1);
+				}
+				Y2.quincunxDownUp();
+				// Transform highpass subband Y2 and put it in image
+				Y2.iFFT2D();
+				Y2.fold();
+				image.putSubimage(Y2.nx, 0, Y2);
+			}
+			else { // even iteration
+				Y1 = R.copyImage();
+				Y1.multiply(H1Dl, l);
+				Y2 = R.copyImage();
+				Y2.multiply(G1Dl, l);
+				// Downsampling
+				Y1.dyadicDownY();
+				Y2.dyadicDownY();
+				// Transform highpass subband Y2 and put it in image
+				Y2.iFFT2D();
+				image.putSubimage(0, Y2.ny, Y2);
+				l *= 2;
+			}
+			R = Y1;
+		}
+		// insert lowpass subband
+		Y1.iFFT2D();
+		if (J % 2 == 1) {
+			Y1.fold();
+		}
+		H1.multiply(2.0);
+		G1.multiply(2.0);
+		image.putSubimage(0, 0, Y1);
+	}
+
+	/**
+	 * Performs the in-place nonredundant quincunx synthesis of image.
+	 * 
+	 * @param image
+	 *            the image to transform
+	 */
+
+	public void quincunxSynthesis(ComplexImage image) {
+		ComplexImage H1 = FS[0];
+		ComplexImage G1 = FS[1];
+		ComplexImage H1D = FS[2];
+		ComplexImage G1D = FS[3];
+		int p = (J - J % 2) / 2;
+		int l = 1;
+		for (int k = 0; k < p; k++, l *= 2)
+			;
+		// Size of lowpass subband
+		int cy = image.ny / l;
+		int cx = image.nx / l;
+		if (J % 2 == 1) {
+			cx = cx / 2;
+		}
+		// Get lowpasssubband
+		ComplexImage Y1 = image.getSubimage(0, cx - 1, 0, cy - 1);
+		if (J % 2 == 1) {
+			Y1.unfold();
+		}
+		Y1.FFT2D();
+		for (int j = J; j > 0; j--) {
+			int mj = j % 2;
+			if (mj == 1) { // odd iteration
+				// Get highpass
+				ComplexImage Y2 = image.getSubimage(cx, 2 * cx - 1, 0, cy - 1);
+				cx *= 2;
+				Y2.unfold();
+				Y2.FFT2D();
+				// filtering
+				Y1.multiplyCircular(H1, l);
+				Y2.multiplyCircular(G1, l);
+				Y1.add(Y2);
+			}
+			else { // even iteration
+				// Get highpass
+				ComplexImage Y2 = image.getSubimage(0, cx - 1, cy, 2 * cy - 1);
+				cy *= 2;
+				Y2.FFT2D();
+				l /= 2;
+				Y1.dyadicUpsample();
+				Y2.dyadicUpsample();
+				// filtering
+				Y1.multiplyCircular(H1D, l);
+				Y2.multiplyCircular(G1D, l);
+				Y1.add(Y2);
+			}
+		}
+		image.real = Y1.real;
+		image.imag = Y1.imag;
+		if (param.prefilter) {
+			image.divide(P, 1.0, 0.0);
+		}
+		image.iFFT2D();
+	}
+
+	/**
+	 * Returns the fully redundant quincunx analysis of image.
+	 * 
+	 * @param image
+	 *            the image to transform
+	 * @return the array of quincunx transform subbands
+	 */
+
+	public ComplexImage[] quincunxAnalysisRedundant(ComplexImage image) {
+		ComplexImage H = FA[0]; // Lowpass odd iteration filter
+		ComplexImage G = FA[1]; // Highpass odd iteration filter
+		ComplexImage HD = FA[2]; // Lowpass even iteration filter
+		ComplexImage GD = FA[3]; // Highpass even iteration filter
+		G.multiply(sqrt2);
+		GD.multiply(sqrt2);
+		H.multiply(sqrt2);
+		HD.multiply(sqrt2);
+		double sqrt2inv = 1.0 / sqrt2;
+		ComplexImage[] array = new ComplexImage[J + 1];
+		int l = 1;
+		// Next index in array
+		ComplexImage R = image.copyImage();
+		R.FFT2D();
+		if (param.prefilter) {
+			R.multiply(P);
+		}
+		for (int j = 1; j < J + 1; j++) {
+			int k = j - 1;
+			array[k] = R.copyImage();
+			if (j % 2 == 1) { // odd iteration
+				array[k].multiplyCircular(G, l); // Multiply with highpass odd
+													// iteration filter
+				R.multiplyCircular(H, l); // Multiply with lowpass odd iteration
+											// filter
+			}
+			else {
+				array[k].multiplyCircular(GD, l); // Multiply with highpass even
+													// iteration filter
+				R.multiplyCircular(HD, l); // Multiply with lowpass even
+											// iteration filter
+				l *= 2;
+			}
+			array[k].multiply(sqrt2inv);
+			R.multiply(sqrt2inv);
+			array[k].iFFT2D();
+		}
+		array[J] = R.copyImage();
+		array[J].iFFT2D();
+		G.multiply(1.0 / sqrt2);
+		GD.multiply(1.0 / sqrt2);
+		H.multiply(1.0 / sqrt2);
+		HD.multiply(1.0 / sqrt2);
+		return array;
+	}
+
+	/**
+	 * Returns the fully redundant quincunx synthesis of array.
+	 * 
+	 * @param array
+	 *            subbands of fully redundant quincunx transform
+	 * @return the result of synthesis
+	 */
+
+	public ComplexImage quincunxSynthesisRedundant(ComplexImage[] array) {
+		ComplexImage H = FS[0]; // Lowpass odd iteration filter
+		ComplexImage G = FS[1]; // Highpass odd iteration filter
+		ComplexImage HD = FS[2]; // Lowpass even iteration filter
+		ComplexImage GD = FS[3]; // Highpass even iteration filter
+		G.multiply(1.0 / sqrt2);
+		GD.multiply(1.0 / sqrt2);
+		H.multiply(1.0 / sqrt2);
+		HD.multiply(1.0 / sqrt2);
+		double sqrt2inv = 1.0 / Math.sqrt(2.0);
+		ComplexImage LP = array[J];
+		ComplexImage HP;
+		LP.FFT2D();
+		int l = 1;
+		for (int j = 1; j < (J + 1) / 2; j++, l *= 2)
+			;
+		for (int j = J; j > 0; j--) {
+			HP = array[j - 1];
+			HP.FFT2D();
+			if (j % 2 == 1) { // odd iteration
+				HP.multiplyCircular(G, l); // Multiply with highpass odd
+											// iteration filter
+				LP.multiplyCircular(H, l); // Multiply with lowpass odd
+											// iteration filter
+				l /= 2;
+			}
+			else {
+				HP.multiplyCircular(GD, l); // Multiply with highpass even
+											// iteration filter
+				LP.multiplyCircular(HD, l); // Multiply with lowpass even
+											// iteration filter
+			}
+			LP.add(HP);
+			LP.multiply(sqrt2inv);
+		}
+		if (param.prefilter) {
+			LP.divide(P, 1.0, 0.0);
+		}
+		LP.iFFT2D();
+		G.multiply(sqrt2);
+		GD.multiply(sqrt2);
+		H.multiply(sqrt2);
+		HD.multiply(sqrt2);
+		return LP;
+	}
+
+	/**
+	 * Returns the result of pyramid quincunx analysis of ComplexImage.
+	 * 
+	 * @param image
+	 *            the image to transform
+	 * @return the array of quincunx transform subbands
+	 */
+
+	public ComplexImage[] quincunxAnalysisPyramid(ComplexImage image) {
+		ComplexImage[] array = new ComplexImage[J + 1];
+		ComplexImage H1 = FA[0].copyImage(); // Filters for odd iteration
+		H1.multiply(0.5);
+		ComplexImage H1Dl = FA[2].getSubimage(0, FA[2].nx / 2 - 1, 0, FA[2].ny - 1); // Filters
+																						// for
+																						// even
+																						// iteration
+		H1Dl.multiply(0.5);
+		ComplexImage G1 = FA[1].copyImage();
+		ComplexImage G1D = FA[3].copyImage();
+		ComplexImage Y2 = null; // highpass subband
+		ComplexImage Y1 = image.copyImage(); // remaining lowpass subband
+		Y1.FFT2D();
+		if (param.prefilter) {
+			Y1.multiply(P);
+		}
+		int l = 1;
+		for (int j = 1; j <= J; j++) {
+			int mj = j % 2;
+			Y2 = Y1.copyImage();
+			if (mj == 1) { // odd iteration
+				Y2.multiply(G1, l);
+				Y1.multiply(H1, l);
+				Y1.quincunxDownUp();
+				// Put Y2 in stack
+				Y2.iFFT2D();
+				array[j - 1] = Y2;
+			}
+			else { // even iteration
+				// Y1=left half of Y1*left half of H1D
+				Y2.multiply(G1D, l);
+				Y2.iFFT2D();
+				Y2 = Y2.rotate(0);
+				array[j - 1] = Y2;
+				Y1 = Y1.getSubimage(0, Y1.nx / 2 - 1, 0, Y1.ny - 1);
+				Y1.multiply(H1Dl, l);
+				Y1.dyadicDownY();
+				l *= 2;
+			}
+		}
+		// insert lowpass subband
+		Y1.iFFT2D();
+		if (J % 2 == 1) {
+			Y1 = Y1.rotate(0.0);
+		}
+		array[J] = Y1;
+		return array;
+	}
+
+	/**
+	 * Returns the result of pyramid quincunx synthesis of array.
+	 * 
+	 * @param array
+	 *            subbands of quincunx pyramid transform
+	 * @return the result of synthesis
+	 */
+
+	public ComplexImage quincunxSynthesisPyramid(ComplexImage[] array) {
+		// Compute Gls
+		ComplexImage Ge = FS[1].copyImage();
+		Ge.multiply(FA[1]);
+		ComplexImage Gemodsqr = Ge.copyImage();
+		Gemodsqr.squareModulus();
+		Gemodsqr.quincunxDownUp();
+		ComplexImage Gls = Ge;
+		Gls.conj();
+		Gls.divide(Gemodsqr, 1.0, 0.0);
+		// Compute GlsD
+		ComplexImage GeD = FS[3].copyImage();
+		GeD.multiply(FA[3]);
+		Gemodsqr = GeD.copyImage();
+		Gemodsqr.squareModulus();
+		Gemodsqr.downUpY();
+		ComplexImage GlsD = GeD.copyImage();
+		GlsD.conj();
+		GlsD.divide(Gemodsqr, 1.0, 0.0);
+		// do synthesis
+		// Filters for odd iteration
+		ComplexImage H = FS[0];
+		ComplexImage H1 = FA[1];
+		ComplexImage G = FS[1];
+		// Filters for even iteration
+		ComplexImage HD = FS[2];
+		ComplexImage H1D = FA[3];
+		ComplexImage GD = FS[3];
+		ComplexImage HP = new ComplexImage(array[1].nx, array[1].ny);
+		ComplexImage LP = new ComplexImage(array[1].nx, array[0].ny);
+		ComplexImage LP1 = new ComplexImage(array[1].nx, array[1].ny);
+		// Get lowpass
+		LP.copyImageContent(array[J]);
+		if (J % 2 == 1) {
+			LP.unrotate(array[J - 1].nx, array[J - 1].ny);
+		}
+		LP.FFT2D();
+		int l = 1;
+		for (int i = 0, J12 = (J - 1) / 2; i < J12; i++, l *= 2)
+			;
+		int mj = J % 2;
+		for (int j = J; j > 0; j--) {
+			HP.copyImageContent(array[j - 1]);
+			if (mj == 0) { // even iteration
+				HP.unrotate(array[j - 2].nx, array[j - 2].ny);
+				HP.FFT2D();
+				LP.dyadicUpsample();
+				LP.multiply(HD, l);
+				LP1.copyImageContent(LP);
+				LP1.multiply(H1D, l);
+				HP.subtract(LP1);
+				HP.multiply(GlsD, l);
+				HP.downUpY();
+				HP.multiplyCircular(GD, l);
+				LP.add(HP);
+			}
+			else { // odd iteration
+				HP.FFT2D();
+				LP.multiplyCircular(H, l);
+				LP1.copyImageContent(LP);
+				LP1.multiply(H1, l);
+				HP.subtract(LP1);
+				HP.multiply(Gls, l);
+				HP.quincunxDownUp();
+				HP.multiply(G, l);
+				LP.add(HP);
+				l /= 2;
+			}
+			mj = 1 - mj;
+		}
+		if (param.prefilter) {
+			LP.divide(P, 1.0, 0.0);
+		}
+		LP.iFFT2D();
+		return LP;
+	}
+
+	/**
+	 * Prepares the quincunx pyramid transform coeffitients for being displayed.
+	 * Rescales the subbands for visualisation and puts all subbands in one
+	 * ComplexImage.
+	 * 
+	 * @param array
+	 *            subbands of quincunx pyramid transform
+	 * @param back
+	 *            background color
+	 * @param rescale
+	 *            if rescale=false there is no rescaling
+	 * @param lp
+	 *            if lp=false the lowpass subband is not displayed
+	 * @return the image to display
+	 */
+
+	public ComplexImage displayPyramid(ComplexImage[] array, double back, boolean rescale, boolean lp) {
+		int nx = array[0].nx;
+		int ny = array[0].ny;
+		int s = (nx + ny) / 2;
+		int l;
+		if (nx > ny) {
+			l = (nx + ny) / 2;
+		}
+		else {
+			l = ny;
+		}
+		ComplexImage display = new ComplexImage(nx + s, 2 * l);
+		display.settoConstant(back, back);
+		int x = 0;
+		int y = 0;
+		for (int j = 0; j < J; j++) {
+			ComplexImage temp = array[j].copyImage();
+			if (rescale) {
+				temp.stretch();
+			}
+			if (j % 2 == 1) { // even iteration
+				temp.unrotate(array[j - 1].nx, array[j - 1].ny);
+				temp = temp.rotate(back);
+			}
+
+			temp.frame(back);
+			display.putSubimage(x, y, temp);
+			if ((j == J - 1) && (J % 2 == 0)) {
+				y += l;
+				x = nx - array[J].nx / 2;
+			}
+			else {
+				if (x == nx) {
+					y += l;
+					l /= 2;
+					x = nx - array[j + 1].nx;
+				}
+				else {
+					x = nx;
+				}
+			}
+		}
+		if (lp) {
+			ComplexImage temp = array[J].copyImage();
+			if (J % 2 == 1) { // even iteration
+				temp.unrotate(array[J - 1].nx, array[J - 1].ny);
+				temp = temp.rotate(back);
+			}
+			temp.stretch();
+			temp.frame(back);
+			display.putSubimage(x, y, temp);
+		}
+		return display;
+	}
+
+	/**
+	 * Prepares a nonredundant quincunx transform for being displayed, stretches
+	 * each subband.
+	 * 
+	 * @param image
+	 *            the basis transform coefficients
+	 * @return the image with rescaled subbands to display
+	 */
+
+	public ComplexImage displayBasis(ComplexImage image) {
+		int dx = image.nx;
+		int dy = image.ny;
+		ComplexImage out = new ComplexImage(dx, dy);
+		ComplexImage sub;
+		for (int j = 1; j <= J; j++) {
+			if (j % 2 == 1) { // Odd iteration
+				sub = image.getSubimage(dx / 2, dx - 1, 0, dy - 1);
+				sub.stretch();
+				out.putSubimage(dx / 2, 0, sub);
+				dx /= 2;
+			}
+			else {
+				sub = image.getSubimage(0, dx - 1, dy / 2, dy - 1);
+				sub.stretch();
+				out.putSubimage(0, dy / 2, sub);
+				dy /= 2;
+			}
+		}
+		sub = image.getSubimage(0, dx - 1, 0, dy - 1);
+		sub.stretch();
+		out.putSubimage(0, 0, sub);
+		return out;
+	}
+}
\ No newline at end of file
diff --git a/src/bilib/src/polyharmonicwavelets/Riesz.java b/src/bilib/src/polyharmonicwavelets/Riesz.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc26422aaceb214056bd142deb8d425a3a7722cc
--- /dev/null
+++ b/src/bilib/src/polyharmonicwavelets/Riesz.java
@@ -0,0 +1,447 @@
+package polyharmonicwavelets;
+
+//
+//  Riesz.java
+//  
+//
+//  Created by Biomedical Imaging Group on 2/14/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+import java.util.*;
+import ij.*;
+
+/**
+ * This class computes the Riesz wavelet transform of the image as well as its
+ * monogenic transform parameters such as the local orientation, amplitude and
+ * the instantaneous frequency in each subband.
+ * 
+ * @author Katarina Balac, EPFL.
+ */
+
+public class Riesz {
+
+	/**
+	 * The image.
+	 */
+	public ComplexImage		image;
+
+	/**
+	 * The polyharmonic wavelet transform of the image.
+	 */
+	public ComplexImage[]	p;
+
+	/**
+	 * The riesz transform of p, the wavelet Riesz transform of the image.
+	 */
+	public ComplexImage[]	q;
+
+	private Parameters		param;
+
+	/**
+	 * Local wavenumber for subband j in wavenumber[j]
+	 */
+	public ComplexImage[]	wavenumber	= null;
+	/**
+	 * Local modulus for subband j in modulus[j]
+	 */
+	public ComplexImage[]	modulus		= null;
+	/**
+	 * Local phase for subband j in phase[j]
+	 */
+	public ComplexImage[]	phase		= null;
+	/**
+	 * Local orientation for subband j in orientation[j]
+	 */
+	public ComplexImage[]	orientation	= null;
+
+	private DyadicTransform	transform;
+	private DyadicFilters	filters;
+	private int				J;
+	private double			order;
+
+	/**
+	 * This variable stores all the lowpass subbands. The j-th subband is in
+	 * lowpassSubbands[j-1], j=1...J+1.
+	 */
+
+	public ComplexImage[]	lowpassSubbands;
+
+	/**
+	 * Creates a Riesz object, computes all the monogenic features desired. The
+	 * modulus is always computes.
+	 * 
+	 * @param imag
+	 *            the image
+	 * @param parameters
+	 *            the transform parameters
+	 * @param phase
+	 *            if true compute the monogenic phase
+	 * @param orientation
+	 *            if true compute the monogenic orientation
+	 * @param wavenumber
+	 *            if true compute the monogenic wavenumber
+	 */
+
+	public Riesz(ComplexImage imag, Parameters parameters, boolean phase, boolean orientation, boolean wavenumber) {
+		image = imag;
+		param = parameters;
+		param.analysesonly = true;
+		param.flavor = param.MARR;
+		param.prefilter = true;
+		param.lattice = param.DYADIC; // only gamma and J can be set from the
+										// outside
+		param.rieszfreq = 0;
+		order = param.order;
+		J = param.J;
+		param.N = 1;
+		filters = new DyadicFilters(param, image.nx, image.ny);
+		filters.calculateFilters();
+		transform = new DyadicTransform(filters, param);
+		switch (parameters.redundancy) {
+		case Parameters.PYRAMID: {
+			q = transform.dyadicAnalysesPyramid(image);
+		}
+			break;
+		case Parameters.REDUNDANT: {
+			q = transform.dyadicAnalysesRedundant(image);
+		}
+			break;
+		}
+		double[] temp1 = filters.FA[0].imag;
+		filters.FA[0].imag = null;
+		double[] temp2 = filters.FA[1].imag;
+		filters.FA[1].imag = null;
+		param.N = 0;
+		filters.setParameters(param);
+		filters.calculateFilters();
+		transform = new DyadicTransform(filters, param);
+		ComplexImage[][] plp = null;
+		switch (param.redundancy) {
+		case Parameters.PYRAMID: {
+			plp = transform.dyadicAnalysesPyramidLowpass(image);
+		}
+			break;
+		case Parameters.REDUNDANT: {
+			plp = transform.dyadicAnalysesRedundantLowpass(image);
+		}
+			break;
+		default: {
+			System.out.println("Redundancy has to be REDUNDANT or PYRAMID");
+		}
+		}
+		p = plp[0];
+		lowpassSubbands = plp[1];
+		filters.FA[0].imag = temp1;
+		filters.FA[1].imag = temp2;
+
+		computeModulus();
+
+		if (orientation) {
+			computeOrientation();
+		}
+		if (phase) {
+			computePhase();
+		}
+		if (wavenumber) {
+			computeWavenumber();
+		}
+	}
+
+	/*
+	 * Computes the local modulus and places it in modulus.
+	 */
+	private void computeModulus() {
+		modulus = new ComplexImage[J];
+		for (int j = 0; j < J; j++) {
+			modulus[j] = new ComplexImage(q[j].nx, q[j].ny, true);
+			int size = modulus[j].nxy;
+			for (int k = 0; k < size; k++) {
+				modulus[j].real[k] = Math.sqrt(p[j].real[k] * p[j].real[k] + q[j].real[k] * q[j].real[k] + q[j].imag[k] * q[j].imag[k]);
+			}
+		}
+	}
+
+	/*
+	 * Computes the local orientation and places it in orientation.
+	 */
+	private void computeOrientation() {
+		orientation = new ComplexImage[J];
+		int lx = image.nx;
+		int ly = image.ny;
+		ComplexImage modq = new ComplexImage(image.nx, image.ny);
+		for (int j = 0; j < J; j++) {
+			orientation[j] = new ComplexImage(q[j].nx, q[j].ny, true);
+			modq.nx = q[j].nx;
+			modq.ny = q[j].ny;
+			int size = orientation[j].nxy;
+			for (int k = 0; k < size; k++) {
+				double a = modulus[j].real[k] * modulus[j].real[k];
+				// Compute the weights modq to smooth
+				// modq.real[k]=(q[j].real[k]*q[j].real[k]+q[j].imag[k]*q[j].imag[k])/a;
+				orientation[j].real[k] = Math.atan2(q[j].real[k], q[j].imag[k]);
+				if (orientation[j].real[k] < 0)
+					orientation[j].real[k] += Math.PI / 2;
+				else
+					orientation[j].real[k] -= Math.PI / 2;
+				orientation[j].real[k] = -orientation[j].real[k];
+			}
+			// Added by Daniel Sage 30.05.2008
+			// Median-like on the angle when the modulus is too low
+			// orientation[j].smooth(modq);
+			/*
+			 * modq.showReal("modq "+ j); for(int k=0; k<modq.nxy; k++) { int x=
+			 * k / modq.nx; int y= k % modq.nx; if (x > 0) if (x <modq.nx-1) if
+			 * (y > 0) if (y <modq.ny-1) { double max = modq.real[k]; int kmax =
+			 * k; double min = modq.real[k]; int kmin = k; for (int u=x-1;
+			 * u<x+1; u++) for (int v=y-1; v<y+1; v++) { if
+			 * (modq.real[u+v*modq.nx] > max) { kmax = u+v*modq.nx; max =
+			 * modq.real[kmax]; } if (modq.real[u+v*modq.nx] < min) { kmin =
+			 * u+v*modq.nx; min = modq.real[kmin]; } } if (max - modq.real[k] >
+			 * 0.8) { orientation[j].real[k] = orientation[j].real[kmax]; } if
+			 * (modq.real[k]-min > 0.8) { orientation[j].real[k] =
+			 * orientation[j].real[kmin]; } } }
+			 */
+		}
+	}
+
+	/*
+	 * Computes the local phase and places it in phase.
+	 */
+	private void computePhase() {
+		phase = new ComplexImage[J];
+		for (int j = 0; j < J; j++) {
+			phase[j] = new ComplexImage(q[j].nx, q[j].ny, true);
+			int size = phase[j].nxy;
+			for (int k = 0; k < size; k++) {
+				double a = modulus[j].real[k];
+				phase[j].real[k] = Math.acos(p[j].real[k] / a);
+				if (q[j].imag[k] < 0) {
+					phase[j].real[k] *= -1.0;
+				}
+			}
+		}
+	}
+
+	/*
+	 * Computes the local wave number and places it in wavenumber.
+	 */
+	private void computeWavenumber() {
+		ComplexImage[] q1xq2y = null;
+		ComplexImage[] pxpy = null;
+		param.rieszfreq = 1;
+		param.N = 0;
+		param.order = order - 1;
+		double[] temp1 = filters.FA[0].imag;
+		filters.FA[0].imag = null;
+		double[] temp2 = filters.FA[1].imag;
+		filters.FA[1].imag = null;
+		filters.setParameters(param);
+		filters.calculateFilters();
+		transform = new DyadicTransform(filters, param);
+		if (param.redundancy == param.PYRAMID) {
+			q1xq2y = transform.dyadicAnalysesPyramid(image);
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			q1xq2y = transform.dyadicAnalysesRedundant(image);
+		}
+		param.N = 1;
+		filters.FA[0].imag = temp1;
+		filters.FA[1].imag = temp2;
+		filters.setParameters(param);
+		filters.calculateFilters();
+		transform = new DyadicTransform(filters, param);
+		if (param.redundancy == param.PYRAMID) {
+			pxpy = transform.dyadicAnalysesPyramid(image);
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			pxpy = transform.dyadicAnalysesRedundant(image);
+		}
+		filters = null;
+		wavenumber = new ComplexImage[J];
+		ComplexImage modq = new ComplexImage(image.nx, image.ny);
+		for (int j = 0; j < J; j++) {
+			wavenumber[j] = new ComplexImage(q[j].nx, q[j].ny, true);
+			modq.nx = q[j].nx;
+			modq.ny = q[j].ny;
+			int size = size = wavenumber[j].nxy;
+			for (int k = 0; k < size; k++) {
+				double a = modulus[j].real[k] * modulus[j].real[k];
+				// Compute the weights modq to smooth
+				// modq.real[k]=(q[j].real[k]*q[j].real[k]+q[j].imag[k]*q[j].imag[k])/a;
+				// wave number
+				wavenumber[j].real[k] = (p[j].real[k] * q1xq2y[j].real[k] + q[j].real[k] * pxpy[j].real[k] + q[j].imag[k] * pxpy[j].imag[k]) / a;
+			}
+			// wavenumber[j].smooth(modq);
+		}
+	}
+
+	/*
+	 * Computes the local wave number and places it in wavenumber.
+	 */
+	public ComplexImage[] computeModifiedRiesz() {
+		ComplexImage[] q1xq2y = null;
+		ComplexImage[] pxpy = null;
+		param.rieszfreq = 1;
+		param.N = 0;
+		param.order = order - 1;
+		double[] temp1 = filters.FA[0].imag;
+		filters.FA[0].imag = null;
+		double[] temp2 = filters.FA[1].imag;
+		filters.FA[1].imag = null;
+		filters.setParameters(param);
+		filters.calculateFilters();
+		transform = new DyadicTransform(filters, param);
+		if (param.redundancy == param.PYRAMID) {
+			q1xq2y = transform.dyadicAnalysesPyramid(image);
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			q1xq2y = transform.dyadicAnalysesRedundant(image);
+		}
+		param.N = 1;
+		filters.FA[0].imag = temp1;
+		filters.FA[1].imag = temp2;
+		filters.setParameters(param);
+		filters.calculateFilters();
+		transform = new DyadicTransform(filters, param);
+		if (param.redundancy == param.PYRAMID) {
+			pxpy = transform.dyadicAnalysesPyramid(image);
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			pxpy = transform.dyadicAnalysesRedundant(image);
+		}
+		return q1xq2y;
+	}
+
+	public ComplexImage[] computeModifiedWavelet() {
+		ComplexImage[] q1xq2y = null;
+		ComplexImage[] pxpy = null;
+		param.rieszfreq = 1;
+		param.N = 0;
+		param.order = order - 1;
+		double[] temp1 = filters.FA[0].imag;
+		filters.FA[0].imag = null;
+		double[] temp2 = filters.FA[1].imag;
+		filters.FA[1].imag = null;
+		filters.setParameters(param);
+		filters.calculateFilters();
+		transform = new DyadicTransform(filters, param);
+		if (param.redundancy == param.PYRAMID) {
+			q1xq2y = transform.dyadicAnalysesPyramid(image);
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			q1xq2y = transform.dyadicAnalysesRedundant(image);
+		}
+		param.N = 1;
+		filters.FA[0].imag = temp1;
+		filters.FA[1].imag = temp2;
+		filters.setParameters(param);
+		filters.calculateFilters();
+		transform = new DyadicTransform(filters, param);
+		if (param.redundancy == param.PYRAMID) {
+			pxpy = transform.dyadicAnalysesPyramid(image);
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			pxpy = transform.dyadicAnalysesRedundant(image);
+		}
+		return pxpy;
+	}
+
+	/**
+	 * Displays the local modulus on the screen.
+	 */
+
+	public void displayModulus() {
+		if (param.redundancy == param.PYRAMID) {
+			ComplexImage disp = transform.displayDyadicPyramidReal(modulus, 0.0, false, false);
+			disp.showReal("Riesz modulus");
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			ComplexImage.displayStack(modulus, "Riesz modulus");
+		}
+	}
+
+	/**
+	 * Displays the local wave number on the screen.
+	 */
+
+	public void displayWaveNumber() {
+		if (param.redundancy == param.PYRAMID) {
+			ComplexImage disp = transform.displayDyadicPyramidReal(wavenumber, 0.0, false, false);
+			disp.showReal("Riesz wave number magnitude");
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			ComplexImage.displayStack(wavenumber, "Riesz wave number magnitude");
+		}
+	}
+
+	/**
+	 * Displays the local phase on the screen.
+	 */
+
+	public void displayPhase() {
+		if (param.redundancy == param.PYRAMID) {
+			ComplexImage disp = transform.displayDyadicPyramidReal(phase, 0.0, false, false);
+			disp.showReal("Riesz phase");
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			ComplexImage.displayStack(phase, "Riesz phase");
+		}
+	}
+
+	/**
+	 * Displays the local orientation on the screen.
+	 */
+
+	public void displayOrientation() {
+		if (param.redundancy == param.PYRAMID) {
+			ComplexImage disp = transform.displayDyadicPyramidReal(orientation, 0.0, false, false);
+			disp.showReal("Riesz orientation");
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			ComplexImage.displayStack(orientation, "Riesz orientation");
+		}
+	}
+
+	/**
+	 * Displays the derivative in y direction.
+	 */
+
+	public void displayRieszY() {
+		if (param.redundancy == param.PYRAMID) {
+			ComplexImage disp = transform.displayDyadicPyramidReal(q, 0.0, false, false);
+			disp.showReal("Riesz transform y");
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			ComplexImage.displayStack(q, "Riesz transform y");
+		}
+	}
+
+	/**
+	 * Displays the derivative in x direction.
+	 */
+
+	public void displayRieszX() {
+		if (param.redundancy == param.PYRAMID) {
+			ComplexImage disp = transform.displayDyadicPyramidReal(q, 0.0, false, false);
+			disp.showImag("Riesz transform x");
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			ComplexImage.displayStackImag(q, "Riesz transform x");
+		}
+	}
+
+	/**
+	 * Displays the mother wavelet transform.
+	 */
+
+	public void displayMother() {
+		if (param.redundancy == param.PYRAMID) {
+			ComplexImage disp = transform.displayDyadicPyramidReal(p, 0.0, false, false);
+			disp.showReal("Mother transform");
+		}
+		if (param.redundancy == param.REDUNDANT) {
+			ComplexImage.displayStack(p, "Mother transform");
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/bilib/src/wavelets/ComplexWaveFilter.java b/src/bilib/src/wavelets/ComplexWaveFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4ab89a4b024ff53866086823f095a84abadb95f
--- /dev/null
+++ b/src/bilib/src/wavelets/ComplexWaveFilter.java
@@ -0,0 +1,283 @@
+package wavelets;
+
+/**
+ * This class generate the complex wavelets filter.
+ * <hr>
+ * <p>
+ * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging
+ * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne,
+ * Switzerland
+ * </p>
+ * <p>
+ * <b>Authors</b>: Jesse Berent, Daniel Sage
+ * </p>
+ * <p>
+ * <b>Reference</b>: B. Forster, D. Van De Ville, J. Berent, D. Sage, M. Unser,
+ * "<a href="http://bigwww.epfl.ch/publications/forster0404.html
+ * ">Complex Wavelets for Extended Depth-of-Field: A New Method for the Fusion of Multichannel Microscopy Images</a>,"
+ * Microscopy Research and Technique, vol. 65, no. 1-2, pp. 33-42, September
+ * 2004.
+ * </p>
+ * <p>
+ * More information: http://bigwww.epfl.ch/demo/edf/index.html
+ * </p>
+ * <p>
+ * Other relevant information are available at: http://bigwww.epfl.ch/
+ * </p>
+ * <hr>
+ * You'll be free to use this software for research purposes, but you should not
+ * redistribute it without our consent. In addition, we expect you to include a
+ * citation or acknowledgement whenever you present or publish results that are
+ * based on it.
+ */
+
+public class ComplexWaveFilter {
+
+	/**
+	 * real lowpass filter.
+	 */
+	public double	h[];
+
+	/**
+	 * real highpass filter.
+	 */
+	public double	g[];
+
+	/**
+	 * imaginary lowpass filter.
+	 */
+	public double	hi[];
+	/**
+	 * imaginary highpass filter.
+	 */
+	public double	gi[];
+
+	/**
+	 * The constructor generates the 4 filters for a giving length.
+	 * 
+	 * @param length
+	 *            length of the filter, 6, 14 or 22
+	 */
+	ComplexWaveFilter(int length) {
+		switch (length) {
+
+		case 6:
+			// Complex Daubechies
+			// Real lowpass filter
+			h = new double[6];
+			h[0] = -0.0662912607;
+			h[1] = 0.1104854346;
+			h[2] = 0.6629126074;
+			h[3] = 0.6629126074;
+			h[4] = 0.1104854346;
+			h[5] = -0.0662912607;
+
+			// Real highpass filter
+			g = new double[6];
+			g[5] = 0.0662912607;
+			g[4] = 0.1104854346;
+			g[3] = -0.6629126074;
+			g[2] = 0.6629126074;
+			g[1] = -0.1104854346;
+			g[0] = -0.0662912607;
+
+			// imaginary lowpass filter
+			hi = new double[6];
+			hi[0] = -0.0855816496;
+			hi[1] = -0.0855816496;
+			hi[2] = 0.1711632992;
+			hi[3] = 0.1711632992;
+			hi[4] = -0.0855816496;
+			hi[5] = -0.0855816496;
+
+			// Imaginary highpass filter
+			gi = new double[6];
+			gi[5] = -0.0855816496;
+			gi[4] = 0.0855816496;
+			gi[3] = 0.1711632992;
+			gi[2] = -0.1711632992;
+			gi[1] = -0.0855816496;
+			gi[0] = 0.0855816496;
+			break;
+
+		case 14:
+			// Complex Daubechies
+			// Real lowpass filter
+			h = new double[14];
+			h[0] = 0.0049120149;
+			h[1] = -0.0054111299;
+			h[2] = -0.0701089996;
+			h[3] = -0.0564377788;
+			h[4] = 0.1872348173;
+			h[5] = 0.3676385056;
+			h[6] = 0.2792793518;
+			h[7] = 0.2792793518;
+			h[8] = 0.3676385056;
+			h[9] = 0.1872348173;
+			h[10] = -0.0564377788;
+			h[11] = -0.0701089996;
+			h[12] = -0.0054111299;
+			h[13] = 0.0049120149;
+
+			// Real highpass filter
+			g = new double[14];
+			g[13] = -0.0049120149;
+			g[12] = -0.0054111299;
+			g[11] = 0.0701089996;
+			g[10] = -0.0564377788;
+			g[9] = -0.1872348173;
+			g[8] = 0.3676385056;
+			g[7] = -0.2792793518;
+			g[6] = 0.2792793518;
+			g[5] = -0.3676385056;
+			g[4] = 0.1872348173;
+			g[3] = 0.0564377788;
+			g[2] = -0.0701089996;
+			g[1] = 0.0054111299;
+			g[0] = 0.0049120149;
+
+			// imaginary lowpass filter
+			hi = new double[14];
+			hi[0] = 0.0018464710;
+			hi[1] = 0.0143947836;
+			hi[2] = 0.0079040001;
+			hi[3] = -0.1169376946;
+			hi[4] = -0.2596312614;
+			hi[5] = -0.0475928095;
+			hi[6] = 0.4000165107;
+			hi[7] = 0.4000165107;
+			hi[8] = -0.0475928095;
+			hi[9] = -0.2596312614;
+			hi[10] = -0.1169376946;
+			hi[11] = 0.0079040001;
+			hi[12] = 0.0143947836;
+			hi[13] = 0.0018464710;
+
+			// Imaginary highpass filter
+			gi = new double[14];
+			gi[13] = 0.0018464710;
+			gi[12] = -0.0143947836;
+			gi[11] = 0.0079040001;
+			gi[10] = 0.1169376946;
+			gi[9] = -0.2596312614;
+			gi[8] = 0.0475928095;
+			gi[7] = 0.4000165107;
+			gi[6] = -0.4000165107;
+			gi[5] = -0.0475928095;
+			gi[4] = 0.2596312614;
+			gi[3] = -0.1169376946;
+			gi[2] = -0.0079040001;
+			gi[1] = 0.0143947836;
+			gi[0] = -0.0018464710;
+			break;
+
+		case 22:
+			// Complex Daubechies
+			// Real lowpass filter
+			h = new double[22];
+			h[0] = -0.0002890832;
+			h[1] = -0.0000935982;
+			h[2] = 0.0059961342;
+			h[3] = 0.0122232015;
+			h[4] = -0.0243700791;
+			h[5] = -0.1092940542;
+			h[6] = -0.0918847036;
+			h[7] = 0.1540094645;
+			h[8] = 0.4014277015;
+			h[9] = 0.3153022916;
+			h[10] = 0.0440795062;
+			h[11] = 0.0440795062;
+			h[12] = 0.3153022916;
+			h[13] = 0.4014277015;
+			h[14] = 0.1540094645;
+			h[15] = -0.0918847036;
+			h[16] = -0.1092940542;
+			h[17] = -0.0243700791;
+			h[18] = 0.0122232015;
+			h[19] = 0.0059961342;
+			h[20] = -0.0000935982;
+			h[21] = -0.0002890832;
+
+			// Real highpass filter
+			g = new double[22];
+			g[21] = 0.0002890832;
+			g[20] = -0.0000935982;
+			g[19] = -0.0059961342;
+			g[18] = 0.0122232015;
+			g[17] = 0.0243700791;
+			g[16] = -0.1092940542;
+			g[15] = 0.0918847036;
+			g[14] = 0.1540094645;
+			g[13] = -0.4014277015;
+			g[12] = 0.3153022916;
+			g[11] = -0.0440795062;
+			g[10] = 0.0440795062;
+			g[9] = -0.3153022916;
+			g[8] = 0.4014277015;
+			g[7] = -0.1540094645;
+			g[6] = -0.0918847036;
+			g[5] = 0.1092940542;
+			g[4] = -0.0243700791;
+			g[3] = -0.0122232015;
+			g[2] = 0.0059961342;
+			g[1] = 0.0000935982;
+			g[0] = -0.0002890832;
+
+			// imaginary lowpass filter
+			hi = new double[22];
+			hi[0] = 0.0000211708;
+			hi[1] = -0.0012780664;
+			hi[2] = -0.0029648612;
+			hi[3] = 0.0144283733;
+			hi[4] = 0.0503067404;
+			hi[5] = -0.0044659104;
+			hi[6] = -0.1999654035;
+			hi[7] = -0.2603015239;
+			hi[8] = 0.0013800055;
+			hi[9] = 0.2232934469;
+			hi[10] = 0.1795460286;
+			hi[11] = 0.1795460286;
+			hi[12] = 0.2232934469;
+			hi[13] = 0.0013800055;
+			hi[14] = -0.2603015239;
+			hi[15] = -0.1999654035;
+			hi[16] = -0.0044659104;
+			hi[17] = 0.0503067404;
+			hi[18] = 0.0144283733;
+			hi[19] = -0.0029648612;
+			hi[20] = -0.0012780664;
+			hi[21] = 0.0000211708;
+
+			// Imaginary highpass filter
+			gi = new double[22];
+			gi[21] = 0.0000211708;
+			gi[20] = 0.0012780664;
+			gi[19] = -0.0029648612;
+			gi[18] = -0.0144283733;
+			gi[17] = 0.0503067404;
+			gi[16] = 0.0044659104;
+			gi[15] = -0.1999654035;
+			gi[14] = 0.2603015239;
+			gi[13] = 0.0013800055;
+			gi[12] = -0.2232934469;
+			gi[11] = 0.1795460286;
+			gi[10] = -0.1795460286;
+			gi[9] = 0.2232934469;
+			gi[8] = -0.0013800055;
+			gi[7] = -0.2603015239;
+			gi[6] = 0.1999654035;
+			gi[5] = -0.0044659104;
+			gi[4] = -0.0503067404;
+			gi[3] = 0.0144283733;
+			gi[2] = 0.0029648612;
+			gi[1] = -0.0012780664;
+			gi[0] = -0.0000211708;
+			break;
+
+		default:
+			throw (new RuntimeException("Invalid length"));
+		}
+
+	}
+
+}
\ No newline at end of file
diff --git a/src/bilib/src/wavelets/ComplexWavelet.java b/src/bilib/src/wavelets/ComplexWavelet.java
new file mode 100644
index 0000000000000000000000000000000000000000..3cf90e1caea05ddc337d47ce2b4a430a8306ed02
--- /dev/null
+++ b/src/bilib/src/wavelets/ComplexWavelet.java
@@ -0,0 +1,509 @@
+package wavelets;
+
+/**
+ * This class generate the complex wavelets filter.
+ * <hr>
+ * <p>
+ * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging
+ * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne,
+ * Switzerland
+ * </p>
+ * <p>
+ * <b>Authors</b>: Jesse Berent, Daniel Sage
+ * </p>
+ * <p>
+ * <b>Reference</b>: B. Forster, D. Van De Ville, J. Berent, D. Sage, M. Unser,
+ * "<a href="http://bigwww.epfl.ch/publications/forster0404.html
+ * ">Complex Wavelets for Extended Depth-of-Field: A New Method for the Fusion of Multichannel Microscopy Images</a>,"
+ * Microscopy Research and Technique, vol. 65, no. 1-2, pp. 33-42, September
+ * 2004.
+ * </p>
+ * <p>
+ * More information: http://bigwww.epfl.ch/demo/edf/index.html
+ * </p>
+ * <p>
+ * Other relevant information are available at: http://bigwww.epfl.ch/
+ * </p>
+ * <hr>
+ * You'll be free to use this software for research purposes, but you should not
+ * redistribute it without our consent. In addition, we expect you to include a
+ * citation or acknowledgement whenever you present or publish results that are
+ * based on it.
+ */
+
+public class ComplexWavelet {
+
+	/**
+	 * This public method computes the complex wavelets transform of a given
+	 * image and a given number of scale.
+	 * 
+	 * @param in
+	 *            input image
+	 * @param n
+	 *            number of scale
+	 * @param length
+	 * @return the wavelets coefficients
+	 */
+	static public ImageAccess[] analysis(ImageAccess in, int n, int length) {
+
+		// Compute the size to the fine and coarse levels
+		int nxfine = in.getWidth();
+		int nyfine = in.getHeight();
+
+		// Declare the object image
+		ImageAccess sub1;
+		ImageAccess sub2;
+		ImageAccess sub3;
+		ImageAccess sub4;
+		ImageAccess subre;
+		ImageAccess subim;
+		ImageAccess outRe;
+		ImageAccess outIm;
+
+		// Initialization
+		int nx = nxfine;
+		int ny = nyfine;
+		outRe = in.duplicate();
+		outIm = in.duplicate();
+
+		int re = 0;
+		int im = 1;
+
+		// From fine to coarse main loop
+		// first iteration
+
+		subre = new ImageAccess(nx, ny);
+		sub1 = new ImageAccess(nx, ny);
+		sub2 = new ImageAccess(nx, ny);
+
+		// Copy in[] into image[]
+		outRe.getSubImage(0, 0, subre);
+
+		// Apply the Wavelet splitting
+		sub1 = split(subre, re, re, length);
+		sub2 = split(subre, im, im, length);
+
+		sub1.subtract(sub1, sub2);
+
+		// Put the result image[] into in[]
+		outRe.putSubImage(0, 0, sub1);
+
+		// Apply the Wavelet splitting
+		sub1 = split(subre, re, im, length);
+		sub2 = split(subre, im, re, length);
+
+		sub1.add(sub1, sub2);
+
+		outIm.putSubImage(0, 0, sub1);
+
+		// Reduce the size by a factor of 2
+		nx = nx / 2;
+		ny = ny / 2;
+
+		for (int i = 1; i < n; i++) {
+
+			// Create a new image array of size [nx,ny]
+			subre = new ImageAccess(nx, ny);
+			subim = new ImageAccess(nx, ny);
+			sub1 = new ImageAccess(nx, ny);
+			sub2 = new ImageAccess(nx, ny);
+			sub3 = new ImageAccess(nx, ny);
+			sub4 = new ImageAccess(nx, ny);
+
+			// Copy in[] into image[]
+			outRe.getSubImage(0, 0, subre);
+			outIm.getSubImage(0, 0, subim);
+
+			sub1 = split(subre, re, re, length);
+			sub2 = split(subre, im, im, length);
+			sub3 = split(subim, re, im, length);
+			sub4 = split(subim, im, re, length);
+
+			sub1.subtract(sub1, sub2);
+			sub1.subtract(sub1, sub3);
+			sub1.subtract(sub1, sub4);
+
+			outRe.putSubImage(0, 0, sub1);
+
+			sub1 = split(subre, re, im, length);
+			sub2 = split(subre, im, re, length);
+			sub3 = split(subim, re, re, length);
+			sub4 = split(subim, im, im, length);
+
+			sub1.add(sub1, sub2);
+			sub1.add(sub1, sub3);
+			sub1.subtract(sub1, sub4);
+
+			outIm.putSubImage(0, 0, sub1);
+
+			// Reduce the size by a factor of 2
+			nx = nx / 2;
+			ny = ny / 2;
+		}
+		ImageAccess[] outComplex = new ImageAccess[2];
+		outComplex[0] = outRe.duplicate();
+		outComplex[1] = outIm.duplicate();
+		return outComplex;
+	}
+
+	/**
+	 * Perform 1 iteration of the wavelet transformation of an ImageObject. The
+	 * algorithm use the separability of the wavelet transformation. The result
+	 * of the computation is put in the ImageObject calling this method.
+	 * 
+	 * @param in
+	 *            an ImageAcess object provided by ImageJ
+	 */
+	static private ImageAccess split(ImageAccess in, int type1, int type2, int length) {
+		int nx = in.getWidth();
+		int ny = in.getHeight();
+		ImageAccess out = new ImageAccess(nx, ny);
+
+		ComplexWaveFilter wf = new ComplexWaveFilter(length);
+
+		if (nx >= 1) {
+			double rowin[] = new double[nx];
+			double rowout[] = new double[nx];
+			for (int y = 0; y < ny; y++) {
+				in.getRow(y, rowin);
+
+				if (type1 == 0)
+					split_1D(rowin, rowout, wf.h, wf.g);
+
+				if (type1 == 1)
+					split_1D(rowin, rowout, wf.hi, wf.gi);
+
+				out.putRow(y, rowout);
+			}
+		}
+		else {
+			// out.copy(in);
+			out = in.duplicate();
+		}
+
+		if (ny > 1) {
+			double colin[] = new double[ny];
+			double colout[] = new double[ny];
+			for (int x = 0; x < nx; x++) {
+				out.getColumn(x, colin);
+
+				if (type2 == 0)
+					split_1D(colin, colout, wf.h, wf.g);
+
+				if (type2 == 1)
+					split_1D(colin, colout, wf.hi, wf.gi);
+
+				out.putColumn(x, colout);
+			}
+		}
+
+		return out;
+	}
+
+	/**
+	 * Perform 1 iteration of the wavelet transformation of a 1D vector using
+	 * the wavelet transformation. The output vector has the same size of the
+	 * input vector and it contains first the low pass part of the wavelet
+	 * transform and then the high pass part of the wavelet transformation.
+	 * 
+	 * @param vin
+	 *            input, a double 1D vector
+	 * @param vout
+	 *            output, a double 1D vector
+	 * @param h
+	 *            input, a double 1D vector, lowpass filter
+	 * @param g
+	 *            input, a double 1D vector, highpass filter
+	 */
+	static private void split_1D(double vin[], double vout[], double h[], double g[]) {
+		int n = vin.length;
+		int n2 = n / 2;
+		int nh = h.length;
+		int ng = g.length;
+
+		double voutL[] = new double[n];
+		double voutH[] = new double[n];
+		double pix;
+		int j1;
+
+		for (int i = 0; i < n; i++) {
+			pix = 0.0;
+			for (int k = 0; k < nh; k++) { // Low pass part
+				j1 = i + k - (nh / 2);
+				if (j1 < 0) { // Periodic conditions
+					while (j1 < n)
+						j1 = n + j1;
+					j1 = (j1) % n;
+				}
+				if (j1 >= n) { // Periodic conditions
+					j1 = (j1) % n;
+				}
+				pix = pix + h[k] * vin[j1];
+			}
+			voutL[i] = pix;
+		}
+
+		for (int i = 0; i < n; i++) {
+			pix = 0.0;
+			for (int k = 0; k < ng; k++) { // Low pass part
+				j1 = i + k - (ng / 2);
+				if (j1 < 0) { // Periodic conditions
+					while (j1 < n)
+						j1 = n + j1;
+					j1 = (j1) % n;
+				}
+				if (j1 >= n) { // Periodic conditions
+					j1 = (j1) % n;
+				}
+				pix = pix + g[k] * vin[j1];
+			}
+			voutH[i] = pix;
+		}
+
+		for (int k = 0; k < n2; k++)
+			vout[k] = voutL[2 * k];
+		for (int k = n2; k < n; k++)
+			vout[k] = voutH[2 * k - n];
+
+	}
+
+	/**
+	 * Perform an inverse wavelet transformation of the ImageObject calling this
+	 * method with n scale. The size of image should be a interger factor of 2
+	 * at the power n. The input is the results of a wavelet transformation. The
+	 * result is the reconstruction. It is put in the ImageObject calling this
+	 * method.
+	 * 
+	 * @param inRe
+	 *            the real part of the wavelets coefficients
+	 * @param inIm
+	 *            the imaginary part of the wavelets coefficients
+	 * @param n
+	 *            a integer value giving the number of scale
+	 * @param length
+	 * @return the reconstructed image
+	 */
+
+	static public ImageAccess[] synthesis(ImageAccess inRe, ImageAccess inIm, int n, int length) {
+		// Compute the size to the fine and coarse levels
+		int div = (int) Math.pow(2.0, (double) (n - 1));
+		int nxcoarse = inRe.getWidth() / div;
+		int nycoarse = inRe.getHeight() / div;
+
+		// Declare the object image
+		ImageAccess subre, subim, sub1, sub2, sub3, sub4;
+		ImageAccess outRe;
+		ImageAccess outIm;
+
+		// Initialisazion
+		int nx = nxcoarse;
+		int ny = nycoarse;
+
+		outRe = inRe.duplicate();
+		outIm = inIm.duplicate();
+
+		int re = 0;
+		int im = 1;
+
+		// From fine to coarse main loop
+		for (int i = 0; i < n; i++) {
+			// Create a new image array of size [nx,ny]
+			subre = new ImageAccess(nx, ny);
+			subim = new ImageAccess(nx, ny);
+			sub1 = new ImageAccess(nx, ny);
+			sub2 = new ImageAccess(nx, ny);
+			sub3 = new ImageAccess(nx, ny);
+			sub4 = new ImageAccess(nx, ny);
+			// Copy in[] into image[]
+			outRe.getSubImage(0, 0, subre);
+			outIm.getSubImage(0, 0, subim);
+
+			// Apply the Wavelet splitting
+			sub1 = merge(subre, re, re, length);
+			sub2 = merge(subre, im, im, length);
+			sub3 = merge(subim, re, im, length);
+			sub4 = merge(subim, im, re, length);
+
+			sub1.subtract(sub1, sub2);
+			sub1.add(sub1, sub3);
+			sub1.add(sub1, sub4);
+
+			outRe.putSubImage(0, 0, sub1);
+
+			// Apply the Wavelet splitting
+			sub1 = merge(subre, re, im, length);
+			sub2 = merge(subre, im, re, length);
+			sub3 = merge(subim, re, re, length);
+			sub4 = merge(subim, im, im, length);
+
+			sub3.subtract(sub3, sub1);
+			sub3.subtract(sub3, sub2);
+			sub3.subtract(sub3, sub4);
+			outIm.putSubImage(0, 0, sub3);
+			// Enlarge the size by a factor of 2
+			nx = nx * 2;
+			ny = ny * 2;
+
+		}
+		ImageAccess[] ReconstComplex = new ImageAccess[2];
+		ReconstComplex[0] = outRe.duplicate();
+		ReconstComplex[1] = outIm.duplicate();
+		return ReconstComplex;
+	}
+
+	/**
+	 * Perform 1 iteration of the inverse wavelet transformation of an
+	 * ImageObject. The algorithm use the separability of the wavelet
+	 * transformation. The result of the computation is put in the ImageAccess
+	 * calling this method.
+	 * 
+	 * @param in
+	 *            an ImageAcess object provided by ImageJ
+	 * @param type1
+	 * @param type2
+	 * @param length
+	 * @return merge
+	 */
+	static private ImageAccess merge(ImageAccess in, int type1, int type2, int length) {
+		int nx = in.getWidth();
+		int ny = in.getHeight();
+		ImageAccess out = new ImageAccess(nx, ny);
+		ComplexWaveFilter wf = new ComplexWaveFilter(length);
+
+		if (nx >= 1) {
+			double rowin[] = new double[nx];
+			double rowout[] = new double[nx];
+			for (int y = 0; y < ny; y++) {
+				in.getRow(y, rowin);
+
+				if (type1 == 0)
+					merge_1D(rowin, rowout, wf.h, wf.g);
+				if (type1 == 1) {
+					merge_1D(rowin, rowout, wf.hi, wf.gi);
+				}
+				out.putRow(y, rowout);
+			}
+		}
+		else {
+			out = in.duplicate();
+		}
+
+		if (ny > 1) {
+			double colin[] = new double[ny];
+			double colout[] = new double[ny];
+			for (int x = 0; x < nx; x++) {
+				out.getColumn(x, colin);
+
+				if (type2 == 0)
+					merge_1D(colin, colout, wf.h, wf.g);
+				if (type2 == 1) {
+					merge_1D(colin, colout, wf.hi, wf.gi);
+				}
+				out.putColumn(x, colout);
+			}
+		}
+		return out;
+	}
+
+	/**
+	 * Perform 1 iteration of the inverse wavelet transformation of a 1D vector
+	 * using the Spline wavelet transformation. The output vector has the same
+	 * size of the input vector and it contains the reconstruction of the input
+	 * signal. The input vector constains first the low pass part of the wavelet
+	 * transform and then the high pass part of the wavelet transformation.
+	 * 
+	 * @param vin
+	 *            input, a double 1D vector
+	 * @param vout
+	 *            output, a double 1D vector
+	 * @param h
+	 *            input, a double 1D vector, lowpass filter
+	 * @param g
+	 *            input, a double 1D vector, highpass filter
+	 */
+	static private void merge_1D(double vin[], double vout[], double h[], double g[]) {
+		int n = vin.length;
+		int n2 = n / 2;
+		int nh = h.length;
+		int ng = g.length;
+		int j1;
+
+		double pix;
+
+		// Upsampling
+
+		double vinL[] = new double[n];
+		double vinH[] = new double[n];
+		for (int k = 0; k < n; k++) {
+			vinL[k] = 0;
+			vinH[k] = 0;
+		}
+
+		for (int k = 0; k < n2; k++) {
+			vinL[2 * k] = vin[k];
+			vinH[2 * k] = vin[k + n2];
+		}
+
+		// filtering
+
+		for (int i = 0; i < n; i++) {
+			pix = 0.0;
+			for (int k = 0; k < nh; k++) { // Low pass part
+				j1 = i - k + (nh / 2);
+				if (j1 < 0) { // Periodic conditions
+					while (j1 < n)
+						j1 = n + j1;
+					j1 = (j1) % n;
+				}
+				if (j1 >= n) { // Periodic conditions
+					j1 = (j1) % n;
+				}
+				pix = pix + h[k] * vinL[j1];
+			}
+			vout[i] = pix;
+		}
+
+		for (int i = 0; i < n; i++) {
+			pix = 0.0;
+			for (int k = 0; k < ng; k++) { // High pass part
+				j1 = i - k + (ng / 2);
+				if (j1 < 0) { // Periodic conditions
+					while (j1 < n)
+						j1 = n + j1;
+					j1 = (j1) % n;
+				}
+				if (j1 >= n) { // Periodic conditions
+					j1 = (j1) % n;
+				}
+				pix = pix + g[k] * vinH[j1];
+			}
+			vout[i] = vout[i] + pix;
+		}
+	}
+
+	/**
+	 * This method computes the modulus from a real ImageAccess and a imaginary
+	 * ImageAccess objects.
+	 * 
+	 * @param inRe
+	 * @param inIm
+	 * @return modulus
+	 */
+	static public ImageAccess modulus(ImageAccess inRe, ImageAccess inIm) {
+		int nx = inRe.getWidth();
+		int ny = inRe.getHeight();
+		double m, r, i;
+		ImageAccess modulus = new ImageAccess(nx, ny);
+		int x, y;
+		for (x = 0; x < nx; x++) {
+			for (y = 0; y < ny; y++) {
+				r = inRe.getPixel(x, y);
+				i = inIm.getPixel(x, y);
+				m = Math.sqrt((r * r) + (i * i));
+				modulus.putPixel(x, y, m);
+			}
+		}
+		return modulus;
+	}
+
+}
diff --git a/src/bilib/src/wavelets/ImageAccess.java b/src/bilib/src/wavelets/ImageAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbb36346c990901f3383cbff1a1aaa6d16f8dba4
--- /dev/null
+++ b/src/bilib/src/wavelets/ImageAccess.java
@@ -0,0 +1,1354 @@
+package wavelets;
+
+import ij.ImagePlus;
+import ij.process.ByteProcessor;
+import ij.process.ColorProcessor;
+import ij.process.FloatProcessor;
+import ij.process.ImageProcessor;
+
+/**
+ * ImageAccess is an interface layer to facilitate the access to the pixels of
+ * ImageJ images. Methods of ImageAccess provides an easy and robust way to
+ * access to the pixels of images. The data are stored in a double array. Many
+ * methods get/put allows to access to the data. If the user try to access
+ * outside of the image, the mirror boundary conditions are applied.
+ * <hr>
+ * <p>
+ * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging
+ * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne,
+ * Switzerland
+ * </p>
+ * <p>
+ * <b>Authors</b>: Daniel Sage
+ * </p>
+ * <p>
+ * <b>Reference</b>: D. Sage, M. Unser, &#34;<a
+ * href="http://bigwww.epfl.ch/publications/sage0303.html">Teaching
+ * Image-Processing Programming in Java</a>,&#34; IEEE Signal Processing
+ * Magazine, vol. 20, no. 6, pp. 43-52, November 2003.
+ * </p>
+ * <p>
+ * More information: http://bigwww.epfl.ch/teaching/iplabsite/index.php
+ * </p>
+ * <p>
+ * Other relevant information are available at: http://bigwww.epfl.ch/
+ * </p>
+ * <hr>
+ * You'll be free to use this software for research purposes, but you should not
+ * redistribute it without our consent. In addition, we expect you to include a
+ * citation or acknowledgement whenever you present or publish results that are
+ * based on it.
+ */
+
+public class ImageAccess {
+	public static final int	PATTERN_SQUARE_3x3	= 0;
+	public static final int	PATTERN_CROSS_3x3	= 1;
+
+	private double			pixels[]			= null; // store the pixel data
+	private int				nx					= 0;	// size in X axis
+	private int				ny					= 0;	// size in Y axis
+	private int				size				= 0;	// size = nx*ny
+
+	/**
+	 * Creates a new ImageAccess object from a 2D double array of pixels. The
+	 * size of the array determines the size of the image.
+	 * 
+	 * @param array
+	 *            an array of pixel (2D)
+	 */
+	public ImageAccess(double[][] array) {
+		if (array == null)
+			throw new ArrayStoreException("Constructor: array == null.");
+		this.ny = array[0].length;
+		this.nx = array.length;
+		this.size = nx * ny;
+		pixels = new double[size];
+		int k = 0;
+		for (int j = 0; j < ny; j++)
+			for (int i = 0; i < nx; i++)
+				pixels[k++] = array[i][j];
+	}
+
+	/**
+	 * Creates a new object of the class ImageAccess from an ImageProcessor
+	 * object.
+	 * 
+	 * ImageProcessor object contains the image data, the size and the type of
+	 * the image. The ImageProcessor is provided by ImageJ, it should by a
+	 * 8-bit, 16-bit.
+	 * 
+	 * @param ip
+	 *            an ImageProcessor object provided by ImageJ
+	 */
+	public ImageAccess(ImageProcessor ip) {
+		if (ip == null)
+			throw new ArrayStoreException("Constructor: ImageProcessor == null.");
+		nx = ip.getWidth();
+		ny = ip.getHeight();
+		size = nx * ny;
+		pixels = new double[size];
+		if (ip.getPixels() instanceof byte[]) {
+			byte[] bsrc = (byte[]) ip.getPixels();
+			for (int k = 0; k < size; k++)
+				pixels[k] = (double) (bsrc[k] & 0xFF);
+
+		}
+		else if (ip.getPixels() instanceof short[]) {
+			short[] ssrc = (short[]) ip.getPixels();
+			for (int k = 0; k < size; k++)
+				pixels[k] = (double) (ssrc[k] & 0xFFFF);
+		}
+		else if (ip.getPixels() instanceof float[]) {
+			float[] fsrc = (float[]) ip.getPixels();
+			for (int k = 0; k < size; k++)
+				pixels[k] = (double) fsrc[k];
+		}
+		else {
+			throw new ArrayStoreException("Constructor: Unexpected image type.");
+		}
+	}
+
+	/**
+	 * Creates a new object of the class ImageAccess from an ColorProcessor
+	 * object.
+	 * 
+	 * ImageProcessor object contains the image data, the size and the type of
+	 * the image. The ColorProcessor is provided by ImageJ, The ImageAccess
+	 * contains one plane (red, green or blue) selected with the colorPlane
+	 * parameter.
+	 * 
+	 * @param cp
+	 *            an ColorProcessor object
+	 * @param colorPlane
+	 *            index of the color plane 0, 1 or 2
+	 */
+	public ImageAccess(ColorProcessor cp, int colorPlane) {
+		if (cp == null)
+			throw new ArrayStoreException("Constructor: ColorProcessor == null.");
+		if (colorPlane < 0)
+			throw new ArrayStoreException("Constructor: colorPlane < 0.");
+		if (colorPlane > 2)
+			throw new ArrayStoreException("Constructor: colorPlane > 2.");
+		nx = cp.getWidth();
+		ny = cp.getHeight();
+		size = nx * ny;
+		pixels = new double[size];
+		byte[] r = new byte[size];
+		byte[] g = new byte[size];
+		byte[] b = new byte[size];
+		cp.getRGB(r, g, b);
+		if (colorPlane == 0)
+			for (int k = 0; k < size; k++)
+				pixels[k] = (double) (r[k] & 0xFF);
+		else if (colorPlane == 1)
+			for (int k = 0; k < size; k++)
+				pixels[k] = (double) (g[k] & 0xFF);
+		else if (colorPlane == 2)
+			for (int k = 0; k < size; k++)
+				pixels[k] = (double) (b[k] & 0xFF);
+	}
+
+	/**
+	 * Creates a new object of the class ImageAccess.
+	 * 
+	 * The size of the image are given as parameter. The data pixels are empty
+	 * and are not initialized.
+	 * 
+	 * @param nx
+	 *            the size of the image along the X-axis
+	 * @param ny
+	 *            the size of the image along the Y-axis
+	 */
+	public ImageAccess(int nx, int ny) {
+		if (nx < 1)
+			throw new ArrayStoreException("Constructor: nx < 1.");
+		if (ny < 1)
+			throw new ArrayStoreException("Constructor: ny < 1.");
+		this.nx = nx;
+		this.ny = ny;
+		size = nx * ny;
+		pixels = new double[size];
+	}
+
+	/**
+	 * Return the width of the image.
+	 * 
+	 * @return the image width
+	 */
+	public int getWidth() {
+		return nx;
+	}
+
+	/**
+	 * Return the height of the image.
+	 * 
+	 * @return the image height
+	 */
+	public int getHeight() {
+		return ny;
+	}
+
+	/**
+	 * Return the maximum value of ImageAccess.
+	 * 
+	 * @return the maximum value
+	 */
+	public double getMaximum() {
+		double maxi = pixels[0];
+		for (int i = 1; i < size; i++)
+			if (pixels[i] > maxi)
+				maxi = pixels[i];
+		return maxi;
+	}
+
+	/**
+	 * Return the minimum value of ImageAccess.
+	 * 
+	 * @return the minimum value
+	 */
+	public double getMinimum() {
+		double mini = pixels[0];
+		for (int i = 1; i < size; i++)
+			if (pixels[i] < mini)
+				mini = pixels[i];
+		return mini;
+	}
+
+	/**
+	 * Return the mean value of ImageAccess.
+	 * 
+	 * @return the mean value
+	 */
+	public double getMean() {
+		double mean = 0.0;
+		for (int i = 0; i < size; i++)
+			mean += pixels[i];
+		mean /= (double) (size);
+		return mean;
+	}
+
+	/**
+	 * Returns a copy of the pixel data organize in a 2D array.
+	 * 
+	 * @return the 2D double array
+	 */
+	public double[][] getArrayPixels() {
+		double[][] array = new double[nx][ny];
+		int k = 0;
+		for (int j = 0; j < ny; j++)
+			for (int i = 0; i < nx; i++)
+				array[i][j] = pixels[k++];
+		return array;
+	}
+
+	/**
+	 * Returns a reference to the pixel data in double (1D).
+	 * 
+	 * @return the 1D double array
+	 */
+	public double[] getPixels() {
+		return pixels;
+	}
+
+	/**
+	 * Create a FloatProcessor from the pixel data. The double values of the
+	 * pixel are simply casted in float.
+	 * 
+	 * @return the FloatProcessor
+	 */
+	public FloatProcessor createFloatProcessor() {
+		FloatProcessor fp = new FloatProcessor(nx, ny);
+		float[] fsrc = new float[size];
+		for (int k = 0; k < size; k++)
+			fsrc[k] = (float) (pixels[k]);
+		fp.setPixels(fsrc);
+		return fp;
+	}
+
+	/**
+	 * Create a ByteProcessor from the pixel data. The double values of the
+	 * pixel are clipped in the [0..255] range.
+	 * 
+	 * @return the ByteProcessor
+	 */
+	public ByteProcessor createByteProcessor() {
+		ByteProcessor bp = new ByteProcessor(nx, ny);
+		byte[] bsrc = new byte[size];
+		double p;
+		for (int k = 0; k < size; k++) {
+			p = pixels[k];
+			if (p < 0)
+				p = 0.0;
+			if (p > 255.0)
+				p = 255.0;
+			bsrc[k] = (byte) p;
+		}
+		bp.setPixels(bsrc);
+		return bp;
+	}
+
+	/**
+	 * Create a new ImageAccess object by duplication of the current the
+	 * ImageAccess object.
+	 * 
+	 * @return a new ImageAccess object
+	 **/
+	public ImageAccess duplicate() {
+		ImageAccess ia = new ImageAccess(nx, ny);
+		for (int i = 0; i < size; i++)
+			ia.pixels[i] = this.pixels[i];
+		return ia;
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting the gray level of a
+	 * selected pixel.
+	 * 
+	 * Mirror border conditions are applied.
+	 * 
+	 * @param x
+	 *            input, the integer x-coordinate of a pixel
+	 * @param y
+	 *            input, the integer y-coordinate of a pixel
+	 * @return the gray level of the pixel (double)
+	 */
+	public double getPixel(int x, int y) {
+		int periodx = 2 * nx - 2;
+		int periody = 2 * ny - 2;
+		if (x < 0) {
+			while (x < 0)
+				x += periodx; // Periodize
+			if (x >= nx)
+				x = periodx - x; // Symmetrize
+		}
+		else if (x >= nx) {
+			while (x >= nx)
+				x -= periodx; // Periodize
+			if (x < 0)
+				x = -x; // Symmetrize
+		}
+
+		if (y < 0) {
+			while (y < 0)
+				y += periody; // Periodize
+			if (y >= ny)
+				y = periody - y; // Symmetrize
+		}
+		else if (y >= ny) {
+			while (y >= ny)
+				y -= periody; // Periodize
+			if (y < 0)
+				y = -y; // Symmetrize
+		}
+		return pixels[x + y * nx];
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting the gray level of a
+	 * selected pixel using a bilinear interpolation. The coordinates can be
+	 * given in double and the bilinear interpolation is applied the find the
+	 * gray level.
+	 * 
+	 * Mirror border conditions are applied.
+	 * 
+	 * @param x
+	 *            input, the double x-coordinate of a pixel
+	 * @param y
+	 *            input, the double y-coordinate of a pixel
+	 * @return the gray level of the pixel (double)
+	 */
+	public double getInterpolatedPixel(double x, double y) {
+		if (Double.isNaN(x))
+			return 0;
+		if (Double.isNaN(y))
+			return 0;
+
+		if (x < 0) {
+			int periodx = 2 * nx - 2;
+			while (x < 0)
+				x += periodx; // Periodize
+			if (x >= nx)
+				x = periodx - x; // Symmetrize
+		}
+		else if (x >= nx) {
+			int periodx = 2 * nx - 2;
+			while (x >= nx)
+				x -= periodx; // Periodize
+			if (x < 0)
+				x = -x; // Symmetrize
+		}
+
+		if (y < 0) {
+			int periody = 2 * ny - 2;
+			while (y < 0)
+				y += periody; // Periodize
+			if (y >= ny)
+				y = periody - y; // Symmetrize
+		}
+		else if (y >= ny) {
+			int periody = 2 * ny - 2;
+			while (y >= ny)
+				y -= periody; // Periodize
+			if (y < 0)
+				y = -y; // Symmetrize
+		}
+		int i;
+		if (x >= 0.0)
+			i = (int) x;
+		else {
+			final int iAdd = (int) x - 1;
+			i = ((int) (x - (double) iAdd) + iAdd);
+		}
+		int j;
+		if (y >= 0.0)
+			j = (int) y;
+		else {
+			final int iAdd = (int) y - 1;
+			j = ((int) (y - (double) iAdd) + iAdd);
+		}
+
+		double dx = x - (double) i;
+		double dy = y - (double) j;
+		int di, dj;
+		if (i >= nx - 1)
+			di = -1;
+		else
+			di = 1;
+		int index = i + j * nx;
+		double v00 = pixels[index];
+		double v10 = pixels[index + di];
+		if (j >= ny - 1)
+			index -= nx;
+		else
+			index += nx;
+		double v01 = pixels[index];
+		double v11 = pixels[index + di];
+		return (dx * (v11 * dy - v10 * (dy - 1.0)) - (dx - 1.0) * (v01 * dy - v00 * (dy - 1.0)));
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting a whole column of the
+	 * image.
+	 * 
+	 * The column should already created with the correct size [ny].
+	 * 
+	 * @param x
+	 *            input, the integer x-coordinate of a column
+	 * @param column
+	 *            output, an array of the type double
+	 */
+	public void getColumn(int x, double[] column) {
+		if (x < 0)
+			throw new IndexOutOfBoundsException("getColumn: x < 0.");
+		if (x >= nx)
+			throw new IndexOutOfBoundsException("getColumn: x >= nx.");
+		if (column == null)
+			throw new ArrayStoreException("getColumn: column == null.");
+		if (column.length != ny)
+			throw new ArrayStoreException("getColumn: column.length != ny.");
+		for (int i = 0; i < ny; i++) {
+			column[i] = pixels[x];
+			x += nx;
+		}
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting a part of column. The
+	 * starting point is given by the y parameter and the ending determine by
+	 * the size of the column parameter. The column parameter should already
+	 * created.
+	 * 
+	 * @param x
+	 *            input, the integer x-coordinate of a column
+	 * @param y
+	 *            input, starting point
+	 * @param column
+	 *            output, an array of the type double
+	 */
+	public void getColumn(int x, int y, double[] column) {
+		if (x < 0)
+			throw new IndexOutOfBoundsException("getColumn: x < 0.");
+		if (x >= nx)
+			throw new IndexOutOfBoundsException("getColumn: x >= nx.");
+		if (column == null)
+			throw new ArrayStoreException("getColumn: column == null.");
+		int by = column.length;
+		if (y >= 0)
+			if (y < ny - by - 1) {
+				int index = y * nx + x;
+				for (int i = 0; i < by; i++) {
+					column[i] = pixels[index];
+					index += nx;
+				}
+				return;
+			}
+		// Getting information outside of the image
+		int yt[] = new int[by];
+		for (int k = 0; k < by; k++) {
+			int ya = y + k;
+			int periody = 2 * ny - 2;
+			while (ya < 0)
+				ya += periody; // Periodize
+			while (ya >= ny) {
+				ya = periody - ya; // Symmetrize
+				if (ya < 0)
+					ya = -ya;
+			}
+			yt[k] = ya;
+		}
+		int index = 0;
+		for (int i = 0; i < by; i++) {
+			index = yt[i] * nx + x;
+			column[i] = pixels[index];
+		}
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting a whole row of the
+	 * image.
+	 * 
+	 * The row should already created with the correct size [nx].
+	 * 
+	 * @param y
+	 *            input, the integer y-coordinate of a row
+	 * @param row
+	 *            output, an array of the type double
+	 */
+	public void getRow(int y, double[] row) {
+		if (y < 0)
+			throw new IndexOutOfBoundsException("getRow: y < 0.");
+		if (y >= ny)
+			throw new IndexOutOfBoundsException("getRow: y >= ny.");
+		if (row == null)
+			throw new ArrayStoreException("getColumn: row == null.");
+		if (row.length != nx)
+			throw new ArrayStoreException("getColumn: row.length != nx.");
+		y *= nx;
+		for (int i = 0; i < nx; i++)
+			row[i] = pixels[y++];
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting a part of row. The
+	 * starting point is given by the y parameter and the ending determine by
+	 * the size of the row parameter. The row parameter should already created.
+	 * 
+	 * @param x
+	 *            input, starting point
+	 * @param y
+	 *            input, the integer y-coordinate of a row
+	 * @param row
+	 *            output, an array of the type double
+	 */
+	public void getRow(int x, int y, double[] row) {
+		if (y < 0)
+			throw new IndexOutOfBoundsException("getRow: y < 0.");
+		if (y >= ny)
+			throw new IndexOutOfBoundsException("getRow: y >= ny.");
+		if (row == null)
+			throw new ArrayStoreException("getRow: row == null.");
+		int bx = row.length;
+		if (x >= 0)
+			if (x < nx - bx - 1) {
+				int index = y * nx + x;
+				for (int i = 0; i < bx; i++) {
+					row[i] = pixels[index++];
+				}
+				return;
+			}
+		int periodx = 2 * nx - 2;
+		int xt[] = new int[bx];
+		for (int k = 0; k < bx; k++) {
+			int xa = x + k;
+			while (xa < 0)
+				xa += periodx; // Periodize
+			while (xa >= nx) {
+				xa = periodx - xa; // Symmetrize
+				if (xa < 0)
+					xa = -xa;
+			}
+			xt[k] = xa;
+		}
+		int somme = 0;
+		int index = y * nx;
+		for (int i = 0; i < bx; i++) {
+			somme = index + xt[i];
+			row[i] = pixels[somme];
+		}
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting a neighborhood
+	 * arround a pixel position.
+	 * 
+	 * The neigh parameter should already created. The size of the array
+	 * determines the neighborhood block.
+	 * 
+	 * <br>
+	 * Mirror border conditions are applied. <br>
+	 * <br>
+	 * The pixel value of (x-n/2, y-n/2) is put into neigh[0][0] <br>
+	 * ... <br>
+	 * The pixel value of (x+n/2, y+n/2) is put into neigh[n-1][n-1] <br>
+	 * <br>
+	 * For example if neigh is a double[4][4]: <br>
+	 * The pixel value of (x-1, y-1) is put into neigh[0][0] <br>
+	 * The pixel value of (x , y ) is put into neigh[1][1] <br>
+	 * The pixel value of (x, y+1) is put into neigh[1][2] <br>
+	 * The pixel value of (x+1, y-2) is put into neigh[2][0] <br>
+	 * ... <br>
+	 * For example if neigh is a double[5][5]: <br>
+	 * The pixel value of (x-2, y-2) is put into neigh[0][0] <br>
+	 * The pixel value of (x-1, y-1) is put into neigh[1][1] <br>
+	 * The pixel value of (x , y ) is put into neigh[2][2] <br>
+	 * The pixel value of (x, y+1) is put into neigh[2][3] <br>
+	 * The pixel value of (x+2, y-2) is put into neigh[4][0]
+	 * 
+	 * @param x
+	 *            the integer x-coordinate of a selected central pixel
+	 * @param y
+	 *            the integer y-coordinate of a selected central pixel
+	 * @param neigh
+	 *            output, a 2D array s
+	 */
+	public void getNeighborhood(int x, int y, double neigh[][]) {
+		int bx = neigh.length;
+		int by = neigh[0].length;
+		int bx2 = (bx - 1) / 2;
+		int by2 = (by - 1) / 2;
+		if (x >= bx2)
+			if (y >= by2)
+				if (x < nx - bx2 - 1)
+					if (y < ny - by2 - 1) {
+						int index = (y - by2) * nx + (x - bx2);
+						for (int j = 0; j < by; j++) {
+							for (int i = 0; i < bx; i++) {
+								neigh[i][j] = pixels[index++];
+							}
+							index += (nx - bx);
+						}
+						return;
+					}
+		int xt[] = new int[bx];
+		for (int k = 0; k < bx; k++) {
+			int xa = x + k - bx2;
+			int periodx = 2 * nx - 2;
+			while (xa < 0)
+				xa += periodx; // Periodize
+			while (xa >= nx) {
+				xa = periodx - xa; // Symmetrize
+				if (xa < 0)
+					xa = -xa;
+			}
+			xt[k] = xa;
+		}
+		int yt[] = new int[by];
+		for (int k = 0; k < by; k++) {
+			int ya = y + k - by2;
+			int periody = 2 * ny - 2;
+			while (ya < 0)
+				ya += periody; // Periodize
+			while (ya >= ny) {
+				ya = periody - ya; // Symmetrize
+				if (ya < 0)
+					ya = -ya;
+			}
+			yt[k] = ya;
+		}
+		int sum = 0;
+		for (int j = 0; j < by; j++) {
+			int index = yt[j] * nx;
+			for (int i = 0; i < bx; i++) {
+				sum = index + xt[i];
+				neigh[i][j] = pixels[sum];
+			}
+		}
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting a neighborhood of a
+	 * predefined pattern around a selected pixel (x,y). <br>
+	 * The available patterns are: <br>
+	 * - a 3*3 block: PATTERN_SQUARE_3x3 (8-connect) <br>
+	 * - a 3*3 cross: PATTERN_CROSS_3x3 (4-connect) <br>
+	 * <br>
+	 * Mirror border conditions are applied. <br>
+	 * The pixel is arranged in a 1D array according the following rules: <br>
+	 * <br>
+	 * If the pattern is PATTERN_SQUARE_3x3 (8-connect) <br>
+	 * The pixel value of (x-1, y-1) are put into neigh[0] <br>
+	 * The pixel value of (x , y-1) are put into neigh[1] <br>
+	 * The pixel value of (x+1, y-1) are put into neigh[2] <br>
+	 * The pixel value of (x-1, y ) are put into neigh[3] <br>
+	 * The pixel value of (x , y ) are put into neigh[4] <br>
+	 * The pixel value of (x+1, y ) are put into neigh[5] <br>
+	 * The pixel value of (x-1, y+1) are put into neigh[6] <br>
+	 * The pixel value of (x , y+1) are put into neigh[7] <br>
+	 * The pixel value of (x+1, y+1) are put into neigh[8] <br>
+	 * <br>
+	 * If the pattern is PATTERN_CROSS_3x3 (4-connect) <br>
+	 * The pixel value of (x , y-1) are put into neigh[0] <br>
+	 * The pixel value of (x-1, y ) are put into neigh[1] <br>
+	 * The pixel value of (x , y ) are put into neigh[2] <br>
+	 * The pixel value of (x+1, y ) are put into neigh[3] <br>
+	 * The pixel value of (x , y+1) are put into neigh[4] <br>
+	 * <br>
+	 * The neigh should already created as a double array of 9 elements for the
+	 * PATTERN_SQUARE_3x3 or 5 elements for the PATTERN_CROSS_3x3.
+	 * 
+	 * @param x
+	 *            x-coordinate of a selected central pixel
+	 * @param y
+	 *            y-coordinate of a selected central pixel
+	 * @param neigh
+	 *            output, an array consisting of 9 or 5 elements
+	 * @param pattern
+	 *            PATTERN_SQUARE_3x3 or PATTERN_CROSS_3x3.
+	 */
+	public void getPattern(int x, int y, double neigh[], int pattern) {
+		if (neigh == null)
+			throw new ArrayStoreException("getPattern: neigh == null.");
+		switch (pattern) {
+		case PATTERN_SQUARE_3x3:
+			if (neigh.length != 9)
+				throw new ArrayStoreException("getPattern: neigh.length != 9.");
+			getPatternSquare3x3(x, y, neigh);
+			break;
+		case PATTERN_CROSS_3x3:
+			if (neigh.length != 5)
+				throw new ArrayStoreException("getPattern: neigh.length != 5");
+			getPatternCross3x3(x, y, neigh);
+			break;
+		default:
+			throw new ArrayStoreException("getPattern: unexpected pattern.");
+		}
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting a 3x3 neighborhood of
+	 * 8-connected pixels around a selected pixel.
+	 * 
+	 * @param x
+	 *            input, the integer x-coordinate of a selected central pixel
+	 * @param y
+	 *            input, the integer y-coordinate of a selected central pixel
+	 * @param neigh
+	 *            output, an array consisting of 9 elements of the type double
+	 */
+	private void getPatternSquare3x3(int x, int y, double neigh[]) {
+		if (x >= 1)
+			if (y >= 1)
+				if (x < nx - 1)
+					if (y < ny - 1) {
+						int index = (y - 1) * nx + (x - 1);
+						neigh[0] = pixels[index++];
+						neigh[1] = pixels[index++];
+						neigh[2] = pixels[index];
+						index += (nx - 2);
+						neigh[3] = pixels[index++];
+						neigh[4] = pixels[index++];
+						neigh[5] = pixels[index];
+						index += (nx - 2);
+						neigh[6] = pixels[index++];
+						neigh[7] = pixels[index++];
+						neigh[8] = pixels[index];
+						return;
+					}
+		int x1 = x - 1;
+		int x2 = x;
+		int x3 = x + 1;
+		int y1 = y - 1;
+		int y2 = y;
+		int y3 = y + 1;
+		if (x == 0)
+			x1 = x3;
+		if (y == 0)
+			y1 = y3;
+		if (x == nx - 1)
+			x3 = x1;
+		if (y == ny - 1)
+			y3 = y1;
+		int offset = y1 * nx;
+		neigh[0] = pixels[offset + x1];
+		neigh[1] = pixels[offset + x2];
+		neigh[2] = pixels[offset + x3];
+		offset = y2 * nx;
+		neigh[3] = pixels[offset + x1];
+		neigh[4] = pixels[offset + x2];
+		neigh[5] = pixels[offset + x3];
+		offset = y3 * nx;
+		neigh[6] = pixels[offset + x1];
+		neigh[7] = pixels[offset + x2];
+		neigh[8] = pixels[offset + x3];
+	}
+
+	/**
+	 * An ImageAccess object calls this method for getting a 3x3 neighborhood of
+	 * 4-connected pixels around a selected pixel.
+	 * 
+	 * @param x
+	 *            input, the integer x-coordinate of a selected central pixel
+	 * @param y
+	 *            input, the integer y-coordinate of a selected central pixel
+	 * @param neigh
+	 *            output, an array consisting of 5 elements of the type double
+	 */
+	private void getPatternCross3x3(int x, int y, double neigh[]) {
+		if (x >= 1)
+			if (y >= 1)
+				if (x < nx - 1)
+					if (y < ny - 1) {
+						int index = (y - 1) * nx + x;
+						neigh[0] = pixels[index];
+						index += (nx - 1);
+						neigh[1] = pixels[index++];
+						neigh[2] = pixels[index++];
+						neigh[3] = pixels[index];
+						index += (nx - 1);
+						neigh[4] = pixels[index];
+						return;
+					}
+		int x1 = x - 1;
+		int x2 = x;
+		int x3 = x + 1;
+		int y1 = y - 1;
+		int y2 = y;
+		int y3 = y + 1;
+		if (x == 0)
+			x1 = x3;
+		if (y == 0)
+			y1 = y3;
+		if (x == nx - 1)
+			x3 = x1;
+		if (y == ny - 1)
+			y3 = y1;
+		int offset = y1 * nx;
+		neigh[0] = pixels[offset + x2];
+		offset = y2 * nx;
+		neigh[1] = pixels[offset + x1];
+		neigh[2] = pixels[offset + x2];
+		neigh[3] = pixels[offset + x3];
+		offset = y3 * nx;
+		neigh[4] = pixels[offset + x2];
+	}
+
+	/**
+	 * An ImageAccess object calls this method to get a sub-image with the upper
+	 * left corner in the coordinate (x,y).
+	 * 
+	 * The sub-image ouptut should be already created.
+	 * 
+	 * @param x
+	 *            x-coordinate in the source image
+	 * @param y
+	 *            y-coordinate in the source image
+	 * @param output
+	 *            an ImageAccess object with the sub-image;
+	 */
+	public void getSubImage(int x, int y, ImageAccess output) {
+		if (output == null)
+			throw new ArrayStoreException("getSubImage: output == null.");
+		if (x < 0)
+			throw new ArrayStoreException("getSubImage: Incompatible image size");
+		if (y < 0)
+			throw new ArrayStoreException("getSubImage: Incompatible image size");
+		if (x >= nx)
+			throw new ArrayStoreException("getSubImage: Incompatible image size");
+		if (y >= ny)
+			throw new ArrayStoreException("getSubImage: Incompatible image size");
+		int nxcopy = output.getWidth();
+		int nycopy = output.getHeight();
+		double[][] neigh = new double[nxcopy][nycopy];
+		int nx2 = (nxcopy - 1) / 2;
+		int ny2 = (nycopy - 1) / 2;
+		this.getNeighborhood(x + nx2, y + ny2, neigh);
+		output.putArrayPixels(neigh);
+	}
+
+	/**
+	 * An ImageAccess object calls this method in order a value of the gray
+	 * level to be put to a position inside it given by the coordinates.
+	 * 
+	 * @param x
+	 *            input, the integer x-coordinate of a pixel
+	 * @param y
+	 *            input, the integer y-coordinate of a pixel
+	 * @param value
+	 *            input, a value of the gray level of the type double
+	 */
+	public void putPixel(int x, int y, double value) {
+		if (x < 0)
+			return;
+		if (x >= nx)
+			return;
+		if (y < 0)
+			return;
+		if (y >= ny)
+			return;
+		pixels[x + y * nx] = value;
+	}
+
+	/**
+	 * An ImageAccess object calls this method to put a whole column in a
+	 * specified position into the image.
+	 * 
+	 * @param x
+	 *            input, the integer x-coordinate of a column
+	 * @param column
+	 *            input, an array of the type double
+	 */
+	public void putColumn(int x, double[] column) {
+		if (x < 0)
+			throw new IndexOutOfBoundsException("putColumn: x < 0.");
+		if (x >= nx)
+			throw new IndexOutOfBoundsException("putColumn: x >= nx.");
+		if (column == null)
+			throw new ArrayStoreException("putColumn: column == null.");
+		if (column.length != ny)
+			throw new ArrayStoreException("putColumn: column.length != ny.");
+		for (int i = 0; i < ny; i++) {
+			pixels[x] = column[i];
+			x += nx;
+		}
+	}
+
+	/**
+	 * An ImageAccess object calls this method to put a part of column into the
+	 * image. The starting poisition in given by y and the ending position is
+	 * determined by the size of the column array.
+	 * 
+	 * @param x
+	 *            input, the integer x-coordinate of a column
+	 * @param y
+	 *            input, the integer y-coordinate of a column
+	 * @param column
+	 *            input, an array of the type double
+	 */
+	public void putColumn(int x, int y, double[] column) {
+		if (x < 0)
+			throw new IndexOutOfBoundsException("putColumn: x < 0.");
+		if (x >= nx)
+			throw new IndexOutOfBoundsException("putColumn: x >= nx.");
+		if (column == null)
+			throw new ArrayStoreException("putColumn: column == null.");
+		int by = column.length;
+		int index = y * nx + x;
+		int top = 0;
+		int bottom = 0;
+		if (y >= 0) {
+			if (y < ny - by)
+				bottom = by;
+			else
+				bottom = -y + ny;
+			for (int i = top; i < bottom; i++) {
+				pixels[index] = column[i];
+				index += nx;
+			}
+			return;
+		}
+		else {
+			index = x;
+			top = -y;
+			if (y < ny - by)
+				bottom = by;
+			else
+				bottom = -y + ny;
+			for (int i = top; i < bottom; i++) {
+				pixels[index] = column[i];
+				index += nx;
+			}
+		}
+	}
+
+	/**
+	 * An ImageAccess object calls this method to put a whole row in a specified
+	 * position into the image.
+	 * 
+	 * @param y
+	 *            input, the integer y-coordinate of a row
+	 * @param row
+	 *            input, an array of the type double
+	 */
+	public void putRow(int y, double[] row) {
+		if (y < 0)
+			throw new IndexOutOfBoundsException("putRow: y < 0.");
+		if (y >= ny)
+			throw new IndexOutOfBoundsException("putRow: y >= ny.");
+		if (row == null)
+			throw new ArrayStoreException("putRow: row == null.");
+		if (row.length != nx)
+			throw new ArrayStoreException("putRow: row.length != nx.");
+		y *= nx;
+		for (int i = 0; i < nx; i++) {
+			pixels[y++] = row[i];
+		}
+
+	}
+
+	/**
+	 * An ImageAccess object calls this method to put a part of row into the
+	 * image. The starting poisition in given by x and the ending position is
+	 * determined by the size of the row array.
+	 * 
+	 * @param x
+	 *            input, the integer x-coordinate of a column
+	 * @param y
+	 *            input, the integer y-coordinate of a row
+	 * @param row
+	 *            input, an array of the type double
+	 */
+	public void putRow(int x, int y, double[] row) {
+		if (y < 0)
+			throw new IndexOutOfBoundsException("putRow: y < 0.");
+		if (y >= ny)
+			throw new IndexOutOfBoundsException("putRow: y >= ny.");
+		if (row == null)
+			throw new ArrayStoreException("putRow: row == null.");
+		int bx = row.length;
+		int index = y * nx + x;
+		int left = 0;
+		int right = 0;
+		if (x >= 0) {
+			if (x < nx - bx)
+				right = bx;
+			else
+				right = -x + nx;
+
+			for (int i = left; i < right; i++) {
+				pixels[index++] = row[i];
+			}
+			return;
+		}
+		else {
+			index = y * nx;
+			left = -x;
+
+			if (x < nx - bx)
+				right = bx;
+			else
+				right = -x + nx;
+
+			for (int i = left; i < right; i++) {
+				pixels[index++] = row[i];
+			}
+		}
+	}
+
+	/**
+	 * An ImageAccess object calls this method in order to put an 2D array of
+	 * double in an ImageAccess.
+	 * 
+	 * @param array
+	 *            input, the double array
+	 */
+	public void putArrayPixels(double[][] array) {
+		if (array == null)
+			throw new IndexOutOfBoundsException("putArrayPixels: array == null.");
+		int bx = array.length;
+		int by = array[0].length;
+		if (bx * by != size)
+			throw new IndexOutOfBoundsException("putArrayPixels: imcompatible size.");
+		int k = 0;
+		for (int j = 0; j < by; j++)
+			for (int i = 0; i < bx; i++)
+				pixels[k++] = array[i][j];
+	}
+
+	/**
+	 * An ImageAccess object calls this method to put a sub-image with the upper
+	 * left corner in the coordinate (x,y).
+	 * 
+	 * The sub-image input should be already created.
+	 * 
+	 * @param x
+	 *            x-coordinate in the source image
+	 * @param y
+	 *            y-coordinate in the source image
+	 * @param input
+	 *            an ImageAccess object that we want to put;
+	 */
+	public void putSubImage(int x, int y, ImageAccess input) {
+		if (input == null)
+			throw new ArrayStoreException("putSubImage: input == null.");
+		if (x < 0)
+			throw new IndexOutOfBoundsException("putSubImage: x < 0.");
+		if (y < 0)
+			throw new IndexOutOfBoundsException("putSubImage: y < 0.");
+		if (x >= nx)
+			throw new IndexOutOfBoundsException("putSubImage: x >= nx.");
+		if (y >= ny)
+			throw new IndexOutOfBoundsException("putSubImage: y >= ny.");
+		int nxcopy = input.getWidth();
+		int nycopy = input.getHeight();
+		// Reduces the size of the area to copy if it is too large
+		if (x + nxcopy > nx)
+			nxcopy = nx - x;
+		if (y + nycopy > ny)
+			nycopy = ny - y;
+		// Copies lines per lines
+		double[] dsrc = input.getPixels();
+		for (int j = 0; j < nycopy; j++)
+			System.arraycopy(dsrc, j * nxcopy, pixels, (j + y) * nx + x, nxcopy);
+	}
+
+	/**
+	 * An ImageAccess object calls this method to set a constant value to all
+	 * pixels of the image.
+	 * 
+	 * @param constant
+	 *            a constant value
+	 */
+	public void setConstant(double constant) {
+		for (int k = 0; k < size; k++)
+			pixels[k] = constant;
+	}
+
+	/**
+	 * Stretches the contrast inside an image so that the gray levels are in the
+	 * range 0 to 255.
+	 */
+	public void normalizeContrast() {
+		double minGoal = 0.0;
+		double maxGoal = 255.0;
+		// Search the min and max
+		double minImage = getMinimum();
+		double maxImage = getMaximum();
+		// Compute the parameter to rescale the gray levels
+		double a;
+		if (minImage - maxImage == 0) {
+			a = 1.0;
+			minImage = (maxGoal - minGoal) / 2.0;
+		}
+		else
+			a = (maxGoal - minGoal) / (maxImage - minImage);
+		for (int i = 0; i < size; i++) {
+			pixels[i] = (float) (a * (pixels[i] - minImage) + minGoal);
+		}
+	}
+
+	/**
+	 * Display an image at a specific position (x, y).
+	 * 
+	 * @param title
+	 *            a string for the title
+	 * @param loc
+	 *            Point for the location
+	 */
+	public void show(String title, java.awt.Point loc) {
+		FloatProcessor fp = createFloatProcessor();
+		fp.resetMinAndMax();
+		ImagePlus impResult = new ImagePlus(title, fp);
+		impResult.show();
+		ij.gui.ImageWindow window = impResult.getWindow();
+		window.setLocation(loc.x, loc.y);
+		impResult.show();
+	}
+
+	/**
+	 * Display an image.
+	 * 
+	 * @param title
+	 *            a string for the title of the window
+	 */
+	public void show(String title) {
+		FloatProcessor fp = createFloatProcessor();
+		fp.resetMinAndMax();
+		ImagePlus impResult = new ImagePlus(title, fp);
+		impResult.show();
+	}
+
+	/**
+	 * Compute the absolute value.
+	 */
+	public void abs() {
+		for (int k = 0; k < size; k++)
+			pixels[k] = Math.abs(pixels[k]);
+	}
+
+	/**
+	 * Compute the square root of an ImageAccess.
+	 */
+	public void sqrt() {
+		for (int k = 0; k < size; k++) {
+			pixels[k] = Math.sqrt(pixels[k]);
+		}
+	}
+
+	/**
+	 * Raised an ImageAccess object to the power a.
+	 * 
+	 * @param a
+	 *            input
+	 */
+	public void pow(final double a) {
+		for (int k = 0; k < size; k++) {
+			pixels[k] = Math.pow(pixels[k], a);
+		}
+	}
+
+	/**
+	 * An ImageAccess object calls this method for adding a constant to each
+	 * pixel.
+	 * 
+	 * @param constant
+	 *            a constant to be added
+	 */
+	public void add(double constant) {
+		for (int k = 0; k < size; k++)
+			pixels[k] += constant;
+	}
+
+	/**
+	 * An ImageAccess object calls this method for multiplying a constant to
+	 * each pixel.
+	 * 
+	 * @param constant
+	 *            a constant to be multiplied
+	 */
+	public void multiply(final double constant) {
+		for (int k = 0; k < size; k++)
+			pixels[k] *= constant;
+	}
+
+	/**
+	 * An ImageAccess object calls this method for adding a constant to each
+	 * pixel.
+	 * 
+	 * @param constant
+	 *            a constant to be subtracted
+	 */
+	public void subtract(final double constant) {
+		for (int k = 0; k < size; k++)
+			pixels[k] -= constant;
+	}
+
+	/**
+	 * An ImageAccess object calls this method for dividing a constant to each
+	 * pixel.
+	 * 
+	 * @param constant
+	 *            a constant to be divided
+	 */
+	public void divide(final double constant) {
+		if (constant == 0.0)
+			throw new ArrayStoreException("divide: Divide by 0");
+		for (int k = 0; k < size; k++)
+			pixels[k] /= constant;
+	}
+
+	/**
+	 * An ImageAccess object calls this method for adding two ImageAccess
+	 * objects.
+	 * 
+	 * [this = im1 + im2]
+	 * 
+	 * The resulting ImageAccess and the two operands should have the same size.
+	 * 
+	 * @param im1
+	 *            an ImageAccess object to be added
+	 * @param im2
+	 *            an ImageAccess object to be added
+	 */
+	public void add(ImageAccess im1, ImageAccess im2) {
+		if (im1.getWidth() != nx)
+			throw new ArrayStoreException("add: incompatible size.");
+		if (im1.getHeight() != ny)
+			throw new ArrayStoreException("add: incompatible size.");
+		if (im2.getWidth() != nx)
+			throw new ArrayStoreException("add: incompatible size.");
+		if (im2.getHeight() != ny)
+			throw new ArrayStoreException("add: incompatible size.");
+		double[] doubleOperand1 = im1.getPixels();
+		double[] doubleOperand2 = im2.getPixels();
+		for (int k = 0; k < size; k++)
+			pixels[k] = doubleOperand1[k] + doubleOperand2[k];
+	}
+
+	/**
+	 * An ImageAccess object calls this method for multiplying two ImageAccess
+	 * objects.
+	 * 
+	 * The resulting ImageAccess and the two operands should have the same size.
+	 * 
+	 * [this = im1 * im2]
+	 * 
+	 * @param im1
+	 *            an ImageAccess object to be multiplied
+	 * @param im2
+	 *            an ImageAccess object to be multiplied
+	 */
+
+	public void multiply(ImageAccess im1, ImageAccess im2) {
+		if (im1.getWidth() != nx)
+			throw new ArrayStoreException("multiply: incompatible size.");
+		if (im1.getHeight() != ny)
+			throw new ArrayStoreException("multiply: incompatible size.");
+		if (im2.getWidth() != nx)
+			throw new ArrayStoreException("multiply: incompatible size.");
+		if (im2.getHeight() != ny)
+			throw new ArrayStoreException("multiply: incompatible size.");
+		double[] doubleOperand1 = im1.getPixels();
+		double[] doubleOperand2 = im2.getPixels();
+		for (int k = 0; k < size; k++)
+			pixels[k] = doubleOperand1[k] * doubleOperand2[k];
+	}
+
+	/**
+	 * An ImageAccess object calls this method for subtracting two ImageAccess
+	 * objects.
+	 * 
+	 * The resulting ImageAccess and the two operands should have the same size.
+	 * 
+	 * [this = im1 - im2]
+	 * 
+	 * @param im1
+	 *            an ImageAccess object to be subtracted
+	 * @param im2
+	 *            an ImageAccess object to be subtracted
+	 */
+
+	public void subtract(ImageAccess im1, ImageAccess im2) {
+		if (im1.getWidth() != nx)
+			throw new ArrayStoreException("subtract: incompatible size.");
+		if (im1.getHeight() != ny)
+			throw new ArrayStoreException("subtract: incompatible size.");
+		if (im2.getWidth() != nx)
+			throw new ArrayStoreException("subtract: incompatible size.");
+		if (im2.getHeight() != ny)
+			throw new ArrayStoreException("subtract: incompatible size.");
+		double[] doubleOperand1 = im1.getPixels();
+		double[] doubleOperand2 = im2.getPixels();
+		for (int k = 0; k < size; k++)
+			pixels[k] = doubleOperand1[k] - doubleOperand2[k];
+	}
+
+	/**
+	 * An ImageAccess object calls this method for dividing two ImageAccess
+	 * objects.
+	 * 
+	 * [this = im1 / im2]
+	 * 
+	 * The resulting ImageAccess and the two operands should have the same size.
+	 * 
+	 * @param im1
+	 *            numerator
+	 * @param im2
+	 *            denominator
+	 */
+
+	public void divide(ImageAccess im1, ImageAccess im2) {
+		if (im1.getWidth() != nx)
+			throw new ArrayStoreException("divide: incompatible size.");
+		if (im1.getHeight() != ny)
+			throw new ArrayStoreException("divide: incompatible size.");
+		if (im2.getWidth() != nx)
+			throw new ArrayStoreException("divide: incompatible size.");
+		if (im2.getHeight() != ny)
+			throw new ArrayStoreException("divide: incompatible size.");
+		double[] doubleOperand1 = im1.getPixels();
+		double[] doubleOperand2 = im2.getPixels();
+		for (int k = 0; k < size; k++)
+			pixels[k] = doubleOperand1[k] / doubleOperand2[k];
+	}
+
+}
diff --git a/src/bilib/src/wavelets/WaveSpline.java b/src/bilib/src/wavelets/WaveSpline.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d16d4a91829cbc8625e730e68b9cc2eeaecab65
--- /dev/null
+++ b/src/bilib/src/wavelets/WaveSpline.java
@@ -0,0 +1,423 @@
+package wavelets;
+
+/**
+ * Spline wavelets transformation
+ * <hr>
+ * <p>
+ * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging
+ * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne,
+ * Switzerland
+ * </p>
+ * <p>
+ * <b>Authors</b>: Daniel Sage
+ * </p>
+ * <p>
+ * <b>Reference</b>: D. Sage, M. Unser, &#34;<a
+ * href="http://bigwww.epfl.ch/publications/sage0303.html">Teaching
+ * Image-Processing Programming in Java</a>,&#34; IEEE Signal Processing
+ * Magazine, vol. 20, no. 6, pp. 43-52, November 2003.
+ * </p>
+ * <p>
+ * More information: http://bigwww.epfl.ch/teaching/iplabsite/index.php
+ * </p>
+ * <p>
+ * Other relevant information are available at: http://bigwww.epfl.ch/
+ * </p>
+ * <hr>
+ * You'll be free to use this software for research purposes, but you should not
+ * redistribute it without our consent. In addition, we expect you to include a
+ * citation or acknowledgement whenever you present or publish results that are
+ * based on it.
+ */
+
+public class WaveSpline {
+
+	/**
+	 * Perform an wavelet transformation of the ImageObject calling this method
+	 * with n scale. The size of image should be a interger factor of 2 at the
+	 * power n. The input is an image. The result is the wavelet coefficients.
+	 * It is put in the ImageObject calling this method.
+	 * 
+	 * @param in
+	 *            an ImageAcess object provided by ImageJ
+	 * @param n
+	 *            a integer value giving the number of scale
+	 */
+	static public ImageAccess analysis(ImageAccess in, int order, int n) {
+
+		// Compute the size to the fine and coarse levels
+		int div = (int) Math.pow(2.0, (double) (n - 1));
+		int nxfine = in.getWidth();
+		int nyfine = in.getHeight();
+		int nxcoarse = nxfine / div;
+		int nycoarse = nyfine / div;
+
+		// Declare the object image
+		ImageAccess sub;
+
+		// Initialization
+		int nx = nxfine;
+		int ny = nyfine;
+		ImageAccess out = in.duplicate();
+
+		// From fine to coarse main loop
+		for (int i = 0; i < n; i++) {
+
+			// Create a new image array of size [nx,ny]
+			sub = new ImageAccess(nx, ny);
+
+			// Copy in[] into image[]
+			out.getSubImage(0, 0, sub);
+
+			// Apply the Wavelet splitting
+			sub = split(sub, order);
+
+			// Put the result image[] into in[]
+			out.putSubImage(0, 0, sub);
+
+			// Reduce the size by a factor of 2
+			nx = nx / 2;
+			ny = ny / 2;
+		}
+		return out;
+	}
+
+	/**
+	 * Perform 1 iteration of the wavelet transformation of an ImageObject. The
+	 * algorithm use the separability of the wavelet transformation. The result
+	 * of the computation is put in the ImageObject calling this method.
+	 * 
+	 * @param in
+	 *            an ImageAcess object provided by ImageJ
+	 */
+	static private ImageAccess split(ImageAccess in, int order) {
+		int nx = in.getWidth();
+		int ny = in.getHeight();
+		ImageAccess out = new ImageAccess(nx, ny);
+
+		WaveSplineFilter wf = new WaveSplineFilter(order);
+
+		if (nx >= 1) {
+			double rowin[] = new double[nx];
+			double rowout[] = new double[nx];
+			for (int y = 0; y < ny; y++) {
+				in.getRow(y, rowin);
+				split_1D(rowin, rowout, wf.h, wf.g);
+				out.putRow(y, rowout);
+			}
+		}
+		else {
+			// out.copy(in);
+			out = in.duplicate();
+		}
+
+		if (ny > 1) {
+			double colin[] = new double[ny];
+			double colout[] = new double[ny];
+			for (int x = 0; x < nx; x++) {
+				out.getColumn(x, colin);
+				split_1D(colin, colout, wf.h, wf.g);
+				out.putColumn(x, colout);
+			}
+		}
+
+		return out;
+	}
+
+	/**
+	 * Perform 1 iteration of the wavelet transformation of a 1D vector using
+	 * the spline wavelet transformation. The output vector has the same size of
+	 * the input vector and it contains first the low pass part of the wavelet
+	 * transform and then the high pass part of the wavelet transformation.
+	 * 
+	 * @param vin
+	 *            input, a double 1D vector
+	 * @param vout
+	 *            output, a double 1D vector
+	 * @param h
+	 *            input, a double 1D vector, lowpass filter
+	 * @param g
+	 *            input, a double 1D vector, highpass filter
+	 */
+	static private void split_1D(double vin[], double vout[], double h[], double g[]) {
+		int n = vin.length;
+		int n2 = n / 2;
+		int nh = h.length;
+		int ng = g.length;
+
+		// ///////////////////////////////////////////
+		// Order is 0 -> Haar Transform
+		// ///////////////////////////////////////////
+		if ((nh <= 1) || (ng <= 1)) {
+			double sqrt2 = Math.sqrt(2);
+			int j;
+			for (int i = 0; i < n2; i++) {
+				j = 2 * i;
+				vout[i] = (vin[j] + vin[j + 1]) / sqrt2;
+				vout[i + n2] = (vin[j] - vin[j + 1]) / sqrt2;
+			}
+			return;
+		}
+
+		// ///////////////////////////////////////////
+		// Order is higher than 0
+		// ///////////////////////////////////////////
+		double pix;
+		int j, k, j1, j2;
+		int period = 2 * n - 2; // period for mirror boundary conditions
+
+		for (int i = 0; i < n2; i++) {
+			j = i * 2;
+			pix = vin[j] * h[0];
+			for (k = 1; k < nh; k++) { // Low pass part
+				j1 = j - k;
+				if (j1 < 0) { // Mirror conditions
+					while (j1 < 0)
+						j1 += period; // Periodize
+					if (j1 >= n)
+						j1 = period - j1; // Symmetrize
+				}
+				j2 = j + k;
+				if (j2 >= n) { // Mirror conditions
+					while (j2 >= n)
+						j2 -= period; // Periodize
+					if (j2 < 0)
+						j2 = -j2; // Symmetrize
+				}
+				pix = pix + h[k] * (vin[j1] + vin[j2]);
+			}
+			vout[i] = pix;
+
+			j = j + 1;
+			pix = vin[j] * g[0]; // High pass part
+			for (k = 1; k < ng; k++) {
+				j1 = j - k;
+				if (j1 < 0) { // Mirror conditions
+					while (j1 < 0)
+						j1 += period; // Periodize
+					if (j1 >= n)
+						j1 = period - j1; // Symmetrize
+				}
+				j2 = j + k;
+				if (j2 >= n) { // Mirror conditions
+					while (j2 >= n)
+						j2 -= period; // Periodize
+					if (j2 < 0)
+						j2 = -j2; // Symmetrize
+				}
+				pix = pix + g[k] * (vin[j1] + vin[j2]);
+			}
+			vout[i + n2] = pix;
+		}
+	}
+
+	/**
+	 * Perform an inverse wavelet transformation of the ImageObject calling this
+	 * method with n scale. The size of image should be a interger factor of 2
+	 * at the power n. The input is the results of a wavelet transformation. The
+	 * result is the reconstruction. It is put in the ImageObject calling this
+	 * method.
+	 * 
+	 * @param in
+	 *            an ImageAcess object provided by ImageJ
+	 * @param n
+	 *            a integer value giving the number of scale
+	 */
+
+	static public ImageAccess synthesis(ImageAccess in, int order, int n) {
+		// Compute the size to the fine and coarse levels
+		int div = (int) Math.pow(2.0, (double) (n - 1));
+		int nxcoarse = in.getWidth() / div;
+		int nycoarse = in.getHeight() / div;
+
+		// Declare the object image
+		ImageAccess sub;
+
+		// Initialisazion
+		int nx = nxcoarse;
+		int ny = nycoarse;
+		ImageAccess out = in.duplicate();
+
+		// From fine to coarse main loop
+		for (int i = 0; i < n; i++) {
+
+			// Create a new image array of size [nx,ny]
+			sub = new ImageAccess(nx, ny);
+
+			// Copy in[] into image[]
+			out.getSubImage(0, 0, sub);
+
+			// Apply the Wavelet splitting
+			sub = merge(sub, order);
+
+			// Put the result image[] into in[]
+			out.putSubImage(0, 0, sub);
+			// Reduce the size by a factor of 2
+			nx = nx * 2;
+			ny = ny * 2;
+		}
+		return out;
+	}
+
+	/**
+	 * Perform 1 iteration of the inverse wavelet transformation of an
+	 * ImageObject. The algorithm use the separability of the wavelet
+	 * transformation. The result of the computation is put in the ImageObject
+	 * calling this method.
+	 * 
+	 * @param in
+	 *            an ImageAcess object provided by ImageJ
+	 */
+	static private ImageAccess merge(ImageAccess in, int order) {
+		int nx = in.getWidth();
+		int ny = in.getHeight();
+		ImageAccess out = new ImageAccess(nx, ny);
+		WaveSplineFilter wf = new WaveSplineFilter(order);
+
+		if (nx >= 1) {
+			int nx2 = nx / 2;
+			double rowin[] = new double[nx];
+			double rowout[] = new double[nx];
+			for (int y = 0; y < ny; y++) {
+				in.getRow(y, rowin);
+				merge_1D(rowin, rowout, wf.h, wf.g);
+				out.putRow(y, rowout);
+			}
+		}
+		else {
+			out = in.duplicate();
+		}
+
+		if (ny > 1) {
+			int ny2 = ny / 2;
+			double colin[] = new double[ny];
+			double colout[] = new double[ny];
+			for (int x = 0; x < nx; x++) {
+				out.getColumn(x, colin);
+				merge_1D(colin, colout, wf.h, wf.g);
+				out.putColumn(x, colout);
+			}
+		}
+		return out;
+	}
+
+	/**
+	 * Perform 1 iteration of the inverse wavelet transformation of a 1D vector
+	 * using the Spline wavelet transformation. The output vector has the same
+	 * size of the input vector and it contains the reconstruction of the input
+	 * signal. The input vector constains first the low pass part of the wavelet
+	 * transform and then the high pass part of the wavelet transformation.
+	 * 
+	 * @param vin
+	 *            input, a double 1D vector
+	 * @param vout
+	 *            output, a double 1D vector
+	 * @param h
+	 *            input, a double 1D vector, lowpass filter
+	 * @param g
+	 *            input, a double 1D vector, highpass filter
+	 */
+	static private void merge_1D(double vin[], double vout[], double h[], double g[]) {
+		int n = vin.length;
+		int n2 = n / 2;
+		int nh = h.length;
+		int ng = g.length;
+
+		// ///////////////////////////////////////////
+		// Order is 0 -> Haar Transform
+		// ///////////////////////////////////////////
+		if ((nh <= 1) || (ng <= 1)) {
+			double sqrt2 = Math.sqrt(2);
+			for (int i = 0; i < n2; i++) {
+				vout[2 * i] = (vin[i] + vin[i + n2]) / sqrt2;
+				vout[2 * i + 1] = (vin[i] - vin[i + n2]) / sqrt2;
+			}
+			return;
+		}
+
+		// ///////////////////////////////////////////
+		// Order is higher than 0
+		// ///////////////////////////////////////////
+		double pix1, pix2;
+		int j, k, kk, i1, i2;
+		int k01 = (nh / 2) * 2 - 1;
+		int k02 = (ng / 2) * 2 - 1;
+
+		int period = 2 * n2 - 1; // period for mirror boundary conditions
+
+		for (int i = 0; i < n2; i++) {
+			j = 2 * i;
+			pix1 = h[0] * vin[i];
+			for (k = 2; k < nh; k += 2) {
+				i1 = i - (k / 2);
+				if (i1 < 0) {
+					i1 = (-i1) % period;
+					if (i1 >= n2)
+						i1 = period - i1;
+				}
+				i2 = i + (k / 2);
+				if (i2 > n2 - 1) {
+					i2 = i2 % period;
+					if (i2 >= n2)
+						i2 = period - i2;
+				}
+				pix1 = pix1 + h[k] * (vin[i1] + vin[i2]);
+			}
+
+			pix2 = 0.;
+			for (k = -k02; k < ng; k += 2) {
+				kk = Math.abs(k);
+				i1 = i + (k - 1) / 2;
+				if (i1 < 0) {
+					i1 = (-i1 - 1) % period;
+					if (i1 >= n2)
+						i1 = period - 1 - i1;
+				}
+				if (i1 >= n2) {
+					i1 = i1 % period;
+					if (i1 >= n2)
+						i1 = period - 1 - i1;
+				}
+				pix2 = pix2 + g[kk] * vin[i1 + n2];
+			}
+
+			vout[j] = pix1 + pix2;
+
+			j = j + 1;
+			pix1 = 0.;
+			for (k = -k01; k < nh; k += 2) {
+				kk = Math.abs(k);
+				i1 = i + (k + 1) / 2;
+				if (i1 < 0) {
+					i1 = (-i1) % period;
+					if (i1 >= n2)
+						i1 = period - i1;
+				}
+				if (i1 >= n2) {
+					i1 = (i1) % period;
+					if (i1 >= n2)
+						i1 = period - i1;
+				}
+				pix1 = pix1 + h[kk] * vin[i1];
+			}
+			pix2 = g[0] * vin[i + n2];
+			for (k = 2; k < ng; k += 2) {
+				i1 = i - (k / 2);
+				if (i1 < 0) {
+					i1 = (-i1 - 1) % period;
+					if (i1 >= n2)
+						i1 = period - 1 - i1;
+				}
+				i2 = i + (k / 2);
+				if (i2 > n2 - 1) {
+					i2 = i2 % period;
+					if (i2 >= n2)
+						i2 = period - 1 - i2;
+				}
+				pix2 = pix2 + g[k] * (vin[i1 + n2] + vin[i2 + n2]);
+			}
+			vout[j] = pix1 + pix2;
+		}
+	}
+
+}
diff --git a/src/bilib/src/wavelets/WaveSplineFilter.java b/src/bilib/src/wavelets/WaveSplineFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..767390e15bd0519628454f0600d1fe87934c7592
--- /dev/null
+++ b/src/bilib/src/wavelets/WaveSplineFilter.java
@@ -0,0 +1,181 @@
+package wavelets;
+
+/**
+ * Spline wavelets transformation
+ * <hr>
+ * <p>
+ * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging
+ * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne,
+ * Switzerland
+ * </p>
+ * <p>
+ * <b>Authors</b>: Daniel Sage
+ * </p>
+ * <p>
+ * <b>Reference</b>: D. Sage, M. Unser, &#34;<a
+ * href="http://bigwww.epfl.ch/publications/sage0303.html">Teaching
+ * Image-Processing Programming in Java</a>,&#34; IEEE Signal Processing
+ * Magazine, vol. 20, no. 6, pp. 43-52, November 2003.
+ * </p>
+ * <p>
+ * More information: http://bigwww.epfl.ch/teaching/iplabsite/index.php
+ * </p>
+ * <p>
+ * Other relevant information are available at: http://bigwww.epfl.ch/
+ * </p>
+ * <hr>
+ * You'll be free to use this software for research purposes, but you should not
+ * redistribute it without our consent. In addition, we expect you to include a
+ * citation or acknowledgement whenever you present or publish results that are
+ * based on it.
+ */
+
+public class WaveSplineFilter {
+
+	/**
+	 * real lowpass filter.
+	 */
+	public double	h[];
+
+	/**
+	 * real highpass filter.
+	 */
+	public double	g[];
+
+	WaveSplineFilter(int order) {
+		switch (order) {
+
+		case 0:
+			h = new double[1];
+			h[0] = Math.sqrt(2.0);
+			break;
+
+		case 1:
+			h = new double[47];
+			h[0] = 0.81764640621546;
+			h[1] = 0.39729708810751;
+			h[2] = -0.06910098743038;
+			h[3] = -0.05194534825542;
+			h[4] = 0.01697104840045;
+			h[5] = 0.00999059568192;
+			h[6] = -0.00388326235731;
+			h[7] = -0.00220195129177;
+			h[8] = 0.00092337104427;
+			h[9] = 0.00051163604209;
+			h[10] = -0.00022429633694;
+			h[11] = -0.00012268632858;
+			h[12] = 0.00005535633860;
+			h[13] = 0.00003001119291;
+			h[14] = -0.00001381880394;
+			h[15] = -0.00000744435611;
+			h[16] = 0.00000347980027;
+			h[17] = 0.00000186561005;
+			h[18] = -0.00000088225856;
+			h[19] = -0.00000047122304;
+			h[20] = 0.00000022491351;
+			h[21] = 0.00000011976480;
+			h[22] = -0.00000005759525;
+			h[23] = -0.00000003059265;
+			h[24] = 0.00000001480431;
+			h[25] = 0.00000000784714;
+			h[26] = -0.00000000381742;
+			h[27] = -0.00000000201987;
+			h[28] = 0.00000000098705;
+			h[29] = 0.00000000052147;
+			h[30] = -0.00000000025582;
+			h[31] = -0.00000000013497;
+			h[32] = 0.00000000006644;
+			h[33] = 0.00000000003501;
+			h[34] = -0.00000000001729;
+			h[35] = -0.00000000000910;
+			h[36] = 0.00000000000451;
+			h[37] = 0.00000000000237;
+			h[38] = -0.00000000000118;
+			h[39] = -0.00000000000062;
+			h[40] = 0.00000000000031;
+			h[41] = 0.00000000000016;
+			h[42] = -0.00000000000008;
+			h[43] = -0.00000000000004;
+			h[44] = 0.00000000000002;
+			h[45] = 0.00000000000001;
+			break;
+
+		case 3:
+			double temp[] = { 0.76613005375980, 0.43392263358931, -0.05020172467149, -0.11003701838811, 0.03208089747022, 0.04206835144072, -0.01717631549201, -0.01798232098097,
+					0.00868529481309, 0.00820147720600, -0.00435383945777, -0.00388242526560, 0.00218671237015, 0.00188213352389, -0.00110373982039, -0.00092719873146, 0.00055993664336,
+					0.00046211522752, -0.00028538371867, -0.00023234729403, 0.00014604186978, 0.00011762760216, -0.00007499842461, -0.00005987934057, 0.00003863216129, 0.00003062054907,
+					-0.00001995254847, -0.00001571784835, 0.00001032898225, 0.00000809408097, -0.00000535805976 - 0.00000417964096, 0.00000278450629, 0.00000216346143, -0.00000144942177,
+					-0.00000112219704, 0.00000075557065, 0.00000058316635, -0.00000039439119, -0.00000030355006, 0.00000020610937, 0.00000015823692, -0.00000010783016, -0.00000008259641,
+					0.00000005646954, 0.00000004316539, -0.00000002959949, -0.00000002258313, 0.00000001552811, 0.00000001182675, -0.00000000815248, -0.00000000619931, 0.00000000428324,
+					0.00000000325227, -0.00000000225188, -0.00000000170752, 0.00000000118465, 0.00000000089713, -0.00000000062357, -0.00000000047167, 0.00000000032841, 0.00000000024814,
+					-0.00000000017305, -0.00000000013062, 0.00000000009123, 0.00000000006879, -0.00000000004811, -0.00000000003625, 0.00000000002539, 0.00000000001911, -0.00000000001340,
+					-0.00000000001008, 0.00000000000708, 0.00000000000532, -0.00000000000374, -0.00000000000281, 0.00000000000198, 0.00000000000148, -0.00000000000104, -0.00000000000078,
+					0.00000000000055, 0.00000000000041, -0.00000000000029, -0.00000000000022, 0.00000000000015, 0.00000000000012, -0.00000000000008 - 0.00000000000006, 0.00000000000004,
+					0.00000000000003, -0.00000000000002, -0.00000000000002, 0.00000000000001, 0.00000000000001, -0.00000000000001, -0.00000000000000 };
+			h = temp;
+			break;
+
+		case 5:
+			h = new double[42];
+			h[0] = 0.74729;
+			h[1] = 0.4425;
+			h[2] = -0.037023;
+			h[3] = -0.12928;
+			h[4] = 0.029477;
+			h[5] = 0.061317;
+			h[6] = -0.021008;
+			h[7] = -0.032523;
+			h[8] = 0.014011;
+			h[9] = 0.01821;
+			h[10] = -0.0090501;
+			h[11] = -0.010563;
+			h[12] = 0.0057688;
+			h[13] = 0.0062796;
+			h[14] = -0.0036605;
+			h[15] = -0.0037995;
+			h[16] = 0.0023214;
+			h[17] = 0.0023288;
+			h[18] = -0.0014738;
+			h[19] = -0.0014414;
+			h[20] = 0.00093747;
+			h[21] = 0.00089889;
+			h[22] = -0.00059753;
+			h[23] = -0.00056398;
+			h[24] = 0.00038165;
+			h[25] = 0.00035559;
+			h[26] = -0.00024423;
+			h[27] = -0.00022512;
+			h[28] = 0.00015658;
+			h[29] = 0.00014301;
+			h[30] = -0.00010055;
+			h[31] = -9.1113e-05;
+			h[32] = 6.4669e-05;
+			h[33] = 5.8198e-05;
+			h[34] = -4.1649e-05;
+			h[35] = -3.7256e-05;
+			h[36] = 2.729e-05;
+			h[37] = 2.458e-05;
+			h[38] = -2.2593e-05;
+			h[39] = -3.5791e-05;
+			h[40] = -1.7098e-05;
+			h[41] = -2.9619e-06;
+			break;
+		}
+
+		g = new double[h.length];
+		if (order > 0) {
+			g[0] = h[0];
+			for (int k = 1; k < h.length; k++) {
+				if ((k / 2) * 2 == k)
+					g[k] = h[k];
+				else
+					g[k] = -h[k];
+			}
+		}
+		else {
+			g[0] = -h[0];
+		}
+
+	}
+
+}
\ No newline at end of file
diff --git a/src/src/eigenpsf/AdvancedSettingsPanel.java b/src/src/eigenpsf/AdvancedSettingsPanel.java
index 68abe9e94e5c1aedf64182a752ecae5bc5ed0f08..c48457a1f0d4cb39de0e8acf702a452ee2b7e1b6 100644
--- a/src/src/eigenpsf/AdvancedSettingsPanel.java
+++ b/src/src/eigenpsf/AdvancedSettingsPanel.java
@@ -21,7 +21,6 @@ import eigenpsf.gui.SpinnerInfoRangeInteger;
 
 public class AdvancedSettingsPanel extends JPanel implements ChangeListener, KeyListener, ItemListener {
 	
-	private SpinnerInfoRangeDouble spnQuantile = new SpinnerInfoRangeDouble(Params.quantile, 0.01, 1000, 0.1, "Threshold to discard bad beads from the quality score (see related column in the result table).", "http://bigwww.epfl.ch/");
 	private SpinnerInfoRangeInteger spnNumberPoints = new SpinnerInfoRangeInteger(Params.nthin, 1, 100, 1, "Number of thin-plate splines used to model the global background. The smaller it is, the smoother the estimated background will be..", "http://bigwww.epfl.ch/");	
 	private SpinnerInfoRangeInteger spnScaleBack = new SpinnerInfoRangeInteger(Params.scaleBack, 1, 100, 1, "Data reduction factor to speed up computation (e.g., if set to 2, then the estimation of the global background will be performed on two-times subsampled images)", "http://bigwww.epfl.ch/");	
 	private SpinnerInfoRangeDouble spnScaleMin = new SpinnerInfoRangeDouble(Params.smin, 0.1, 10, 0.1, "Minimum scaling factor of the basis used in SIFT detection.", "http://bigwww.epfl.ch/");
@@ -95,11 +94,7 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key
 		i++;
 		pn1.place(i, 0, 4, 1, "<html> <b> Compute EigenPSF </b> </html>");
 		i++;
-		pn1.place(i, 0, 4, 1, new JSeparator());
-		i++;
-		pn1.place(i, 1,"Quality threshold");
-		pn1.place(i, 2,spnQuantile);
-		pn1.place(i, 3,spnQuantile.info);
+		pn1.place(i, 0, 4, 1, new JSeparator());		
 		i++;
 		pn1.place(i, 1,"Beads normalization");
 		pn1.place(i, 2,cmbNormalization);
@@ -190,7 +185,7 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key
 		setLayout(new BorderLayout());
 		add(pn1, BorderLayout.CENTER);
 
-		spnQuantile.addChangeListener(this);
+		//spnQuantile.addChangeListener(this);
 		spnNumberPoints.addChangeListener(this);
 		spnScaleBack.addChangeListener(this);
 		spnScaleMin.addChangeListener(this);
@@ -199,7 +194,7 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key
 		spnNbBasis.addChangeListener(this);
 		cmbNormalization.addItemListener(this);
 		
-		spnQuantile.addKeyListener(this);
+		//spnQuantile.addKeyListener(this);
 		spnNumberPoints.addKeyListener(this);
 		spnScaleBack.addKeyListener(this);
 		spnScaleMin.addKeyListener(this);
@@ -229,7 +224,6 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key
 	private void updateParams() {
 		
 		try { 
-			Params.quantile = spnQuantile.get(); 
 			Params.nthin = spnNumberPoints.get(); 
 			//Params.nthin2D = spnNumberPoints.get(); 
 			//Params.nthin3D = spnNumberPoints.get(); 
@@ -256,7 +250,7 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key
 	
 	private void setGUI() {
 		try {
-			spnQuantile.set(Params.quantile); 
+			//spnQuantile.set(Params.quantile); 
 			spnNumberPoints.set(Params.nthin); 
 			//spnNumberPoints.set(Params.nthin2D); 
 			//spnNumberPoints.set(Params.nthin3D); 
diff --git a/src/src/eigenpsf/Constants.java b/src/src/eigenpsf/Constants.java
index 533af14bfc2cfcce1ab567995d97693796f45bab..b581c6af14cc74dcf5b0504b59431e80e0b4c710 100644
--- a/src/src/eigenpsf/Constants.java
+++ b/src/src/eigenpsf/Constants.java
@@ -5,7 +5,7 @@ public class Constants {
 	static final public String title = "(c) CNRS and BIG EPFL";
 	static final public String name = "Eigen PSF";
 	static final public String version = "0.3";
-	static final public String url = "http://bigwww.epfl.ch/";
+	static final public String url = "https://gitlab.irit.fr/mambo/eigenpsf-extractor";
 	static final public String copyright = "(c) CNRS and BIG EPFL";
 	
 }
diff --git a/src/src/eigenpsf/SettingsDialog.java b/src/src/eigenpsf/SettingsDialog.java
index e11e0587635fb2d7710012ef3b2db8872459b9d9..a6d124fdd76b9bf3e1cebefd6b90ba7ca313f082 100644
--- a/src/src/eigenpsf/SettingsDialog.java
+++ b/src/src/eigenpsf/SettingsDialog.java
@@ -49,6 +49,8 @@ public class SettingsDialog extends JDialog implements ActionListener, KeyListen
 	private SpinnerRangeInteger spnPSFZ = new SpinnerRangeInteger(Params.psf_visible_width[2], 1, 9999, 2);
 	private JRadioButton bn2D = new JRadioButton("2D", true);
 	private JRadioButton bn3D = new JRadioButton("3D");
+	private SpinnerInfoRangeDouble spnQuantile = new SpinnerInfoRangeDouble(Params.quantile, 0.01, 1000, 0.1, "Threshold to discard bad beads from the quality score (see related column in the result table).", "http://bigwww.epfl.ch/");
+
 
 	private JButton bnGetROI = new JButton("Get ROI");
 	private JTabbedPane tab = new JTabbedPane();
@@ -138,23 +140,18 @@ public class SettingsDialog extends JDialog implements ActionListener, KeyListen
 		i++;
 		pn.place(i, 0, 4, 1, new JSeparator());
 		i++;
-		pn.place(i, 0, 4, 1, "<html><b>Modality</b></html>");
-		pn.place(i, 1, cmbModality);
-		pn.place(i, 2, bn2D);
-		pn.place(i, 3, bn3D);
+		pn.place(i, 0, 4, 1, "<html><b>2D/3D</b></html>");
+		//pn.place(i, 1, cmbModality);
+		pn.place(i, 1, bn2D);
+		pn.place(i, 2, bn3D);
 		i++;
-		pn.place(i, 0, "PSF Support (XY)");
+		pn.place(i, 0, "PSF Support XY (px)");
 		pn.place(i, 1, spnPSFXY);
-		pn.place(i, 2, "px");
-		pn.place(i, 3, bnGetROI);
+		pn.place(i, 2, bnGetROI);
 		i++;
-		pn.place(i, 0, "PSF Support (Z)");
+		pn.place(i, 0, "PSF Support Z (nb slices)");
 		pn.place(i, 1, spnPSFZ);
-		pn.place(i, 2, 2, 1, "number of slices");
-		i++; 
-		pn.place(i, 0, "Eigen Components");
-		pn.place(i, 1, spnNbEigen);
-		pn.place(i, 2, spnNbEigen.info);
+		
 
 		i++;
 		pn.place(i, 0, 4, 1, new JSeparator());
@@ -163,12 +160,26 @@ public class SettingsDialog extends JDialog implements ActionListener, KeyListen
 		i++;
 		pn.place(i, 0, 4, 1, new JSeparator());
 		i++;
-		pn.place(i, 0, "<html><b>Detection Threshold</b></html>");
+		pn.place(i, 0, "Detection Threshold");
 		//pn.place(i, 1, 3, 1, cmbDetection);
 		//i++;
 		//pn.place(i, 1, "Threshold");
 		pn.place(i, 1, spnThreshold);
 		pn.place(i, 2, spnThreshold.info);
+		i++;
+		pn.place(i, 0, 4, 1, new JSeparator());
+		i++;
+		pn.place(i, 0, 4, 1, "<html> <b> Compute EigenPSF </b> </html>");
+		i++;
+		pn.place(i, 0, 4, 1, new JSeparator());	
+		i++; 
+		pn.place(i, 0, "Eigen Components");
+		pn.place(i, 1, spnNbEigen);
+		pn.place(i, 2, spnNbEigen.info);
+		i++;
+		pn.place(i, 0,"Quality threshold");
+		pn.place(i, 1,spnQuantile);
+		pn.place(i, 2,spnQuantile.info);
 
 		//i++;
 		//pn.place(i, 0, "<html><b>Registration</b></html>");
@@ -250,6 +261,7 @@ public class SettingsDialog extends JDialog implements ActionListener, KeyListen
 			Params.thresholdDetection = spnThreshold.get();
 			Params.NeigenElements = spnNbEigen.get();
 			Params.dim = bn2D.isSelected() ? 2 : 3;
+			Params.quantile = spnQuantile.get(); 
 		} catch (Exception ex) {
 		}
 	}
diff --git a/src/src/eigenpsf/processing/Processing.java b/src/src/eigenpsf/processing/Processing.java
index c4b416ef94c306375109ed0767c79f83af7085f1..7c8afb80559c0a52855fecbc6807ad61e1f2b05c 100644
--- a/src/src/eigenpsf/processing/Processing.java
+++ b/src/src/eigenpsf/processing/Processing.java
@@ -117,6 +117,7 @@ public class Processing {
 					// HyperMatrix tmp2 = large_patch.getPatch(center,Params.psf_visible_width);
 					 //tmp2.display("Before register patch",null);
 					shift = reg_meth.process(large_patch); // Register patch
+					//System.out.printf("%f | %f | %f \n", shift[0],shift[1],shift[2]);
 					// -- Update patch position
 					double xx = patch.xp + shift[0];
 					double yy = patch.yp + shift[1];
@@ -124,28 +125,28 @@ public class Processing {
 					patch.xp = (int) Math.round(xx);
 					patch.yp = (int) Math.round(yy);
 					patch.zp = (int) Math.round(zz);
-					patch.x = patch.xp - xx;
-					patch.y = patch.yp - yy;
-					patch.z = patch.zp - zz;
+					patch.x = xx - patch.xp;
+					patch.y = yy - patch.yp;
+					patch.z = zz - patch.zp;
 					// -- Extract and store patch
-					int[] shift_fl = { (int) Math.round(shift[0]) + center[0],
-							(int) Math.round(shift[1]) + center[1], (int) Math.round(shift[2]) + center[2] };
+					int[] shift_fl = {  center[0] + (int) Math.round(shift[0]),
+							center[1] + (int) Math.round(shift[1]) , center[2] + (int) Math.round(shift[2])  };
 					patch.data = large_patch.getPatch(shift_fl, Params.psf_visible_width);
 					//patch.data.display("After register patch",null);
 					// -- Preprocess patch for SVD
 					//project.patchesForSvd.add(large_patch.imtranslate(patch.x, patch.y, patch.z)
 					//		.getPatch(shift_fl, Params.psf_visible_width).normalize());
-					patch.data_register = large_patch.imtranslate(patch.x, patch.y, patch.z).getPatch(shift_fl, Params.psf_visible_width).normalize();
+					patch.data_register = large_patch.imtranslate(-patch.x, -patch.y,- patch.z).getPatch(shift_fl, Params.psf_visible_width).normalize();
 					//project.patchesForSvd.get(project.patchesForSvd.size()-1).display("After translation patch",null);
 				}
 			}
 		}
 		
 		// Prune bad beads
+		project.isPreprocessed = true;
 		pruneBeads(project);
 		
 		Log.progress("End Preprocess patches", 100);
-		project.isPreprocessed = true;
 	}
 	
 	public static void pruneBeads(Project project) {
diff --git a/src/src/eigenpsf/project/MainDialog.java b/src/src/eigenpsf/project/MainDialog.java
index ea4f79349103d78500ca67eedf8795a47773260c..578b1bcff1ea022386cc30bbf9e2f61bf3a682bb 100644
--- a/src/src/eigenpsf/project/MainDialog.java
+++ b/src/src/eigenpsf/project/MainDialog.java
@@ -7,7 +7,10 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.WindowEvent;
 import java.awt.event.WindowListener;
+import java.io.BufferedWriter;
 import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.util.ArrayList;
 
 import javax.swing.BorderFactory;
@@ -23,12 +26,19 @@ import eigenpsf.Constants;
 import eigenpsf.Log;
 import eigenpsf.Params;
 import eigenpsf.SettingsDialog;
+import eigenpsf.data.HyperMatrix;
 import eigenpsf.filemanager.IO;
 import eigenpsf.gui.WalkBar;
 import eigenpsf.lib.Loader;
 import eigenpsf.processing.ProcessingPanel;
+import eigenpsf.stack.Patch;
 import eigenpsf.stack.ZStack;
+import eigenpsf.stack.Patches;
+import ij.IJ;
+import ij.ImagePlus;
 import ij.gui.GUI;
+import ij.process.Blitter;
+import ij.process.ImageProcessor;
 
 public class MainDialog extends JDialog implements ActionListener, WindowListener {
 
@@ -92,7 +102,7 @@ public class MainDialog extends JDialog implements ActionListener, WindowListene
 	
 	@Override
 	public void actionPerformed(ActionEvent e) {
-		if (e.getSource() == toolbar.bnReset) {
+		/*if (e.getSource() == toolbar.bnReset) {
 			int ret = JOptionPane.showConfirmDialog(null, "Do you want to clear all the project?", "Warning", JOptionPane.YES_NO_OPTION);
 			if (ret == JOptionPane.YES_OPTION) {
 				project.reset();
@@ -101,10 +111,11 @@ public class MainDialog extends JDialog implements ActionListener, WindowListene
 				Log.clear();
 			}
 		}
-		else if (e.getSource() == toolbar.bnSetting) {
+		else */
+		if (e.getSource() == toolbar.bnSetting) {
 			settingsDialog.setVisible(true);
 		}
-		else if (e.getSource() == toolbar.bnOpen) {
+		/*else if (e.getSource() == toolbar.bnOpen) {
 			project.path = IO.browseOpenProject(project.path);
 			String filename = project.path + File.separator + "listfiles.csv";
 			File file = new File(filename);
@@ -116,12 +127,109 @@ public class MainDialog extends JDialog implements ActionListener, WindowListene
 			else {
 				Log.write(project.path + " is empty");
 			}
-		}
+		}*/
 		else if (e.getSource() == toolbar.bnSave) {
 			project.path = IO.browseSaveProject(project.path);
-			if (project.path != null)
+			if (project.path != null) {
 				project.save(project.path);
-			
+				pnProcessing.table.update(project);
+				
+				File file = new File(project.path + File.separator +  "patchesTable.csv");
+				try {
+					System.out.printf("Coucou");
+					BufferedWriter buffer = new BufferedWriter(new FileWriter(file));
+					int nrows = pnProcessing.table.getRowCount();
+					int ncols = pnProcessing.table.getColumnCount();
+					System.out.printf("Coucou-1");
+					String row = "";
+					//for (int c = 0; c < ncols; c++)						
+					//	row += pnProcessing.table.columns.get(c).getHeader() + (c == ncols - 1 ? "" : "\t ");
+					//buffer.write(row + "\n");
+					// Add header manually ...
+					buffer.write("Image name \t ID \t X \t Y \t Z \t Quality \t Dist \t Max \t Valid  \n");
+					
+					for (int r = 0; r < nrows; r++) {
+						row = "";
+						for (int c = 0; c < ncols; c++) {
+							Object O = pnProcessing.table.getModel().getValueAt(r, c);
+							row += O + (c == ncols - 1 ? "" : "\t ");
+						}
+						buffer.write(row + "\n");
+					}
+					buffer.close();
+				}
+				catch (Exception ex) {
+					System.out.println(ex);
+				}
+				
+				int[] sz;
+				if (Params.dim == 2) {
+					sz = new int[] { Params.psf_visible_width[0], Params.psf_visible_width[1],1 };
+				}
+				else {
+					sz = new int[] { Params.psf_visible_width[0], Params.psf_visible_width[1], Params.psf_visible_width[2] };
+				}
+				Patches sel = project.getAllPatchesSortedByGlobId();
+				ImagePlus imp_raw = IJ.createHyperStack("Raw Patches", sz[0], sz[1], 1, sz[2], sel.size(), 32);
+				ImagePlus imp_process = IJ.createHyperStack("Processed Patches", sz[0], sz[1], 1, sz[2], sel.size(), 32);
+				ImagePlus imp_back = IJ.createHyperStack("Background Patches", sz[0], sz[1], 1, sz[2], sel.size(), 32);
+				ImagePlus imp_proj = IJ.createHyperStack("Projected Patches", sz[0], sz[1], 1, sz[2], sel.size(), 32);
+				int i = 1;
+				for (Patch patch : sel) {
+					int pos[];
+					if (Params.dim == 2) {
+						pos = new int[] { patch.xp, patch.yp, 0};
+					}
+					else {
+						pos = new int[] { patch.xp, patch.yp, patch.zp };
+					}
+					HyperMatrix im = patch.stack.getHyperMatrix();
+					ImagePlus p_raw = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz));
+					ImagePlus p_back = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz).sub(patch.data));
+					ImagePlus p_proj = HyperMatrix.HyperMatrix2ImagePlus(project.eigen.projPatch(patch));
+					ImagePlus p_process;
+					if (project.isPreprocessed)
+						p_process = HyperMatrix.HyperMatrix2ImagePlus(patch.data_register);			
+					else
+						p_process = HyperMatrix.HyperMatrix2ImagePlus(patch.data);	
+					if (p_raw != null) {
+						for (int z = 1; z <= Math.min(p_raw.getNSlices(), sz[2]); z++) {
+							// Raw
+							ImageProcessor ip = p_raw.getStack().getProcessor(z);
+							imp_raw.setPositionWithoutUpdate(1, z, i);
+							ImageProcessor op = imp_raw.getProcessor();
+							op.copyBits(ip, 0, 0, Blitter.COPY);
+							// Processed
+							ip = p_process.getStack().getProcessor(z);
+							imp_process.setPositionWithoutUpdate(1, z, i);
+							op = imp_process.getProcessor();
+							op.copyBits(ip, 0, 0, Blitter.COPY);
+							// Back
+							ip = p_back.getStack().getProcessor(z);
+							imp_back.setPositionWithoutUpdate(1, z, i);
+							op = imp_back.getProcessor();
+							op.copyBits(ip, 0, 0, Blitter.COPY);
+							// Proj
+							ip = p_proj.getStack().getProcessor(z);
+							imp_proj.setPositionWithoutUpdate(1, z, i);
+							op = imp_proj.getProcessor();
+							op.copyBits(ip, 0, 0, Blitter.COPY);
+						}
+					}
+					i++;
+				}
+				ij.IJ.save(imp_raw,project.path + File.separator +  "RawPatches.tif");
+				ij.IJ.save(imp_process,project.path + File.separator +  "ProcessedPatches.tif");
+				ij.IJ.save(imp_back,project.path + File.separator +  "BackgroundPatches.tif");
+				ij.IJ.save(imp_proj,project.path + File.separator +  "ProjectedPatches.tif");
+						
+				
+				ImagePlus eig = HyperMatrix.HyperMatrix2ImagePlus(project.eigen.eigenElements);
+				ij.IJ.save(eig,project.path + File.separator +  "EigenPSF.tif");
+				
+				
+
+			}			
 		}
 		
 		else if (e.getSource() == bnAdd) {
diff --git a/src/src/eigenpsf/project/Toolbar.java b/src/src/eigenpsf/project/Toolbar.java
index 27355cae8a949501962f4d20a7aec415550d402d..95b990a6f54a7462bca7d7acc28d89ac2834dc96 100644
--- a/src/src/eigenpsf/project/Toolbar.java
+++ b/src/src/eigenpsf/project/Toolbar.java
@@ -47,15 +47,15 @@ public class Toolbar extends JToolBar implements ActionListener {
 	public Toolbar(MainDialog main) {
 		this.setFloatable(false);
 		JPanel tool1 = new JPanel(new GridLayout(1, 7, 0, 0));
-		tool1.add(bnReset);
-		tool1.add(bnOpen);
+		//tool1.add(bnReset);
+		//tool1.add(bnOpen);
 		tool1.add(bnSave);
 		tool1.add(new JSeparator(SwingConstants.VERTICAL));
 		tool1.add(bnSetting);
 		tool1.add(new JSeparator(SwingConstants.VERTICAL));
 		
 		JPanel tool2 = new JPanel(new GridLayout(1, 4, 0, 0));
-		tool2.add(bnGit);
+		//tool2.add(bnGit);
 		tool2.add(bnHelp);
 		tool2.add(bnAbout);
 		setBorder(BorderFactory.createEtchedBorder());
@@ -65,13 +65,13 @@ public class Toolbar extends JToolBar implements ActionListener {
 		add(new JLabel(""), BorderLayout.CENTER);
 		add(tool2, BorderLayout.EAST);
 
-		bnReset.addActionListener(main);
-		bnOpen.addActionListener(main);
+		//bnReset.addActionListener(main);
+		//bnOpen.addActionListener(main);
 		bnSave.addActionListener(main);
 		bnSetting.addActionListener(main);
 		bnAbout.addActionListener(this);
 		bnHelp.addActionListener(this);
-		bnGit.addActionListener(this);
+		//bnGit.addActionListener(this);
 	}
 
 	@Override
@@ -82,8 +82,8 @@ public class Toolbar extends JToolBar implements ActionListener {
 		if (e.getSource() == bnHelp)
 			WebBrowser.open(Constants.url);
 
-		if (e.getSource() == bnGit)
-			WebBrowser.open(Constants.url);		
+		//if (e.getSource() == bnGit)
+		//	WebBrowser.open(Constants.url);		
 	}
 
 	public void showAbout() {
diff --git a/src/src/eigenpsf/stack/Patch.java b/src/src/eigenpsf/stack/Patch.java
index 52e4a607e292a5423a848e06fabf49522b4702ea..d7140f0886dea3c9c14a7c43dc85cf528dfc7d8c 100644
--- a/src/src/eigenpsf/stack/Patch.java
+++ b/src/src/eigenpsf/stack/Patch.java
@@ -1,7 +1,10 @@
 package eigenpsf.stack;
 
 import java.awt.Point;
+
+import eigenpsf.Params;
 import eigenpsf.data.HyperMatrix;
+import ij.ImagePlus;
 
 public class Patch {
 	
@@ -70,4 +73,5 @@ public class Patch {
 	public String toString() {
 		return "(id: " + id + " x:" + xp + " y:" + yp + ")";
 	}
+	
 }
diff --git a/todo.txt b/todo.txt
index 5291133b67d8cbbf7299a24165ac5c6ae0a532e9..da3223d26d9a25fdc30db8cf75b336bc3f08c069 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,4 +1,3 @@
-* unvalid -> invalid (dans la table)
 * Option de sauvegarde de la table comme fichier json, xslx,..
 *