Pārlūkot izejas kodu

assira.collection.map.Column - added

assira.collection.rmq - added

assira.generic.ValueUtils#NATURAL_COMPARATOR

assira.math.MathUtils#log2floor
assira.math.MathUtils#log2ceil
Ranides Atterwim 12 gadi atpakaļ
vecāks
revīzija
ada85df623

+ 109 - 0
src/main/java/net/ranides/assira/collection/map/Column.java

@@ -0,0 +1,109 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.collection.map;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import net.ranides.assira.reflection.ClassInspector;
+import net.ranides.assira.reflection.Invoker;
+
+/**
+ *
+ * @author Mariusz Sieron <m.sieron@samsung.com>
+ */
+public class Column<K,V> {
+
+    private final K key;
+    private final Class<V> type;
+
+    public Column(K key, Class<V> type) {
+        this.key = key;
+        this.type = type;
+    }
+
+    public static <K,V> Column<K,V> make(K key, Class<V> type) {
+        return new Column<K, V>(key, type);
+    }
+
+    public V get(Map<K, ? super V> map) {
+        return type.cast(map.get(key));
+    }
+
+    public void put(Map<K, ? super V> map, V value) {
+        map.put(key, value);
+    }
+
+    public Entry<K,V> entry(Map<K, V> map) {
+        return entry(map, key);
+    }
+
+    public static <K,V> Entry<K,V> entry(Map<K, V> map, K key) {
+        // @todo (assira # 5) MapEntry#get - support
+        //  - EnumMap
+        //  - WeakHashMap
+        //  - ConcurrentHashMap
+        //  - ConcurrentSkipListMap $SubMap
+        //  - TreeMap.NavigableSubMap
+        //  - IdentityHashMap
+        //  - assira.xml.NodeCollection.LMap
+
+        if( map.isEmpty() ) {
+            return null;
+        }
+        if(map instanceof TreeMap<?,?>) {
+            return (Map.Entry<K,V>)Invoker.invoke(map, EntryAccess.TREE, key);
+        }
+        if(map instanceof HashMap<?,?>) {
+            return (Map.Entry<K,V>)Invoker.invoke(map, EntryAccess.HASH, key);
+        }
+        return new EntryView<K, V>(map, key);
+    }
+
+    private static final class EntryAccess {
+
+        private static final Method TREE;
+        private static final Method HASH;
+
+        static {
+            TREE = ClassInspector.findDeclaredMethod(TreeMap.class, "getEntry");
+            HASH = ClassInspector.findDeclaredMethod(HashMap.class, "getEntry");
+        }
+
+    }
+
+    private static final class EntryView<K,V> implements Map.Entry<K, V> {
+
+        private final Map<K,V> map;
+        private final K key;
+
+        public EntryView(Map<K, V> map, K key) {
+            this.map = map;
+            this.key = key;
+        }
+
+        @Override
+        public K getKey() {
+            return key;
+        }
+
+        @Override
+        public V getValue() {
+            return map.get(key);
+        }
+
+        @Override
+        public V setValue(V value) {
+            return map.put(key, value);
+        }
+
+    }
+
+}

+ 56 - 0
src/main/java/net/ranides/assira/collection/rmq/RMQLinear.java

@@ -0,0 +1,56 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/sotong-range
+ */
+
+package net.ranides.assira.collection.rmq;
+
+import java.util.Comparator;
+import java.util.List;
+import net.ranides.assira.generic.ValueUtils;
+
+/**
+ *
+ * @author Mariusz Sieron <m.sieron@samsung.com>
+ * @param <T>
+ */
+public class RMQLinear<T extends Comparable<T>> implements RMQList<T> {
+
+    private final List<? extends T> data;
+    private final Comparator<? super T> cmp;
+
+    public RMQLinear(List<? extends T> input) {
+        this(input, ValueUtils.<T>naturalComparator());
+    }
+
+    public RMQLinear(List<? extends T> input, Comparator<? super T> cmp) {
+        this.data = input;
+        this.cmp = cmp;
+    }
+
+    @Override
+    public T find(int begin, int end) {
+        T ret = null;
+        for(int i=begin; i<end; i++) {
+            ret = min(ret, data.get(i));
+        }
+        return ret;
+    }
+
+    private T min(T a, T b) {
+        if(a == null) {
+            return b;
+        }
+        if(b == null) {
+            return a;
+        }
+        if(cmp.compare(a, b) < 0) {
+            return a;
+        } else {
+            return b;
+        }
+    }
+
+}

+ 20 - 0
src/main/java/net/ranides/assira/collection/rmq/RMQList.java

