com.simiacryptus.codes.HammingCode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-util Show documentation
Show all versions of java-util Show documentation
Miscellaneous Utilities (Pure Java)
package com.simiacryptus.codes;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import com.simiacryptus.binary.BitInputStream;
import com.simiacryptus.binary.Bits;
import com.simiacryptus.bitset.CountTreeBitsCollection;
public class HammingCode>
{
public class HammingCodeCollection extends CountTreeBitsCollection
{
public HammingCodeCollection()
{
super();
}
public HammingCodeCollection(final BitInputStream data) throws IOException
{
super(data);
}
public HammingCodeCollection(final byte[] data) throws IOException
{
super(data);
}
@Override
public CodeType getType(final Bits bits)
{
final Entry code = HammingCode.this.decode(bits);
if (null == code) { return CodeType.Prefix; }
assert bits.equals(code.getKey());
return CodeType.Terminal;
}
}
private static class SubCode> implements
Comparable>
{
final long count;
final TreeMap codes;
final TreeMap index;
public SubCode(final long count, final X item)
{
super();
this.count = count;
this.codes = new TreeMap();
this.index = new TreeMap();
this.codes.put(Bits.NULL, item);
this.index.put(item, Bits.NULL);
}
public SubCode(final SubCode zero, final SubCode one)
{
super();
this.count = zero.count + one.count;
this.codes = new TreeMap();
this.index = new TreeMap();
for (final Entry e : zero.codes.entrySet())
{
assert !one.index.containsKey(e.getValue());
final Bits code = Bits.ZERO.concatenate(e.getKey());
this.assertNull(this.codes.put(code, e.getValue()));
this.assertNull(this.index.put(e.getValue(), code));
}
for (final Entry e : one.codes.entrySet())
{
assert !zero.index.containsKey(e.getValue());
final Bits code = Bits.ONE.concatenate(e.getKey());
this.assertNull(this.codes.put(code, e.getValue()));
this.assertNull(this.index.put(e.getValue(), code));
}
}
private void assertNull(final Object obj)
{
assert null == obj;
}
@Override
public int compareTo(final SubCode o)
{
if (this.count < o.count) { return -1; }
if (this.count > o.count) { return 1; }
final int compareTo = this.index.firstKey().compareTo(o.index.firstKey());
assert 0 != compareTo;
return compareTo;
}
}
public static boolean isPrefixFreeCode(final Set keySet)
{
final TreeSet check = new TreeSet();
for (final Bits code : keySet)
{
final Bits ceiling = check.ceiling(code);
if (null != ceiling
&& (ceiling.startsWith(code) || code.startsWith(ceiling))) { return false; }
final Bits floor = check.floor(code);
if (null != floor && (floor.startsWith(code) || code.startsWith(floor))) { return false; }
check.add(code);
}
return true;
}
protected final TreeMap forwardIndex = new TreeMap();
protected final HashMap reverseIndex = new HashMap();
protected final HashMap weights = new HashMap();
protected final long totalWeight;
public HammingCode(final Collection> symbols)
{
if (0 < symbols.size())
{
final TreeSet> assemblySet = new TreeSet>();
for (final HammingSymbol s : symbols)
{
this.weights.put(s.key, s.count);
assemblySet.add(new SubCode(s.count, s.key));
}
while (assemblySet.size() > 1)
{
final SubCode zero = assemblySet.pollFirst();
final SubCode one = assemblySet.pollFirst();
assemblySet.add(new SubCode(zero, one));
}
final SubCode root = assemblySet.first();
this.forwardIndex.putAll(root.codes);
this.reverseIndex.putAll(root.index);
totalWeight = root.count;
}
else
{
totalWeight = 0;
}
assert this.verifyIndexes();
assert this.forwardIndex.size() == symbols.size();
}
public int codeSize()
{
return this.forwardIndex.size();
}
public T decode(final BitInputStream in) throws IOException
{
Bits remainder = in.readAhead(0);
Entry entry = this.forwardIndex.floorEntry(remainder);
while (entry == null || !remainder.startsWith(entry.getKey()))
{
remainder = in.readAhead();
entry = this.forwardIndex.floorEntry(remainder);
}
in.read(entry.getKey().bitLength);
return entry.getValue();
}
public Entry decode(final Bits data)
{
if (null == data) { throw new IllegalArgumentException(); }
Entry entry = this.forwardIndex.floorEntry(data);
// TestUtil.openJson(new JSONObject(forwardIndex));
if (entry != null && !data.startsWith(entry.getKey()))
{
entry = null;
}
// assert(null != entry || verifyIndexes());
return entry;
}
public Bits encode(final T key)
{
final Bits bits = this.reverseIndex.get(key);
assert null != bits || this.verifyIndexes();
return bits;
}
public SortedMap getCodes(final Bits fromKey)
{
final Bits next = fromKey.next();
final SortedMap subMap = null == next ? this.forwardIndex
.tailMap(fromKey) : this.forwardIndex.subMap(fromKey, next);
return subMap;
}
public CountTreeBitsCollection getSetEncoder()
{
return new HammingCodeCollection();
}
public CountTreeBitsCollection getSetEncoder(final BitInputStream data)
throws IOException
{
return new HammingCodeCollection(data);
}
public CountTreeBitsCollection getSetEncoder(final byte[] data)
throws IOException
{
return new HammingCodeCollection(data);
}
public Map getWeights()
{
return Collections.unmodifiableMap(this.weights);
}
public boolean verifyIndexes()
{
if (!isPrefixFreeCode(this.forwardIndex.keySet())) { return false; }
for (final Entry e : this.forwardIndex.entrySet())
{
if (!e.getKey().equals(this.reverseIndex.get(e.getValue()))) { return false; }
if (!e.getValue().equals(this.forwardIndex.get(e.getKey()))) { return false; }
}
for (final Entry e : this.reverseIndex.entrySet())
{
if (!e.getKey().equals(this.forwardIndex.get(e.getValue()))) { return false; }
if (!e.getValue().equals(this.reverseIndex.get(e.getKey()))) { return false; }
}
if (this.reverseIndex.size() != this.forwardIndex.size()) { return false; }
return true;
}
public int totalWeight()
{
// TODO Auto-generated method stub
return 0;
}
}