class NN_TSM {

    public static int compute(final float[][] in,
                              final int getNumNodesInput,
                              final float[][] out,
                              final int getNumNodesOutput,
                              final int width,
                              final int[] mask,
                              final int errmask,
                              final float[] a) {
        // (c) M. Schaale, WeW, 2002-2006
        final int rcheck[];
        final int nodes_input = 18;
        final int nodes_output = 1;
        final int nodes_input_bias = 1;
        final int nodes_input_pca = 1;
        final int nodes_hidden = 80;
        final int nodes_hidden_bias = 1;
        final double nodes_hidden_temperature = 1.000000;
        final double t_input = nodes_hidden_temperature / (double) (nodes_input);
        final double t_hidden = nodes_hidden_temperature / (double) (nodes_hidden);
        final double[] vt;
        final double[] vt1;
        //double limexp = -Math.log(Float.MIN_VALUE), dh, dhr, dhe, dhmax=Float.MAX_VALUE, expo;

        // Return dimensions on request
        if (getNumNodesInput <= 0) {
            return (nodes_input);
        }
        if (getNumNodesOutput <= 0) {
            return (nodes_output);
        }

        // Crude compatibility checks
        if (getNumNodesInput != nodes_input) {
            return (-1);
        }
        if (getNumNodesOutput != nodes_output) {
            return (-2);
        }

        vt = new double[nodes_input + nodes_input_bias];
        vt1 = new double[nodes_hidden + nodes_hidden_bias];

        // Range check ??
        rcheck = new int[width];
        for (int x = 0; x < width; x++) {
            rcheck[x] = 0;
            if (a[x] < 0.0f) {
                rcheck[x] = 1;
            }
            a[x] = 1.0f;
        }

        for (int x = 0; x < width; x++) {
            if (mask[x] == 0) {
                // Check input range
                if (rcheck[x] != 0) {
                    for (int i = 0; i < nodes_input && a[x] > 0.0f; i++) {
                        if ((in[i][x] < (float) NN_General.NODES_INPUT_SCALE_LIMITS[i][0]) || (in[i][x] > (float) NN_General.NODES_INPUT_SCALE_LIMITS[i][1])) {
                            a[x] -= 3.0f;
                        }
                    }
                    if (a[x] < 0.0f) {
                        mask[x] |= errmask;
                    }
                }

                //if(mask[x] == 0) {
                // Apply input transformation
                for (int i = 0; i < nodes_input; i++) {
                    if (NN_General.NODES_INPUT_SCALE_FLAG[i] == -1) {
                        in[i][x] = (float) Math.log((double) in[i][x]);
                    }
                    if (NN_General.NODES_INPUT_SCALE_FLAG[i] == -2) {
                        in[i][x] = (float) Math.exp((double) in[i][x]);
                    }
                }

                // Apply input scale layer parameters
                for (int i = 0; i < nodes_input; i++) {
                    in[i][x] = (float) NN_General.NODES_INPUT_SCALE_OFF[i] + (in[i][x] - (float) nodes_input_scale_run39[i][0]) / (float) nodes_input_scale_run39[i][1];
                }

                // Apply input PCA layer parameters
                if (nodes_input_pca != 0) {
                    for (int i = 0; i < nodes_input; i++) {
                        vt[i] = (double) in[i][x];
                        if (NN_General.NODES_INPUT_SCALE_FLAG[i] == 1) {
                            vt[i] = 0.0;
                            for (int j = 0; j < nodes_input; j++) {
                                if (NN_General.NODES_INPUT_SCALE_FLAG[j] == 1) {
                                    vt[i] += (double) in[j][x] * nodes_input_pca_evec_run39[j][i];
                                }
                            }
                        }
                    }
                    for (int i = 0; i < nodes_input; i++) {
                        in[i][x] = (float) vt[i];
                    }
                }

                // Pump through the first layer
                for (int i = 0; i < nodes_input; i++) {
                    vt[i] = (double) in[i][x];
                }
                for (int i = nodes_input; i < nodes_input + nodes_input_bias; i++) {
                    vt[i] = 1.0;
                }

                for (int i = 0; i < nodes_hidden; i++) {
                    vt1[i] = 0.0;
                    for (int j = 0; j < nodes_input + nodes_input_bias; j++) {
                        vt1[i] += vt[j] * nodes_hidden_weights_run39[j][i];
                    }
                }
                // Pump through sigmoid
                for (int i = 0; i < nodes_hidden; i++) {
                    vt1[i] = 1.0 / (1.0 + Math.exp(-t_input * vt1[i]));
                }

                // Pump through the second layer
                for (int i = nodes_hidden; i < nodes_hidden + nodes_hidden_bias; i++) {
                    vt1[i] = 1.0f;
                }
                for (int i = 0; i < nodes_output; i++) {
                    out[i][x] = 0.0f;
                    for (int j = 0; j < nodes_hidden + nodes_hidden_bias; j++) {
                        out[i][x] += (float) (vt1[j] * nodes_output_weights_run39[j][i]);
                    }
                }
                // Pump through sigmoid
                for (int i = 0; i < nodes_output; i++) {
                    out[i][x] = (float) (1.0 / (1.0 + Math.exp(-t_hidden * out[i][x])));
                }

                // Apply output scale layer parameters
                for (int i = 0; i < nodes_output; i++) {
                    out[i][x] = (float) nodes_output_scale_run39[i][0] + (out[i][x] - (float) nodes_output_scale_off_run39[i]) * (float) nodes_output_scale_run39[i][1];
                }

                // Apply output transformation
                for (int i = 0; i < nodes_output; i++) {
                    if (nodes_output_scale_flag_run39[i] == -1) {
                        out[i][x] = (float) Math.log((double) out[i][x]);
                    }
                    if (nodes_output_scale_flag_run39[i] == -2) {
                        out[i][x] = (float) Math.exp((double) out[i][x]);
                    }
                }

                // Check output range
                if (rcheck[x] != 0) {
                    for (int i = 0; i < nodes_output && a[x] >= -2.0f; i++) {
                        if ((out[i][x] < (float) nodes_output_scale_limits_run39[i][0]) || (out[i][x] > (float) nodes_output_scale_limits_run39[i][1])) {
                            a[x] -= 20.0f;
                        }
                    }
                    if (a[x] < 0.0f) {
                        mask[x] |= errmask;
                    }
                }

                //} // mask
            } // mask
        } // x
        return 0;
    }

}