@@ -0,0 +1,20 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/sotong-range
+ */
+
+package net.ranides.assira.collection.rmq;
+
+/**
+ *
+ * @author Mariusz Sieron <m.sieron@samsung.com>
+ * @param <T>
+ */
+public interface RMQList<T extends Comparable<T>> {
+
+    T find(int begin, int end);
+
+
+}

+ 96 - 0
src/main/java/net/ranides/assira/collection/rmq/RMQSegmentTree.java

@@ -0,0 +1,96 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/sotong-range
+ */
+
+package net.ranides.assira.collection.rmq;
+
+import java.util.Comparator;
+import java.util.List;
+import net.ranides.assira.generic.ValueUtils;
+
+/**
+ * @param <T>
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class RMQSegmentTree<T extends Comparable<T>> implements RMQList<T> {
+
+    private final Comparator<? super T> cmp;
+    private final T[] data;
+
+
+    public RMQSegmentTree(List<? extends T> input) {
+        this(input, ValueUtils.<T>naturalComparator());
+    }
+
+    public RMQSegmentTree(List<? extends T> input, Comparator<? super T> cmp) {
+        int N = pow2c(input.size());
+        this.cmp = cmp;
+        this.data = (T[])new Object[2 * N]; // unchecked cast
+
+        int index = 0;
+        for(T item : input) {
+            data[index++] = item;
+        }
+
+        int bound = N;
+        int target = N;
+        while(bound > 1) {
+            for(int s = target - bound, smax=target; s < smax; s+=2) {
+                data[target++] = min(data[s+0], data[s+1]);
+            }
+            bound /= 2;
+        }
+    }
+
+    @Override
+    public T find(int begin, int end) {
+        return find(null, 0, 0, begin, end);
+    }
+
+    private T find(T last, int level, int index, int b, int e) {
+        if( b >= e ) {
+            return last;
+        }
+
+        int this_size = data.length / (2<<(level));
+        int this_b = index * this_size;
+        int this_e = this_b + this_size;
+
+        if( this_b>=b && this_e<=e ) { // inside
+            return (T)min(last, data[ data.length - (2<<level) + index ]);
+        }
+
+        if( Math.max(this_b, b) < Math.min(this_e, e) ) { // overlaps
+            int c = Math.max(b, Math.min(e, (this_b + this_e) / 2));
+            index*=2;
+            level++;
+            return find(find(last, level, index, b, c), level, index+1, c, e);
+        }
+        return last;
+    }
+
+    private T min(T a, T b) {
+        if(a == null) {
+            return b;
+        }
+        if(b == null) {
+            return a;
+        }
+        if(cmp.compare(a, b) < 0) {
+            return a;
+        } else {
+            return b;
+        }
+    }
+
+    private static int pow2c(int n){
+        int r = 31 - Integer.numberOfLeadingZeros(n);
+        int m = 1 << r;
+        return m==n ? m : 2*m;
+    }
+
+}

+ 65 - 0
src/main/java/net/ranides/assira/collection/rmq/RMQTable.java

@@ -0,0 +1,65 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/sotong-range
+ */
+
+package net.ranides.assira.collection.rmq;
+
+import java.util.Comparator;
+import java.util.List;
+import net.ranides.assira.generic.ValueUtils;
+import net.ranides.assira.math.MathUtils;
+
+/**
+ *
+ * @author Mariusz Sieron <m.sieron@samsung.com>
+ * @param <T>
+ */
+public class RMQTable<T extends Comparable<T>> implements RMQList<T> {
+
+    private final Comparator<? super T> cmp;
+    private final List<? extends T> data;
+    private final int[][] spdata;
+
+    public RMQTable(List<? extends T> input) {
+        this(input, ValueUtils.<T>naturalComparator());
+    }
+
+    public RMQTable(List<? extends T> input, Comparator<? super T> cmp) {
+        int N = input.size();
+        int L = MathUtils.log2ceil(N);
+        this.cmp = cmp;
+        this.data = input;
+        this.spdata = new int[N][L];
+
+        for(int i=0; i<N; i++) {
+            spdata[i][0] = i;
+        }
+        for(int j=1; (1<<j)<=N; j++) {
+            for(int i=0; i + (1<<j)-1 < N; i++) {
+                spdata[i][j] = min(spdata[i][j - 1], spdata[i + (1 << (j - 1))][j - 1]);
+            }
+        }
+    }
+
+    @Override
+    public T find(int begin, int end) {
+        end--;
+        if(end<=begin) {
+            return null;
+        }
+        int k = MathUtils.log2floor(end-begin+1);
+        return min( data.get(spdata[begin][k]), data.get(spdata[end - (1<<k) +1 ][k]) );
+    }
+
+    private T min(T av, T bv) {
+        return cmp.compare(av, bv) < 0 ? av : bv;
+    }
+
+    private int min(int ai, int bi) {
+        return cmp.compare(data.get(ai), data.get(bi)) < 0 ? ai : bi;
+    }
+
+}

