/* Copyright (c) 2011 Danish Maritime Authority * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this library. If not, see <http://www.gnu.org/licenses/>. */ package dk.dma.ais.abnormal.stat.db.data; import com.google.common.primitives.Ints; import gnu.trove.iterator.TShortIterator; import gnu.trove.map.hash.TShortIntHashMap; import java.util.HashMap; import java.util.TreeMap; /** * * This is a memory-consumption optimised implementation of FourKeyMap intended to store * AIS statistic statistics for one grid cell. * */ public abstract class FourKeyStatisticData implements StatisticData, FourKeyMap { private final TShortIntHashMap data; final byte MAX_KEY_1; final byte MAX_KEY_2; final byte MAX_KEY_3; protected FourKeyStatisticData(int maxKey1, int maxKey2, int maxKey3, int maxNumKey4) { if (maxKey1 <= 0) { throw new IllegalArgumentException("maxKey1 <= 0 not supported."); } if (maxKey2 <= 0) { throw new IllegalArgumentException("maxKey2 <= 0 not supported."); } if (maxKey3 <= 0) { throw new IllegalArgumentException("maxKey3 <= 0 not supported."); } if (maxNumKey4 != 1) { throw new IllegalArgumentException("maxNumKey4 != 1 not supported."); } this.MAX_KEY_1 = (byte) maxKey1; this.MAX_KEY_2 = (byte) maxKey2; this.MAX_KEY_3 = (byte) maxKey3; this.data = new TShortIntHashMap(1); } public final int getMaxKey1() { return MAX_KEY_1; } public final int getMaxKey2() { return MAX_KEY_2; } public final int getMaxKey3() { return MAX_KEY_3; } @Override public void incrementValue(int key1, int key2, int key3, String key4) { short key = computeMapKey(key1, key2, key3, key4); if (data.get(key) != data.getNoEntryValue()) { data.increment(key); } else { data.put(key, 1); data.compact(); } } @Override public void setValue(int key1, int key2, int key3, String key4, int value) { short key = computeMapKey(key1, key2, key3, key4); data.put(key, value); data.compact(); } @Override public Integer getValue(int key1, int key2, int key3, String key4) { short key = computeMapKey(key1, key2, key3, key4); Integer statisticsValue = data.get(key); statisticsValue = statisticsValue == data.getNoEntryValue() ? null : statisticsValue; return statisticsValue; } public int aggregateSumOverKey1(int key2, int key3, String key4) { int shipCount = 0; final int n = getMaxKey1(); for (int i=0; i<n; i++) { final Integer c = getValue(i, key2, key3, key4); if (c != null) { shipCount += c.intValue(); } } return shipCount; } @Override public int getSumFor(String key4) { return Ints.asList(data.values()).stream().mapToInt(value -> value).sum(); } @Override public String getStatisticName() { return this.getClass().getSimpleName(); } @Override public String getStatisticDataType() { return FourKeyMap.class.getSimpleName(); } @Override public TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, HashMap<String, Integer>>>> getData() { TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, HashMap<String, Integer>>>> root = new TreeMap<>(); TShortIterator keys = data.keySet().iterator(); while(keys.hasNext()) { short key = keys.next(); int key1 = extractKey1(key); int key2 = extractKey2(key); int key3 = extractKey3(key); int bucket1 = key1 + 1; int bucket2 = key2 + 1; int bucket3 = key3 + 1; TreeMap<Integer, TreeMap<Integer, HashMap<String, Integer>>> level1 = root.get(bucket1); if (level1 == null) { level1 = new TreeMap<>(); root.put(bucket1, level1); } TreeMap<Integer, HashMap<String, Integer>> level2 = level1.get(bucket2); if (level2 == null) { level2 = new TreeMap<>(); level1.put(bucket2, level2); } HashMap<String, Integer> level3 = level2.get(bucket3); if (level3 == null) { level3 = new HashMap<>(); level2.put(bucket3, level3); } Integer value = getValue(key1, key2, key3, getNameOfOnlySupportedValueOfKey4()); if (value != null) { level3.put(getNameOfOnlySupportedValueOfKey4(), value); } } return root; } protected abstract String getNameOfOnlySupportedValueOfKey4(); short computeMapKey(int key1, int key2, int key3, String key4) { if (key1 > MAX_KEY_1) { throw new IllegalArgumentException("key1 must be 0-" + MAX_KEY_1 + " - not " + key1 + "."); } if (key2 > MAX_KEY_2) { throw new IllegalArgumentException("key2 must be 0-" + MAX_KEY_2 + " - not " + key2 + "."); } if (key3 > MAX_KEY_3) { throw new IllegalArgumentException("key3 must be 0-" + MAX_KEY_3 + " - not " + key3 + "."); } if (! getNameOfOnlySupportedValueOfKey4().equals(key4)) { throw new IllegalArgumentException("key4 '" + key4 + "' is not supported."); } final int d2 = MAX_KEY_2 + 1; final int d3 = MAX_KEY_3 + 1; return (short) (key1*d2*d3 + key2*d3 + key3); } int extractKey1(short key) { final int d2 = MAX_KEY_2 + 1; final int d3 = MAX_KEY_3 + 1; return key / (d2*d3); } int extractKey2(short key) { final int d2 = MAX_KEY_2 + 1; final int d3 = MAX_KEY_3 + 1; return (key - extractKey1(key)*d2*d3) / d3; } int extractKey3(short key) { final int d2 = MAX_KEY_2 + 1; final int d3 = MAX_KEY_3 + 1; return (key - extractKey1(key)*d2*d3 - extractKey2(key)*d3); } int extractKey4(short key) { return 0; } }