mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-11-02 16:49:39 +01:00
Actually it is CompressedWords, rather.
This commit is contained in:
parent
17ded8adee
commit
f2b29da6a6
@ -18,7 +18,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharPrefixTree(NodeFactory<Character, N> nodeFactory, LookupEntryFactory<Character, N, L> resultFactory) {
|
public CharPrefixTree(final NodeFactory<Character, N> nodeFactory, final LookupEntryFactory<Character, N, L> resultFactory) {
|
||||||
super(nodeFactory, resultFactory);
|
super(nodeFactory, resultFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
|||||||
* @param create
|
* @param create
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public L lookup(final char[] chars, boolean create){
|
public L lookup(final char[] chars, final boolean create){
|
||||||
return lookup(toCharacterList(chars), create);
|
return lookup(toCharacterList(chars), create);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
|||||||
* @param create
|
* @param create
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public L lookup(final String input, boolean create){
|
public L lookup(final String input, final boolean create){
|
||||||
return lookup(input.toCharArray(), create);
|
return lookup(input.toCharArray(), create);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
|||||||
return feed(toCharacterList(chars));
|
return feed(toCharacterList(chars));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void feedAll(final Collection<String> inputs, boolean trim, boolean lowerCase){
|
public void feedAll(final Collection<String> inputs, final boolean trim, final boolean lowerCase){
|
||||||
for (String input : inputs){
|
for (String input : inputs){
|
||||||
if (trim) input = input.toLowerCase();
|
if (trim) input = input.toLowerCase();
|
||||||
if (lowerCase) input = input.toLowerCase();
|
if (lowerCase) input = input.toLowerCase();
|
||||||
@ -118,7 +118,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
|||||||
}
|
}
|
||||||
}, new LookupEntryFactory<Character, CharNode, CharLookupEntry<CharNode>>() {
|
}, new LookupEntryFactory<Character, CharNode, CharLookupEntry<CharNode>>() {
|
||||||
@Override
|
@Override
|
||||||
public CharLookupEntry<CharNode> newLookupEntry(CharNode node, CharNode insertion, int depth, boolean hasPrefix) {
|
public final CharLookupEntry<CharNode> newLookupEntry(final CharNode node, final CharNode insertion, final int depth, final boolean hasPrefix) {
|
||||||
return new CharLookupEntry<CharNode>(node, insertion, depth, hasPrefix);
|
return new CharLookupEntry<CharNode>(node, insertion, depth, hasPrefix);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -83,7 +83,7 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
|||||||
/** If nodes visit method shall be called. */
|
/** If nodes visit method shall be called. */
|
||||||
protected boolean visit;
|
protected boolean visit;
|
||||||
|
|
||||||
public PrefixTree(NodeFactory<K, N> nodeFactory, LookupEntryFactory<K, N, L> resultFactory){
|
public PrefixTree(final NodeFactory<K, N> nodeFactory, final LookupEntryFactory<K, N, L> resultFactory){
|
||||||
this.nodeFactory = nodeFactory;
|
this.nodeFactory = nodeFactory;
|
||||||
this.root = nodeFactory.newNode(null);
|
this.root = nodeFactory.newNode(null);
|
||||||
this.resultFactory = resultFactory;
|
this.resultFactory = resultFactory;
|
||||||
@ -113,7 +113,7 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
|||||||
* @param create
|
* @param create
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public L lookup(K[] keys, final boolean create){
|
public L lookup(final K[] keys, final boolean create){
|
||||||
return lookup(Arrays.asList(keys), create);
|
return lookup(Arrays.asList(keys), create);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,9 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
|||||||
else if (depth == keys.size()){
|
else if (depth == keys.size()){
|
||||||
node = current;
|
node = current;
|
||||||
}
|
}
|
||||||
return resultFactory.newLookupEntry(node, insertion, depth, hasPrefix);
|
final L result = resultFactory.newLookupEntry(node, insertion, depth, hasPrefix);
|
||||||
|
decorate(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,6 +168,13 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
|||||||
protected void visit(final N node){
|
protected void visit(final N node){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorate before returning.
|
||||||
|
* @param result
|
||||||
|
*/
|
||||||
|
protected void decorate(final L result){
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param keys
|
* @param keys
|
||||||
@ -258,7 +267,7 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
|||||||
}
|
}
|
||||||
}, new LookupEntryFactory<K, Node<K>, LookupEntry<K,Node<K>>>() {
|
}, new LookupEntryFactory<K, Node<K>, LookupEntry<K,Node<K>>>() {
|
||||||
@Override
|
@Override
|
||||||
public LookupEntry<K, Node<K>> newLookupEntry(Node<K> node, Node<K> insertion, int depth, boolean hasPrefix) {
|
public final LookupEntry<K, Node<K>> newLookupEntry(final Node<K> node, final Node<K> insertion, final int depth, final boolean hasPrefix) {
|
||||||
return new LookupEntry<K, PrefixTree.Node<K>>(node, insertion, depth, hasPrefix);
|
return new LookupEntry<K, PrefixTree.Node<K>>(node, insertion, depth, hasPrefix);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -25,7 +25,7 @@ public class SimpleCharPrefixTree extends CharPrefixTree<CharNode, SimpleCharLoo
|
|||||||
}
|
}
|
||||||
}, new LookupEntryFactory<Character, CharNode, SimpleCharLookupEntry>() {
|
}, new LookupEntryFactory<Character, CharNode, SimpleCharLookupEntry>() {
|
||||||
@Override
|
@Override
|
||||||
public SimpleCharLookupEntry newLookupEntry(CharNode node, CharNode insertion, int depth, boolean hasPrefix) {
|
public final SimpleCharLookupEntry newLookupEntry(final CharNode node, final CharNode insertion, final int depth, final boolean hasPrefix) {
|
||||||
return new SimpleCharLookupEntry(node, insertion, depth, hasPrefix);
|
return new SimpleCharLookupEntry(node, insertion, depth, hasPrefix);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
package fr.neatmonster.nocheatplus.checks.chat.analysis.ds;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimpleTimedCharPrefixTree.SimpleTimedCharLookupEntry;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.TimedCharPrefixTree.TimedCharLookupEntry;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.TimedCharPrefixTree.TimedCharNode;
|
||||||
|
|
||||||
|
public class SimpleTimedCharPrefixTree extends TimedCharPrefixTree<TimedCharNode, SimpleTimedCharLookupEntry> {
|
||||||
|
|
||||||
|
public static class SimpleTimedCharLookupEntry extends TimedCharLookupEntry<TimedCharNode>{
|
||||||
|
public SimpleTimedCharLookupEntry(TimedCharNode node, TimedCharNode insertion, int depth, boolean hasPrefix) {
|
||||||
|
super(node, insertion, depth, hasPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleTimedCharPrefixTree(final boolean access){
|
||||||
|
super(
|
||||||
|
new NodeFactory<Character, TimedCharNode>(){
|
||||||
|
@Override
|
||||||
|
public final TimedCharNode newNode(final TimedCharNode parent) {
|
||||||
|
final long ts;
|
||||||
|
if (parent == null) ts = System.currentTimeMillis();
|
||||||
|
else ts = parent.ts;
|
||||||
|
return new TimedCharNode(ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,
|
||||||
|
new LookupEntryFactory<Character, TimedCharNode, SimpleTimedCharLookupEntry>() {
|
||||||
|
@Override
|
||||||
|
public final SimpleTimedCharLookupEntry newLookupEntry(final TimedCharNode node,
|
||||||
|
final TimedCharNode insertion, final int depth, final boolean hasPrefix) {
|
||||||
|
return new SimpleTimedCharLookupEntry(node, insertion, depth, hasPrefix);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
access);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package fr.neatmonster.nocheatplus.checks.chat.analysis.ds;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.TimedCharPrefixTree.TimedCharLookupEntry;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.TimedCharPrefixTree.TimedCharNode;
|
||||||
|
|
||||||
|
public class TimedCharPrefixTree<N extends TimedCharNode, L extends TimedCharLookupEntry<N>> extends CharPrefixTree<N, L> {
|
||||||
|
|
||||||
|
public static class TimedCharLookupEntry<N extends TimedCharNode> extends CharLookupEntry<N>{
|
||||||
|
public long[] timeInsertion = null;
|
||||||
|
public TimedCharLookupEntry(N node, N insertion, int depth, boolean hasPrefix){
|
||||||
|
super(node, insertion, depth, hasPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TimedCharNode extends CharNode{
|
||||||
|
public long ts = 0;
|
||||||
|
public TimedCharNode(long ts){
|
||||||
|
this.ts = ts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long ts;
|
||||||
|
|
||||||
|
protected long[] timeInsertion = new long[200];
|
||||||
|
|
||||||
|
protected final boolean access;
|
||||||
|
|
||||||
|
protected boolean updateTime = false;
|
||||||
|
|
||||||
|
protected int depth;
|
||||||
|
|
||||||
|
protected float arrayGrowth = 1.3f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param nodeFactory
|
||||||
|
* @param resultFactory
|
||||||
|
* @param access If to set timestamps, even if create is false.
|
||||||
|
*/
|
||||||
|
public TimedCharPrefixTree(final NodeFactory<Character, N> nodeFactory, final LookupEntryFactory<Character, N, L> resultFactory, final boolean access) {
|
||||||
|
super(nodeFactory, resultFactory);
|
||||||
|
visit = true;
|
||||||
|
this.access = access;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public L lookup(final List<Character> keys, final boolean create) {
|
||||||
|
ts = System.currentTimeMillis();
|
||||||
|
updateTime = access || create;
|
||||||
|
depth = 0;
|
||||||
|
return super.lookup(keys, create);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visit(final N node) {
|
||||||
|
if (depth == timeInsertion.length){
|
||||||
|
// This might be excluded by contract.
|
||||||
|
timeInsertion = Arrays.copyOf(timeInsertion, (int) (timeInsertion.length * arrayGrowth));
|
||||||
|
}
|
||||||
|
timeInsertion[depth] = node.ts;
|
||||||
|
if (updateTime) node.ts = ts;
|
||||||
|
depth ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decorate(final L result) {
|
||||||
|
result.timeInsertion = Arrays.copyOf(timeInsertion, depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param access If to set timestamps, even if create is false.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static TimedCharPrefixTree<TimedCharNode, TimedCharLookupEntry<TimedCharNode>> newTimedCharPrefixTree(final boolean access) {
|
||||||
|
return new TimedCharPrefixTree<TimedCharNode, TimedCharLookupEntry<TimedCharNode>>(
|
||||||
|
new NodeFactory<Character, TimedCharNode>(){
|
||||||
|
@Override
|
||||||
|
public final TimedCharNode newNode(final TimedCharNode parent) {
|
||||||
|
final long ts;
|
||||||
|
if (parent == null) ts = System.currentTimeMillis();
|
||||||
|
else ts = parent.ts;
|
||||||
|
return new TimedCharNode(ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,
|
||||||
|
new LookupEntryFactory<Character, TimedCharNode, TimedCharLookupEntry<TimedCharNode>>() {
|
||||||
|
@Override
|
||||||
|
public final TimedCharLookupEntry<TimedCharNode> newLookupEntry(final TimedCharNode node,
|
||||||
|
final TimedCharNode insertion, final int depth, final boolean hasPrefix) {
|
||||||
|
return new TimedCharLookupEntry<TimedCharNode>(node, insertion, depth, hasPrefix);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
access
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,78 +0,0 @@
|
|||||||
package fr.neatmonster.nocheatplus.checks.chat.analysis.engine;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.MessageLetterCount;
|
|
||||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.WordLetterCount;
|
|
||||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimplePrefixTree;
|
|
||||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimplePrefixTree.SimpleLookupEntry;
|
|
||||||
|
|
||||||
public class CompressedChars extends AbstractWordProcessor{
|
|
||||||
|
|
||||||
protected final SimplePrefixTree<Character> tree = new SimplePrefixTree<Character>();
|
|
||||||
|
|
||||||
protected final int maxAdd;
|
|
||||||
|
|
||||||
protected int added = 0;
|
|
||||||
|
|
||||||
private final boolean sort;
|
|
||||||
|
|
||||||
public CompressedChars(int maxAdd, boolean sort) {
|
|
||||||
super("CompressedChars");
|
|
||||||
this.maxAdd = maxAdd;
|
|
||||||
this.sort = sort;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start(MessageLetterCount message) {
|
|
||||||
// This allows adding up to maximum messge length more characters,
|
|
||||||
// but also allows to set size of nodes exactly.
|
|
||||||
// TODO: Some better method than blunt clear (extra LinkedHashSet/LRU?).
|
|
||||||
if (added > maxAdd) tree.clear();
|
|
||||||
added = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float loop(long ts, int index, String key, WordLetterCount word) {
|
|
||||||
final int len = word.counts.size();
|
|
||||||
final List<Character> letters = new ArrayList<Character>(len);
|
|
||||||
final List<Character> numbers = new ArrayList<Character>(Math.min(len, 10));
|
|
||||||
final List<Character> other = new ArrayList<Character>(Math.min(len, 10));
|
|
||||||
for (Character c : word.counts.keySet()){
|
|
||||||
if (Character.isLetter(c)) letters.add(c);
|
|
||||||
else if (Character.isDigit(c)) numbers.add(c);
|
|
||||||
else other.add(c);
|
|
||||||
}
|
|
||||||
if (sort){
|
|
||||||
Collections.sort(letters);
|
|
||||||
Collections.sort(numbers);
|
|
||||||
Collections.sort(other);
|
|
||||||
}
|
|
||||||
float score = 0;
|
|
||||||
if (!letters.isEmpty()){
|
|
||||||
score += getScore(letters);
|
|
||||||
}
|
|
||||||
if (!numbers.isEmpty()){
|
|
||||||
score += getScore(numbers);
|
|
||||||
}
|
|
||||||
if (!other.isEmpty()){
|
|
||||||
score += getScore(other);
|
|
||||||
}
|
|
||||||
return word.counts.isEmpty()?0f:(score / (float) len);
|
|
||||||
}
|
|
||||||
|
|
||||||
private float getScore(List<Character> chars) {
|
|
||||||
final int len = chars.size();
|
|
||||||
SimpleLookupEntry<Character> entry = tree.lookup(chars, true);
|
|
||||||
float score = (float) (entry.depth*entry.depth) / (float) (len*len) ;
|
|
||||||
if (entry.depth == chars.size()){
|
|
||||||
score += 0.2;
|
|
||||||
if (entry.insertion.isEnd) score += 0.2;
|
|
||||||
}
|
|
||||||
if (len != entry.depth) added += len - entry.depth;
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,91 @@
|
|||||||
|
package fr.neatmonster.nocheatplus.checks.chat.analysis.engine;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.checks.chat.analysis.MessageLetterCount;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.chat.analysis.WordLetterCount;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimpleTimedCharPrefixTree;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimpleTimedCharPrefixTree.SimpleTimedCharLookupEntry;
|
||||||
|
|
||||||
|
public class CompressedWords extends AbstractWordProcessor{
|
||||||
|
|
||||||
|
protected final SimpleTimedCharPrefixTree tree = new SimpleTimedCharPrefixTree(true);
|
||||||
|
|
||||||
|
protected final int maxAdd;
|
||||||
|
|
||||||
|
protected int added = 0;
|
||||||
|
|
||||||
|
protected final boolean sort;
|
||||||
|
|
||||||
|
protected final long durExpire;
|
||||||
|
|
||||||
|
protected final List<Character> letters = new ArrayList<Character>(10);
|
||||||
|
protected final List<Character> digits = new ArrayList<Character>(10);
|
||||||
|
protected final List<Character> other = new ArrayList<Character>(10);
|
||||||
|
|
||||||
|
public CompressedWords(long durExpire, int maxAdd, boolean sort) {
|
||||||
|
super("CompressedWords");
|
||||||
|
this.durExpire = durExpire;
|
||||||
|
this.maxAdd = maxAdd;
|
||||||
|
this.sort = sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(MessageLetterCount message) {
|
||||||
|
// This allows adding up to maximum messge length more characters,
|
||||||
|
// but also allows to set size of nodes exactly.
|
||||||
|
// TODO: Some better method than blunt clear (extra LinkedHashSet/LRU?).
|
||||||
|
if (added > maxAdd) tree.clear();
|
||||||
|
added = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float loop(final long ts, final int index, final String key, final WordLetterCount word) {
|
||||||
|
final int len = word.counts.size();
|
||||||
|
letters.clear();
|
||||||
|
digits.clear();
|
||||||
|
other.clear();
|
||||||
|
for (Character c : word.counts.keySet()){
|
||||||
|
if (Character.isLetter(c)) letters.add(c);
|
||||||
|
else if (Character.isDigit(c)) digits.add(c);
|
||||||
|
else other.add(c);
|
||||||
|
}
|
||||||
|
if (sort){
|
||||||
|
Collections.sort(letters);
|
||||||
|
Collections.sort(digits);
|
||||||
|
Collections.sort(other);
|
||||||
|
}
|
||||||
|
float score = 0;
|
||||||
|
if (!letters.isEmpty()){
|
||||||
|
score += getScore(letters, ts);
|
||||||
|
}
|
||||||
|
if (!digits.isEmpty()){
|
||||||
|
score += getScore(digits, ts);
|
||||||
|
}
|
||||||
|
if (!other.isEmpty()){
|
||||||
|
score += getScore(other, ts);
|
||||||
|
}
|
||||||
|
return word.counts.isEmpty()?0f:(score / (float) len);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected float getScore(final List<Character> chars, final long ts) {
|
||||||
|
final int len = chars.size();
|
||||||
|
final SimpleTimedCharLookupEntry entry = tree.lookup(chars, true);
|
||||||
|
final int depth = entry.depth;
|
||||||
|
float score = 0f;
|
||||||
|
for (int i = 0; i < depth ; i++){
|
||||||
|
final long age = ts - entry.timeInsertion[i];
|
||||||
|
if (age < durExpire)
|
||||||
|
score += 1f / (float) (depth - i) * (float) (durExpire - age) / (float) durExpire;
|
||||||
|
}
|
||||||
|
if (depth == len){
|
||||||
|
score += 0.2;
|
||||||
|
if (entry.insertion.isEnd) score += 0.2;
|
||||||
|
}
|
||||||
|
if (len != depth) added += len - depth;
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -25,7 +25,7 @@ public class LetterEngine {
|
|||||||
}
|
}
|
||||||
if (config.getBoolean(ConfPaths.CHAT_GLOBALCHAT_ENGINE_GLCOMPRWORDS_CHECK, false)){
|
if (config.getBoolean(ConfPaths.CHAT_GLOBALCHAT_ENGINE_GLCOMPRWORDS_CHECK, false)){
|
||||||
// TODO: Make aspects configurable.
|
// TODO: Make aspects configurable.
|
||||||
processors.add(new CompressedChars(2000, false));
|
processors.add(new CompressedWords(30000, 2000, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user