+ 11 - 0
src/main/java/net/ranides/assira/generic/ValueUtils.java

@@ -7,6 +7,7 @@
 
 package net.ranides.assira.generic;
 
+import java.util.Comparator;
 
 /**
  *
@@ -97,4 +98,14 @@ public final class ValueUtils {
         return (value instanceof Boolean) && !(Boolean)value;
     }
 
+    public static final Comparator NATURAL_COMPARATOR = new Comparator() {
+        @Override
+        public int compare(Object a, Object b) {
+            return ((Comparable)a).compareTo(b);
+        }
+    };
+
+    public static <T extends Comparable<T>> Comparator<T> naturalComparator() {
+        return NATURAL_COMPARATOR;
+    }
 }

+ 41 - 25
src/main/java/net/ranides/assira/math/MathUtils.java

@@ -7,15 +7,15 @@
 package net.ranides.assira.math;
 
 /**
- * 
+ *
  * @author ranides
  */
 public final class MathUtils {
-    
+
     private MathUtils() {
         // utility class
     }
-    
+
     /**
      * Sprawdza, czy podana wartość mieści się w określonym zakresie (domkniętym).
      * @param value
@@ -26,7 +26,7 @@ public final class MathUtils {
     public static boolean inRange(int value, int min, int max) {
         return value >= min && value <= max;
     }
-    
+
     /**
      * Sprawdza, czy podana wartość mieści się w określonym zakresie (domkniętym).
      * @param value
@@ -37,7 +37,7 @@ public final class MathUtils {
     public static boolean inRange(byte value, byte min, byte max) {
         return value >= min && value <= max;
     }
-    
+
     /**
      * Sprawdza, czy podana wartość mieści się w określonym zakresie (domkniętym).
      * @param value
@@ -48,9 +48,9 @@ public final class MathUtils {
     public static boolean inRange(long value, long min, long max) {
         return value >= min && value <= max;
     }
-    
+
     /**
-     * Przycina podaną wartość do podanego zakresu. To znaczy, że jeśli 
+     * Przycina podaną wartość do podanego zakresu. To znaczy, że jeśli
      * {@code value < min}, to zwraca {@code min}, jeśli
      * {@code value > max}, to zwraca {@code max}, jeśli wartość mieści się w
      * podanym zakresie, to jest zwracana bez zmian.
@@ -63,9 +63,9 @@ public final class MathUtils {
         assert min <= max;
         return value < min ? min : value > max ? max : value;
     }
-    
+
     /**
-     * Przycina podaną wartość do podanego zakresu. To znaczy, że jeśli 
+     * Przycina podaną wartość do podanego zakresu. To znaczy, że jeśli
      * {@code value < min}, to zwraca {@code min}, jeśli
      * {@code value > max}, to zwraca {@code max}, jeśli wartość mieści się w
      * podanym zakresie, to jest zwracana bez zmian.
@@ -78,9 +78,9 @@ public final class MathUtils {
         assert min <= max;
         return value < min ? min : value > max ? max : value;
     }
-    
+
     /**
-     * Przycina podaną wartość do podanego zakresu. To znaczy, że jeśli 
+     * Przycina podaną wartość do podanego zakresu. To znaczy, że jeśli
      * {@code value < min}, to zwraca {@code min}, jeśli
      * {@code value > max}, to zwraca {@code max}, jeśli wartość mieści się w
      * podanym zakresie, to jest zwracana bez zmian.
@@ -93,12 +93,12 @@ public final class MathUtils {
         assert min <= max;
         return value < min ? min : value > max ? max : value;
     }
-    
+
     /**
-     * Przycina podaną wartość do podanego zakresu i zaokrągla do liczby całkowitej. 
+     * Przycina podaną wartość do podanego zakresu i zaokrągla do liczby całkowitej.
      * To znaczy, że jeśli {@code value < min}, to zwraca {@code min}, jeśli
      * {@code value > max}, to zwraca {@code max}, jeśli wartość mieści się w
-     * podanym zakresie, to jest zwracana bez zmian. 
+     * podanym zakresie, to jest zwracana bez zmian.
      * @param value
      * @param min
      * @param max
@@ -108,12 +108,12 @@ public final class MathUtils {
         assert min <= max;
         return (int)Math.round(value < min ? min : value > max ? max : value);
     }
-    
+
     /**
-     * Przycina podaną wartość do podanego zakresu i zaokrągla do liczby całkowitej. 
+     * Przycina podaną wartość do podanego zakresu i zaokrągla do liczby całkowitej.
      * To znaczy, że jeśli {@code value < min}, to zwraca {@code min}, jeśli
      * {@code value > max}, to zwraca {@code max}, jeśli wartość mieści się w
-     * podanym zakresie, to jest zwracana bez zmian. 
+     * podanym zakresie, to jest zwracana bez zmian.
      * @param value
      * @param min
      * @param max
@@ -123,15 +123,15 @@ public final class MathUtils {
         assert min <= max;
         return Math.round(value < min ? min : value > max ? max : value);
     }
-    
+
     /**
      * Alternate to signum for use in compare. Not a true signum, since it
      * returns ints other than +/-1. Where there is any possibility of overflow,
-     * you should compare two longs with < rather than subtraction. 
+     * you should compare two longs with < rather than subtraction.
      * <p>
      * In Pentium assembler you could implement this algorthm with following code:
      * <pre>
-     *  # diff = edx:eax 
+     *  # diff = edx:eax
      *  # result = eax
      *  mov ebx,eax
      *  shl eax,1
@@ -149,11 +149,11 @@ public final class MathUtils {
      *   or     eax,edx
      * </pre>
      * This is 4 cycles, still one more than lohi. Why was Piotr so much
-     * faster on JET? Peter has no pipeline-confounding jumps. Further, the lo 
-     * then high operands actually come from the ram-based stack. Piotr nicely 
-     * separates the accesses giving plenty of for pre-emptive fetch of hi. 
-     * lohi insists on having them both upfront, so it has to wait for memory 
-     * access. Piotr does not have to wait. Modern CPUS hurry up and wait for 
+     * faster on JET? Peter has no pipeline-confounding jumps. Further, the lo
+     * then high operands actually come from the ram-based stack. Piotr nicely
+     * separates the accesses giving plenty of for pre-emptive fetch of hi.
+     * lohi insists on having them both upfront, so it has to wait for memory
+     * access. Piotr does not have to wait. Modern CPUS hurry up and wait for
      * RAM most of the time.
      * </p>
      * @param diff number to be collapsed to an int preserving sign and
@@ -165,5 +165,21 @@ public final class MathUtils {
     public static int signum(long diff) {
         return (int) (diff >>> 32) | ((int) diff | (int) diff << 1) >>> 1;
     }
+
+    public static int log2floor(int x) {
+        return 31 - Integer.numberOfLeadingZeros(x);
+    }
+
+    public static int log2ceil(int x) {
+        return 32 - Integer.numberOfLeadingZeros(x - 1);
+    }
+
+    public static int log2floor(long x) {
+        return 63 - Long.numberOfLeadingZeros(x);
+    }
+
+    public static int log2ceil(long x) {
+        return 64 - Long.numberOfLeadingZeros(x - 1);
+    }
 }
 

+ 113 - 0
src/test/java/net/ranides/assira/collection/map/ColumnTest.java

@@ -0,0 +1,113 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.collection.map;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import net.ranides.assira.asm.MethodInspector;
+import net.ranides.assira.reflection.ClassInspector;
+import net.ranides.assira.reflection.Invoker;
+import net.ranides.assira.reflection.MethodPointer;
+import net.ranides.assira.text.format.AnsiString;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Mariusz Sieron <m.sieron@samsung.com>
+ */
+public class ColumnTest {
+
+
+    private static final Column<String, Float> FIELD_X = Column.make("x", Float.class);
+    private static final Column<String, Float> FIELD_Y = Column.make("y", Float.class);
+    private static final Column<String, Integer> FIELD_Z = Column.make("z", Integer.class);
+    private static final Column<String, Long> FIELD_ID = Column.make("id", Long.class);
+
+    private static final Column<String, String> FIELD_A = Column.make("a", String.class);
+    private static final Column<String, CharSequence> FIELD_B = Column.make("b", CharSequence.class);
+
+
+    @Test
+    public void testGet() {
+        Float vx = 2.5f;
+        Float vy = 3.5f;
+        Integer vz = 15;
+        Long vid = 1017L;
+        String va = "hello";
+        AnsiString vb = new AnsiString("world");
+
+        Map<String, Number> idata = new HashMap<String, Number>();
+        idata.put("x", vx);
+        idata.put("y", vy);
+        idata.put("z", vz);
+        idata.put("id", vid);
+
+        Map<String, CharSequence> sdata = new HashMap<String, CharSequence>();
+        sdata.put("a", va);
+        sdata.put("b", vb);
+
+        Map<String, Object> odata = new HashMap<String, Object>();
+        odata.putAll(idata);
+        odata.putAll(sdata);
+
+        assertEquals(vx, FIELD_X.get(idata), 1e-4f);
+        assertEquals(vy, FIELD_Y.get(idata), 1e-4f);
+        assertEquals(vz, FIELD_Z.get(idata) );
+        assertEquals(vid, FIELD_ID.get(idata) );
+
+        assertEquals(va, FIELD_A.get(sdata));
+        assertEquals(vb, FIELD_B.get(sdata));
+
+    }
+
+    @Test
+    public void testEntry() {
+
+        Map<String, Integer> map1 = new HashMap<String, Integer>();
+        Map<String, Integer> map2 = new TreeMap<String, Integer>();
+
+        map1.put("a", 17);
+        map2.put("b", 14);
+
+        final Entry<String,Integer> entry1 = Column.entry(map1, "a");
+        entry1.setValue(entry1.getValue() + 1);
+
+        final Entry<String,Integer> entry2 = Column.entry(map2, "b");
+        entry2.setValue(entry2.getValue() + 1);
+
+        assertEquals( (Integer)18, map1.get("a"));
+        assertEquals( (Integer)15, map2.get("b"));
+    }
+
+    @Test
+    public void testEntryColumn() {
+
+        final Column<String, Integer> FA = Column.make("a", Integer.class);
+        final Column<String, Integer> FB = Column.make("b", Integer.class);
+
+        Map<String, Integer> map1 = new HashMap<String, Integer>();
+        Map<String, Integer> map2 = new TreeMap<String, Integer>();
+
+        FA.put(map1, 17);
+        FB.put(map2, 14);
+
+        final Entry<String,Integer> entry1 = FA.entry(map1);
+        entry1.setValue(entry1.getValue() + 1);
+
+        final Entry<String,Integer> entry2 = FB.entry(map2);
+        entry2.setValue(entry2.getValue() + 1);
+
+        assertEquals( (Integer)18, map1.get("a"));
+        assertEquals( (Integer)15, map2.get("b"));
+    }
+
+}

+ 60 - 0
src/test/java/net/ranides/assira/math/MathUtilsTest.java

@@ -0,0 +1,60 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.math;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Mariusz Sieron <m.sieron@samsung.com>
+ */
+public class MathUtilsTest {
+
+
+    @Test
+    public void testLog2floor() {
+
+        assertEquals(2, MathUtils.log2floor(4));
+        assertEquals(2, MathUtils.log2floor(5));
+        assertEquals(2, MathUtils.log2floor(7));
+
+        assertEquals(3, MathUtils.log2floor(8));
+        assertEquals(3, MathUtils.log2floor(9));
+        assertEquals(3, MathUtils.log2floor(10));
+
+        assertEquals(16, MathUtils.log2floor(256 * 256));
+        assertEquals(24, MathUtils.log2floor(256 * 256 * 256));
+        assertEquals(31, MathUtils.log2floor(128 * 256 * 256 * 256));
+
+        assertEquals(30, MathUtils.log2floor(128 * 256 * 256 * 256 - 1000));
+        assertEquals(31, MathUtils.log2floor(128 * 256 * 256 * 256 + 1000));
+
+    }
+
+    @Test
+    public void testLog2ceil() {
+
+        assertEquals(2, MathUtils.log2ceil(4));
+        assertEquals(3, MathUtils.log2ceil(5));
+        assertEquals(3, MathUtils.log2ceil(7));
+
+        assertEquals(3, MathUtils.log2ceil(8));
+        assertEquals(4, MathUtils.log2ceil(9));
+        assertEquals(4, MathUtils.log2ceil(10));
+
+        assertEquals(16, MathUtils.log2ceil(256 * 256));
+        assertEquals(24, MathUtils.log2ceil(256 * 256 * 256));
+        assertEquals(31, MathUtils.log2ceil(128 * 256 * 256 * 256));
+
+        assertEquals(31, MathUtils.log2ceil(128 * 256 * 256 * 256 - 1000));
+        assertEquals(32, MathUtils.log2ceil(128 * 256 * 256 * 256 + 1000));
+
+    }
+
+}