Sfoglia il codice sorgente

new: RandomUtils#uniqueInts
new: BeanUtils#BeanMapper
new: OptionalReference

Ranides Atterwim 7 anni fa
parent
commit
2e317afebf

+ 3 - 3
assira/src/main/java/net/ranides/assira/collection/arrays/NativeArrayUtils.java

@@ -136,11 +136,11 @@ public final class NativeArrayUtils {
     }
     
     public static void shuffle(Random random, int size, Swapper swapper) {
-        for(int i=0; i<size; i++) {
-            swapper.swap(random.nextInt(size), random.nextInt(size));
+        for(int i=size; i>1; i--) {
+            swapper.swap(i-1, random.nextInt(i));
         }
     }
-    
+
     public static int size(NativeArray array) {
         return array.size();
     }

+ 156 - 0
assira/src/main/java/net/ranides/assira/generic/OptionalReference.java

@@ -0,0 +1,156 @@
+package net.ranides.assira.generic;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.io.Serializable;
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class OptionalReference<V> extends AbstractCollection<V> implements Set<V>, Serializable {
+
+    private V value;
+
+    private OptionalReference(V value) {
+        this.value = value;
+    }
+
+    public static <V> OptionalReference<V> empty() {
+        return new OptionalReference<>(null);
+    }
+
+    public static <V> OptionalReference<V> of(V value) {
+        return new OptionalReference<>(Objects.requireNonNull(value));
+    }
+
+    public static <V> OptionalReference<V> ofNullable(V value) {
+        return new OptionalReference<>(value);
+    }
+
+    public V get() {
+        if (value == null) {
+            throw new NoSuchElementException("No value present");
+        }
+        return value;
+    }
+
+    public boolean isPresent() {
+        return value != null;
+    }
+
+    public void ifPresent(Consumer<? super V> consumer) {
+        if (value != null)
+            consumer.accept(value);
+    }
+
+    public V orElse(V other) {
+        return value != null ? value : other;
+    }
+
+    public V orElseGet(Supplier<? extends V> other) {
+        return value != null ? value : other.get();
+    }
+
+    public <X extends Throwable> V orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
+        if (value != null) {
+            return value;
+        } else {
+            throw exceptionSupplier.get();
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof OptionalReference)) {
+            return false;
+        }
+        OptionalReference<?> other = (OptionalReference<?>) obj;
+        return Objects.equals(value, other.value);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(value);
+    }
+
+    @Override
+    public String toString() {
+        return value != null ? String.format("OptionalReference[%s]", value) : "OptionalReference.empty";
+    }
+
+    @NotNull
+    @Override
+    public Iterator<V> iterator() {
+        return new SingleIterator();
+    }
+
+    @Override
+    public int size() {
+        return this.value != null ? 1 : 0;
+    }
+
+    @Override
+    public boolean contains(Object o) {
+        return value!=null && value.equals(o);
+    }
+
+    @Override
+    public boolean add(V v) {
+        if(value != null) {
+            throw new IllegalStateException("Already has element");
+        }
+        value = Objects.requireNonNull(v);
+        return true;
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        if(value != null && o!=null && value.equals(o)) {
+            value = null;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void clear() {
+        value = null;
+    }
+
+    private final class SingleIterator implements Iterator<V> {
+
+        private boolean hasNext = true;
+
+        public boolean hasNext() {
+            return hasNext && value!=null;
+        }
+
+        @Override
+        public V next() {
+            if (hasNext()) {
+                hasNext = false;
+                return value;
+            }
+            throw new NoSuchElementException();
+        }
+
+        public void remove() {
+            if(hasNext) {
+                throw new IllegalStateException();
+            }
+            value = null;
+        }
+
+        @Override
+        public void forEachRemaining(Consumer<? super V> action) {
+            Objects.requireNonNull(action);
+            if (hasNext()) {
+                action.accept(value);
+                hasNext = false;
+            }
+        }
+    }
+}

+ 35 - 0
assira/src/main/java/net/ranides/assira/math/RandomUtils.java

@@ -0,0 +1,35 @@
+package net.ranides.assira.math;
+
+import net.ranides.assira.collection.lists.IntArrayList;
+import net.ranides.assira.collection.lists.IntList;
+
+import java.util.Random;
+
+public class RandomUtils {
+
+    /**
+     * Reimplementation of
+     * <a href="https://tfetimes.com/wp-content/uploads/2015/04/ProgrammingPearls2nd.pdf">Knuth's algorithm</a>.
+     * It generates count distinct numbers from range [begin,end). <br/>Complexity: {@code O(count)}.
+     * @param count
+     * @param begin
+     * @param end
+     * @return
+     */
+    public static IntList uniqueInts(int count, int begin, int end) {
+        return uniqueInts(new Random(), count, begin, end);
+    }
+
+    public static IntList uniqueInts(Random rng, int count, int begin, int end) {
+        int n = end - begin;
+        IntList result = new IntArrayList(count);
+        for(int i=0; i<n; i++) {
+            if( Long.remainderUnsigned(rng.nextLong(), n-i) < count) {
+                result.push(begin + i);
+                count--;
+            }
+        }
+        return result;
+    }
+
+}

+ 63 - 3
assira/src/main/java/net/ranides/assira/reflection/util/BeanUtils.java

@@ -7,10 +7,17 @@
 
 package net.ranides.assira.reflection.util;
 
-import java.beans.XMLDecoder;
-import java.beans.XMLEncoder;
 import net.ranides.assira.io.StringInput;
 import net.ranides.assira.io.StringOutput;
+import net.ranides.assira.reflection.BeanModel;
+import net.ranides.assira.reflection.BeanProperty;
+import net.ranides.assira.reflection.IClass;
+
+import java.beans.XMLDecoder;
+import java.beans.XMLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
 
 /**
  *
@@ -40,5 +47,58 @@ public final class BeanUtils {
     public static Object copy(Object object) {
         return fromXML( toXML(object) );
     }
-    
+
+    public static <T,S> BeanMapper<T,S> mapper(IClass<T> target, IClass<S> source) {
+        return new BeanMapper<>(target, source);
+    }
+
+    public static <T> T map(T target, Object source) {
+        match(BeanModel.typefor(target), BeanModel.typefor(source), (pt, ps)-> { pt.set(target, ps.get(source)); });
+        return target;
+    }
+
+    private static void match(BeanModel mt, BeanModel ms, BiConsumer<BeanProperty, BeanProperty> matcher) {
+        for (BeanProperty ps : ms.properties().values()) {
+            if(ps.isReadable()) {
+                BeanProperty pt = mt.properties().get(ps.name());
+                if(pt!=null && pt.isWritable() && pt.type().isEquivalent(ps.type())) {
+                    matcher.accept(pt, ps);
+                }
+            }
+        }
+    }
+
+    public static class BeanMapper<T,S> {
+
+        private final List<BeanPropertyMapper> matched = new ArrayList<>();
+
+        private BeanMapper(IClass<T> target, IClass<S> source) {
+            match(BeanModel.typeinfo(target), BeanModel.typeinfo(source), (pt,ps) -> {
+                matched.add(new BeanPropertyMapper(pt, ps));
+            });
+        }
+
+        public void map(T target, S source) {
+            for(BeanPropertyMapper bpm : matched) {
+                bpm.move(target, source);
+            }
+        }
+    }
+
+    private static class BeanPropertyMapper {
+
+        private final BeanProperty tp;
+
+        private final BeanProperty sp;
+
+        public BeanPropertyMapper(BeanProperty target, BeanProperty source) {
+            this.tp = target;
+            this.sp = source;
+        }
+
+        public void move(Object target, Object source) {
+            tp.set(target, sp.get(source));
+        }
+    }
+
 }

+ 84 - 0
assira/src/test/java/net/ranides/assira/math/RandomUtilsTest.java

@@ -0,0 +1,84 @@
+package net.ranides.assira.math;
+
+import net.ranides.assira.collection.lists.IntArrayList;
+import net.ranides.assira.collection.lists.IntList;
+import org.junit.Test;
+
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+public class RandomUtilsTest {
+
+    @Test
+    public void testStreamPerformance() {
+        Random random = new SecureRandom();
+
+        for(int i=0; i<5;i++) {
+            timeUniqueStream(random, 100_000, 0, 1_000_000);
+            timeUniqueStream(random, 100_000, 0, 200_000);
+            timeUniqueStream(random, 100_000, 0, 111_000);
+            System.out.println();
+        }
+        assertTrue(true);
+    }
+
+    private void timeUniqueStream(Random random, int count, int start, int end) {
+        long dt = System.currentTimeMillis();
+        int a = 0;
+        for(int i=0; i<10; i++) {
+            int[] values = uniqueFromStream(random, count, start, end);
+            a += values[0];
+        }
+
+        dt = System.currentTimeMillis() - dt;
+        System.out.printf("%d time = %.0f%% %d%n", a%10, (100.0*count)/(end-start),dt);
+    }
+
+    @Test
+    public void testDoubleP() {
+        Random rng = new Random();
+        for(int i=0; i<50; i++) {
+            System.out.println(Arrays.toString(uniqueDoubleP(rng,5, 0, 10)));
+        }
+    }
+
+    @Test
+    public void testKnuth() {
+        Random rng = new Random();
+        for(int i=0; i<50; i++) {
+            System.out.println(RandomUtils.uniqueInts(rng,5, 0, 10));
+        }
+    }
+
+    @Test
+    public void testStream() {
+        Random rng = new Random();
+        for(int i=0; i<50; i++) {
+            System.out.println(Arrays.toString(uniqueFromStream(rng, 10, 5, 0)));
+        }
+    }
+
+    public static int[] uniqueDoubleP(Random rng, int count, int start, int end) {
+
+        IntList result = new IntArrayList(count);
+        int remaining = end - start;
+        for (int i = start; i < end && count > 0; i++) {
+            double probability = rng.nextDouble();
+            if (probability < ((double) count) / (double) remaining) {
+                count--;
+                result.push(i);
+            }
+            remaining--;
+        }
+        return result.toIntArray();
+    }
+
+    private static int[] uniqueFromStream(Random random, int count, int start, int end) {
+        return random.ints(start, end).distinct().limit(count).toArray();
+    }
+
+
+}