public <T> void update(T[][] observations, int iterations, ToIntFunction<T> ordinal) {
        update(
                Arrays.stream(observations)
                        .map(sequence -> Arrays.stream(sequence).mapToInt(ordinal).toArray())
                        .toArray(int[][]::new),
                iterations);
    }
@Test
    public void testUpdate() {
        System.out.println("update");
        MathEx.setSeed(19650218); // to get repeatable results.

        EmpiricalDistribution initial = new EmpiricalDistribution(pi);

        EmpiricalDistribution[] transition = new EmpiricalDistribution[a.length];
        for (int i = 0; i < transition.length; i++) {
            transition[i] = new EmpiricalDistribution(a[i]);
        }

        EmpiricalDistribution[] emission = new EmpiricalDistribution[b.length];
        for (int i = 0; i < emission.length; i++) {
            emission[i] = new EmpiricalDistribution(b[i]);
        }

        int[][] sequences = new int[5000][];
        int[][] labels = new int[5000][];
        for (int i = 0; i < sequences.length; i++) {
            sequences[i] = new int[30 * (MathEx.randomInt(5) + 1)];
            labels[i] = new int[sequences[i].length];
            int state = (int) initial.rand();
            sequences[i][0] = (int) emission[state].rand();
            labels[i][0] = state;
            for (int j = 1; j < sequences[i].length; j++) {
                state = (int) transition[state].rand();
                sequences[i][j] = (int) emission[state].rand();
                labels[i][j] = state;
            }
        }

        double[] expPi2 = {0.47245901561967496, 0.527540984380325};
        double[][] expA2 = {{0.8006, 0.1994}, {0.1986, 0.8014}};
        double[][] expB2 = {{0.6008, 0.3992}, {0.3997, 0.6003}};
        HMM model = new HMM(pi, Matrix.of(a), Matrix.of(b));
        model.update(sequences, 100);
        System.out.println(model);

        double[] pi2 = model.getInitialStateProbabilities();
        for (int i = 0; i < pi.length; i++) {
            assertEquals(expPi2[i], pi2[i], 1E-4);
        }

        Matrix a2 = model.getStateTransitionProbabilities();
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < a[i].length; j++) {
                assertEquals(expA2[i][j], a2.get(i, j), 1E-4);
            }
        }

        Matrix b2 = model.getSymbolEmissionProbabilities();
        for (int i = 0; i < b.length; i++) {
            for (int j = 0; j < b[i].length; j++) {
                assertEquals(expB2[i][j], b2.get(i, j), 1E-4);
            }
        }
    }