1f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin/*
2f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin * Copyright (C) 2012 The Android Open Source Project
3f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin *
4f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin * you may not use this file except in compliance with the License.
6f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin * You may obtain a copy of the License at
7f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin *
8f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin *
10f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin * Unless required by applicable law or agreed to in writing, software
11f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin * See the License for the specific language governing permissions and
14f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin * limitations under the License.
15f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin */
16f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linpackage android.bordeaux.services;
17f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.location.Location;
18f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.text.format.Time;
19f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.util.Log;
20f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
21f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport java.lang.Math;
22f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport java.util.ArrayList;
235d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Linimport java.util.HashMap;
245d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Linimport java.util.Map;
25f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
26f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linpublic class BaseCluster {
27f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
28f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    public static String TAG = "BaseCluster";
29f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
305d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    public double[] mCenter;
315d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin  // protected double[] mCenter;
325d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
331253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    protected static final int VECTOR_LENGTH = 3;
341253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin
351253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    private static final long FORGETTING_ENUMERATOR = 95;
361253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    private static final long FORGETTING_DENOMINATOR = 100;
371253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin
385d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    // Histogram illustrates the pattern of visit during time of day,
395d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    protected HashMap<String, Long> mHistogram = new HashMap<String, Long>();
40f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
41f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    protected long mDuration;
42f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
435d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    protected String mSemanticId;
44f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
455d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    protected static final double EARTH_RADIUS = 6378100f;
46f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
4778a66d98346a69f65e9d38bb0c96a5418c007197Ruei-sung Lin    public BaseCluster(Location location) {
48f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        mCenter = getLocationVector(location);
49f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        mDuration = 0;
50f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    }
51f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
521253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    public BaseCluster(String semanticId, double longitude, double latitude,
531253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin                     long duration) {
541253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        mSemanticId = semanticId;
551253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        mCenter = getLocationVector(longitude, latitude);
561253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        mDuration = duration;
575d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
585d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
595d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    public String getSemanticId() {
605d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        return mSemanticId;
615d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
625d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
631253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    public void generateSemanticId(long index) {
645d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        mSemanticId = "cluser: " + String.valueOf(index);
655d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
665d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
671253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    public long getDuration() {
681253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        return mDuration;
695d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
705d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
715d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    public boolean hasSemanticId() {
725d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        return mSemanticId != null;
735d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
745d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
75f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    protected double[] getLocationVector(Location location) {
765d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        return getLocationVector(location.getLongitude(), location.getLatitude());
775d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
785d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
795d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    protected double[] getLocationVector(double longitude, double latitude) {
801253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        double vector[] = new double[VECTOR_LENGTH];
815d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        double lambda = Math.toRadians(longitude);
825d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        double phi = Math.toRadians(latitude);
835d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
84f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        vector[0] = Math.cos(lambda) * Math.cos(phi);
85f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        vector[1] = Math.sin(lambda) * Math.cos(phi);
86f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        vector[2] = Math.sin(phi);
87f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        return vector;
88f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    }
89f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
905d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    protected double getCenterLongitude() {
915d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        // Because latitude ranges from -90 to 90 degrees, cosPhi >= 0.
925d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        double cosPhi = Math.cos(Math.asin(mCenter[2]));
935d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        double longitude = Math.toDegrees(Math.asin(mCenter[1] / cosPhi));
945d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        if (mCenter[0] < 0) {
955d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin            longitude = (longitude > 0) ? 180f - longitude : -180 - longitude;
965d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        }
975d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        return longitude;
985d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
995d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
1005d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    protected double getCenterLatitude() {
1015d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        return Math.toDegrees(Math.asin(mCenter[2]));
1025d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
1035d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
104f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    private double computeDistance(double[] vector1, double[] vector2) {
105f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        double product = 0f;
1061253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        for (int i = 0; i < VECTOR_LENGTH; ++i) {
107f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin            product += vector1[i] * vector2[i];
108f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        }
109f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        double radian = Math.acos(Math.min(product, 1f));
110f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        return radian * EARTH_RADIUS;
111f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    }
112f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
113f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    /*
114f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin     * This computes the distance from loation to the cluster center in meter.
115f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin     */
116f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    public float distanceToCenter(Location location) {
117f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        return (float) computeDistance(mCenter, getLocationVector(location));
118f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    }
119f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
120f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    public float distanceToCluster(BaseCluster cluster) {
121f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        return (float) computeDistance(mCenter, cluster.mCenter);
122f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    }
123f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
124f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    public void absorbCluster(BaseCluster cluster) {
1251253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        averageCenter(cluster.mCenter, cluster.mDuration);
1265d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        absorbHistogram(cluster);
1275d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
1285d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
1295d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    public void setCluster(BaseCluster cluster) {
1301253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        for (int i = 0; i < VECTOR_LENGTH; ++i) {
1315d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin            mCenter[i] = cluster.mCenter[i];
1325d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        }
1335d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        mHistogram.clear();
1345d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        mHistogram.putAll(cluster.mHistogram);
1355d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        mDuration = cluster.mDuration;
1365d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    }
1375d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
1385d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    private void absorbHistogram(BaseCluster cluster) {
1395d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        for (Map.Entry<String, Long> entry : cluster.mHistogram.entrySet()) {
1405d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin            String timeLabel = entry.getKey();
1415d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin            long duration = entry.getValue();
1425d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
1435d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin            if (mHistogram.containsKey(timeLabel)) {
1445d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin                duration += mHistogram.get(timeLabel);
1455d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin            }
1465d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin            mHistogram.put(timeLabel, duration);
1475d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        }
148f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        mDuration += cluster.mDuration;
149f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    }
150f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
151f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    public boolean passThreshold(long durationThreshold) {
152f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        // TODO: might want to keep semantic cluster
153f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        return mDuration > durationThreshold;
154f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    }
15583954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin
15683954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin    public final HashMap<String, Long> getHistogram() {
15783954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin        return mHistogram;
15883954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin    }
15983954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin
16083954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin    public void setHistogram(Map<String, Long> histogram) {
16183954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin        mHistogram.clear();
16283954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin        mHistogram.putAll(histogram);
16383954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin
16483954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin        mDuration = 0;
16583954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin        if (mHistogram.containsKey(TimeStatsAggregator.WEEKEND)) {
16683954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin            mDuration += mHistogram.get(TimeStatsAggregator.WEEKEND);
16783954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin        }
16883954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin        if (mHistogram.containsKey(TimeStatsAggregator.WEEKDAY)) {
16983954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin            mDuration += mHistogram.get(TimeStatsAggregator.WEEKDAY);
17083954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin        }
17183954e853dc1e1a28b2c3efbe1585f188266df02Ruei-sung Lin    }
1721253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin
1731253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    public void forgetPastHistory() {
1741253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        mDuration *= FORGETTING_ENUMERATOR;
1751253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        mDuration /= FORGETTING_DENOMINATOR;
1761253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin
1771253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        for (Map.Entry<String, Long> entry : mHistogram.entrySet()) {
1781253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            String key = entry.getKey();
1791253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            long value = entry.getValue();
1801253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin
1811253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            value *= FORGETTING_ENUMERATOR;
1821253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            value /= FORGETTING_DENOMINATOR;
1831253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin
1841253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            mHistogram.put(key, value);
1851253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        }
1861253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    }
1871253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin
1881253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    protected void normalizeCenter() {
1891253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        double norm = 0;
1901253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        for (int i = 0; i < VECTOR_LENGTH; ++i) {
1911253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            norm += mCenter[i] * mCenter[i];
1921253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        }
1931253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        norm = Math.sqrt(norm);
1941253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        for (int i = 0; i < VECTOR_LENGTH; ++i) {
1951253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            mCenter[i] /= norm;
1961253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        }
1971253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    }
1981253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin
1991253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    protected void averageCenter(double[] newCenter, long newDuration) {
2001253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        double weight = ((double) mDuration) / (mDuration + newDuration);
2011253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        double newWeight = 1f - weight;
2021253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin
2031253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        double norm = 0;
2041253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        for (int i = 0; i < VECTOR_LENGTH; ++i) {
2051253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            mCenter[i] = weight * mCenter[i] + newWeight * newCenter[i];
2061253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            norm += mCenter[i] * mCenter[i];
2071253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        }
2081253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        norm = Math.sqrt(norm);
2091253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        for (int i = 0; i < VECTOR_LENGTH; ++i) {
2101253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin            mCenter[i] /= norm;
2111253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin        }
2121253e9fb0b5570ab8adaed222655a5b052aa072eRuei-sung Lin    }
213f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin}
214