com.simiacryptus.util.binary.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)
The newest version!
/*
* Copyright (c) 2019 by Andrew Charneski.
*
* The author licenses this file to you under the
* Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance
* with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.simiacryptus.util.binary.codes;
import com.simiacryptus.ref.lang.RefUtil;
import com.simiacryptus.ref.wrappers.RefCollection;
import com.simiacryptus.ref.wrappers.RefTreeSet;
import com.simiacryptus.util.binary.BitInputStream;
import com.simiacryptus.util.binary.Bits;
import com.simiacryptus.util.binary.bitset.CountTreeBitsCollection;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
public class HammingCode> {
protected final TreeMap forwardIndex = new TreeMap();
protected final HashMap reverseIndex = new HashMap();
protected final HashMap weights = new HashMap();
protected final long totalWeight;
public HammingCode(@Nonnull final RefCollection> symbols) {
if (0 < symbols.size()) {
final RefTreeSet> assemblySet = new RefTreeSet>();
symbols.forEach(symbol -> {
this.weights.put(symbol.key, symbol.count);
assemblySet.add(new SubCode<>(symbol.count, symbol.key));
});
while (assemblySet.size() > 1) {
final SubCode zero = assemblySet.pollFirst();
final SubCode one = assemblySet.pollFirst();
assert one != null;
assert zero != null;
assemblySet.add(new SubCode(zero, one));
}
final SubCode root = assemblySet.first();
assemblySet.freeRef();
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();
symbols.freeRef();
}
@Nonnull
public CountTreeBitsCollection getSetEncoder() {
return new HammingCodeCollection(HammingCode.this);
}
@Nonnull
public Map getWeights() {
return Collections.unmodifiableMap(this.weights);
}
public static boolean isPrefixFreeCode(@Nonnull final Set keySet) {
final RefTreeSet check = new RefTreeSet();
for (final Bits code : keySet) {
final Bits ceiling = check.ceiling(code);
if (null != ceiling && (ceiling.startsWith(code) || code.startsWith(ceiling))) {
check.freeRef();
return false;
}
final Bits floor = check.floor(code);
if (null != floor && (floor.startsWith(code) || code.startsWith(floor))) {
check.freeRef();
return false;
}
check.add(code);
}
check.freeRef();
return true;
}
public int codeSize() {
return this.forwardIndex.size();
}
public T decode(@Nonnull 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();
if (null != entry) RefUtil.freeRef(entry);
entry = this.forwardIndex.floorEntry(remainder);
}
in.read(entry.getKey().bitLength);
T temp_05_0001 = entry.getValue();
RefUtil.freeRef(entry);
return temp_05_0001;
}
@Nullable
public Entry decode(@Nullable 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())) {
RefUtil.freeRef(entry);
return null;
} else {
// 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;
}
@Nonnull
public SortedMap getCodes(@Nonnull final Bits fromKey) {
final Bits next = fromKey.next();
return null == next ? this.forwardIndex.tailMap(fromKey) : this.forwardIndex.subMap(fromKey, next);
}
@Nonnull
public CountTreeBitsCollection getSetEncoder(@Nonnull final BitInputStream data) throws IOException {
return new HammingCodeCollection(data, HammingCode.this);
}
@Nonnull
public CountTreeBitsCollection getSetEncoder(final byte[] data) throws IOException {
return new HammingCodeCollection(data, HammingCode.this);
}
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;
}
}
return this.reverseIndex.size() == this.forwardIndex.size();
}
public int totalWeight() {
// TODO Auto-generated method stub
return 0;
}
public static class HammingCodeCollection> extends CountTreeBitsCollection {
private final HammingCode parent;
public HammingCodeCollection(HammingCode parent) {
super();
this.parent = parent;
}
public HammingCodeCollection(@Nonnull final BitInputStream data, HammingCode parent) throws IOException {
super(data);
this.parent = parent;
}
public HammingCodeCollection(final byte[] data, HammingCode parent) throws IOException {
super(data);
this.parent = parent;
}
@Nonnull
@Override
public CodeType getType(@Nonnull final Bits bits) {
final Entry code = parent.decode(bits);
if (null == code) {
return CodeType.Prefix;
}
assert bits.equals(code.getKey());
RefUtil.freeRef(code);
return CodeType.Terminal;
}
public @SuppressWarnings("unused")
void _free() {
super._free();
}
@Nonnull
public @Override
@SuppressWarnings("unused")
HammingCodeCollection addRef() {
return (HammingCodeCollection) super.addRef();
}
}
private static class SubCode> implements Comparable> {
final long count;
@Nonnull
final TreeMap codes;
@Nonnull
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(@Nonnull final SubCode zero, @Nonnull 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));
}
}
@Override
public int compareTo(@Nonnull 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;
}
private void assertNull(@Nullable final Object obj) {
assert null == obj;
}
}
}