/**
 ** LmaProducts.java - JNA/Java gate for the rung-3 LMA normal-equation kernel
 **
 ** Copyright (C) 2026 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  LmaProducts.java 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.
 **
 **  LmaProducts.java 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 LmaProducts.java.  If not, see <http://www.gnu.org/licenses/>.
 ** -----------------------------------------------------------------------------**
 **
 */
package com.elphel.imagej.gpu.jna;

/**
 * Full Java -> JNA -> NVRTC gate for {@code lma_normal_products}. It uses the
 * current lean shape (3 parameters, 2*150 samples + 3 regularization rows) and
 * compares every returned double bit-for-bit with Java's own expression order.
 *
 * Run after building libtileproc.so:
 * <pre>
 * java -Djna.library.path=/home/elphel/git/tile_processor_gpu/jna \
 *   -cp target/classes:~/.m2/repository/net/java/dev/jna/jna/5.14.0/jna-5.14.0.jar \
 *   com.elphel.imagej.gpu.jna.LmaProducts \
 *   /home/elphel/git/tile_processor_gpu/src \
 *   /usr/local/cuda-12.8/targets/x86_64-linux/lib/libcudadevrt.a
 * </pre>
 * By Codex on 07/14/2026.
 */
public class LmaProducts {
	public static void main(final String [] args) {
		final String srcdir = (args.length > 0) ? args[0] : "/home/elphel/git/tile_processor_gpu/src";
		final String devrt = (args.length > 1) ? args[1] :
				"/usr/local/cuda-12.8/targets/x86_64-linux/lib/libcudadevrt.a";
		final int numParams = 3;
		final int numValues = 303;
		final double [] weights = new double [numValues];
		final double [][] jt = new double [numParams][numValues];
		final double [] ymfx = new double [numValues];
		for (int k = 0; k < numValues; k++) {
			weights[k] = ((k % 11) == 0) ? 0.0 : (0.00037 * (k + 1) / (k + 3.25));
			ymfx[k] = ((k & 1) != 0 ? -1.0 : 1.0) * (0.017 + 0.000031 * k * k);
			for (int i = 0; i < numParams; i++) {
				final double sign = (((k + 2 * i) & 1) != 0) ? -1.0 : 1.0;
				jt[i][k] = sign * (13.7 * (i + 1) + 0.00091 * (k + 1) +
						0.0000013 * ((k * (i + 3)) % 17));
			}
		}
		final double [] expected = new double [numParams * numParams + numParams];
		for (int i = 0; i < numParams; i++) {
			for (int j = i; j < numParams; j++) {
				double sum = 0.0;
				for (int k = 0; k < numValues; k++) {
					sum += weights[k] * jt[i][k] * jt[j][k];
				}
				expected[i * numParams + j] = expected[j * numParams + i] = sum;
			}
			double sum = 0.0;
			for (int k = 0; k < numValues; k++) {
				sum += jt[i][k] * ymfx[k];
			}
			expected[numParams * numParams + i] = sum;
		}

		int bad = 0;
		final GpuQuadJna gpu = new GpuQuadJna(16, 16, 1, srcdir, devrt, -1);
		try {
			final double [] got = gpu.execLmaNormalProducts(weights, jt, ymfx);
			for (int i = 0; i < got.length; i++) {
				if (Double.doubleToRawLongBits(got[i]) != Double.doubleToRawLongBits(expected[i])) {
					System.out.println("["+i+"] got "+got[i]+" expected "+expected[i]+" -> FAIL");
					bad++;
				}
			}
		} finally {
			gpu.close();
		}
		System.out.println("Java/JNA lma_normal_products: "+expected.length+
				" doubles, bit-exact -> "+((bad == 0) ? "PASS" : "FAIL"));
		if (bad != 0) System.exit(1);
	}
}
