1607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok/* 2607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * Copyright (C) 2012 The Android Open Source Project 3607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * 4607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * Licensed under the Apache License, Version 2.0 (the "License"); 5607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * you may not use this file except in compliance with the License. 6607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * You may obtain a copy of the License at 7607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * 8607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * http://www.apache.org/licenses/LICENSE-2.0 9607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * 10607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * Unless required by applicable law or agreed to in writing, software 11607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * distributed under the License is distributed on an "AS IS" BASIS, 12607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * See the License for the specific language governing permissions and 14607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok * limitations under the License. 15607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok */ 16607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 17607a9244861ee22c25aaea6ffdfa19fccf497b0bsatokpackage com.android.inputmethod.latin; 18607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 19fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satokimport android.text.format.DateUtils; 20fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satokimport android.util.Log; 21fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 221e11c44d1b5f9ddf593c5407cb14c458be0056f2Tadashi G. Takaokapublic final class UserHistoryForgettingCurveUtils { 23fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok private static final String TAG = UserHistoryForgettingCurveUtils.class.getSimpleName(); 24fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok private static final boolean DEBUG = false; 25607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok private static final int FC_FREQ_MAX = 127; 26607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok /* package */ static final int COUNT_MAX = 3; 27607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok private static final int FC_LEVEL_MAX = 3; 28607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok /* package */ static final int ELAPSED_TIME_MAX = 15; 29607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok private static final int ELAPSED_TIME_INTERVAL_HOURS = 6; 30fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok private static final long ELAPSED_TIME_INTERVAL_MILLIS = ELAPSED_TIME_INTERVAL_HOURS 31fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok * DateUtils.HOUR_IN_MILLIS; 32607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok private static final int HALF_LIFE_HOURS = 48; 33ec2981a487b91a682caade486700d8b2377a5c52satok private static final int MAX_PUSH_ELAPSED = (FC_LEVEL_MAX + 1) * (ELAPSED_TIME_MAX + 1); 34607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 35fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok private UserHistoryForgettingCurveUtils() { 36fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok // This utility class is not publicly instantiable. 37fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 38fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 39a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka public static final class ForgettingCurveParams { 40fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok private byte mFc; 41fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok long mLastTouchedTime = 0; 42c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka private final boolean mIsValid; 43fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 44fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok private void updateLastTouchedTime() { 45fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok mLastTouchedTime = System.currentTimeMillis(); 46fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 47fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 48c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka public ForgettingCurveParams(boolean isValid) { 49c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka this(System.currentTimeMillis(), isValid); 50fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 51fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 52c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka private ForgettingCurveParams(long now, boolean isValid) { 537214617622fce8f3fea6620e782c16336260a2a3Jean Chalard this(pushCount((byte)0, isValid), now, now, isValid); 54fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 55fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 56c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka /** This constructor is called when the user history bigram dictionary is being restored. */ 57fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok public ForgettingCurveParams(int fc, long now, long last) { 58c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka // All words with level >= 1 had been saved. 59c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka // Invalid words with level == 0 had been saved. 60c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka // Valid words words with level == 0 had *not* been saved. 61c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka this(fc, now, last, fcToLevel((byte)fc) > 0); 62c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka } 63c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka 64c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka private ForgettingCurveParams(int fc, long now, long last, boolean isValid) { 65c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka mIsValid = isValid; 66fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok mFc = (byte)fc; 67fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok mLastTouchedTime = last; 68fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok updateElapsedTime(now); 69fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 70fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 71c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka public boolean isValid() { 72c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka return mIsValid; 73c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka } 74c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka 75fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok public byte getFc() { 76fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok updateElapsedTime(System.currentTimeMillis()); 77fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok return mFc; 78fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 79fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 80fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok public int getFrequency() { 81fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok updateElapsedTime(System.currentTimeMillis()); 82fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok return UserHistoryForgettingCurveUtils.fcToFreq(mFc); 83fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 84fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 85fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok public int notifyTypedAgainAndGetFrequency() { 86fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok updateLastTouchedTime(); 87fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok // TODO: Check whether this word is valid or not 88fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok mFc = pushCount(mFc, false); 89fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok return UserHistoryForgettingCurveUtils.fcToFreq(mFc); 90fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 91fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 92fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok private void updateElapsedTime(long now) { 93fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok final int elapsedTimeCount = 94fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok (int)((now - mLastTouchedTime) / ELAPSED_TIME_INTERVAL_MILLIS); 95fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok if (elapsedTimeCount <= 0) { 96fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok return; 97fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 98ec2981a487b91a682caade486700d8b2377a5c52satok if (elapsedTimeCount >= MAX_PUSH_ELAPSED) { 99ec2981a487b91a682caade486700d8b2377a5c52satok mLastTouchedTime = now; 100ec2981a487b91a682caade486700d8b2377a5c52satok mFc = 0; 101ec2981a487b91a682caade486700d8b2377a5c52satok return; 102ec2981a487b91a682caade486700d8b2377a5c52satok } 103fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok for (int i = 0; i < elapsedTimeCount; ++i) { 104fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok mLastTouchedTime += ELAPSED_TIME_INTERVAL_MILLIS; 105fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok mFc = pushElapsedTime(mFc); 106fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 107fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 108fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 109fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 110607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok /* package */ static int fcToElapsedTime(byte fc) { 111607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok return fc & 0x0F; 112607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 113607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 114607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok /* package */ static int fcToCount(byte fc) { 115607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok return (fc >> 4) & 0x03; 116607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 117607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 118607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok /* package */ static int fcToLevel(byte fc) { 119607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok return (fc >> 6) & 0x03; 120607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 121607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 122607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok private static int calcFreq(int elapsedTime, int count, int level) { 123607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok if (level <= 0) { 124fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok // Reserved words, just return -1 125fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok return -1; 126607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 127607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok if (count == COUNT_MAX) { 128607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok // Temporary promote because it's frequently typed recently 129607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok ++level; 130607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 131607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int et = Math.min(FC_FREQ_MAX, Math.max(0, elapsedTime)); 132607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int l = Math.min(FC_LEVEL_MAX, Math.max(0, level)); 133607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok return MathUtils.SCORE_TABLE[l - 1][et]; 134607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 135607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 136607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok /* pakcage */ static byte calcFc(int elapsedTime, int count, int level) { 137607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int et = Math.min(FC_FREQ_MAX, Math.max(0, elapsedTime)); 138607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int c = Math.min(COUNT_MAX, Math.max(0, count)); 139607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int l = Math.min(FC_LEVEL_MAX, Math.max(0, level)); 140607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok return (byte)(et | (c << 4) | (l << 6)); 141607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 142607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 143607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok public static int fcToFreq(byte fc) { 144607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int elapsedTime = fcToElapsedTime(fc); 145607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int count = fcToCount(fc); 146607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int level = fcToLevel(fc); 147607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok return calcFreq(elapsedTime, count, level); 148607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 149607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 150607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok public static byte pushElapsedTime(byte fc) { 151607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok int elapsedTime = fcToElapsedTime(fc); 152607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok int count = fcToCount(fc); 153607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok int level = fcToLevel(fc); 154607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok if (elapsedTime >= ELAPSED_TIME_MAX) { 155607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok // Downgrade level 156607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok elapsedTime = 0; 157607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok count = COUNT_MAX; 158607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok --level; 159607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } else { 160607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok ++elapsedTime; 161607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 162607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok return calcFc(elapsedTime, count, level); 163607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 164607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 165607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok public static byte pushCount(byte fc, boolean isValid) { 166607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int elapsedTime = fcToElapsedTime(fc); 167607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok int count = fcToCount(fc); 168607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok int level = fcToLevel(fc); 169607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok if ((elapsedTime == 0 && count >= COUNT_MAX) || (isValid && level == 0)) { 170607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok // Upgrade level 171607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok ++level; 172607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok count = 0; 173fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok if (DEBUG) { 174fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok Log.d(TAG, "Upgrade level."); 175fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 176607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } else { 177607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok ++count; 178607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 179607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok return calcFc(0, count, level); 180607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 181607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok 182fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok // TODO: isValid should be false for a word whose frequency is 0, 183fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok // or that is not in the dictionary. 184bc5688506229bd5cd5e6f4dcdc73c21dc6b80ecbSatoshi Kataoka /** 185bc5688506229bd5cd5e6f4dcdc73c21dc6b80ecbSatoshi Kataoka * Check wheather we should save the bigram to the SQL DB or not 186bc5688506229bd5cd5e6f4dcdc73c21dc6b80ecbSatoshi Kataoka */ 187bc5688506229bd5cd5e6f4dcdc73c21dc6b80ecbSatoshi Kataoka public static boolean needsToSave(byte fc, boolean isValid, boolean addLevel0Bigram) { 188fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok int level = fcToLevel(fc); 189bc5688506229bd5cd5e6f4dcdc73c21dc6b80ecbSatoshi Kataoka if (level == 0) { 190bc5688506229bd5cd5e6f4dcdc73c21dc6b80ecbSatoshi Kataoka if (isValid || !addLevel0Bigram) { 191bc5688506229bd5cd5e6f4dcdc73c21dc6b80ecbSatoshi Kataoka return false; 192bc5688506229bd5cd5e6f4dcdc73c21dc6b80ecbSatoshi Kataoka } 193fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 194fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok final int elapsedTime = fcToElapsedTime(fc); 195fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok return (elapsedTime < ELAPSED_TIME_MAX - 1 || level > 0); 196fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok } 197fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok 198a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka private static final class MathUtils { 199607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok public static final int[][] SCORE_TABLE = new int[FC_LEVEL_MAX][ELAPSED_TIME_MAX + 1]; 200607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok static { 201607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok for (int i = 0; i < FC_LEVEL_MAX; ++i) { 202d10c473347c7e21c383c56786c9eb96fd6513a5cJean Chalard final float initialFreq; 203607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok if (i >= 2) { 2047214617622fce8f3fea6620e782c16336260a2a3Jean Chalard initialFreq = FC_FREQ_MAX; 205607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } else if (i == 1) { 206d10c473347c7e21c383c56786c9eb96fd6513a5cJean Chalard initialFreq = FC_FREQ_MAX / 2; 207607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } else if (i == 0) { 208d10c473347c7e21c383c56786c9eb96fd6513a5cJean Chalard initialFreq = FC_FREQ_MAX / 4; 209607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } else { 210607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok continue; 211607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 212607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok for (int j = 0; j < ELAPSED_TIME_MAX; ++j) { 213d10c473347c7e21c383c56786c9eb96fd6513a5cJean Chalard final float elapsedHours = j * ELAPSED_TIME_INTERVAL_HOURS; 214e7b34b9f867b64eabc3606e5ef21e26eda8de0f6Ken Wakasa final float freq = initialFreq 215bcec82de66f52655593dc233346f11468f5077a0Ken Wakasa * (float)Math.pow(initialFreq, elapsedHours / HALF_LIFE_HOURS); 216607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok final int intFreq = Math.min(FC_FREQ_MAX, Math.max(0, (int)freq)); 217607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok SCORE_TABLE[i][j] = intFreq; 218607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 219607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 220607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 221607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok } 222607a9244861ee22c25aaea6ffdfa19fccf497b0bsatok} 223