Optim: compressed resolver

This commit is contained in:
2017-09-22 15:53:50 +02:00
parent 77469496b3
commit dbfa1892ee
11 changed files with 327 additions and 130 deletions

11
pom.xml
View File

@@ -11,6 +11,17 @@
<artifactId>commons-cli</artifactId> <artifactId>commons-cli</artifactId>
<version>1.4</version> <version>1.4</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>
<!-- Unit testing --> <!-- Unit testing -->
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>

View File

@@ -3,14 +3,9 @@ package fr.pavnay.scrabble;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
public class DictionaryBuilder { public class DictionaryBuilder {
@@ -58,7 +53,7 @@ public class DictionaryBuilder {
public static Enigma generateEnigma(String language, int minLength, int maxLength) { public static Enigma generateEnigma(String language, int minLength, int maxLength) {
Resolver resolver; Resolver resolver;
try { try {
resolver = loadResolver(language); resolver = ScrabbleUtils.loadResolver(language);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
@@ -66,51 +61,19 @@ public class DictionaryBuilder {
return resolver.generateEnigma(minLength, maxLength); return resolver.generateEnigma(minLength, maxLength);
} }
public static void generateResolver(String language) throws IOException {
File file = new File(
"C:/Users/viaduc105.smallbusiness/Documents/workspace-sts/SocialArena/resources/" + language);
if (!file.exists()) {
System.out.println("No dictionary for " + language);
return;
}
Resolver resolver = generateResolver(new Resolver(), file, 3, 7);
resolver.computeStatistics();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( public static void main(String[] args) throws Exception {
new File("C:/Users/viaduc105.smallbusiness/Documents/workspace-sts/SocialArena/resources/resolver/" for( String language : new String[] {"english", "french"} ) {
+ language))); long avg = 0;
oos.writeObject(resolver); for( int i = 0; i < 100; i++) {
oos.flush(); resolvers.clear();
oos.close(); long t1 = System.currentTimeMillis();
} ScrabbleUtils.loadResolver(language);
long t2 = System.currentTimeMillis() - t1;
public static void loadResolvers() throws IOException, ClassNotFoundException { avg += t2;
List<String> languages = ScrabbleUtils.loadLanguages();
for (String language : languages) {
long t1 = System.currentTimeMillis();
loadResolver(language);
System.out.println("Resolver in " + language + " loaded in " + (System.currentTimeMillis() - t1) + "ms.");
}
}
private static Resolver loadResolver(String language)
throws FileNotFoundException, IOException, ClassNotFoundException {
Resolver resolver = (Resolver) resolvers.get(language);
if (resolver == null) {
long t1 = System.currentTimeMillis();
String path = DictionaryBuilder.class.getResource("/resolvers/" + language).getPath();
File file = new File(path);
if (!file.exists()) {
System.out.println("No resolver for " + language);
return null;
} }
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); System.out.println(language + " : " + (avg/100));
resolver = (Resolver) ois.readObject();
ois.close();
resolvers.put(language, resolver);
System.out.println("Resolver in " + language + " loaded in " + (System.currentTimeMillis() - t1) + "ms.");
} }
return resolver;
} }
} }

View File

@@ -1,9 +1,7 @@
package fr.pavnay.scrabble; package fr.pavnay.scrabble;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.List; import java.util.List;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
@@ -70,16 +68,15 @@ public class Main {
try { try {
Resolver resolver = DictionaryBuilder.generateResolver(new Resolver(), file, min, max); Resolver resolver = DictionaryBuilder.generateResolver(new Resolver(), file, min, max);
resolver.computeStatistics(); resolver.computeStatistics();
resolver.displayStatistics();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("src/main/resources/resolvers/" + language))); ScrabbleUtils.writeResolver(resolver, language);
oos.writeObject(resolver);
oos.flush();
oos.close();
} catch( IOException e) { } catch( IOException e) {
System.err.println(e.getMessage()); System.err.println(e.getMessage());
} }
} }
private static void getEnigma(CommandLine line) { private static void getEnigma(CommandLine line) {
final String language = line.getOptionValue("lang", ""); final String language = line.getOptionValue("lang", "");
final int min = Integer.parseInt(line.getOptionValue("min", "3")); final int min = Integer.parseInt(line.getOptionValue("min", "3"));

View File

@@ -1,73 +1,14 @@
package fr.pavnay.scrabble; package fr.pavnay.scrabble;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
public class Node implements Serializable { public interface Node extends Serializable {
private static final long serialVersionUID = 3539215265338287172L;
private Node[] nodes = new Node[26]; public void addWord(String word);
private List<String> words; public List<String> getWords();
private String parent; public Node getNode(char character);
public Object getNodes();
public int getWordsCount();
public Node(String parent) {
this.parent = parent;
}
public void addWord(String word) {
if (this.words == null) {
this.words = new ArrayList<String>();
}
if (!this.words.contains(word)) {
this.words.add(word);
}
}
public List<String> getWords() {
if (this.words == null) {
return null;
}
Collections.sort(this.words);
return this.words;
}
public Node getNode(char character) {
Node node = this.nodes[(character - 'a')];
if (node == null) {
node = new Node(this.parent + character);
this.nodes[(character - 'a')] = node;
}
return node;
}
public int getWordsCount() {
int total = 0;
for (int i = 0; i < 26; i++) {
Node current = this.nodes[i];
if (current != null) {
total += current.getWordsCount();
}
if (this.words != null) {
total += this.words.size();
}
}
return total;
}
public String toString() {
StringBuilder sb = new StringBuilder("Parent " + this.parent);
if (this.words != null) {
sb.append(this.words.toString());
} else {
sb.append('-');
}
for (int i = 0; i < 26; i++) {
Node current = this.nodes[i];
if (current != null) {
sb.append(current);
}
}
return sb.toString();
}
} }

View File

@@ -8,20 +8,20 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import fr.pavnay.scrabble.impl.HashNodeImpl;
public class Resolver implements Serializable { public class Resolver implements Serializable {
private static final long serialVersionUID = 8267126995323709570L;
private Node[] nodes = new Node[26]; private static final long serialVersionUID = 3424195161013074975L;
public Node nodes = new HashNodeImpl();
private float[] statistics = new float[26]; private float[] statistics = new float[26];
private float totalCharacter = 0.0F; private float totalCharacter = 0.0F;
public Node getNode(char character) { public Node getNode(char character) {
Node node = this.nodes[(character - 'a')]; Node node = nodes.getNode(character);
this.statistics[(character - 'a')] += 1.0F; this.statistics[(character - 'a')] += 1.0F;
this.totalCharacter += 1.0F; this.totalCharacter += 1.0F;
if (node == null) {
node = new Node(Character.toString(character));
this.nodes[(character - 'a')] = node;
}
return node; return node;
} }
@@ -132,7 +132,7 @@ public class Resolver implements Serializable {
int setSize = letters.length; int setSize = letters.length;
if (size == setSize) { if (size == setSize) {
Node currentNode = this.nodes[(sortLetters[0] - 'a')]; Node currentNode = nodes.getNode(sortLetters[0]);
if (currentNode == null) { if (currentNode == null) {
return null; return null;
} }
@@ -175,7 +175,8 @@ public class Resolver implements Serializable {
public int countWords() { public int countWords() {
int total = 0; int total = 0;
for (int i = 0; i < 26; i++) { for (int i = 0; i < 26; i++) {
Node current = this.nodes[i]; // Node current = this.nodes[i];
Node current = nodes.getNode((char)(i + 'a'));
if (current != null) { if (current != null) {
total += current.getWordsCount(); total += current.getWordsCount();
} }

View File

@@ -1,9 +1,24 @@
package fr.pavnay.scrabble; package fr.pavnay.scrabble;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleAbstractTypeResolver;
import com.fasterxml.jackson.databind.module.SimpleModule;
import fr.pavnay.scrabble.impl.HashNodeImpl;
import fr.pavnay.scrabble.serializer.NodeSerializer;
public class ScrabbleUtils { public class ScrabbleUtils {
@@ -14,4 +29,82 @@ public class ScrabbleUtils {
return Arrays.asList(files); return Arrays.asList(files);
} }
public static Resolver loadResolver(String language) {
long t1 = System.currentTimeMillis();
String path = DictionaryBuilder.class.getResource("/resolvers/" + language).getPath();
File file = new File(path);
if (!file.exists()) {
System.out.println("No resolver for " + language);
return null;
}
Resolver resolver = null;
try {
ObjectMapper mapper = getObjectMapper();
resolver = mapper.readerFor(Resolver.class).readValue(loadFile(file) );
} catch(IOException ioe) {
}
System.out.println("Resolver in " + language + " loaded in " + (System.currentTimeMillis() - t1) + "ms.");
return resolver;
}
public static boolean writeResolver(Resolver resolver, String language) {
ObjectMapper mapper = getObjectMapper();
try {
String serialized = mapper.writeValueAsString(resolver);
writeFile(serialized.getBytes(), language);
}catch(IOException e) {
return false;
}
return true;
}
private static void writeFile(byte[] content, String language) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
gzipOutputStream.write(content);
gzipOutputStream.close();
FileOutputStream out = new FileOutputStream( "src/main/resources/resolvers/" + language );
out.write(byteArrayOutputStream.toByteArray());
out.flush();
out.close();
}
private static InputStream loadFile(File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count = 0;
while ((count = fis.read(buffer))!=-1){
out.write(buffer, 0, count);
}
byte[] b = out.toByteArray();
fis.close();
return new GZIPInputStream(new ByteArrayInputStream(b));
}
private static ObjectMapper getObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
SimpleAbstractTypeResolver resolver = new SimpleAbstractTypeResolver();
resolver.addMapping(Node.class, HashNodeImpl.class);
module.setAbstractTypes(resolver);
module.addSerializer(Node.class, new NodeSerializer());
mapper.registerModule(module);
return mapper;
}
} }

View File

@@ -0,0 +1,81 @@
package fr.pavnay.scrabble.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import fr.pavnay.scrabble.Node;
public class HashNodeImpl implements Node {
private static final long serialVersionUID = 4221818951064573126L;
private Map<Character,Node> nodes = new HashMap<Character, Node>();
private List<String> words;
private String parent;
public HashNodeImpl() {
}
public HashNodeImpl(String parent) {
this.parent = parent;
}
public void addWord(String word) {
if (this.words == null) {
this.words = new ArrayList<String>();
}
if (!this.words.contains(word)) {
this.words.add(word);
}
}
public List<String> getWords() {
if (this.words == null) {
return null;
}
Collections.sort(this.words);
return this.words;
}
public Map<Character, Node> getNodes() {
return nodes;
}
public Node getNode(char character) {
Node node = this.nodes.get(character);
if (node == null) {
node = new HashNodeImpl(this.parent + character);
nodes.put(character, node);
}
return node;
}
public int getWordsCount() {
int total = 0;
for(Map.Entry<Character, Node> node : nodes.entrySet()) {
total += node.getValue().getWordsCount();
}
if (this.words != null) {
total += this.words.size();
}
return total;
}
public String toString() {
StringBuilder sb = new StringBuilder("Parent " + this.parent);
if (this.words != null) {
sb.append(this.words.toString());
} else {
sb.append('-');
}
for(Map.Entry<Character, Node> node : nodes.entrySet()) {
sb.append(node.getValue());
}
return sb.toString();
}
}

View File

@@ -0,0 +1,81 @@
package fr.pavnay.scrabble.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import fr.pavnay.scrabble.Node;
public class NodeImpl implements Node {
private static final long serialVersionUID = 6637351994367583747L;
public Node[] nodes = new Node[26];
public List<String> words;
private String parent;
public NodeImpl() {}
public NodeImpl(String parent) {
this.parent = parent;
}
public void addWord(String word) {
if (this.words == null) {
this.words = new ArrayList<String>();
}
if (!this.words.contains(word)) {
this.words.add(word);
}
}
public List<String> getWords() {
if (this.words == null) {
return null;
}
Collections.sort(this.words);
return this.words;
}
public Node[] getNodes() {
return this.nodes;
}
public Node getNode(char character) {
Node node = this.nodes[(character - 'a')];
if (node == null) {
node = new NodeImpl(this.parent + character);
this.nodes[(character - 'a')] = node;
}
return node;
}
public int getWordsCount() {
int total = 0;
for (int i = 0; i < 26; i++) {
Node current = this.nodes[i];
if (current != null) {
total += current.getWordsCount();
}
if (this.words != null) {
total += this.words.size();
}
}
return total;
}
public String toString() {
StringBuilder sb = new StringBuilder("Parent " + this.parent);
if (this.words != null) {
sb.append(this.words.toString());
} else {
sb.append('-');
}
for (int i = 0; i < 26; i++) {
Node current = this.nodes[i];
if (current != null) {
sb.append(current);
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,29 @@
package fr.pavnay.scrabble.serializer;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import fr.pavnay.scrabble.Node;
public class NodeSerializer extends StdSerializer<Node> {
private static final long serialVersionUID = -5136242177329213286L;
public NodeSerializer() {
this(null);
}
public NodeSerializer(Class<Node> t) {
super(t);
}
@Override
public void serialize(Node node, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeStartObject();
generator.writeObjectField("nodes", node.getNodes());
generator.writeObjectField("words", node.getWords());
generator.writeEndObject();
}
}

Binary file not shown.

Binary file not shown.