public Dictionary <string, string> ProcessInput(string epcIdentifier, string parameterList)
        {
            // Translation process according to the TDT standard.

            // 1. SETUP

            // Read the input value and the supplied extra parameters.

            // Populate an associative array of key-value pairs with the supplied extra parameters.
            Dictionary <string, string> parameterDictionary = new Dictionary <string, string>();

            if (parameterList != null)
            {
                try
                {
                    ParseInput(parameterList, parameterDictionary);
                }
                catch (Exception)
                {
                    throw new TDTTranslationException("TDTParameterErrorException");
                }
            }

            // During the translation process, this associative array will be populated with additional
            // values of extracted fields or fields obtained through the application of rules of type
            // 'EXTRACT' or 'FORMAT'

            // 2. DETERMINE THE CODING SCHEME AND INBOUND REPRESENTATION LEVEL.

            // To find the scheme and level that matches the input value, consider all schemes and the
            // prefixMatch attribute of each level element within each scheme.
            Dictionary <Level, Scheme> inputLevelsSchemes = new Dictionary <Level, Scheme>();

            foreach (EpcTagDataTranslation e in epcTagDataTranslations)
            {
                Scheme s = e.scheme[0];

                foreach (Level l in s.level)
                {
                    // If the prefixMatch string matches the input value at the beginning, the scheme and
                    // level should be considered as a candidate for the inbound representation.
                    if (l.prefixMatch == null)
                    {
                        continue;
                    }

                    if (epcIdentifier.StartsWith(l.prefixMatch, StringComparison.CurrentCulture))
                    {
                        // If the scheme
                        // element specifies a taglength attribute, then if the value of this attribute does not
                        // match the value of the taglength key in the associative array, then this scheme and
                        // level should no longer be considered as a candidate for the inbound representation.
                        if (s.tagLength != null)
                        {
                            string taglength;
                            if (parameterDictionary.TryGetValue("taglength", out taglength))
                            {
                                if (!taglength.Equals(s.tagLength))
                                {
                                    continue;
                                }
                            }
                        }

                        inputLevelsSchemes.Add(l, s);
                    }
                }
            }

            if (inputLevelsSchemes.Count == 0)
            {
                throw new TDTTranslationException("TDTSchemeNotFound");
            }

            // 3. DETERMINE THE OPTION THAT MATCHES THE INPUT VALUE

            // To find the option that matches the input value, consider any scheme+level candidates
            // from the previous step.
            Level  inputLevel  = null;
            Scheme inputScheme = null;
            Option inputOption = null;

            foreach (KeyValuePair <Level, Scheme> kvp in inputLevelsSchemes)
            {
                Level  l = (Level)kvp.Key;
                Scheme s = (Scheme)kvp.Value;

                // For each of these schemes, if the optionKey attribute is
                // specified within the scheme element in terms of the name of a supplied parameter (e.g.
                // gs1companyprefixlength), check the associative array of supplied parameters to
                // see if a corresponding value is defined and if so, select the option element for which
                // the optionKey attribute of the option element has the corresponding value.
                //
                // e.g. if a candidate scheme has a scheme attribute
                // optionKey="gs1companyprefixlength" and the associative array of supplied
                // extra parameters has a key=value pair gs1companyprefixlength=7, then only the
                // option element having attribute optionKey="7" should be considered.
                foreach (Option o in l.option)
                {
                    // If the optionKey attribute is not specified within the scheme element or if the
                    // corresponding value is not present in the associative array of supplied extra parameters,
                    // then consider each option element within each scheme+level candidate and check
                    // whether the pattern attribute of the option element matches the input value at the
                    // start of the string.
                    if (s.optionKey != null)
                    {
                        string value;

                        int integer;
                        if (int.TryParse(s.optionKey, out integer))
                        {
                            value = s.optionKey;
                        }
                        else
                        {
                            parameterDictionary.TryGetValue(s.optionKey, out value);
                        }

                        // create exception for empty parameter lists, when no information is necessary to decode
                        if (value != null)
                        {
                            if (o.optionKey != value)
                            {
                                continue;
                            }
                        }
                    }

                    // When a match is found, this option should be considered further and the corresponding
                    // value of the optionKey attribute of the option element should be noted for use in
                    // step 6.

                    // unescape input if Pure Identity or Tag Encoding
                    string epcIdentifierUnescaped;
                    if (l.type == LevelTypeList.PURE_IDENTITY || l.type == LevelTypeList.TAG_ENCODING)
                    {
                        epcIdentifierUnescaped = UnEscape(epcIdentifier);
                    }
                    else
                    {
                        epcIdentifierUnescaped = epcIdentifier;
                    }

                    Regex regex = new Regex("^" + o.pattern + "$");
                    if (regex.IsMatch(epcIdentifierUnescaped))
                    {
                        inputScheme   = s;
                        inputLevel    = l;
                        inputOption   = o;
                        epcIdentifier = epcIdentifierUnescaped;
                        break;
                    }
                }

                if (inputOption != null)
                {
                    break;
                }
            }

            if (inputOption == null)
            {
                throw new TDTTranslationException("TDTOptionNotFound");
            }

            // add option key and scheme name to parameter dictionary
            parameterDictionary.Add("optionkey", inputOption.optionKey);
            parameterDictionary.Add("schemename", inputScheme.name);
            if (!parameterDictionary.ContainsKey("taglength"))
            {
                parameterDictionary.Add("taglength", inputScheme.tagLength);
            }

            // check if all parsing parameters are present
            if (inputLevel.requiredParsingParameters != null)
            {
                foreach (string s in inputLevel.requiredParsingParameters.Split(','))
                {
                    if (!parameterDictionary.ContainsKey(s))
                    {
                        var exception = new TDTTranslationException("TDTUndefinedField");
                        exception.Data.Add("key", s);
                        throw exception;
                    }
                }
            }

            // 4. PARSE THE INPUT VALUE TO EXTRACT VALUES FOR EACH FIELD WITHIN THE OPTION

            // Having found a scheme, level and option matching the input value, consider the field
            // elements nested within the option element.

            // Matching of the input value against the regular expression provided in the pattern
            // attribute of the option element should result in a number of backreference strings being
            // extracted. These should be considered as the values for the field elements, where the
            // seq attribute of the field element indicates the sequence in which the fields are extracted
            // as backreferences, from the start of the input value, e.g. the value from the first
            // backreference should be considered as the value of the field element with seq="1",
            // the value of the second backreference is the value of the field element with seq="2".
            Regex r = new Regex("^" + inputOption.pattern + "$");
            Match m = r.Match(epcIdentifier);

            if (!m.Success)
            {
                throw new TDTTranslationException("TDTEPCIdentifierParseException");
            }

            // sort all fields on seq
            Field[] fields       = inputOption.field;
            Field[] fieldsSorted = fields.OrderBy(c => c.seq).ToArray();

            for (int i = 1; i < m.Groups.Count; i++)
            {
                Field  inputField      = fieldsSorted[i - 1];
                string name            = inputField.name;
                string variableElement = m.Groups[i].Value;

                // For each field element, if a characterSet attribute is specified, check that the
                // value of the field falls entirely within the specified character set.
                if (inputField.characterSet != null)
                {
                    if (!ValidateCharacterset(variableElement, inputField.characterSet))
                    {
                        throw new TDTTranslationException("TDTFieldOutsideCharacterSet");
                    }
                }

                // For each field element, if the compaction attribute is null, treat the field as an
                // integer. If the type attribute of the input level was "BINARY", treat the string of 0 and
                // 1 characters matched by the regular expression backreference as a binary string and
                // convert it to a decimal integer.
                if (inputLevel.type == LevelTypeList.BINARY)
                {
                    // If the inbound representation was binary, perform any necessary stripping, conversion of
                    // binary to integer or string, padding, referring to the procedure described in the flowchart
                    // Figure 9b.
                    if (inputField.compactionSpecified)
                    {
                        //TODO: implement check for bitPadChar; somehow not used in TDS1.6.

                        // Convert sequence of bit into characters,
                        // considering that each byte may have been compacted,
                        // as indicated by the compaction attribute.
                        int compactionBits = 0;
                        switch (inputField.compaction)
                        {
                        case CompactionMethodList.Item5bit:
                            compactionBits = 5;
                            break;

                        case CompactionMethodList.Item6bit:
                            compactionBits = 6;
                            break;

                        case CompactionMethodList.Item7bit:
                            compactionBits = 7;
                            break;

                        case CompactionMethodList.Item8bit:
                            compactionBits = 8;
                            break;
                        }

                        List <byte> byteList = new List <byte>();
                        for (int j = 0; j < variableElement.Length; j += compactionBits)
                        {
                            if (j + compactionBits <= variableElement.Length)
                            {
                                string character = variableElement.Substring(j, compactionBits);
                                // ISO/IEC 15962
                                if (compactionBits == 5)
                                {
                                    // During the decode process, each 5-bit segment of the compacted bit string has “010” added as a prefix to re- create the 8-bit value of the source data.
                                    character = "010" + character;
                                }
                                else if (compactionBits == 6)
                                {
                                    // During the decode process, each 6-bit segment of the compacted bit string is analysed.
                                    // a. If the first bit is “1”, the bits “00” are added as a prefix before converting to values 20 to 3FHEX.
                                    // b. If the first bit is “0”, the bits “01” are added as a prefix before converting to values 40 to 5FHEX.
                                    if (character[0] == '1')
                                    {
                                        character = "00" + character;
                                    }
                                    else
                                    {
                                        character = "01" + character;
                                    }
                                }
                                byteList.Add(Convert.ToByte(character, 2));
                            }
                        }

                        // convert byte list to string
                        variableElement = Encoding.ASCII.GetString(byteList.ToArray());

                        // strip null characters at the end of the string
                        variableElement = variableElement.TrimEnd('\0');
                    }
                    else
                    {
                        //TODO: implement check for bitPadChar; somehow not used in TDS1.6.

                        Int64 integer = Convert.ToInt64(variableElement, 2);
                        variableElement = Convert.ToString(integer);
                    }

                    // Corresponding string field in TAG-ENCODING level
                    Field tagEncodingField = new Field();
                    foreach (Level l in inputScheme.level)
                    {
                        if (l.type.Equals(LevelTypeList.TAG_ENCODING))
                        {
                            foreach (Option o in l.option)
                            {
                                if (o.optionKey.Equals(inputOption.optionKey))
                                {
                                    tagEncodingField = o.field[i - 1];
                                    break;
                                }
                            }
                            break;
                        }
                    }

                    bool padCharInBinary      = false;
                    bool padCharInTagEncoding = false;

                    if (inputField.padChar != null)
                    {
                        padCharInBinary = true;
                    }

                    if (tagEncodingField.padChar != null)
                    {
                        padCharInTagEncoding = true;
                    }

                    if (padCharInBinary && padCharInTagEncoding)
                    {
                        // error in TDT definition file
                        throw new TDTTranslationException(@"TDTInvalidDefinitionFile");
                    }

                    if (padCharInBinary && !padCharInTagEncoding)
                    {
                        if (inputField.padDir.Equals(PadDirectionList.LEFT))
                        {
                            variableElement = variableElement.TrimStart(inputField.padChar[0]);
                        }
                        else
                        {
                            variableElement = variableElement.TrimEnd(inputField.padChar[0]);
                        }
                    }

                    if (!padCharInBinary && padCharInTagEncoding)
                    {
                        // Pad at the padDir edge with character indicated by padChar attribute to reach a total length of characters indicated by length attribute
                        if (tagEncodingField.padDir.Equals(PadDirectionList.LEFT))
                        {
                            variableElement = variableElement.PadLeft(int.Parse(tagEncodingField.length), tagEncodingField.padChar[0]);
                        }
                        else
                        {
                            variableElement = variableElement.PadRight(int.Parse(tagEncodingField.length), tagEncodingField.padChar[0]);
                        }

                        // Manage the exception where the length is set to 0.
                        if (int.Parse(tagEncodingField.length) == 0)
                        {
                            variableElement = "";
                        }
                    }
                }

                // If the decimalMinimum attribute is specified, check that the value is not less than the
                // decimal minimum value specified.
                if (inputField.decimalMinimum != null)
                {
                    // only check if the length is larger than zero
                    if (variableElement.Length > 0)
                    {
                        BigInteger integer = BigInteger.Parse(variableElement);
                        if (ValidateMinimum(integer, inputField.decimalMinimum))
                        {
                            throw new TDTTranslationException("TDTFieldBelowMinimum");
                        }
                    }
                }

                // If the decimalMaximum attribute is specified, check that the value is not greater than
                // the decimal maximum value specified.
                if (inputField.decimalMaximum != null)
                {
                    // only check if the length is larger than zero
                    if (variableElement.Length > 0)
                    {
                        BigInteger integer = BigInteger.Parse(variableElement);
                        if (ValidateMaximum(integer, inputField.decimalMaximum))
                        {
                            throw new TDTTranslationException("TDTFieldAboveMaximum");
                        }
                    }
                }

                parameterDictionary[name] = variableElement;
            }

            // 5. PERFORM ANY RULES OF TYPE EXTRACT WITHIN THE INBOUND OPTION IN ORDER TO CALCULATE ADDITIONAL DERIVED FIELDS

            // Now run the rules that have attribute type="EXTRACT" in sequence, to determine any
            // additional derived fields that must be calculated after parsing of the input value.
            if (inputLevel.rule != null)
            {
                Rule[] extractRules       = inputLevel.rule;
                Rule[] extractRulesSorted = extractRules.OrderBy(c => c.seq).ToArray();
                ExecuteRules(extractRulesSorted, ModeList.EXTRACT, parameterDictionary);
            }

            return(parameterDictionary);
        }
        string ProcessOutput(Dictionary <string, string> parameterDictionary, string outputFormat)
        {
            // Note the desired outbound level.
            LevelTypeList outputFormatType;

            try
            {
                outputFormatType = (LevelTypeList)Enum.Parse(typeof(LevelTypeList), outputFormat);
            }
            catch (Exception)
            {
                throw new TDTTranslationException("TDTOutputFormatUnknownException");
            }

            // 6. FIND THE CORRESPONDING OPTION IN THE OUTBOUND REPRESENTATION

            // To find the corresponding option in the outbound representation within the same scheme,
            // select the level element having the desired outbound representation and within that,
            // select the option element that has the same value of the optionKey attribute that was
            // noted at the end of step 3
            Scheme outputScheme = null;
            Level  outputLevel  = null;
            Option outputOption = null;

            foreach (EpcTagDataTranslation e in epcTagDataTranslations)
            {
                Scheme s = e.scheme[0];

                if (s.name != parameterDictionary["schemename"])
                {
                    continue;
                }

                outputScheme = s;

                foreach (Level l in s.level)
                {
                    if (l.type != outputFormatType)
                    {
                        continue;
                    }

                    outputLevel = l;

                    foreach (Option o in l.option)
                    {
                        if (o.optionKey == parameterDictionary["optionkey"])
                        {
                            outputOption = o;
                        }
                    }
                }
            }

            if (outputLevel == null || outputOption == null)
            {
                throw new TDTTranslationException("TDTOutputNotKnown");
            }

            // check if all formatting parameters are present
            if (outputLevel.requiredFormattingParameters != null)
            {
                foreach (string s in outputLevel.requiredFormattingParameters.Split(','))
                {
                    if (!parameterDictionary.ContainsKey(s))
                    {
                        var exception = new TDTTranslationException("TDTUndefinedField");
                        exception.Data.Add("key", s);
                        throw exception;
                    }
                }
            }

            // 7. PERFORM ANY RULES OF TYPE FORMAT WITHIN THE OUTBOUND REPRESENTATION IN ORDER TO CALCULATE ADDITIONAL DERIVED FIELDS

            // Run any rules with attribute type="FORMAT" in sequence, to determine any additional
            // derived fields that must be calculated in order to prepare the output format.
            //
            // Store the resulting key-value pairs in the associative array after checking that the value
            // falls entirely within the permitted characterSet (if specified) or within the permitted
            // numeric range (if decimalMinimum or decimalMaximum are specified) and
            // performing any necessary padding or stripping of characters.
            if (outputLevel.rule != null)
            {
                Rule[] formatRules       = outputLevel.rule;
                Rule[] formatRulesSorted = formatRules.OrderBy(c => c.seq).ToArray();
                ExecuteRules(formatRulesSorted, ModeList.FORMAT, parameterDictionary);
            }

            // 8. USE THE GRAMMAR string AND SUBSTITUTIONS FROM THE ASSOCIATIVE ARRAY TO BUILD THE OUTPUT VALUE

            // Consider the grammar string for that option as a sequence of fixed literal strings (the
            // characters between the single quotes) interspersed with a number of variable elements,
            // whose key names are indicated by alphanumeric strings without any enclosing single
            // quotation marks.
            string grammarstring = outputOption.grammar;

            StringBuilder outputString = new StringBuilder();

            Regex           abnf       = new Regex(@"\'.*?\'|\s*[\w]+\s*");
            MatchCollection collection = abnf.Matches(grammarstring);

            foreach (var c in collection)
            {
                string s = c.ToString();

                if (s[0] == '\'')
                {
                    outputString.Append(s.Substring(1, s.Length - 2));
                }
                else
                {
                    s = s.Trim();

                    // Perform lookups of each key name in the associative array to substitute the value of each
                    // variable element, substituting the corresponding value in place of the key name.
                    string variableElement;
                    if (!parameterDictionary.TryGetValue(s, out variableElement))
                    {
                        var exception = new TDTTranslationException("TDTUndefinedField");
                        exception.Data.Add("key", s);
                        throw exception;
                    }

                    // Note that if the outbound representation is binary, it is necessary to convert values from
                    // decimal integer or string to binary, performing any necessary stripping or padding,
                    // following the method described in the flowchart Figure 9a.
                    if (outputLevel.type == LevelTypeList.BINARY)
                    {
                        // According to flowchart Figure 9a of the standard

                        // Corresponding string field in TAG-ENCODING level
                        Field tagEncodingField = new Field();
                        foreach (Level l in outputScheme.level)
                        {
                            if (l.type.Equals(LevelTypeList.TAG_ENCODING))
                            {
                                foreach (Option o in l.option)
                                {
                                    if (o.optionKey.Equals(parameterDictionary["optionkey"]))
                                    {
                                        foreach (Field f in o.field)
                                        {
                                            if (s.Equals(f.name))
                                            {
                                                tagEncodingField = f;
                                                break;
                                            }
                                        }
                                        break;
                                    }
                                }
                                break;
                            }
                        }

                        // Corresponding string field in BINARY level
                        Field binaryField = new Field();
                        foreach (Field f in outputOption.field)
                        {
                            if (s.Equals(f.name))
                            {
                                binaryField = f;
                                break;
                            }
                        }

                        bool padCharInTagEncoding = false;
                        bool padCharInBinary      = false;

                        if (tagEncodingField.padChar != null)
                        {
                            padCharInTagEncoding = true;
                        }

                        if (binaryField.padChar != null)
                        {
                            padCharInBinary = true;
                        }

                        if (padCharInTagEncoding && padCharInBinary)
                        {
                            // error in TDT definition file
                            throw new TDTTranslationException(@"TDTInvalidDefinitionFile");
                        }

                        if (padCharInTagEncoding && !padCharInBinary)
                        {
                            // Strip at the padDir edge of any successive characters indicated by padChar attribute.
                            if (tagEncodingField.padDir.Equals(PadDirectionList.LEFT))
                            {
                                variableElement = variableElement.TrimStart(tagEncodingField.padChar[0]);
                            }
                            else
                            {
                                variableElement = variableElement.TrimEnd(tagEncodingField.padChar[0]);
                            }
                        }

                        if (!padCharInTagEncoding && padCharInBinary)
                        {
                            // Pad at the padDir edge with character indicated by padChar attribute to reach a total length of characters indicated by length attribute
                            if (binaryField.padDir.Equals(PadDirectionList.LEFT))
                            {
                                variableElement = variableElement.PadLeft(int.Parse(binaryField.length), binaryField.padChar[0]);
                            }
                            else
                            {
                                variableElement = variableElement.PadRight(int.Parse(binaryField.length), binaryField.padChar[0]);
                            }
                        }

                        // Check for compaction attribute in BINARY level
                        if (binaryField.compactionSpecified)
                        {
                            // Create an empty buffer for storage of bits.
                            StringBuilder bits = new StringBuilder();

                            // For each character of the string, starting at the left, perform compaction of the corresponding ASCII byte,
                            byte[] bytes = Encoding.ASCII.GetBytes(variableElement);

                            int compactionBits = 0;
                            switch (binaryField.compaction)
                            {
                            case CompactionMethodList.Item5bit:
                                compactionBits = 5;
                                break;

                            case CompactionMethodList.Item6bit:
                                compactionBits = 6;
                                break;

                            case CompactionMethodList.Item7bit:
                                compactionBits = 7;
                                break;

                            case CompactionMethodList.Item8bit:
                                compactionBits = 8;
                                break;
                            }

                            // as indicated by the compaction attribute and append the resulting bits to the buffer.
                            // Consider the entire bits in the buffer as a sequence of bits
                            foreach (byte b in bytes)
                            {
                                string binary = Convert.ToString(b, 2);
                                if (binary.Length > compactionBits)
                                {
                                    binary = binary.Substring(binary.Length - compactionBits);
                                }
                                else if (binary.Length < compactionBits)
                                {
                                    binary = binary.PadLeft(compactionBits, '0');
                                }
                                bits.Append(binary);
                            }

                            variableElement = bits.ToString();
                        }
                        else
                        {
                            Int64 result;
                            if (!Int64.TryParse(variableElement, out result))
                            {
                                result = 0;
                            }
                            variableElement = Convert.ToString(result, 2);
                        }

                        // Check for bit padding in BINARY level
                        if (binaryField.bitPadDirSpecified)
                        {
                            if (binaryField.bitPadDir.Equals(PadDirectionList.LEFT))
                            {
                                variableElement = variableElement.PadLeft(int.Parse(binaryField.bitLength), '0');
                            }
                            else
                            {
                                variableElement = variableElement.PadRight(int.Parse(binaryField.bitLength), '0');
                            }
                        }
                    }
                    else
                    {
                        // Validate field
                        foreach (var f in outputOption.field)
                        {
                            if (f.name == s)
                            {
                                if (!ValidateCharacterset(variableElement, f.characterSet))
                                {
                                    throw new TDTTranslationException("TDTFieldOutsideCharacterSet");
                                }
                            }
                        }
                    }

                    if (outputLevel.type == LevelTypeList.PURE_IDENTITY || outputLevel.type == LevelTypeList.TAG_ENCODING)
                    {
                        variableElement = Escape(variableElement);
                    }

                    // Concatenate the fixed literal strings and values of variable together in the sequence
                    // indicated by the grammar string and consider this as the output value.
                    outputString.Append(variableElement);
                }
            }

            return(outputString.ToString());
        }