1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.inputmethod.keyboard.internal;
18
19import com.android.inputmethod.annotations.UsedForTesting;
20import com.android.inputmethod.keyboard.internal.MatrixUtils.MatrixOperationFailedException;
21
22import android.util.Log;
23
24import java.util.Arrays;
25
26/**
27 * Utilities to smooth coordinates. Currently, we calculate 3d least squares formula by using
28 * Lagrangian smoothing
29 */
30@UsedForTesting
31public class SmoothingUtils {
32    private static final String TAG = SmoothingUtils.class.getSimpleName();
33    private static final boolean DEBUG = false;
34
35    private SmoothingUtils() {
36        // not allowed to instantiate publicly
37    }
38
39    /**
40     * Find a most likely 3d least squares formula for specified coordinates.
41     * "retval" should be a 1x4 size matrix.
42     */
43    @UsedForTesting
44    public static void get3DParameters(final float[] xs, final float[] ys,
45            final float[][] retval) throws MatrixOperationFailedException {
46        final int COEFF_COUNT = 4; // Coefficient count for 3d smoothing
47        if (retval.length != COEFF_COUNT || retval[0].length != 1) {
48            Log.d(TAG, "--- invalid length of 3d retval " + retval.length + ", "
49                    + retval[0].length);
50            return;
51        }
52        final int N = xs.length;
53        // TODO: Never isntantiate the matrix
54        final float[][] m0 = new float[COEFF_COUNT][COEFF_COUNT];
55        final float[][] m0Inv = new float[COEFF_COUNT][COEFF_COUNT];
56        final float[][] m1 = new float[COEFF_COUNT][N];
57        final float[][] m2 = new float[N][1];
58
59        // m0
60        for (int i = 0; i < COEFF_COUNT; ++i) {
61            Arrays.fill(m0[i], 0);
62            for (int j = 0; j < COEFF_COUNT; ++j) {
63                final int pow = i + j;
64                for (int k = 0; k < N; ++k) {
65                    m0[i][j] += (float) Math.pow(xs[k], pow);
66                }
67            }
68        }
69        // m0Inv
70        MatrixUtils.inverse(m0, m0Inv);
71        if (DEBUG) {
72            MatrixUtils.dump("m0-1", m0Inv);
73        }
74
75        // m1
76        for (int i = 0; i < COEFF_COUNT; ++i) {
77            for (int j = 0; j < N; ++j) {
78                m1[i][j] = (i == 0) ? 1.0f : m1[i - 1][j] * xs[j];
79            }
80        }
81
82        // m2
83        for (int i = 0; i < N; ++i) {
84            m2[i][0] = ys[i];
85        }
86
87        final float[][] m0Invxm1 = new float[COEFF_COUNT][N];
88        if (DEBUG) {
89            MatrixUtils.dump("a0", m0Inv);
90            MatrixUtils.dump("a1", m1);
91        }
92        MatrixUtils.multiply(m0Inv, m1, m0Invxm1);
93        if (DEBUG) {
94            MatrixUtils.dump("a2", m0Invxm1);
95            MatrixUtils.dump("a3", m2);
96        }
97        MatrixUtils.multiply(m0Invxm1, m2, retval);
98        if (DEBUG) {
99            MatrixUtils.dump("result", retval);
100        }
101    }
102}
103