From 96d42c23a65f9f6406dd5deb86eefc716cca31ff Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Wed, 21 Sep 2022 18:10:49 -0500 Subject: [PATCH] progress on aztec --- src/aztec/DetectorTest.rs | 38 ++-- src/aztec/encoder/AztecCode.java | 89 -------- src/aztec/encoder/BinaryShiftToken.java | 61 ----- src/aztec/encoder/SimpleToken.java | 45 ---- src/aztec/encoder/State.java | 195 ---------------- src/aztec/encoder/Token.java | 46 ---- src/aztec/encoder/aztec_code.rs | 87 +++++++ src/aztec/encoder/binary_shift_token.rs | 72 ++++++ ...evelEncoder.java => high_level_encoder.rs} | 191 ++++++++++------ src/aztec/encoder/mod.rs | 13 ++ src/aztec/encoder/simple_token.rs | 54 +++++ src/aztec/encoder/state.rs | 212 ++++++++++++++++++ src/aztec/encoder/token.rs | 75 +++++++ src/aztec/mod.rs | 5 +- 14 files changed, 654 insertions(+), 529 deletions(-) delete mode 100644 src/aztec/encoder/AztecCode.java delete mode 100644 src/aztec/encoder/BinaryShiftToken.java delete mode 100644 src/aztec/encoder/SimpleToken.java delete mode 100644 src/aztec/encoder/State.java delete mode 100644 src/aztec/encoder/Token.java create mode 100644 src/aztec/encoder/aztec_code.rs create mode 100644 src/aztec/encoder/binary_shift_token.rs rename src/aztec/encoder/{HighLevelEncoder.java => high_level_encoder.rs} (73%) create mode 100644 src/aztec/encoder/mod.rs create mode 100644 src/aztec/encoder/simple_token.rs create mode 100644 src/aztec/encoder/state.rs create mode 100644 src/aztec/encoder/token.rs diff --git a/src/aztec/DetectorTest.rs b/src/aztec/DetectorTest.rs index 65c2ff8..b644418 100644 --- a/src/aztec/DetectorTest.rs +++ b/src/aztec/DetectorTest.rs @@ -56,17 +56,17 @@ use crate::common::BitMatrix; #[test] fn testErrorInParameterLocatorNotCompact() { - String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz"; - testErrorInParameterLocator(alphabet + alphabet + alphabet); + let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz"; + testErrorInParameterLocator(&format!("{}{}{}",alphabet , alphabet , alphabet)); } // Test that we can tolerate errors in the parameter locator bits fn testErrorInParameterLocator( data:&str) { - AztecCode aztec = Encoder.encode(data, 25, Encoder.DEFAULT_AZTEC_LAYERS); - Random random = new Random(aztec.getMatrix().hashCode()); // pseudo-random, but deterministic - int layers = aztec.getLayers(); - boolean compact = aztec.isCompact(); - List orientationPoints = getOrientationPoints(aztec); + let aztec = Encoder.encode(data, 25, Encoder.DEFAULT_AZTEC_LAYERS); + let random = new Random(aztec.getMatrix().hashCode()); // pseudo-random, but deterministic + let layers = aztec.getLayers(); + let compact = aztec.isCompact(); + let orientationPoints = getOrientationPoints(aztec); for (boolean isMirror : new boolean[] { false, true }) { for (BitMatrix matrix : getRotations(aztec.getMatrix())) { // Systematically try every possible 1- and 2-bit error. @@ -125,16 +125,16 @@ use crate::common::BitMatrix; // Returns a list of the four rotations of the BitMatrix. fn getRotations( matrix0:&BitMatrix)-> Vec { - BitMatrix matrix90 = rotateRight(matrix0); - BitMatrix matrix180 = rotateRight(matrix90); - BitMatrix matrix270 = rotateRight(matrix180); + let matrix90 = rotateRight(matrix0); + let matrix180 = rotateRight(matrix90); + let matrix270 = rotateRight(matrix180); return Arrays.asList(matrix0, matrix90, matrix180, matrix270); } // Rotates a square BitMatrix to the right by 90 degrees fn rotateRight( input:&BitMatrix) -> BitMatrix{ - int width = input.getWidth(); - BitMatrix result = new BitMatrix(width); + let width = input.getWidth(); + let result = new BitMatrix(width); for (int x = 0; x < width; x++) { for (int y = 0; y < width; y++) { if (input.get(x,y)) { @@ -149,7 +149,7 @@ use crate::common::BitMatrix; // matrix to the right, and then flipping it left-to-right fn transpose( input:&BitMatrix) -> BitMatrix { let width = input.getWidth(); - BitMatrix result = new BitMatrix(width); + let result = new BitMatrix(width); for (int x = 0; x < width; x++) { for (int y = 0; y < width; y++) { if (input.get(x, y)) { @@ -161,8 +161,8 @@ use crate::common::BitMatrix; } fn clone( input:&BitMatrix) -> BitMatrix { - int width = input.getWidth(); - BitMatrix result = new BitMatrix(width); + let width = input.getWidth(); + let result = new BitMatrix(width); for (int x = 0; x < width; x++) { for (int y = 0; y < width; y++) { if (input.get(x,y)) { @@ -173,10 +173,10 @@ use crate::common::BitMatrix; return result; } - fn getOrientationPoints( code::&AztecCode) -> Vec { - int center = code.getMatrix().getWidth() / 2; - int offset = code.isCompact() ? 5 : 7; - List result = new ArrayList<>(); + fn getOrientationPoints( code:&AztecCode) -> Vec { + let center = code.getMatrix().getWidth() / 2; + let offset = code.isCompact() ? 5 : 7; + let result = new ArrayList<>(); for (int xSign = -1; xSign <= 1; xSign += 2) { for (int ySign = -1; ySign <= 1; ySign += 2) { result.add(new Point(center + xSign * offset, center + ySign * offset)); diff --git a/src/aztec/encoder/AztecCode.java b/src/aztec/encoder/AztecCode.java deleted file mode 100644 index 6426d4e..0000000 --- a/src/aztec/encoder/AztecCode.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2013 ZXing authors - * - * Licensed 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.google.zxing.aztec.encoder; - -import com.google.zxing.common.BitMatrix; - -/** - * Aztec 2D code representation - * - * @author Rustam Abdullaev - */ -public final class AztecCode { - - private boolean compact; - private int size; - private int layers; - private int codeWords; - private BitMatrix matrix; - - /** - * @return {@code true} if compact instead of full mode - */ - public boolean isCompact() { - return compact; - } - - public void setCompact(boolean compact) { - this.compact = compact; - } - - /** - * @return size in pixels (width and height) - */ - public int getSize() { - return size; - } - - public void setSize(int size) { - this.size = size; - } - - /** - * @return number of levels - */ - public int getLayers() { - return layers; - } - - public void setLayers(int layers) { - this.layers = layers; - } - - /** - * @return number of data codewords - */ - public int getCodeWords() { - return codeWords; - } - - public void setCodeWords(int codeWords) { - this.codeWords = codeWords; - } - - /** - * @return the symbol image - */ - public BitMatrix getMatrix() { - return matrix; - } - - public void setMatrix(BitMatrix matrix) { - this.matrix = matrix; - } - -} diff --git a/src/aztec/encoder/BinaryShiftToken.java b/src/aztec/encoder/BinaryShiftToken.java deleted file mode 100644 index 969b4dc..0000000 --- a/src/aztec/encoder/BinaryShiftToken.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2013 ZXing authors - * - * Licensed 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.google.zxing.aztec.encoder; - -import com.google.zxing.common.BitArray; - -final class BinaryShiftToken extends Token { - - private final int binaryShiftStart; - private final int binaryShiftByteCount; - - BinaryShiftToken(Token previous, - int binaryShiftStart, - int binaryShiftByteCount) { - super(previous); - this.binaryShiftStart = binaryShiftStart; - this.binaryShiftByteCount = binaryShiftByteCount; - } - - @Override - public void appendTo(BitArray bitArray, byte[] text) { - int bsbc = binaryShiftByteCount; - for (int i = 0; i < bsbc; i++) { - if (i == 0 || (i == 31 && bsbc <= 62)) { - // We need a header before the first character, and before - // character 31 when the total byte code is <= 62 - bitArray.appendBits(31, 5); // BINARY_SHIFT - if (bsbc > 62) { - bitArray.appendBits(bsbc - 31, 16); - } else if (i == 0) { - // 1 <= binaryShiftByteCode <= 62 - bitArray.appendBits(Math.min(bsbc, 31), 5); - } else { - // 32 <= binaryShiftCount <= 62 and i == 31 - bitArray.appendBits(bsbc - 31, 5); - } - } - bitArray.appendBits(text[binaryShiftStart + i], 8); - } - } - - @Override - public String toString() { - return "<" + binaryShiftStart + "::" + (binaryShiftStart + binaryShiftByteCount - 1) + '>'; - } - -} diff --git a/src/aztec/encoder/SimpleToken.java b/src/aztec/encoder/SimpleToken.java deleted file mode 100644 index d3b0c45..0000000 --- a/src/aztec/encoder/SimpleToken.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2013 ZXing authors - * - * Licensed 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.google.zxing.aztec.encoder; - -import com.google.zxing.common.BitArray; - -final class SimpleToken extends Token { - - // For normal words, indicates value and bitCount - private final short value; - private final short bitCount; - - SimpleToken(Token previous, int value, int bitCount) { - super(previous); - this.value = (short) value; - this.bitCount = (short) bitCount; - } - - @Override - void appendTo(BitArray bitArray, byte[] text) { - bitArray.appendBits(value, bitCount); - } - - @Override - public String toString() { - int value = this.value & ((1 << bitCount) - 1); - value |= 1 << bitCount; - return '<' + Integer.toBinaryString(value | (1 << bitCount)).substring(1) + '>'; - } - -} diff --git a/src/aztec/encoder/State.java b/src/aztec/encoder/State.java deleted file mode 100644 index 9771fba..0000000 --- a/src/aztec/encoder/State.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2013 ZXing authors - * - * Licensed 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.google.zxing.aztec.encoder; - -import java.nio.charset.StandardCharsets; - -import java.util.ArrayList; -import java.util.List; - -import com.google.zxing.common.BitArray; - -/** - * State represents all information about a sequence necessary to generate the current output. - * Note that a state is immutable. - */ -final class State { - - static final State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0); - - // The current mode of the encoding (or the mode to which we'll return if - // we're in Binary Shift mode. - private final int mode; - // The list of tokens that we output. If we are in Binary Shift mode, this - // token list does *not* yet included the token for those bytes - private final Token token; - // If non-zero, the number of most recent bytes that should be output - // in Binary Shift mode. - private final int binaryShiftByteCount; - // The total number of bits generated (including Binary Shift). - private final int bitCount; - private final int binaryShiftCost; - - private State(Token token, int mode, int binaryBytes, int bitCount) { - this.token = token; - this.mode = mode; - this.binaryShiftByteCount = binaryBytes; - this.bitCount = bitCount; - this.binaryShiftCost = calculateBinaryShiftCost(binaryBytes); - } - - int getMode() { - return mode; - } - - Token getToken() { - return token; - } - - int getBinaryShiftByteCount() { - return binaryShiftByteCount; - } - - int getBitCount() { - return bitCount; - } - - State appendFLGn(int eci) { - State result = shiftAndAppend(HighLevelEncoder.MODE_PUNCT, 0); // 0: FLG(n) - Token token = result.token; - int bitsAdded = 3; - if (eci < 0) { - token = token.add(0, 3); // 0: FNC1 - } else if (eci > 999999) { - throw new IllegalArgumentException("ECI code must be between 0 and 999999"); - } else { - byte[] eciDigits = Integer.toString(eci).getBytes(StandardCharsets.ISO_8859_1); - token = token.add(eciDigits.length, 3); // 1-6: number of ECI digits - for (byte eciDigit : eciDigits) { - token = token.add(eciDigit - '0' + 2, 4); - } - bitsAdded += eciDigits.length * 4; - } - return new State(token, mode, 0, bitCount + bitsAdded); - } - - // Create a new state representing this state with a latch to a (not - // necessary different) mode, and then a code. - State latchAndAppend(int mode, int value) { - int bitCount = this.bitCount; - Token token = this.token; - if (mode != this.mode) { - int latch = HighLevelEncoder.LATCH_TABLE[this.mode][mode]; - token = token.add(latch & 0xFFFF, latch >> 16); - bitCount += latch >> 16; - } - int latchModeBitCount = mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5; - token = token.add(value, latchModeBitCount); - return new State(token, mode, 0, bitCount + latchModeBitCount); - } - - // Create a new state representing this state, with a temporary shift - // to a different mode to output a single value. - State shiftAndAppend(int mode, int value) { - Token token = this.token; - int thisModeBitCount = this.mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5; - // Shifts exist only to UPPER and PUNCT, both with tokens size 5. - token = token.add(HighLevelEncoder.SHIFT_TABLE[this.mode][mode], thisModeBitCount); - token = token.add(value, 5); - return new State(token, this.mode, 0, this.bitCount + thisModeBitCount + 5); - } - - // Create a new state representing this state, but an additional character - // output in Binary Shift mode. - State addBinaryShiftChar(int index) { - Token token = this.token; - int mode = this.mode; - int bitCount = this.bitCount; - if (this.mode == HighLevelEncoder.MODE_PUNCT || this.mode == HighLevelEncoder.MODE_DIGIT) { - int latch = HighLevelEncoder.LATCH_TABLE[mode][HighLevelEncoder.MODE_UPPER]; - token = token.add(latch & 0xFFFF, latch >> 16); - bitCount += latch >> 16; - mode = HighLevelEncoder.MODE_UPPER; - } - int deltaBitCount = - (binaryShiftByteCount == 0 || binaryShiftByteCount == 31) ? 18 : - (binaryShiftByteCount == 62) ? 9 : 8; - State result = new State(token, mode, binaryShiftByteCount + 1, bitCount + deltaBitCount); - if (result.binaryShiftByteCount == 2047 + 31) { - // The string is as long as it's allowed to be. We should end it. - result = result.endBinaryShift(index + 1); - } - return result; - } - - // Create the state identical to this one, but we are no longer in - // Binary Shift mode. - State endBinaryShift(int index) { - if (binaryShiftByteCount == 0) { - return this; - } - Token token = this.token; - token = token.addBinaryShift(index - binaryShiftByteCount, binaryShiftByteCount); - return new State(token, mode, 0, this.bitCount); - } - - // Returns true if "this" state is better (or equal) to be in than "that" - // state under all possible circumstances. - boolean isBetterThanOrEqualTo(State other) { - int newModeBitCount = this.bitCount + (HighLevelEncoder.LATCH_TABLE[this.mode][other.mode] >> 16); - if (this.binaryShiftByteCount < other.binaryShiftByteCount) { - // add additional B/S encoding cost of other, if any - newModeBitCount += other.binaryShiftCost - this.binaryShiftCost; - } else if (this.binaryShiftByteCount > other.binaryShiftByteCount && other.binaryShiftByteCount > 0) { - // maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it) - newModeBitCount += 10; - } - return newModeBitCount <= other.bitCount; - } - - BitArray toBitArray(byte[] text) { - List symbols = new ArrayList<>(); - for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) { - symbols.add(token); - } - BitArray bitArray = new BitArray(); - // Add each token to the result in forward order - for (int i = symbols.size() - 1; i >= 0; i--) { - symbols.get(i).appendTo(bitArray, text); - } - return bitArray; - } - - @Override - public String toString() { - return String.format("%s bits=%d bytes=%d", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount); - } - - private static int calculateBinaryShiftCost(int binaryShiftByteCount) { - if (binaryShiftByteCount > 62) { - return 21; // B/S with extended length - } - if (binaryShiftByteCount > 31) { - return 20; // two B/S - } - if (binaryShiftByteCount > 0) { - return 10; // one B/S - } - return 0; - } - -} diff --git a/src/aztec/encoder/Token.java b/src/aztec/encoder/Token.java deleted file mode 100644 index 8178f38..0000000 --- a/src/aztec/encoder/Token.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2013 ZXing authors - * - * Licensed 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.google.zxing.aztec.encoder; - -import com.google.zxing.common.BitArray; - -abstract class Token { - - static final Token EMPTY = new SimpleToken(null, 0, 0); - - private final Token previous; - - Token(Token previous) { - this.previous = previous; - } - - final Token getPrevious() { - return previous; - } - - final Token add(int value, int bitCount) { - return new SimpleToken(this, value, bitCount); - } - - final Token addBinaryShift(int start, int byteCount) { - //int bitCount = (byteCount * 8) + (byteCount <= 31 ? 10 : byteCount <= 62 ? 20 : 21); - return new BinaryShiftToken(this, start, byteCount); - } - - abstract void appendTo(BitArray bitArray, byte[] text); - -} diff --git a/src/aztec/encoder/aztec_code.rs b/src/aztec/encoder/aztec_code.rs new file mode 100644 index 0000000..55db13b --- /dev/null +++ b/src/aztec/encoder/aztec_code.rs @@ -0,0 +1,87 @@ +/* + * Copyright 2013 ZXing authors + * + * Licensed 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. + */ + +use crate::common::BitMatrix; + +/** + * Aztec 2D code representation + * + * @author Rustam Abdullaev + */ +pub struct AztecCode { + compact: bool, + size: u32, + layers: u32, + code_words: u32, + matrix: BitMatrix, +} + +impl AztecCode { + /** + * @return {@code true} if compact instead of full mode + */ + pub fn isCompact(&self) -> bool { + self.compact + } + + pub fn setCompact(&mut self, compact: bool) { + self.compact = compact; + } + + /** + * @return size in pixels (width and height) + */ + pub fn getSize(&self) -> u32 { + self.size + } + + pub fn setSize(&mut self, size: u32) { + self.size = size; + } + + /** + * @return number of levels + */ + pub fn getLayers(&self) -> u32 { + self.layers + } + + pub fn setLayers(&mut self, layers: u32) { + self.layers = layers; + } + + /** + * @return number of data codewords + */ + pub fn getCodeWords(&self) -> u32 { + self.code_words + } + + pub fn setCodeWords(&mut self, code_words: u32) { + self.code_words = code_words; + } + + /** + * @return the symbol image + */ + pub fn getMatrix(&self) -> &BitMatrix { + &self.matrix + } + + pub fn setMatrix(&mut self, matrix: BitMatrix) { + self.matrix = matrix; + } +} diff --git a/src/aztec/encoder/binary_shift_token.rs b/src/aztec/encoder/binary_shift_token.rs new file mode 100644 index 0000000..ba2ed5e --- /dev/null +++ b/src/aztec/encoder/binary_shift_token.rs @@ -0,0 +1,72 @@ +/* + * Copyright 2013 ZXing authors + * + * Licensed 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. + */ + +use std::fmt; + +use crate::common::BitArray; + +#[derive(Debug,PartialEq, Eq,Clone)] +pub struct BinaryShiftToken { + binaryShiftStart: u32, + binaryShiftByteCount: u32, +} + +impl BinaryShiftToken { + pub fn new(binaryShiftStart: u32, binaryShiftByteCount: u32) -> Self { + Self { + binaryShiftStart, + binaryShiftByteCount, + } + } + + pub fn appendTo(&self, bitArray: &mut BitArray, text: &[u8]) { + let bsbc = self.binaryShiftByteCount as usize; + for i in 0..bsbc { + // for (int i = 0; i < bsbc; i++) { + if (i == 0 || (i == 31 && bsbc <= 62)) { + // We need a header before the first character, and before + // character 31 when the total byte code is <= 62 + bitArray.appendBits(31, 5); // BINARY_SHIFT + if (bsbc > 62) { + bitArray.appendBits(bsbc as u32 - 31, 16); + } else if (i == 0) { + // 1 <= binaryShiftByteCode <= 62 + bitArray.appendBits(bsbc.min( 31) as u32, 5); + } else { + // 32 <= binaryShiftCount <= 62 and i == 31 + bitArray.appendBits(bsbc as u32 - 31, 5); + } + } + bitArray.appendBits(text[self.binaryShiftStart as usize + i].into(), 8); + } + } + + // @Override + // public String toString() { + // return "<" + binaryShiftStart + "::" + (binaryShiftStart + binaryShiftByteCount - 1) + '>'; + // } +} + +impl fmt::Display for BinaryShiftToken { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "<{}::{}>", + self.binaryShiftStart, + (self.binaryShiftStart + self.binaryShiftByteCount - 1) + ) + } +} diff --git a/src/aztec/encoder/HighLevelEncoder.java b/src/aztec/encoder/high_level_encoder.rs similarity index 73% rename from src/aztec/encoder/HighLevelEncoder.java rename to src/aztec/encoder/high_level_encoder.rs index 1d0cd4b..8c736f1 100644 --- a/src/aztec/encoder/HighLevelEncoder.java +++ b/src/aztec/encoder/high_level_encoder.rs @@ -14,20 +14,9 @@ * limitations under the License. */ -package com.google.zxing.aztec.encoder; +use crate::common::BitArray; -import com.google.zxing.common.BitArray; -import com.google.zxing.common.CharacterSetECI; -import java.nio.charset.Charset; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Deque; -import java.util.Iterator; -import java.util.LinkedList; /** * This produces nearly optimal encodings of text into the first-level of @@ -41,133 +30,191 @@ import java.util.LinkedList; * @author Frank Yellin * @author Rustam Abdullaev */ -public final class HighLevelEncoder { +pub struct HighLevelEncoder { - static final String[] MODE_NAMES = {"UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT"}; + text:Vec, + charset: &'static dyn encoding::Encoding, - static final int MODE_UPPER = 0; // 5 bits - static final int MODE_LOWER = 1; // 5 bits - static final int MODE_DIGIT = 2; // 4 bits - static final int MODE_MIXED = 3; // 5 bits - static final int MODE_PUNCT = 4; // 5 bits +} + +impl HighLevelEncoder { + pub const MODE_NAMES : [&str] = ["UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT"]; + + const MODE_UPPER :u32= 0; // 5 bits + const MODE_LOWER :u32 = 1; // 5 bits + const MODE_DIGIT :u32 = 2; // 4 bits + const MODE_MIXED :u32 = 3; // 5 bits + const MODE_PUNCT :u32 = 4; // 5 bits // The Latch Table shows, for each pair of Modes, the optimal method for // getting from one mode to another. In the worst possible case, this can // be up to 14 bits. In the best possible case, we are already there! // The high half-word of each entry gives the number of bits. // The low half-word of each entry are the actual bits necessary to change - static final int[][] LATCH_TABLE = { - { + const LATCH_TABLE : [[u32]]= [ + [ 0, (5 << 16) + 28, // UPPER -> LOWER (5 << 16) + 30, // UPPER -> DIGIT (5 << 16) + 29, // UPPER -> MIXED (10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT - }, - { + ], + [ (9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER 0, (5 << 16) + 30, // LOWER -> DIGIT (5 << 16) + 29, // LOWER -> MIXED (10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT - }, - { + ], + [ (4 << 16) + 14, // DIGIT -> UPPER (9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER 0, (9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED (14 << 16) + (14 << 10) + (29 << 5) + 30, - // DIGIT -> UPPER -> MIXED -> PUNCT - }, - { + ] // DIGIT -> UPPER -> MIXED -> PUNCT + , + [ (5 << 16) + 29, // MIXED -> UPPER (5 << 16) + 28, // MIXED -> LOWER (10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT 0, (5 << 16) + 30, // MIXED -> PUNCT - }, - { + ], + [ (5 << 16) + 31, // PUNCT -> UPPER (10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER (10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT (10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED 0, - }, - }; + ], + ]; // A reverse mapping from [mode][char] to the encoding for that character // in that mode. An entry of 0 indicates no mapping exists. - private static final int[][] CHAR_MAP = new int[5][256]; - static { - CHAR_MAP[MODE_UPPER][' '] = 1; + const CHAR_MAP : [[u32]] = { + let char_map = vec![vec![0u32;256];5]; + char_map[Self::MODE_UPPER][' '] = 1; for (int c = 'A'; c <= 'Z'; c++) { - CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2; + char_map[Self::MODE_UPPER][c] = c - 'A' + 2; } - CHAR_MAP[MODE_LOWER][' '] = 1; + char_map[Self::MODE_LOWER][' '] = 1; for (int c = 'a'; c <= 'z'; c++) { - CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2; + char_map[Self::MODE_LOWER][c] = c - 'a' + 2; } - CHAR_MAP[MODE_DIGIT][' '] = 1; + char_map[Self::MODE_DIGIT][' '] = 1; for (int c = '0'; c <= '9'; c++) { - CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2; + char_map[Self::MODE_DIGIT][c] = c - '0' + 2; } - CHAR_MAP[MODE_DIGIT][','] = 12; - CHAR_MAP[MODE_DIGIT]['.'] = 13; - int[] mixedTable = { + char_map[Self::MODE_DIGIT][','] = 12; + char_map[Self::MODE_DIGIT]['.'] = 13; + let mixedTable = [ '\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n', '\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^', '_', '`', '|', '~', '\177' - }; + ]; for (int i = 0; i < mixedTable.length; i++) { CHAR_MAP[MODE_MIXED][mixedTable[i]] = i; } - int[] punctTable = { + let punctTable = [ '\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', ']', '{', '}' - }; + ]; for (int i = 0; i < punctTable.length; i++) { if (punctTable[i] > 0) { CHAR_MAP[MODE_PUNCT][punctTable[i]] = i; } } - } + }; + // private static final int[][] CHAR_MAP = new int[5][256]; + // static { + // CHAR_MAP[MODE_UPPER][' '] = 1; + // for (int c = 'A'; c <= 'Z'; c++) { + // CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2; + // } + // CHAR_MAP[MODE_LOWER][' '] = 1; + // for (int c = 'a'; c <= 'z'; c++) { + // CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2; + // } + // CHAR_MAP[MODE_DIGIT][' '] = 1; + // for (int c = '0'; c <= '9'; c++) { + // CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2; + // } + // CHAR_MAP[MODE_DIGIT][','] = 12; + // CHAR_MAP[MODE_DIGIT]['.'] = 13; + // int[] mixedTable = { + // '\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n', + // '\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^', + // '_', '`', '|', '~', '\177' + // }; + // for (int i = 0; i < mixedTable.length; i++) { + // CHAR_MAP[MODE_MIXED][mixedTable[i]] = i; + // } + // int[] punctTable = { + // '\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'', + // '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', + // '[', ']', '{', '}' + // }; + // for (int i = 0; i < punctTable.length; i++) { + // if (punctTable[i] > 0) { + // CHAR_MAP[MODE_PUNCT][punctTable[i]] = i; + // } + // } + // } // A map showing the available shift codes. (The shifts to BINARY are not // shown - static final int[][] SHIFT_TABLE = new int[6][6]; // mode shift codes, per table - static { - for (int[] table : SHIFT_TABLE) { - Arrays.fill(table, -1); + const SHIFT_TABLE : [[i32]]= { // mode shift codes, per table + let mut shift_table = [[-1i32;6];6]; + + shift_table[Self::MODE_UPPER][Self::MODE_PUNCT] = 0; + + shift_table[Self::MODE_LOWER][Self::MODE_PUNCT] = 0; + shift_table[Self::MODE_LOWER][Self::MODE_UPPER] = 28; + + shift_table[Self::MODE_MIXED][Self::MODE_PUNCT] = 0; + + shift_table[Self::MODE_DIGIT][Self::MODE_PUNCT] = 0; + shift_table[Self::MODE_DIGIT][Self::MODE_UPPER] = 15; + + shift_table + }; + // const SHIFT_TABLE : [[u32]]= new int[6][6]; // mode shift codes, per table + // static { + // for (int[] table : SHIFT_TABLE) { + // Arrays.fill(table, -1); + // } + // SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0; + + // SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0; + // SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28; + + // SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0; + + // SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0; + // SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15; + // } + + + pub fn new(text:Vec) -> Self{ + Self{ + text, + charset: encoding::all::UTF_8, } - SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0; - - SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0; - SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28; - - SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0; - - SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0; - SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15; } - private final byte[] text; - private final Charset charset; - - public HighLevelEncoder(byte[] text) { - this.text = text; - this.charset = null; - } - - public HighLevelEncoder(byte[] text, Charset charset) { - this.text = text; - this.charset = charset; + pub fn with_charset(text:Vec, charset:&'static dyn encoding::Encoding) -> Self{ + Self{ + text, + charset, + } } /** * @return text represented by this encoder encoded as a {@link BitArray} */ - public BitArray encode() { + pub fn encode(&self)-> BitArray { State initialState = State.INITIAL_STATE; if (charset != null) { CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(charset); diff --git a/src/aztec/encoder/mod.rs b/src/aztec/encoder/mod.rs new file mode 100644 index 0000000..27434ad --- /dev/null +++ b/src/aztec/encoder/mod.rs @@ -0,0 +1,13 @@ +mod aztec_code; +mod token; +mod simple_token; +mod binary_shift_token; +mod state; +mod high_level_encoder; + +pub use aztec_code::*; +pub use token::*; +pub use simple_token::*; +pub use binary_shift_token::*; +pub use state::*; +pub use high_level_encoder::*; \ No newline at end of file diff --git a/src/aztec/encoder/simple_token.rs b/src/aztec/encoder/simple_token.rs new file mode 100644 index 0000000..fe7f599 --- /dev/null +++ b/src/aztec/encoder/simple_token.rs @@ -0,0 +1,54 @@ +/* + * Copyright 2013 ZXing authors + * + * Licensed 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. + */ + +use std::fmt; + +use crate::common::BitArray; + +#[derive(Debug,PartialEq, Eq,Clone)] +pub struct SimpleToken { + // For normal words, indicates value and bitCount + value: u16, + bitCount: u16, +} + +impl SimpleToken { + pub fn new(value: i32, bitCount: u32) -> Self { + Self { + value: value as u16, + bitCount: bitCount as u16, + } + } + + pub fn appendTo(&self, bitArray: &mut BitArray, text: &[u8]) { + bitArray.appendBits(self.value as u32, self.bitCount as usize).expect("append should never fail"); + } + + // @Override + // public String toString() { + // int value = this.value & ((1 << bitCount) - 1); + // value |= 1 << bitCount; + // return '<' + Integer.toBinaryString(value | (1 << bitCount)).substring(1) + '>'; + // } +} + +impl fmt::Display for SimpleToken { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut value = self.value & ((1 << self.bitCount) - 1); + value |= 1 << self.bitCount; + write!(f, "<{:#016b}>", value | (1 << self.bitCount)) + } +} diff --git a/src/aztec/encoder/state.rs b/src/aztec/encoder/state.rs new file mode 100644 index 0000000..423579b --- /dev/null +++ b/src/aztec/encoder/state.rs @@ -0,0 +1,212 @@ +/* + * Copyright 2013 ZXing authors + * + * Licensed 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. + */ + +use std::fmt; + +use crate::{exceptions::Exceptions, common::BitArray}; + +use super::{Token, TokenType}; + + +/** + * State represents all information about a sequence necessary to generate the current output. + * Note that a state is immutable. + */ +pub struct State { + + // static final State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0); + + // The current mode of the encoding (or the mode to which we'll return if + // we're in Binary Shift mode. + mode:u32, + // The list of tokens that we output. If we are in Binary Shift mode, this + // token list does *not* yet included the token for those bytes + token:Token, + // If non-zero, the number of most recent bytes that should be output + // in Binary Shift mode. + binaryShiftByteCount:u32, + // The total number of bits generated (including Binary Shift). + bitCount:u32, + binaryShiftCost:u32, +} + impl State { + pub fn new( token:Token, mode:u32, binaryBytes:u32, bitCount:u32) -> Self{ + Self{ + mode, + token, + binaryShiftByteCount: binaryBytes, + bitCount, + binaryShiftCost: Self::calculateBinaryShiftCost(binaryBytes), + } + } + + pub fn getMode(&self) -> u32{ + self.mode + } + + pub fn getToken(&self) -> &Token{ + &self.token + } + + pub fn getBinaryShiftByteCount(&self) -> u32{ + self.binaryShiftByteCount + } + + pub fn getBitCount(&self) -> u32{ + self.bitCount + } + + pub fn appendFLGn(&self, eci:u32) -> Result { + let result = self.shiftAndAppend(HighLevelEncoder::MODE_PUNCT, 0); // 0: FLG(n) + let token = result.token; + let bitsAdded = 3; + if eci < 0 { + token.add(0, 3); // 0: FNC1 + } else if eci > 999999 { + return Err(Exceptions::IllegalArgumentException("ECI code must be between 0 and 999999".to_owned())); + // throw new IllegalArgumentException("ECI code must be between 0 and 999999"); + } else { + let eciDigits = Integer.toString(eci).getBytes(StandardCharsets.ISO_8859_1); + token.add(eciDigits.length, 3); // 1-6: number of ECI digits + for eciDigit in eciDigits { + // for (byte eciDigit : eciDigits) { + token.add(eciDigit - '0' + 2, 4); + } + bitsAdded += eciDigits.length * 4; + } + Ok(State::new(token, self.mode, 0, self.bitCount + bitsAdded)) + // return new State(token, mode, 0, bitCount + bitsAdded); + } + + // Create a new state representing this state with a latch to a (not + // necessary different) mode, and then a code. + pub fn latchAndAppend(&self, mode:u32, value:u32) -> State{ + let bitCount = self.bitCount; + let token = self.token; + if mode != self.mode { + let latch = HighLevelEncoder.LATCH_TABLE[this.mode][mode]; + token.add(latch & 0xFFFF, latch >> 16); + bitCount += latch >> 16; + } + let latchModeBitCount = mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5; + token.add(value, latchModeBitCount); + + State::new(token, mode, 0, bitCount + latchModeBitCount) + } + + // Create a new state representing this state, with a temporary shift + // to a different mode to output a single value. + pub fn shiftAndAppend(&self, mode:u32, value:u32) -> State{ + let token = this.token; + let thisModeBitCount = this.mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5; + // Shifts exist only to UPPER and PUNCT, both with tokens size 5. + token = token.add(HighLevelEncoder.SHIFT_TABLE[this.mode][mode], thisModeBitCount); + token = token.add(value, 5); + State::new(token, this.mode, 0, this.bitCount + thisModeBitCount + 5) + } + + // Create a new state representing this state, but an additional character + // output in Binary Shift mode. + pub fn addBinaryShiftChar(&self, index:u32) -> State{ + let token = this.token; + let mode = this.mode; + let bitCount = this.bitCount; + if self.mode == HighLevelEncoder.MODE_PUNCT || self.mode == HighLevelEncoder.MODE_DIGIT { + let latch = HighLevelEncoder.LATCH_TABLE[mode][HighLevelEncoder.MODE_UPPER]; + token.add(latch & 0xFFFF, latch >> 16); + bitCount += latch >> 16; + mode = HighLevelEncoder.MODE_UPPER; + } + let deltaBitCount = + (binaryShiftByteCount == 0 || binaryShiftByteCount == 31) ? 18 : + (binaryShiftByteCount == 62) ? 9 : 8; + let result = State::new(token, mode, binaryShiftByteCount + 1, bitCount + deltaBitCount); + if (result.binaryShiftByteCount == 2047 + 31) { + // The string is as long as it's allowed to be. We should end it. + result = result.endBinaryShift(index + 1); + } + result + } + + // Create the state identical to this one, but we are no longer in + // Binary Shift mode. + pub fn endBinaryShift(self, index:u32) -> State{ + if self.binaryShiftByteCount == 0 { + return self; + } + let token = self.token; + self.token.addBinaryShift(index - self.binaryShiftByteCount, self.binaryShiftByteCount); + + State::new(token, self.mode, 0, self.bitCount) + } + + // Returns true if "this" state is better (or equal) to be in than "that" + // state under all possible circumstances. + pub fn isBetterThanOrEqualTo(&self, other:&State)->bool { + let newModeBitCount = self.bitCount + (HighLevelEncoder.LATCH_TABLE[this.mode][other.mode] >> 16); + if self.binaryShiftByteCount < other.binaryShiftByteCount { + // add additional B/S encoding cost of other, if any + newModeBitCount += other.binaryShiftCost - self.binaryShiftCost; + } else if self.binaryShiftByteCount > other.binaryShiftByteCount && other.binaryShiftByteCount > 0 { + // maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it) + newModeBitCount += 10; + } + newModeBitCount <= other.bitCount + } + + pub fn toBitArray(&self, text:&[u8]) -> BitArray{ + let symbols = Vec::new(); + let mut tok = self.endBinaryShift(text.len() as u32).token; + let mut tkn = tok.getPrevious(); + while tkn != &TokenType::Empty { + // for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) { + symbols.push(tkn); + tkn = tok.getPrevious(); + } + let bitArray = BitArray::new(); + // Add each token to the result in forward order + for i in (0..symbols.len()-1).rev() { + // for (int i = symbols.size() - 1; i >= 0; i--) { + symbols.get(i).unwrap().appendTo(bitArray, text); + } + bitArray + } + + // @Override + // public String toString() { + // return String.format("%s bits=%d bytes=%d", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount); + // } + + fn calculateBinaryShiftCost( binaryShiftByteCount:u32) -> u32{ + if binaryShiftByteCount > 62 { + return 21; // B/S with extended length + } + if binaryShiftByteCount > 31 { + return 20; // two B/S + } + if binaryShiftByteCount > 0 { + return 10; // one B/S + } + return 0; + } + +} + +impl fmt::Display for State { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f,"{} bits={} bytes={}", HighLevelEncoder::MODE_NAMES[mode], self.bitCount, self.binaryShiftByteCount) + } +} \ No newline at end of file diff --git a/src/aztec/encoder/token.rs b/src/aztec/encoder/token.rs new file mode 100644 index 0000000..4a42647 --- /dev/null +++ b/src/aztec/encoder/token.rs @@ -0,0 +1,75 @@ +/* + * Copyright 2013 ZXing authors + * + * Licensed 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. + */ + +use std::rc::Rc; + +use crate::common::BitArray; + +use super::{SimpleToken, BinaryShiftToken}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum TokenType { + Simple(SimpleToken), BinaryShift(BinaryShiftToken), Empty +} + +pub struct Token { + tokens: Vec, + current_pointer: usize, +} + +impl Token { + pub fn new () -> Self { + Self { + tokens: vec![TokenType::Empty], + current_pointer: 0, + } + } + pub fn getPrevious(&mut self) -> &TokenType{ + self.current_pointer -= 1; + &self.tokens[self.current_pointer] + } + pub fn add(&mut self, value:i32, bit_count: u32,) -> &TokenType { + self.current_pointer+=1; + self.tokens.push(TokenType::Simple(SimpleToken::new(value,bit_count))); + &self.tokens[self.current_pointer] + } + pub fn addBinaryShift(&mut self, start: u32, byte_count: u32) -> &TokenType { + self.current_pointer+=1; + self.tokens.push(TokenType::BinaryShift(BinaryShiftToken::new(start,byte_count))); + &self.tokens[self.current_pointer] + } +} + +// pub enum Token{ +// Simple(Rc), +// BinaryShift(), +// Empty, +// } + +// pub trait TokenTrait { +// fn getPrevious(&self)->&Token; + +// fn add(&self, value: i32, bitCount: u32) -> Token{ +// Token::Simple(Rc::new(SimpleToken::new(self, value, bitCount))) +// } + +// fn addBinaryShift(&self, start: i32, byteCount: u32) -> &Token; //{ +// // //int bitCount = (byteCount * 8) + (byteCount <= 31 ? 10 : byteCount <= 62 ? 20 : 21); +// // return new BinaryShiftToken(this, start, byteCount); +// // } + +// fn appendTo(&self, bitArray: BitArray, text: &[u8]); +// } diff --git a/src/aztec/mod.rs b/src/aztec/mod.rs index 4070fef..454f36d 100644 --- a/src/aztec/mod.rs +++ b/src/aztec/mod.rs @@ -1,12 +1,13 @@ mod AztecDetectorResult; pub mod decoder; pub mod detector; +pub mod encoder; #[cfg(test)] mod DecoderTest; // #[cfg(test)] // mod EncoderTest; -#[cfg(test)] -mod DetectorTest; +// #[cfg(test)] +// mod DetectorTest; mod shared_test_methods; \ No newline at end of file