Merge commit '4b7961a' into pyasn1_0.4.3
am: 5934c9ecff

Change-Id: I5a2f6bf5ea885c16fe6a02ceb096e5561c37d397
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..77f208c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+# Python stuff
+*.pyc
+__pycache__
+
+# vim swapfiles
+*.sw?
+
+# python packaging
+MANIFEST
+dist/
+build/
+*.egg-info/
+
+# PyCharm stuff
+.idea/
+
+# Sphinx template
+docs/source/.templates/layout.html
+
+# Eclipse stuff
+.project
+.pydevproject
+
+# Virtual envs
+venv*
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..d87a749
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,22 @@
+
+language: python
+python:
+  - "2.6"
+  - "2.7"
+  - "3.2"
+  - "3.3"
+  - "3.4"
+  - "3.5"
+  - "3.6"
+  - "nightly"
+  - "pypy"
+  - "pypy3"
+install:
+  - pip install codecov
+  - pip install -r requirements.txt -r devel-requirements.txt
+  - pip install -e .
+script:
+  - PYTHONPATH=.:$PYTHONPATH python tests/__main__.py
+after_success:
+  - PYTHONPATH=.:$PYTHONPATH coverage run --omit=*test* tests/__main__.py
+  - codecov
diff --git a/CHANGES.rst b/CHANGES.rst
new file mode 100644
index 0000000..e72e35a
--- /dev/null
+++ b/CHANGES.rst
@@ -0,0 +1,628 @@
+
+Revision 0.4.3, released 23-05-2018
+-----------------------------------
+
+- Copyright notice extended to the year 2018
+- Fixed GeneralizedTime.asDateTime to perform milliseconds conversion
+  correctly
+
+Revision 0.4.2, released 23-11-2017
+-----------------------------------
+
+- Fixed explicit tag splitting in chunked encoding mode at
+  OctetString and BitString encoders
+
+Revision 0.4.1, released 23-11-2017
+-----------------------------------
+
+- ANY DEFINED BY clause support implemented
+- Encoders refactored to take either a value (as ASN.1 object)
+  or a Python value plus ASN.1 schema
+- BitString decoder optimised for better performance when running on
+  constructed encoding
+- Constructed types' .getComponentBy*() methods accept the `default`
+  parameter to return instead if schema object is to be returned
+- Constructed types' .getComponentBy*() methods accept the `instantiate`
+  parameter to disable automatic inner component instantiation
+- The ASN.1 types' `__repr__` implementation reworked for better readability
+  at the cost of not being `eval`-compliant
+- Most ASN.1 types' `__str__` magic methods (except for OctetString and
+  character types) reworked to call `.prettyPrint()` rather than
+  `.prettyPrint` calling `__str__` as it was before. The intention is
+  to eventually deprecate `.prettyPrint()` in favor of `str()`.
+  The other related change is that `str()` of enumerations and boolean
+  types will return string label instead of number.
+- Fixed Choice.clear() to fully reset internal state of the object
+- Sphinx documentation rearranged, simplified and reworded
+- The `isValue` singleton is now the only way to indicate ASN.1 schema
+  as opposed to ASN.1 schema instance. The legacy `None` initializer
+  support has been removed.
+- Changed `Null` object initialization behaviour: previous default
+  value (`''`) is not set anymore. Thus `Null()` call produces a
+  ASN.1 schema object, while `Null('')` - value object.
+- Migrated all docs and references from SourceForge
+- Imports PEP8'ed
+- Fixed ASN.1 encoder not to omit empty substrate produced for inner
+  component if the inner component belongs to the simple class (as
+  opposed to constructed class)
+- Fixed CER/DER encoders to respect tagged CHOICE when ordering
+  SET components
+- Fixed ASN.1 types not to interfere with the Pickle protocol
+- Fixed Sequence/SequenceOf types decoding heuristics in schema-less
+  decoding mode
+
+Revision 0.3.7, released 04-10-2017
+-----------------------------------
+
+- Fixed ASN.1 time types pickling/deepcopy'ing
+
+Revision 0.3.6, released 21-09-2017
+-----------------------------------
+
+- End-of-octets encoding optimized at ASN.1 encoders
+- The __getitem__/__setitem__ behavior of Set/Sequence and SetOf/SequenceOf
+  objects aligned with the canonical Mapping and Sequence protocols in part
+- Fixed crash in ASN.1 encoder when encoding an explicitly tagged
+  component of a Sequence
+
+Revision 0.3.5, released 16-09-2017
+-----------------------------------
+
+- Codecs signatures unified and pass the options kwargs through the
+  call chain
+- Explicit tag encoding optimized to avoid unnecessary copying
+- End-of-octets sentinel encoding optimized
+- Refactored ASN.1 codecs properties to silently enforce proper
+  length and chunk size encoding modes
+- Fixed DER encoder to always produce primitive encoding
+- Fixed crash at SequenceOf native decoder
+- Fixed Real.prettyPrint() to fail gracefully on overflow
+- Fixed a couple of crashes when debug mode is enabled
+
+Revision 0.3.4, released 07-09-2017
+-----------------------------------
+
+- Fixed Native encoder to handle SEQUENCE/SET objects without
+  the componentType property
+- Added missing component-less SEQUENCE/SET objects dict duck-typing support
+- Fixed unnecessary duplicate tags detection at NamesType.tagMap
+- Fixed crash at SEQUENCE and SEQUENCE OF CER encoder when running
+  in schemaless mode
+- Fixed Character types instantiation from OctetString type -- double
+  unicode decoding may have scrambled the data
+
+Revision 0.3.3, released 27-08-2017
+-----------------------------------
+
+- Improved ASN.1 types instantiation performance
+- Improved BER/CER/DER decoder performance by not unconditionally casting
+  substrate into str/bytes.
+- Fixed exponential index size growth bug when building ambiguous
+  NamedTypes tree
+- Fixed constructed types decoding failure at BER codec if running
+  in schema-less mode
+- Fixed crash on prettyPrint'ing a SEQUENCE with no defined components
+- Fixed SetOf ordering at CER/DER encoder
+- Fixed crash on conditional binascii module import
+- Fix to TagSet hash value build
+
+Revision 0.3.2, released 04-08-2017
+-----------------------------------
+
+- Fixed SequenceOf/SetOf types initialization syntax to remain
+  backward compatible with pyasn1 0.2.*
+- Rectified thread safety issues by moving lazy, run-time computation
+  into object initializer.
+- Fixed .isValue property to return True for empty SetOf/SequenceOf
+  objects
+- Fixed GeneralizedTime/UTCTime CER/DER codecs to actually get invoked
+- Fixed DER/CER encoders handling optional SEQUENCE/SET fields containing
+  nested SEQUENCE/SET with optional fields.
+- Fixed crash in SequenceOf/SetOf pretty printing and decoding (in some
+  cases)
+- Fixed documentation markup issues.
+
+Revision 0.3.1, released 26-07-2017
+-----------------------------------
+
+- ASN.1 types __init__(), .clone() and .subtype() signatures
+  refactored into keyword arguments to simplify their signatures.
+- ASN.1 types initialization refactored to minimize the use of
+  relatively expensive isNoValue() call
+- Lazily pre-populate list of values of Sequence/Set/Choice types
+- NamedTypes comparison made more efficient
+- More efficient constraints computation and code clean up
+- The __getitem__() implementation of some ASN.1 types & tag object
+  refactored for better performance
+- BER/CER/DER value encoders refactored to produce either tuple of
+  bytes or octet-stream depending on what is more optimal
+- Reduced the frequency of expensive isinstance() calls
+- Tag-related classes optimized, refactored into properties and
+  documented.
+- The NamedValues implementation refactored to mimic Python dict, its use
+  in ASN.1 types refactored into properties and better documented.
+  WARNING: this change introduces a deviation from original API.
+- NamedType family of classes overhauled, optimized and documented.
+- The `componentType` attribute of constructed ASN.1 types turned
+  read-only on instances.
+- Sequence/Set DER/CER/DER decoder optimized to skip the case of
+  reordered components handling when not necessary.
+- Tags and constraints-related getter methods refactored into read-only
+  instance attributes.
+- The .hasValue() method refactored into .isValue property. All ASN.1
+  objects now support them, not just scalars.
+- The Real.{isInfinity, isPlusInfinity, isMinusInfinity} methods
+  refactored into properties and renamed into IsInf, IsPlusInf and isMinusInf
+- The end-of-octets type refactored to ensure it is a singleton. Codecs
+  changed to rely on that for better performance.
+- Codecs lookup made more efficient at BER/CER/DER decoder main loop by
+  assigning `typeId` to every ASN.1 type, not just ambiguous ones.
+- The .getComponent*() methods of constructed ASN.1 types changed
+  to lazily instantiate underlying type rather than return `None`.
+  This should simplify its API as initialization like `X[0][1] = 2` becomes
+  possible.
+  WARNING: this change introduces a deviation from the original API.
+- The .setComponent*() methods of SetOf/SequenceOf types changed not
+  to allow uninitialized "holes" inside the sequences of their components.
+  They now behave similarly to Python lists.
+  WARNING: this change introduces a deviation from the original API.
+- Default and optional components en/decoding of Constructed type
+  refactored towards better efficiency and more control.
+- OctetsString and Any decoder optimized to avoid creating ASN.1
+  objects for chunks of substrate. Instead they now join substrate
+  chunks together and create ASN.1 object from it just once.
+- The GeneralizedTime and UTCTime types now support to/from Python
+  datetime object conversion.
+- Unit tests added for the `compat` sub-package.
+- Fixed BitString named bits initialization bug.
+- Fixed non-functional tag cache (when running Python 2) at DER decoder.
+- Fixed chunked encoding restriction on DER encoder.
+- Fixed SET components ordering at DER encoder.
+- Fixed BIT STRING & OCTET STRING encoding to be always non-chunked (e.g.
+  primitive) at DER encoder
+- Fixed `compat.integer.from_bytes()` behaviour on empty input.
+
+Revision 0.2.3, released 25-02-2017
+-----------------------------------
+
+- Improved SEQUENCE/SET/CHOICE decoding performance by maintaining a single shared
+  NamedType object for all instances of SEQUENCE/SET object.
+- Improved INTEGER encoding/decoding by switching to Python's built-in
+  integer serialisation functions.
+- Improved BitString performance by rebasing it onto Python int type and leveraging
+  fast Integer serialisation functions.
+- BitString type usability improved in many ways: for example bitshifting and
+  numeric operation on BitString is now possible.
+- Minor ObjectIdentifier type performance optimization.
+- ASN.1 character types refactored to keep unicode contents internally
+  (rather than serialised octet stream) and duck-type it directly.
+- ASN.1 OctetString initialized from a Python object performs bytes()
+  on it when running on Python 3 (used to do str() which is probably
+  less logical).
+- Missing support for NoValue.__sizeof__ added.
+- Added checks to make sure SEQUENCE/SET components being assigned
+  match the prototypes.
+- Setter methods for constructed types consistently accept matchTags
+  and matchConstraints flags to control the strictness of inner
+  components compatibility verification. Previously, these checks
+  were tied to verifyConstraints flag, now they are all independent.
+- General documentation improvements here and there.
+- Fix to __reversed__() magic to make it returning an iterator.
+- Test suite simplified and unified.
+- The __all__ variable added to most of the Python modules.
+- The "test" directory renamed into "tests" not to collide with
+  the "test" module.
+
+Revision 0.2.2, released 07-02-2017
+-----------------------------------
+
+- FIX TO A SECURITY WEAKNESS: define length only decoders could have successfully
+  processed indefinite length serialisation.
+- FIX TO A SECURITY WEAKNESS: canonical decoders (CER/DER) may have successfully
+  consumed non-canonical variations of (otherwise valid) serialisation.
+- Broken Enumerated subtyping fixed.
+
+Revision 0.2.1, released 05-02-2017
+-----------------------------------
+
+- FIX TO A SECURITY WEAKNESS: BER decoder improperly cached long tags.
+- New "native" codec implemented to transform pyasn1 types to Python built-in types and back.
+- Switched to new-style classes.
+- Sphinx documentation added.
+- BitString improvements:
+
+  * simple string of binary digits is now supported as initializer
+  * default str() yields string of binary digits (used to yield str(tuple())
+  * binValue and hexValue initializers added
+  * .asNumbers(), .asOctets() and asInteger() representation added
+
+- Components of constructed ASN.1 types can now be populated with
+  uninitialized ASN.1 objects by assigning either noValue sentinel or
+  setupComponent() function return in addition to now-legacy None sentinel.
+  This should improve code readability.
+- NoValue class improved to become a singleton and catch more kinds
+  of access to it.
+- Compatibility wrappers str2octs() and oct2strs() fixed to run over
+  iso-8859-1 encoding.
+- Integer changed to emit Real instance if division produces a float.
+- True division operation now supported by Integer type.
+- The __contains__(), __reverse__() methods implemented for container types
+- Iterator protocol support implemented for all container types.
+  Warning, warning, warning: this change may potentially affect backward
+  compatibility when:
+
+  * user class overrides __getitem__() without overriding __iter__()
+  * when user code iterates over SEQUENCE object to get its components (now keys will be yielded)
+
+- Almost complete Python list and dict protocols added to SequenceOf/SetOf and
+  Sequence/Set respectively
+- Fix to divmod operation implementation in Integer type.
+- Fix to IntegerDecoder's precomputed value map on Python 3.
+- Fix to base ASN.1 types to run in "unicode_literals" mode.
+- Fix to composite constraints "+" operands ordering (AbstractConstraintSet.__radd__)
+- Fix to constraints merge in .subtype() -- on merge existing constraints are
+  expanded to accommodate new constraints, not the other way round. When existing
+  constraints are wrapped in ConstraintsIntersection composite, additional
+  constraints being added on subtyping effectively further narrow the set of
+  allowed values, which aligns well with the notion of subtyping.
+- Fix to NamedTypes methods to handle .getTagMap() returning None
+- Fix to Set/Sequence.setDefaultComponents() to return self
+- Copyright notice added to non-trivial source code files.
+- Author's email changed, copyright extended to 2017
+
+Revision 0.1.9, released 28-09-2015
+-----------------------------------
+
+- Wheel distribution format now supported.
+- Extensions added to text files, CVS attic flushed.
+- Fix to make uninitialized pyasn1 objects failing properly on hash().
+- Fix to ObjectIdentifier initialization from unicode string.
+- Fix to CER/DER Boolean decoder - fail on non single-octet payload.
+
+Revision 0.1.8, released 22-06-2015
+-----------------------------------
+
+- ObjectIdentifier codec fixed to work properly with arc 0 and arc 2 values.
+- Explicit limit on ObjectIdentifier arc value size removed.
+- Unicode initializer support added to OctetString type and derivatives.
+- New prettyPrintType() abstract method implemented to base pyasn1 types
+  to facilitate encoding errors analysis.
+- The __str__() method implemented to Tag, TagSet and TagMap classes to
+  ease encoding errors troubleshooting.
+  easing encoding errors
+- Fix to SEQUENCE and SET types to give them their private componentTypes
+  collection (which is a NamedTypes object) so that they won't collide in
+  a MT execution environment.
+- Missing T61String,ISO646String character types and ObjectDescriptor useful
+  type added.
+- Distribute is gone, switched to setuptools completely.
+- Missing NamedValues.__repr__() added.
+- The base.NoValue() class, that indicates uninitialized ASN.1 object,
+  made public.
+- The base.NoValue() class instances now support __repr__() what makes
+  possible to perform repr() on uninitialized pyasn1 types objects.
+- When comparing ASN.1 types, by-tag and/or by-constraints matching
+  can now be performed with the isSuperTypeOf()/isSameTypeWith() optional
+  flags.
+- Constructed types now verify their consistency by invoking 
+  isSameTypeWith(matchTags=True, matchConstraints=False) and
+  isSuperTypeOf(matchTags=False, matchConstraints=True) for each of their
+  components rather than isSuperTypeOf() as it used to be. Constriants check 
+  could be enforced to isSameTypeWith() with the strictConstraints=True
+  constructed classes attribute.
+- Constructed types can now be initialized with new .setComponents() method
+  which accepts both var-args and keyword-args. Default repr() modified to
+  reflect this change.
+- NamedTypes() and NamedValues() made comparable.
+- Test coverage extended to cover pyasn1 types __repr__() function.
+- The abs(Integer()) & abs(Real()) operation now returns respective pyasn1 
+  type, not a Python type.
+- More Python magic methods implementations added to Integer & Real classes
+  (e.g.  __pos__, __neg__, __round__, __floor__, __ceil__, __trunc__)
+- The Integer.__invert__ Python magic method implemented.
+- The OctetString.__int__() and .__float__() magic methods implemented.
+- Handle the case of null writer at Debug printer.
+- BitString encoder/decoder performance improved.
+- Built-in debugging is now based on Python logging module.
+- Fix to NamedType.__repr__() to work properly.
+- Fixes to __repr__() implementation of many built-in ASN.1 types to take into
+  account all of their initializers such as tagSet, subtypeSpec etc.
+- String typed float initializer to REAL type now supported.
+- Float typed mantissa initializer to REAL type for base 2 added.
+- Encoding bases 8 and 16 support for REAL type binary encoder added.
+- More strict CER/DER encoders added for GeneralizedTime and UTCTime types.
+- Asn1Item.hasValue() added to easily distinguish initalized ASN.1 objects
+  from uninitialized ones (e.g. pure types).
+- Fix to REAL type binary decoder to handle different bases and scale factor.
+- Fix to TagSet.repr() to include [obsolete] baseTag information.
+- Fix to broken REAL type decoding handling.
+- Fix to BitString and OctetString decoders dealing with constructed
+  encoding -- it used to be possible to embed other types in substrate.
+- DER codec hardened not to tolerate indefinite length encoding/decoding.
+- Fix to end-of-octest sentinel handling:
+
+  + require strict two-zeros sentinel encoding
+  + recognize EOO sentinel only when explicitly requested by caller
+    of the decoder via allowEoo=True parameter (warning: API change)
+
+Revision 0.1.7
+--------------
+
+- License updated to vanilla BSD 2-Clause to ease package use
+  (https://opensource.org/licenses/BSD-2-Clause).
+- Test suite made discoverable by unittest/unittest2 discovery feature.
+- Fix to decoder working on indefinite length substrate -- end-of-octets
+  marker is now detected by both tag and value. Otherwise zero values may
+  interfere with end-of-octets marker.
+- Fix to decoder to fail in cases where tagFormat indicates inappropriate
+  format for the type (e.g. BOOLEAN is always PRIMITIVE, SET is always 
+  CONSTRUCTED and OCTET STRING is either of the two)
+- Fix to REAL type encoder to force primitive encoding form encoding.
+- Fix to CHOICE decoder to handle explicitly tagged, indefinite length
+  mode encoding
+- Fix to REAL type decoder to handle negative REAL values correctly. Test
+  case added.
+
+Revision 0.1.6
+--------------
+
+- The compact (valueless) way of encoding zero INTEGERs introduced in
+  0.1.5 seems to fail miserably as the world is filled with broken
+  BER decoders. So we had to back off the *encoder* for a while.
+  There's still the IntegerEncoder.supportCompactZero flag which
+  enables compact encoding form whenever it evaluates to True.
+- Report package version on debugging code initialization.
+
+Revision 0.1.5
+--------------
+
+- Documentation updated and split into chapters to better match
+  web-site contents.
+- Make prettyPrint() working for non-initialized pyasn1 data objects. It
+  used to throw an exception.
+- Fix to encoder to produce empty-payload INTEGER values for zeros
+- Fix to decoder to support empty-payload INTEGER and REAL values
+- Fix to unit test suites imports to be able to run each from
+  their current directory
+
+Revision 0.1.4
+--------------
+
+- Built-in codec debugging facility added
+- Added some more checks to ObjectIdentifier BER encoder catching
+  posible 2^8 overflow condition by two leading sub-OIDs
+- Implementations overriding the AbstractDecoder.valueDecoder method
+  changed to return the rest of substrate behind the item being processed
+  rather than the unprocessed substrate within the item (which is usually
+  empty).
+- Decoder's recursiveFlag feature generalized as a user callback function
+  which is passed an uninitialized object recovered from substrate and
+  its uninterpreted payload.
+- Catch inappropriate substrate type passed to decoder.
+- Expose tagMap/typeMap/Decoder objects at DER decoder to uniform API.
+- Obsolete __init__.MajorVersionId replaced with __init__.__version__
+  which is now in-sync with distutils.
+- Package classifiers updated.
+- The __init__.py's made non-empty (rumors are that they may be optimized 
+  out by package managers).
+- Bail out gracefully whenever Python version is older than 2.4.
+- Fix to Real codec exponent encoding (should be in 2's complement form),
+  some more test cases added.
+- Fix in Boolean truth testing built-in methods
+- Fix to substrate underrun error handling at ObjectIdentifier BER decoder
+- Fix to BER Boolean decoder that allows other pre-computed
+  values besides 0 and 1
+- Fix to leading 0x80 octet handling in DER/CER/DER ObjectIdentifier decoder.
+  See https://www.esat.kuleuven.be/cosic/publications/article-1432.pdf
+
+Revision 0.1.3
+--------------
+
+- Include class name into asn1 value constraint violation exception.
+- Fix to OctetString.prettyOut() method that looses leading zero when
+  building hex string.
+
+Revision 0.1.2
+--------------
+
+- Fix to __long__() to actually return longs on py2k
+- Fix to OctetString.__str__() workings of a non-initialized object.
+- Fix to quote initializer of OctetString.__repr__()
+- Minor fix towards ObjectIdentifier.prettyIn() reliability
+- ObjectIdentifier.__str__() is aliased to prettyPrint()
+- Exlicit repr() calls replaced with '%r'
+
+Revision 0.1.1
+--------------
+
+- Hex/bin string initializer to OctetString object reworked
+  (in a backward-incompatible manner)
+- Fixed float() infinity compatibility issue (affects 2.5 and earlier)
+- Fixed a bug/typo at Boolean CER encoder.
+- Major overhawl for Python 2.4 -- 3.2 compatibility:
+  + get rid of old-style types
+  + drop string module usage
+  + switch to rich comparation
+  + drop explicit long integer type use
+  + map()/filter() replaced with list comprehension
+  + apply() replaced with \*/\*\*args
+  + switched to use 'key' sort() callback function
+  + support both __nonzero__() and __bool__() methods
+  + modified not to use py3k-incompatible exception syntax
+  + getslice() operator fully replaced with getitem()
+  + dictionary operations made 2K/3K compatible
+  + base type for encoding substrate and OctetString-based types
+  is now 'bytes' when running py3k and 'str' otherwise
+  + OctetString and derivatives now unicode compliant.
+  + OctetString now supports two python-neutral getters: asOcts() & asInts()
+  + print OctetString content in hex whenever it is not printable otherwise
+  + in test suite, implicit relative import replaced with the absolute one
+  + in test suite, string constants replaced with numerics
+
+Revision 0.0.13
+---------------
+
+- Fix to base10 normalization function that loops on univ.Real(0)
+
+Revision 0.0.13b
+----------------
+
+- ASN.1 Real type is now supported properly.
+- Objects of Constructed types now support __setitem__()
+- Set/Sequence objects can now be addressed by their field names (string index)
+  and position (integer index).
+- Typo fix to ber.SetDecoder code that prevented with schema decoding
+  operation.
+- Fix to explicitly tagged items decoding support.
+- Fix to OctetString.prettyPrint() to better handle non-printable content.
+- Fix to repr() workings of Choice objects.
+
+Revision 0.0.13a
+----------------
+
+- Major codec re-design.
+- Documentation significantly improved.
+- ASN.1 Any type is now supported.
+- All example ASN.1 modules moved to separate pyasn1-modules package.
+- Fix to initial sub-OID overflow condition detection an encoder.
+- BitString initialization value verification improved.
+- The Set/Sequence.getNameByPosition() method implemented.
+- Fix to proper behaviour of PermittedAlphabetConstraint object.
+- Fix to improper Boolean substrate handling at CER/DER decoders.
+- Changes towards performance improvement:
+
+  + all dict.has_key() & dict.get() invocations replaced with modern syntax
+    (this breaks compatibility with Python 2.1 and older).
+  + tag and tagset caches introduced to decoder
+  + decoder code improved to prevent unnecessary pyasn1 objects creation
+  + allow disabling components verification when setting components to
+    structured types, this is used by decoder whilst running with schema
+    mode.
+  + BER decoder for integer values now looks up a small set of pre-computed
+    substrate values to save on decoding.
+  + a few pre-computed values configured to ObjectIdentifier BER encoder.
+  + ChoiceDecoder split-off SequenceOf one to save on unnecessary checks.
+  + replace slow hasattr()/getattr() calls with isinstance() introspection.
+  + track the number of initialized components of Constructed types to save
+    on default/optional components initialization.
+  + added a shortcut ObjectIdentifier.asTuple() to be used instead of
+    __getitem__() in hotspots.
+  + use Tag.asTuple() and pure integers at tag encoder.
+  + introduce and use in decoder the baseTagSet attribute of the built-in
+    ASN.1 types.
+
+Revision 0.0.12a
+----------------
+
+- The individual tag/length/value processing methods of 
+  encoder.AbstractItemEncoder renamed (leading underscore stripped)
+  to promote overloading in cases where partial substrate processing
+  is required.
+- The ocsp.py, ldap.py example scripts added.
+- Fix to univ.ObjectIdentifier input value handler to disallow negative
+  sub-IDs.
+
+Revision 0.0.11a
+----------------
+
+- Decoder can now treat values of unknown types as opaque OctetString.
+- Fix to Set/SetOf type decoder to handle uninitialized scalar SetOf 
+  components correctly.
+
+Revision 0.0.10a
+----------------
+
+- API versioning mechanics retired (pyasn1.v1 -> pyasn1) what makes
+  it possible to zip-import pyasn1 sources (used by egg and py2exe).
+
+Revision 0.0.9a
+---------------
+
+- Allow any non-zero values in Boolean type BER decoder, as it's in
+  accordnance with the standard.
+
+Revision 0.0.8a
+---------------
+
+- Integer.__index__() now supported (for Python 2.5+).
+- Fix to empty value encoding in BitString encoder, test case added.
+- Fix to SequenceOf decoder that prevents it skipping possible Choice
+  typed inner component.
+- Choice.getName() method added for getting currently set component
+  name.
+- OctetsString.prettyPrint() does a single str() against its value
+  eliminating an extra quotes.
+
+Revision 0.0.7a
+---------------
+
+- Large tags (>31) now supported by codecs.
+- Fix to encoder to properly handle explicitly tagged untagged items.
+- All possible value lengths (up to 256^126) now supported by encoders.
+- Fix to Tag class constructor to prevent negative IDs.
+
+Revision 0.0.6a
+---------------
+
+- Make use of setuptools.
+- Constraints derivation verification (isSuperTypeOf()/isSubTypeOf()) fixed.
+- Fix to constraints comparation logic -- can't cmp() hash values as it
+  may cause false positives due to hash conflicts.
+
+Revision 0.0.5a
+---------------
+
+- Integer BER codec reworked fixing negative values encoding bug.
+- clone() and subtype() methods of Constructed ASN.1 classes now 
+  accept optional cloneValueFlag flag which controls original value
+  inheritance. The default is *not* to inherit original value for 
+  performance reasons (this may affect backward compatibility).
+  Performance penalty may be huge on deeply nested Constructed objects
+  re-creation.
+- Base ASN.1 types (pyasn1.type.univ.*) do not have default values
+  anymore. They remain uninitialized acting as ASN.1 types. In 
+  this model, initialized ASN.1 types represent either types with
+  default value installed or a type instance.
+- Decoders' prototypes are now class instances rather than classes.
+  This is to simplify initial value installation to decoder's
+  prototype value.
+- Bugfix to BitString BER decoder (trailing bits not regarded).
+- Bugfix to Constraints use as mapping keys.
+- Bugfix to Integer & BitString clone() methods
+- Bugix to the way to distinguish Set from SetOf at CER/DER SetOfEncoder
+- Adjustments to make it running on Python 1.5.
+- In tests, substrate constants converted from hex escaped literals into
+  octals to overcome indefinite hex width issue occuring in young Python.
+- Minor performance optimization of TagSet.isSuperTagSetOf() method
+- examples/sshkey.py added
+
+Revision 0.0.4a
+---------------
+
+* Asn1ItemBase.prettyPrinter() -> \*.prettyPrint()
+
+Revision 0.0.3a
+---------------
+
+* Simple ASN1 objects now hash to their Python value and don't
+  depend upon tag/constraints/etc.
+* prettyIn & prettyOut methods of SimplleAsn1Object become public
+* many syntax fixes
+
+Revision 0.0.2a
+---------------
+
+* ConstraintsIntersection.isSuperTypeOf() and 
+  ConstraintsIntersection.hasConstraint() implemented
+* Bugfix to NamedValues initialization code
+* +/- operators added to NamedValues objects
+* Integer.__abs__() & Integer.subtype() added
+* ObjectIdentifier.prettyOut() fixes
+* Allow subclass components at SequenceAndSetBase
+* AbstractConstraint.__cmp__() dropped
+* error.Asn1Error replaced with error.PyAsn1Error
+
+Revision 0.0.1a
+---------------
+
+* Initial public alpha release
diff --git a/LICENSE.rst b/LICENSE.rst
new file mode 100644
index 0000000..011bb08
--- /dev/null
+++ b/LICENSE.rst
@@ -0,0 +1,24 @@
+Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, 
+    this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. 
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..c605b0e
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,5 @@
+include *.rst *.md
+recursive-include tests *.py
+recursive-include docs Makefile *.rst *.svg conf.py
+prune docs/build
+prune docs/source/.templates
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..3cd6f97
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,16 @@
+name: "pyasn1"
+description:
+    "ASN.1 types and codecs"
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://pypi.python.org/project/pyasn1"
+  }
+  url {
+    type: GIT
+    value: "https://github.com/etingof/pyasn1"
+  }
+  version: "0.4.3"
+  last_upgrade_date { year: 2018 month: 5 day: 23 }
+}
diff --git a/MODULE_LICENSE_BSD b/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_BSD
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..bf2a4e0
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+LICENSE.rst
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fe5ad9a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,184 @@
+
+ASN.1 library for Python
+------------------------
+[![PyPI](https://img.shields.io/pypi/v/pyasn1.svg?maxAge=2592000)](https://pypi.org/project/pyasn1)
+[![Python Versions](https://img.shields.io/pypi/pyversions/pyasn1.svg)](https://pypi.org/project/pyasn1/)
+[![Build status](https://travis-ci.org/etingof/pyasn1.svg?branch=master)](https://secure.travis-ci.org/etingof/pyasn1)
+[![Coverage Status](https://img.shields.io/codecov/c/github/etingof/pyasn1.svg)](https://codecov.io/github/etingof/pyasn1)
+[![GitHub license](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/etingof/pyasn1/master/LICENSE.txt)
+
+This is a free and open source implementation of ASN.1 types and codecs
+as a Python package. It has been first written to support particular
+protocol (SNMP) but then generalized to be suitable for a wide range
+of protocols based on
+[ASN.1 specification](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.208-198811-W!!PDF-E&type=items).
+
+Features
+--------
+
+* Generic implementation of ASN.1 types (X.208)
+* Standards compliant BER/CER/DER codecs
+* Dumps/loads ASN.1 structures from Python types
+* 100% Python, works with Python 2.4 up to Python 3.6
+* MT-safe
+* Contributed ASN.1 compiler [Asn1ate](https://github.com/kimgr/asn1ate)
+
+Why using pyasn1
+----------------
+
+ASN.1 solves the data serialisation problem. This solution was
+designed long ago by the wise Ancients. Back then, they did not
+have the luxury of wasting bits. That is why ASN.1 is designed
+to serialise data structures of unbounded complexity into
+something compact and efficient when it comes to processing
+the data.
+
+That probably explains why many network protocols and file formats
+still rely on the 30+ years old technology. Including a number of
+high-profile Internet protocols and file formats.
+
+Quite a number of books cover the topic of ASN.1. 
+[Communication between heterogeneous systems](http://www.oss.com/asn1/dubuisson.html)
+by Olivier Dubuisson is one of those high quality books freely 
+available on the Internet.
+
+The pyasn1 package is designed to help Python programmers tackling
+network protocols and file formats at the comfort of their Python
+prompt. The tool struggles to capture all aspects of a rather
+complicated ASN.1 system and to represent it on the Python terms.
+
+How to use pyasn1
+-----------------
+
+With pyasn1 you can build Python objects from ASN.1 data structures.
+For example, the following ASN.1 data structure:
+
+```bash
+Record ::= SEQUENCE {
+  id        INTEGER,
+  room  [0] INTEGER OPTIONAL,
+  house [1] INTEGER DEFAULT 0
+}
+```
+
+Could be expressed in pyasn1 like this:
+
+```python
+class Record(Sequence):
+    componentType = NamedTypes(
+        NamedType('id', Integer()),
+        OptionalNamedType(
+            'room', Integer().subtype(
+                implicitTag=Tag(tagClassContext, tagFormatSimple, 0)
+            )
+        ),
+        DefaultedNamedType(
+            'house', Integer(0).subtype(
+                implicitTag=Tag(tagClassContext, tagFormatSimple, 1)
+            )
+        )
+    )
+```
+
+It is in the spirit of ASN.1 to take abstract data description 
+and turn it into a programming language specific form.
+Once you have your ASN.1 data structure expressed in Python, you
+can use it along the lines of similar Python type (e.g. ASN.1
+`SET` is similar to Python `dict`, `SET OF` to `list`):
+
+```python
+>>> record = Record()
+>>> record['id'] = 123
+>>> record['room'] = 321
+>>> str(record)
+Record:
+ id=123
+ room=321
+>>>
+```
+
+Part of the power of ASN.1 comes from its serialisation features. You
+can serialise your data structure and send it over the network.
+
+```python
+>>> from pyasn1.codec.der.encoder import encode
+>>> substrate = encode(record)
+>>> hexdump(substrate)
+00000: 30 07 02 01 7B 80 02 01 41
+```
+
+Conversely, you can turn serialised ASN.1 content, as received from
+network or read from a file, into a Python object which you can
+introspect, modify, encode and send back.
+
+```python
+>>> from pyasn1.codec.der.decoder import decode
+>>> received_record, rest_of_substrate = decode(substrate, asn1Spec=Record())
+>>>
+>>> for field in received_record:
+>>>    print('{} is {}'.format(field, received_record[field]))
+id is 123
+room is 321
+house is 0
+>>>
+>>> record == received_record
+True
+>>> received_record.update(room=123)
+>>> substrate = encode(received_record)
+>>> hexdump(substrate)
+00000: 30 06 02 01 7B 80 01 7B
+```
+
+The pyasn1 classes struggle to emulate their Python prototypes (e.g. int,
+list, dict etc.). But ASN.1 types exhibit more complicated behaviour.
+To make life easier for a Pythonista, they can turn their pyasn1
+classes into Python built-ins:
+
+```python
+>>> from pyasn1.codec.native.encoder import encode
+>>> encode(record)
+{'id': 123, 'room': 321, 'house': 0}
+```
+
+Or vice-versa -- you can initialize an ASN.1 structure from a tree of
+Python objects:
+
+```python
+>>> from pyasn1.codec.native.decoder import decode
+>>> record = decode({'id': 123, 'room': 321, 'house': 0}, asn1Spec=Record())
+>>> str(record)
+Record:
+ id=123
+ room=321
+>>>
+```
+
+With ASN.1 design, serialisation codecs are decoupled from data objects,
+so you could turn every single ASN.1 object into many different 
+serialised forms. As of this moment, pyasn1 supports BER, DER, CER and
+Python built-ins codecs. The extremely compact PER encoding is expected
+to be introduced in the upcoming pyasn1 release.
+
+More information on pyasn1 APIs can be found in the
+[documentation](http://snmplabs.com/pyasn1/),
+compiled ASN.1 modules for different protocols and file formats
+could be found in the pyasn1-modules 
+[repo](https://github.com/etingof/pyasn1-modules).
+
+How to get pyasn1
+-----------------
+
+The pyasn1 package is distributed under terms and conditions of 2-clause
+BSD [license](http://snmplabs.com/pyasn1/license.html). Source code is freely
+available as a GitHub [repo](https://github.com/etingof/pyasn1).
+
+You could `pip install pyasn1` or download it from [PyPI](https://pypi.org/project/pyasn1).
+
+If something does not work as expected, 
+[open an issue](https://github.com/etingof/pyasn1/issues) at GitHub or
+post your question [on Stack Overflow](https://stackoverflow.com/questions/ask)
+or try browsing pyasn1 
+[mailing list archives](https://sourceforge.net/p/pyasn1/mailman/pyasn1-users/).
+
+Copyright (c) 2005-2018, [Ilya Etingof](mailto:etingof@gmail.com).
+All rights reserved.
diff --git a/THANKS.txt b/THANKS.txt
new file mode 100644
index 0000000..890ff72
--- /dev/null
+++ b/THANKS.txt
@@ -0,0 +1,10 @@
+Denis S. Otkidach
+Gregory Golberg
+Bud P. Bruegger
+Jacek Konieczny
+Tanya Tereschenko
+Matěj Cepl
+Alex Gaynor
+Geoffrey Thomas
+Daniel Bratell
+Kim Gräsman
diff --git a/TODO.rst b/TODO.rst
new file mode 100644
index 0000000..5c79ee7
--- /dev/null
+++ b/TODO.rst
@@ -0,0 +1,92 @@
+
+Things to be done
+=================
+
+Big things to tackle, anyone interested is welcome to fork pyasn1, work on
+it and come up with a PR!
+
+New codecs
+----------
+
+* PER
+* OER
+* XER
+* LWER
+* JSON (alinged with existing experimental schemas)
+
+Lazy codecs
+-----------
+
+Implement a thin layer over base types to cache pieces
+of substrate being decoded till the very moment of ASN.1
+object access in the parse tree.
+
+Codecs generator interface
+--------------------------
+
+For indefinite length or chunked encoding mode, make codecs
+iterable producing/consuming substrate/objects.
+
+ASN.1 schema compiler
+---------------------
+
+Ideally, the compiler should parse modern schema files and be
+designed to emit code for arbitrary languages (including SQL).
+
+Base types
+----------
+
+Implement X.680 constructs, including information schema.
+
+Examples
+--------
+
+Add examples, including advanced/obscure use cases.
+
+Documentation
+-------------
+
+Document more API, add notes and example snippets.
+
+More fresh modules
+------------------
+
+Compile and ship more Pythonized ASN.1 modules for
+various ASN.1-based protocols (e.g. Kerberos etc).
+Refresh outdated modules in pyasn1-packages.
+
+Minor, housekeeping things
+--------------------------
+
+* more PEP8'ing at places
+* consider simplifying repr(), otherwise it tend to be too hard to grasp
+* Specialize ASN.1 character and useful types
+
+* ber.decoder:
+
+    * suspend codec on underrun error ?
+    * present subtypes ?
+    * component presence check wont work at innertypeconst
+    * type vs value, defaultValue
+
+* ber.encoder:
+
+    * Asn1Item.clone() / shallowcopy issue
+    * large length encoder?
+    * lookup type by tag first to allow custom codecs for non-base types
+
+* type.useful:
+
+    * may need to implement prettyIn/Out
+
+* type.char:
+
+    * may need to implement constraints
+
+* type.namedtypes
+
+    * type vs tagset name convention
+
+* how untagged TagSet should be initialized?
+
+* type and codecs for Real needs refactoring
diff --git a/devel-requirements.txt b/devel-requirements.txt
new file mode 100644
index 0000000..45da368
--- /dev/null
+++ b/devel-requirements.txt
@@ -0,0 +1 @@
+sphinx<1.5; python_version < '3.4'
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..214532e
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,192 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  applehelp  to make an Apple Help Book"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  xml        to make Docutils-native XML files"
+	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+	@echo "  coverage   to run coverage check of the documentation (if enabled)"
+
+clean:
+	rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyASN1.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyASN1.qhc"
+
+applehelp:
+	$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+	@echo
+	@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+	@echo "N.B. You won't be able to view it unless you put it in" \
+	      "~/Library/Documentation/Help or install it in your application" \
+	      "bundle."
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/PyASN1"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyASN1"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through platex and dvipdfmx..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
+
+coverage:
+	$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+	@echo "Testing of coverage in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/coverage/python.txt."
+
+xml:
+	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+	@echo
+	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+	@echo
+	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/docs/source/.static/css/rtdimproved.css b/docs/source/.static/css/rtdimproved.css
new file mode 100644
index 0000000..981f84e
--- /dev/null
+++ b/docs/source/.static/css/rtdimproved.css
@@ -0,0 +1,10 @@
+@import url("../pygments.css");
+@import url("theme.css");
+
+/* fix horizontal padding to accomodate adsense banners */
+.wy-nav-content {
+    padding: 1.618em 2.236em;
+    height: 100%;
+    /* max-width: 800px; */
+    margin: auto;
+}
diff --git a/docs/source/.static/favicon.ico b/docs/source/.static/favicon.ico
new file mode 100644
index 0000000..87528bb
--- /dev/null
+++ b/docs/source/.static/favicon.ico
Binary files differ
diff --git a/docs/source/.static/logo.svg b/docs/source/.static/logo.svg
new file mode 100644
index 0000000..e6062aa
--- /dev/null
+++ b/docs/source/.static/logo.svg
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg viewBox="0 0 788.22937 829.02386" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+  <filter id="j">
+   <feGaussianBlur stdDeviation="6.2732"/>
+  </filter>
+  <filter id="g">
+   <feGaussianBlur stdDeviation=".64973"/>
+  </filter>
+  <filter id="l" x="-.06193" y="-.19499" width="1.1239" height="1.39">
+   <feGaussianBlur stdDeviation="7.46855"/>
+  </filter>
+  <filter id="k" x="-.25093" y="-.14188" width="1.5019" height="1.2838">
+   <feGaussianBlur stdDeviation="3.07792"/>
+  </filter>
+  <filter id="i" x="-.03871" y="-.12187" width="1.0774" height="1.2437">
+   <feGaussianBlur stdDeviation="4.66785"/>
+  </filter>
+  <filter id="h" x="-.12783" y="-.07846" width="1.2557" height="1.1569">
+   <feGaussianBlur stdDeviation="1.708"/>
+  </filter>
+  <linearGradient id="e">
+   <stop stop-color="#817e7e" offset="0"/>
+   <stop stop-color="#f4ebeb" stop-opacity=".98824" offset=".5"/>
+   <stop stop-color="#241b1b" stop-opacity=".97826" offset="1"/>
+  </linearGradient>
+  <linearGradient id="f">
+   <stop stop-color="#1a5b78" offset="0"/>
+   <stop stop-color="#136890" offset="1"/>
+  </linearGradient>
+  <linearGradient id="r" x1="301.38" x2="318.52" y1="716.86" y2="106.67" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#3aacfa" offset="0"/>
+   <stop stop-color="#2699d7" stop-opacity=".99608" offset=".83216"/>
+   <stop stop-color="#78c1e7" stop-opacity=".99216" offset=".98972"/>
+   <stop stop-color="#cae9f7" stop-opacity=".99216" offset="1"/>
+  </linearGradient>
+  <linearGradient id="q" x1="-6.9187" x2="583.27" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#d4f6f7" offset="0"/>
+   <stop stop-color="#bdf0f2" offset=".5"/>
+   <stop stop-color="#76eaf0" offset=".75"/>
+   <stop stop-color="#465758" offset="1"/>
+  </linearGradient>
+  <linearGradient id="d" x1="172.72" x2="402.47" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#2a9cf9" offset="0"/>
+   <stop stop-color="#afe2eb" offset="1"/>
+  </linearGradient>
+  <linearGradient id="c" x1="286.18" x2="292.27" y1="332.78" y2="297.07" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#241b1b" offset="0"/>
+   <stop stop-color="#241b1b" stop-opacity="0" offset="1"/>
+  </linearGradient>
+  <linearGradient id="b" x1="291.93" x2="290.42" y1="654.44" y2="584.74" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#4ec7ff" offset="0"/>
+   <stop stop-color="#177ba9" offset="1"/>
+  </linearGradient>
+  <linearGradient id="p" x1="166.1" x2="410.06" y1="529.93" y2="527.91" gradientUnits="userSpaceOnUse" xlink:href="#f"/>
+  <linearGradient id="o" x1="257.31" x2="320.16" gradientUnits="userSpaceOnUse" xlink:href="#e"/>
+  <linearGradient id="n" x1="229.49" x2="343.96" gradientUnits="userSpaceOnUse" xlink:href="#e"/>
+  <linearGradient id="m" x1="63.378" x2="507.69" y1="571" y2="567.46" gradientUnits="userSpaceOnUse" xlink:href="#f"/>
+  <radialGradient id="a" cx="288.79" cy="314.87" r="47.676" gradientTransform="matrix(.95889 0 0 .36279 11.873 202.26)" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#f9faf8" offset="0"/>
+   <stop stop-color="#ccceca" stop-opacity=".67816" offset=".80581"/>
+   <stop stop-color="#a0a39d" stop-opacity=".98551" offset="1"/>
+  </radialGradient>
+ </defs>
+ <g transform="translate(36.408 -60.696)">
+  <path d="m217.12 214.13 1.8398 157.37s-68.072 76.035-114.07 139.69-123.27 152.07-110.39 206.89 147.18 111.4 270.45 109.63 316.44-30.06 318.28-100.79-88.31-185.67-121.43-229.87-101.19-123.78-101.19-123.78l-3.6796-159.14s-27.597 14.146-69.912 14.146-69.912-14.146-69.912-14.146z" fill="url(#q)" opacity=".59004"/>
+  <g transform="matrix(1.0193 0 0 .9797 275.35 -97.577)">
+   <path transform="matrix(1.0949 0 0 1.1946 -27.22 -91.679)" d="m433.86 608.91c0 25.384-64.786 45.962-144.7 45.962s-144.7-20.578-144.7-45.962 64.786-45.962 144.7-45.962 144.7 20.578 144.7 45.962z" filter="url(#l)"/>
+   <path d="m320.16 384.59c-20.034 6.3449-42.056 6.5046-62.156 0.15625 0.29535 26.623 0.5955 5.2459 0.875 31.875 6e-3 1.2834-0.46958 2.5635-1.3125 3.5312-27.411 31.834-52.856 65.234-76.938 99.75-17.564 25.17-36.956 49.209-44.688 77.531l-0.0312 0.0625c-2.7636 9.7018-0.36414 20.52 6.75 27.375l0.0937 0.0937c19.862 20.02 48.023 30.265 75.875 37.5 41.373 10.675 85.409 6.8178 128.31 1.0625v-0.0312c28.981-4.768 58.19-10.111 82.5-24.812-3e-5 -0.0104-3e-5 -0.0208 0-0.0312 4.5579-2.7227 8.8864-6.5506 11.625-10.781s3.9643-8.6335 3.0312-13.531c-0.0253-0.1242-0.0461-0.24931-0.0625-0.375-3.0304-25.717-17.046-49.326-30.906-72.375-0.0239-0.0398-0.0386-0.0852-0.0625-0.125-26.805-42.168-58.009-81.435-89.844-120.41-0.75007-0.90889-1.1862-2.072-1.2188-3.25-0.64083-27.08-1.2331-6.1494-1.8438-33.219z" fill="url(#p)"/>
+   <path d="m308.76 387.93c-15.75 1.6761-28.556 1.9621-44.482-1.3589 0.21917 26.636 0.31563 3.3544 0.52303 29.996 5e-3 1.284-0.34845 2.5647-0.97395 3.533-20.341 31.85-39.222 65.266-57.092 99.798-13.034 25.182-27.423 49.233-33.161 77.569l-0.0232 0.0625c-2.0508 9.7065-0.27021 20.53 5.0089 27.388l0.0696 0.0937c11.203 12.958 20.695 21.066 48.728 28.023s68.254 7.0598 102.79 2.5782c20.824-2.7021 47.44-9.1506 61.22-16.876-3e-5 -0.0104-3e-5 -0.0208 0-0.0312 3.3822-2.724 6.5943-6.5538 8.6265-10.786s2.9418-8.6377 2.2494-13.538c-0.0188-0.12426-0.0342-0.24943-0.0464-0.37518-2.2487-25.729-12.649-49.35-22.934-72.41-0.0178-0.0398-0.0286-0.0852-0.0464-0.12506-19.891-42.189-43.047-81.475-66.67-120.46-0.5566-0.90934-0.88028-2.073-0.90439-3.2516-0.47553-27.093 0.0951-3.1219-0.35803-30.204z" fill="url(#d)" filter="url(#j)"/>
+   <path transform="matrix(1 0 0 .9375 0 20.254)" d="m324.07 315.36c0 4.8113-15.991 8.7116-35.718 8.7116s-35.718-3.9003-35.718-8.7116 15.991-8.7116 35.718-8.7116 35.718 3.9003 35.718 8.7116z" fill="url(#c)"/>
+   <path transform="matrix(1 0 0 1.087 0 -51.618)" d="m433.86 608.91c0 25.384-64.786 45.962-144.7 45.962s-144.7-20.578-144.7-45.962 64.786-45.962 144.7-45.962 144.7 20.578 144.7 45.962z" fill="url(#b)" filter="url(#i)" opacity=".7"/>
+   <path transform="matrix(.74812 .4869 -.42145 .93332 324.55 94.283)" d="m105.06 429.6c0 15.342-4.7487 27.779-10.607 27.779s-10.607-12.437-10.607-27.779 4.7487-27.779 10.607-27.779 10.607 12.437 10.607 27.779z" fill="#fff" filter="url(#h)"/>
+   <path transform="matrix(.69501 .29687 -.29983 .73496 329.84 101.99)" d="m105.06 429.6c0 15.342-4.7487 27.779-10.607 27.779s-10.607-12.437-10.607-27.779 4.7487-27.779 10.607-27.779 10.607 12.437 10.607 27.779z" fill="#fff" filter="url(#k)"/>
+   <path d="m293.58 299.25c4.5514 0.12881 9.3278 0.24858 13.379 0.77697 5.2851 0.68931 10.077 1.7034 14.201 3.0024s7.6027 2.8509 10.281 4.932 4.6532 4.9568 4.3302 8.2969-2.8562 6.2388-5.9368 8.3199-6.8597 3.633-11.235 4.932c-8.7499 2.598-19.953 4.0562-32.144 4.0562s-23.083-1.4582-31.33-4.0562c-4.1238-1.299-7.6317-2.8509-10.31-4.932s-4.6509-4.9799-4.328-8.3199 2.8539-6.2158 5.9346-8.2969 6.8887-3.633 11.264-4.932c6.6932-1.9873 14.805-3.3077 23.705-3.8187 2.7349-0.15701-1.2073-0.23758 1.6582-0.23758l0.0765 9.2646c-3.7487 0.11199-7.3905 0.29917-9.7411 0.60179-4.7649 0.61344-9.0159 1.4835-12.472 2.5098s-6.0905 2.2331-7.5611 3.2266-1.6214 1.4742-1.6415 1.6824 0.0354 0.71198 1.3139 1.7055 3.6792 2.2003 6.9372 3.2266c6.5161 2.0526 16.331 3.4801 27.355 3.4801s21.144-1.4275 28.057-3.4801c3.4565-1.0263 6.0905-2.2331 7.5612-3.2266s1.5946-1.4972 1.6147-1.7055-9e-3 -0.68892-1.2872-1.6824-3.6792-2.2002-6.9372-3.2266-7.348-1.8963-12.002-2.5098-5.0792-0.75252-10.591-0.75252z" fill="url(#a)" filter="url(#g)" opacity=".64751"/>
+   <path d="m257.31 330.38c17.886 5.8187 39.891 3.5219 62.41-1.0835l0.44026 55.295c-21.953 6.8399-42.524 6.0827-62.156 0.15625z" fill="url(#o)" opacity=".49808"/>
+   <path d="m286.61 386.36h0.43558v3.0491h-0.43558z" fill="#241b1b" opacity=".64751"/>
+   <path d="m290.1 385.92h0.43558v3.4846h-0.43558z" fill="#241b1b" opacity=".64751"/>
+   <path d="m317.86 382.77c0 3.7423-12.687 6.776-28.336 6.776s-28.336-3.0337-28.336-6.776 12.687-6.776 28.336-6.776 28.336 3.0337 28.336 6.776z" fill="#135f9b" opacity=".68199"/>
+  </g>
+  <path transform="matrix(1.9941 0 0 2.091 -288.72 -517.12)" d="m433.86 608.91c0 25.384-64.786 45.962-144.7 45.962s-144.7-20.578-144.7-45.962 64.786-45.962 144.7-45.962 144.7 20.578 144.7 45.962z" filter="url(#l)"/>
+  <path d="m343.96 316.59c-36.488 11.107-76.596 11.386-113.2 0.27351 0.53792 46.603 1.0846 9.1828 1.5936 55.796 0.0109 2.2465-0.85524 4.4873-2.3904 6.1814-49.924 55.725-96.267 114.19-140.13 174.61-31.99 44.059-67.307 86.139-81.389 135.72l-0.05682 0.1094c-5.0334 16.983-0.66321 35.921 12.294 47.919 0.05755 0.0539 0.11456 0.10871 0.17066 0.16402 36.175 35.044 87.464 52.978 138.19 65.643 75.353 18.686 155.55 11.934 233.69 1.8599v-0.0546c52.783-8.3462 105.98-17.699 150.26-43.434-5e-5 -0.0182-5e-5 -0.0364 0-0.0546 8.3013-4.766 16.185-11.467 21.173-18.872s7.2202-15.113 5.5208-23.686c-0.0461-0.21741-0.084-0.43641-0.11383-0.65643-5.5192-45.016-31.045-86.344-56.289-126.69-0.0435-0.0697-0.0703-0.14914-0.11383-0.21881-48.821-73.814-105.65-142.55-163.63-210.77-1.3661-1.591-2.1605-3.627-2.2197-5.689-1.1671-47.402-2.2458-10.764-3.358-58.149z" fill="url(#m)"/>
+  <path transform="matrix(1.8213 0 0 1.7505 -239.14 -356.63)" d="m308.76 387.93c-15.75 1.6761-28.556 1.9621-44.482-1.3589 0.21917 26.636 0.31563 3.3544 0.52303 29.996 5e-3 1.284-0.34845 2.5647-0.97395 3.533-20.341 31.85-39.222 65.266-57.092 99.798-13.034 25.182-27.423 49.233-33.161 77.569l-0.0232 0.0625c-2.0508 9.7065-0.27021 20.53 5.0089 27.388l0.0696 0.0937c11.203 12.958 20.695 21.066 48.728 28.023s68.254 7.0598 102.79 2.5782c20.824-2.7021 47.44-9.1506 61.22-16.876-3e-5 -0.0104-3e-5 -0.0208 0-0.0312 3.3822-2.724 6.5943-6.5538 8.6265-10.786s2.9418-8.6377 2.2494-13.538c-0.0188-0.12426-0.0342-0.24943-0.0464-0.37518-2.2487-25.729-12.649-49.35-22.934-72.41-0.0178-0.0398-0.0286-0.0852-0.0464-0.12506-19.891-42.189-43.047-81.475-66.67-120.46-0.5566-0.90934-0.88028-2.073-0.90439-3.2516-0.47553-27.093 0.0951-3.1219-0.35803-30.204z" fill="url(#d)" filter="url(#j)"/>
+  <path transform="matrix(1.8213 0 0 1.6411 -239.14 -321.18)" d="m324.07 315.36c0 4.8113-15.991 8.7116-35.718 8.7116s-35.718-3.9003-35.718-8.7116 15.991-8.7116 35.718-8.7116 35.718 3.9003 35.718 8.7116z" fill="url(#c)"/>
+  <path transform="matrix(1.8213 0 0 1.9027 -239.14 -446.99)" d="m433.86 608.91c0 25.384-64.786 45.962-144.7 45.962s-144.7-20.578-144.7-45.962 64.786-45.962 144.7-45.962 144.7 20.578 144.7 45.962z" fill="url(#b)" filter="url(#i)" opacity=".7"/>
+  <path transform="matrix(1.3625 .8523 -.76759 1.6338 351.96 -191.6)" d="m105.06 429.6c0 15.342-4.7487 27.779-10.607 27.779s-10.607-12.437-10.607-27.779 4.7487-27.779 10.607-27.779 10.607 12.437 10.607 27.779z" fill="#fff" filter="url(#h)"/>
+  <path transform="matrix(1.2658 .51966 -.54607 1.2865 361.6 -178.11)" d="m105.06 429.6c0 15.342-4.7487 27.779-10.607 27.779s-10.607-12.437-10.607-27.779 4.7487-27.779 10.607-27.779 10.607 12.437 10.607 27.779z" fill="#fff" filter="url(#k)"/>
+  <path d="m282.86 319.68h0.79332v5.3373h-0.79332z" fill="#241b1b" opacity=".64751"/>
+  <path d="m289.21 318.92h0.79332v6.0998h-0.79332z" fill="#241b1b" opacity=".64751"/>
+  <path d="m229.49 221.69c32.576 10.186 72.653 6.165 113.67-1.8966l0.80184 96.793c-39.983 11.973-77.45 10.648-113.2 0.27351z" fill="url(#n)" opacity=".49808"/>
+  <path d="m314.53 88.096c11.175-7.4188 26.676-9.6276 40.922-9.6276 20.515 0 42.424 0.67751 63.119 1.3129-14.157 12.706-20.02 32.833-20.603 60.884s7.4772 46.002 19.522 56.234 26.603 12.035 36.71 12.035c9.5446 0 23.331-0.79894 35.231-9.8464s20.303-25.487 22.083-52.296c1.8812-28.327-6.4708-49.691-19.01-63.838 12.054 0.0088 22.878-0.32242 30.962 0.43762 11.434 1.0751 18.465 3.5429 26.636 14.168 6.5038 8.4571 10.278 28.096 11.099 50.764s-0.56916 48.252-0.56916 70.183h18.213c0-21.201 1.4303-46.992 0.56916-70.785s-3.2322-45.719-14.684-60.61c-10.902-14.176-25.129-19.764-39.499-21.115s-29.235 0.31775-46.898-0.71113c-4.6471-0.27069-8.9502-0.18951-13.261-0.10941-4.37-1.2458-8.7613-2.0787-13.091-2.0787-25.276 0-63.498-2.1334-96.529-2.1334-16.045 1.8e-5 -35.249 2.0482-51.281 12.691s-27.971 33.041-27.875 62.294l-0.30018 580.36c6.7459 3.5107 13.492 6.3162 20.238 0l-0.41466-580.94c0.37865-26.774 7.5356-39.862 18.711-47.281zm147.01-5.525h0.0568c15.896 5.5007 34.337 24.301 31.759 63.127-1.5853 23.871-8.3043 34.419-15.196 39.659s-15.555 6.072-23.961 6.072c-7.843 0-17.341-1.4545-24.644-7.6583s-13.892-17.899-13.375-42.723c0.53821-25.865 5.9461-40.522 14.798-48.466 6.639-5.9586 16.402-9.3 30.564-10.011z" fill="url(#r)" stroke="#000" stroke-opacity=".51587" stroke-width=".53566"/>
+  <path transform="matrix(1.8213 0 0 1.7505 -239.14 -356.63)" d="m293.58 299.25c4.5514 0.12881 9.3278 0.24858 13.379 0.77697 5.2851 0.68931 10.077 1.7034 14.201 3.0024s7.6027 2.8509 10.281 4.932 4.6532 4.9568 4.3302 8.2969-2.8562 6.2388-5.9368 8.3199-6.8597 3.633-11.235 4.932c-8.7499 2.598-19.953 4.0562-32.144 4.0562s-23.083-1.4582-31.33-4.0562c-4.1238-1.299-7.6317-2.8509-10.31-4.932s-4.6509-4.9799-4.328-8.3199 2.8539-6.2158 5.9346-8.2969 6.8887-3.633 11.264-4.932c6.6932-1.9873 14.805-3.3077 23.705-3.8187 2.7349-0.15701-1.2073-0.23758 1.6582-0.23758l0.0765 9.2646c-3.7487 0.11199-7.3905 0.29917-9.7411 0.60179-4.7649 0.61344-9.0159 1.4835-12.472 2.5098s-6.0905 2.2331-7.5611 3.2266-1.6214 1.4742-1.6415 1.6824 0.0354 0.71198 1.3139 1.7055 3.6792 2.2003 6.9372 3.2266c6.5161 2.0526 16.331 3.4801 27.355 3.4801s21.144-1.4275 28.057-3.4801c3.4565-1.0263 6.0905-2.2331 7.5612-3.2266s1.5946-1.4972 1.6147-1.7055-9e-3 -0.68892-1.2872-1.6824-3.6792-2.2002-6.9372-3.2266-7.348-1.8963-12.002-2.5098-5.0792-0.75252-10.591-0.75252z" fill="url(#a)" filter="url(#g)" opacity=".64751"/>
+  <path transform="matrix(1.8213 0 0 1.7505 -239.14 -356.63)" d="m317.86 382.77c0 3.7423-12.687 6.776-28.336 6.776s-28.336-3.0337-28.336-6.776 12.687-6.776 28.336-6.776 28.336 3.0337 28.336 6.776z" fill="#135f9b" opacity=".68199"/>
+ </g>
+</svg>
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
new file mode 100644
index 0000000..095fc83
--- /dev/null
+++ b/docs/source/changelog.rst
@@ -0,0 +1,6 @@
+
+Changelog
+=========
+
+.. include:: ../../CHANGES.rst
+
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..ccfbd1c
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,326 @@
+# -*- coding: utf-8 -*-
+#
+# PyASN1 documentation build configuration file, created by
+# sphinx-quickstart on Sat Jun 27 23:15:54 2015.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.napoleon',
+    'sphinx.ext.intersphinx',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The encoding of source files.
+# source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'contents'
+
+# General information about the project.
+project = u'ASN.1 types and codecs'
+copyright = u'2005-2018, Ilya Etingof <etingof@gmail.com>'
+author = u'Ilya Etingof <etingof@gmail.com>'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.4'
+# The full version, including alpha/beta/rc tags.
+release = '0.4'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+# today = ''
+# Else, today_fmt is used as the format for a strftime call.
+# today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'alabaster'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+html_theme_options = {
+    'logo': 'logo.svg',
+    'description': '<p align=left><i><b>Brewing free software for the greater good</i></b></p>',
+    'show_powered_by': False,
+    'github_user': 'etingof',
+    'github_repo': 'pyasn1',
+    'fixed_sidebar': True,
+}
+
+html_sidebars = {
+    '**': [
+        'about.html',
+        'navigation.html',
+        'relations.html',
+        'searchbox.html',
+        'donate.html',
+    ]
+}
+
+# Add any paths that contain custom themes here, relative to this directory.
+# html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+# html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+# html_logo = ""
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+html_favicon = '.static/favicon.ico'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['.static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+# html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+# html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+# html_domain_indices = True
+
+# If false, no index is generated.
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+# html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+html_show_sourcelink = False
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+html_show_sphinx = False
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+# html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+# html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+# html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'pyasn1doc'
+
+# html_context = {
+#     'include_analytics': 'PYASN1DEV' in os.environ
+# }
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'PyASN1.tex', u'PyASN1 Documentation',
+     u'Ilya Etingof \\textless{}etingof@gmail.com\\textgreater{}', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+# latex_appendices = []
+
+# If false, no module index is generated.
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'pyasn1', u'PyASN1 Documentation',
+     [author], 1)
+]
+
+# If true, show URL addresses after external links.
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'PyASN1', u'PyASN1 Documentation',
+     author, 'PyASN1', 'One line description of project.',
+     'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+# texinfo_no_detailmenu = False
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'python': ('https://docs.python.org/3.4/', None)}
+
+# this merges constructor docstring with class docstring
+autoclass_content = 'both'
+# Sort members by type
+autodoc_member_order = 'bysource'
+# autodoc_member_order = 'groupwise'
+
+# Napoleon settings
+napoleon_google_docstring = False
+napoleon_numpy_docstring = True
+napoleon_include_private_with_doc = False
+napoleon_include_special_with_doc = True
+napoleon_use_admonition_for_examples = False
+napoleon_use_admonition_for_notes = False
+napoleon_use_admonition_for_references = False
+napoleon_use_ivar = False
+napoleon_use_param = False
+napoleon_use_rtype = False
diff --git a/docs/source/contents.rst b/docs/source/contents.rst
new file mode 100644
index 0000000..a2cd805
--- /dev/null
+++ b/docs/source/contents.rst
@@ -0,0 +1,161 @@
+
+ASN.1 library for Python
+========================
+
+.. toctree::
+   :maxdepth: 1
+
+Abstract Syntax Notation One (`ASN.1
+<http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_1x>`_) is a
+technology for exchanging structured data in a universally understood,
+hardware agnostic way. Many industrial, security and telephony
+applications heavily rely on ASN.1.
+
+The `pyasn1 <https://pypi.org/project/pyasn1/>`_ library implements
+ASN.1 support in pure-Python.
+
+What is ASN.1
+-------------
+
+ASN.1 is a large, arguably over-engineered and extremely old data modelling and
+serialisation tool. It is probably among the first serialisation protocols in
+the history of computer science and technology.
+
+ASN.1 started its life over 30 years ago as a serialisation mechanism for the first
+electronic mail (known as X.400). Later on if was split off the e-mail application
+and become a stand-alone tech still being actively supported by its designers
+and widely used in industry and technology.
+
+Since then ASN.1 is sort of haunted by its relations with the OSI model -- the
+first, unsuccessful, version of the Internet. You can read many interesting
+`discussions <https://news.ycombinator.com/item?id=8871453>`_ on that topic.
+
+In the following years, generations of software engineers tackled the serialisation
+problem many times. We can see that in Google's `ProtoBuffers <https://developers.google.com/protocol-buffers/>`_
+or `FlatBuffers <https://google.github.io/flatbuffers/>`_, for example.
+Interestingly, many new takes on binary protocol design do not depart
+far from ASN.1 from technical perspective. It's more of a matter of striking
+a balance between processing overhead, wire format overhead and human
+readability.
+
+Looking at what ASN.1 has to offer, it has three loosely coupled parts:
+
+* Data types: the standard introduces a collection of basic data types
+  (integers, bits, strings, arrays and records) that can be used for describing
+  arbitrarily complex, nested data structures.
+
+* Serialisation protocols: the above data structures could be converted into a
+  series of octets for storage or transmission over the wire as well as
+  recovered back into their structured form. The system is fully agnostic
+  to hardware architectures differences.
+
+* Schema language: ASN.1 data structures could be described in terms
+  of a schema language for ASN.1 compiler to turn it into platform-specific
+  implementation.
+
+ASN.1 applications
+------------------
+
+Being an old and generally successful standard, ASN.1 is widely
+adopted for many uses. To give you an example, these technologies
+use ASN.1 for their data exchange needs:
+
+* Signaling standards for the public switched telephone network (SS7 family)
+* Network management standards (SNMP, CMIP)
+* Directory standards (X.500 family, LDAP)
+* Public Key Infrastructure standards (X.509, etc.)
+* PBX control (CSTA)
+* IP-based Videoconferencing (H.323 family)
+* Biometrics (BIP, CBEFF, ACBio)
+* Intelligent transportation (SAE J2735)
+* Cellular telephony (GSM, GPRS/EDGE, UMTS, LTE)
+
+ASN.1 gotchas
+-------------
+
+Apparently, ASN.1 is hard to implement properly. Quality open-source
+ASN.1 tools are rare, but ad-hoc implementations are numerous. Judging from the
+`statistics <http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=ASN.1>`_ on discovered
+security vulnerabilities, many people have implemented ASN.1 parsers
+and oftentimes fell victim to its edge cases.
+
+On the bright side, ASN.1 has been around for a long time, it is well understood
+and security reviewed.
+
+Documentation
+-------------
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/contents
+
+Use case
+--------
+
+.. toctree::
+   :maxdepth: 2
+
+   /example-use-case
+
+Download & Install
+------------------
+
+.. toctree::
+   :maxdepth: 2
+
+   /download
+
+Changes
+-------
+
+All changes and release history is maintained in changelog.  There you
+could also download the latest unreleased pyasn1 tarball containing
+the latest fixes and improvements.
+
+.. toctree::
+   :maxdepth: 1
+
+   /changelog
+
+License
+-------
+
+The PyASN1 software is distributed under 2-clause BSD License.
+
+.. toctree::
+   :maxdepth: 2
+
+   /license
+
+Getting help
+------------
+
+Please, file your `issues <https://github.com/etingof/pyasn1/issues>`_
+and `PRs <https://github.com/etingof/pyasn1/pulls>`_ at GitHub.
+Alternatively, you could ask for help at
+`Stack Overflow <http://stackoverflow.com/questions/tagged/pyasn1>`_
+or search
+`pyasn1-users <https://lists.sourceforge.net/lists/listinfo/pyasn1-users>`_
+mailing list archive.
+
+Books on ASN.1
+--------------
+
+The pyasn1 implementation is largely based on reading up the following awesome
+books:
+
+* `ASN.1 - Communication between heterogeneous systems <http://www.oss.com/asn1/dubuisson.html>`_ by Olivier Dubuisson
+* `ASN.1 Complete <http://www.oss.com/asn1/resources/books-whitepapers-pubs/larmouth-asn1-book.pdf>`_ by Prof John Larmouth
+
+Here you can get the official standards which is hard to read:
+
+* `ITU standards <http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-X.693-0207w.zip>`_
+
+On the other end of the readability spectrum, here is a quick and sweet write up:
+
+* `A Layman's Guide to a Subset of ASN.1, BER, and DER <ftp://ftp.rsasecurity.com/pub/pkcs/ascii/layman.asc>`_ by Burton S. Kaliski
+
+If you are working with ASN.1, we'd highly recommend reading a proper
+book on the subject.
+
diff --git a/docs/source/download.rst b/docs/source/download.rst
new file mode 100644
index 0000000..bbbd66c
--- /dev/null
+++ b/docs/source/download.rst
@@ -0,0 +1,27 @@
+
+Download & Install
+==================
+
+The *pyasn1* library is a pure-Python package with no external
+dependencies. Considering how much industrial or finance software
+can be stuck with an old platform (think RHEL 5), we struggle to
+maintain its compatibility back to the very pre-historic Python
+(which is 2.4!).
+
+The best way to obtain PyASN1 is by running `pip`:
+
+.. code-block:: bash
+
+   $ virtualenv venv
+   $ source venv/bin/activate
+   $ pip install pyasn1
+
+You may also want to use `pyasn1-modules`:
+
+.. code-block:: bash
+
+   $ pip install pyasn1-modules
+
+Alternatively, you can download the latest release from
+`GitHub <https://github.com/etingof/pyasn1/releases>`_
+or `PyPI <https://pypi.org/project/pyasn1>`_.
diff --git a/docs/source/example-use-case.rst b/docs/source/example-use-case.rst
new file mode 100644
index 0000000..4efdc81
--- /dev/null
+++ b/docs/source/example-use-case.rst
@@ -0,0 +1,193 @@
+
+Example use case
+================
+
+.. toctree::
+   :maxdepth: 2
+
+To briefly explain how to approach pyasn1, consider a quick workflow example.
+
+Grab ASN.1 schema for SSH keys
+------------------------------
+
+ASN.1 is widely used in many Internet protocols. Frequently, whenever ASN.1 is employed,
+data structures are described in ASN.1 schema language right in the RFC.
+Take `RFC2437 <https://www.ietf.org/rfc/rfc2437.txt>`_ for example -- we can look into
+it and weed out data structures specification into a local file:
+
+.. code-block:: python
+
+    # pkcs-1.asn
+
+    PKCS-1 {iso(1) member(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) modules(0) pkcs-1(1)}
+
+    DEFINITIONS EXPLICIT TAGS ::= BEGIN
+        RSAPrivateKey ::= SEQUENCE {
+             version Version,
+             modulus INTEGER,
+             publicExponent INTEGER,
+             privateExponent INTEGER,
+             prime1 INTEGER,
+             prime2 INTEGER,
+             exponent1 INTEGER,
+             exponent2 INTEGER,
+             coefficient INTEGER
+        }
+        Version ::= INTEGER
+    END
+
+Compile ASN.1 schema into Python
+--------------------------------
+
+In the best case, you should be able to automatically compile ASN.1 spec into
+Python classes. For that purpose we have the `asn1ate <https://github.com/kimgr/asn1ate>`_
+tool:
+
+.. code-block:: bash
+
+    $ pyasn1gen.py pkcs-1.asn > rsakey.py
+
+Though it may not work out as, as it stands now, asn1ate does not support
+all ASN.1 language constructs.
+
+Alternatively, you could check out the `pyasn1-modules <https://github.com/etingof/pyasn1-modules>`_
+package to see if it already has the ASN.1 spec you are looking for compiled and shipped
+there. Then just install the package, import the data structure you need and use it:
+
+.. code-block:: bash
+
+    $ pip install pyasn1-modules
+
+As a last resort, you could express ASN.1 in Python by hand. The end result
+should be a declarative Python code resembling original ASN.1 syntax like
+this:
+
+.. code-block:: python
+
+    # rsakey.py
+
+    class Version(Integer):
+        pass
+
+    class RSAPrivateKey(Sequence):
+        componentType = NamedTypes(
+            NamedType('version', Version()),
+            NamedType('modulus', Integer()),
+            NamedType('publicExponent', Integer()),
+            NamedType('privateExponent', Integer()),
+            NamedType('prime1', Integer()),
+            NamedType('prime2', Integer()),
+            NamedType('exponent1', Integer()),
+            NamedType('exponent2', Integer()),
+            NamedType('coefficient', Integer())
+        )
+
+Read your ~/.ssh/id_rsa
+-----------------------
+
+Given we've put our Python classes into the `rsakey.py` module, we could import
+the top-level object for SSH keys container and initialize it from our
+`~/.ssh/id_rsa` file (for sake of simplicity here we assume no passphrase is
+set on the key file):
+
+.. code-block:: python
+
+    from base64 import b64decode
+    from pyasn1.codec.der.decoder import decode as der_decoder
+    from rsakey import RSAPrivateKey
+
+    # Read SSH key from file (assuming no passphrase)
+    with open open('.ssh/id_rsa') as key_file:
+        b64_serialisation = ''.join(key_file.readlines()[1:-1])
+
+    # Undo BASE64 serialisation
+    der_serialisation = b64decode(b64_serialisation)
+
+    # Undo DER serialisation, reconstruct SSH key structure
+    private_key, rest_of_input = der_decoder(der_serialisation, asn1Spec=RSAPrivateKey())
+
+Once we have Python ASN.1 structures initialized, we could inspect them:
+
+.. code-block:: pycon
+
+    >>> print('%s' % private_key)
+    RSAPrivateKey:
+     version=0
+     modulus=280789907761334970323210643584308373...
+     publicExponent=65537
+     privateExponent=1704567874679144879123080924...
+     prime1=1780178536719561265324798296279384073...
+     prime2=1577313184995269616049017780493740138...
+     exponent1=1193974819720845247396384239609024...
+     exponent2=9240965721817961178848297404494811...
+     coefficient=10207364473358910343346707141115...
+
+Play with the keys
+------------------
+
+As well as use them nearly as we do with native Python types:
+
+.. code-block:: pycon
+
+    >>> pk = private_key
+    >>>
+    >>> pk['prime1'] * pk['prime2'] == pk['modulus']
+    True
+    >>> pk['prime1'] == pk['modulus'] // pk['prime2']
+    True
+    >>> pk['exponent1'] == pk['privateExponent'] % (pk['prime1'] - 1)
+    True
+    >>> pk['exponent2'] == pk['privateExponent'] % (pk['prime2'] - 1)
+    True
+
+Technically, pyasn1 classes `emulate <https://docs.python.org/3/reference/datamodel.html#emulating-container-types>`_
+Python built-in types.
+
+Transform to built-ins
+----------------------
+
+ASN.1 data structures exhibit a way more complicated behaviour compared to
+Python types. You may wish to simplify things by turning the whole tree of
+pyasn1 objects into an analogous tree made of base Python types:
+
+.. code-block:: pycon
+
+    >>> from pyasn1.codec.native.encoder import encode
+    >>> ...
+    >>> py_private_key = encode(private_key)
+    >>> py_private_key
+    {'version': 0, 'modulus': 280789907761334970323210643584308373, 'publicExponent': 65537,
+     'privateExponent': 1704567874679144879123080924, 'prime1': 1780178536719561265324798296279384073,
+     'prime2': 1577313184995269616049017780493740138, 'exponent1': 1193974819720845247396384239609024,
+     'exponent2': 9240965721817961178848297404494811, 'coefficient': 10207364473358910343346707141115}
+
+You can do vice-versa: initialize ASN.1 structure from a dict:
+
+.. code-block:: pycon
+
+    >>> from pyasn1.codec.native.decoder import decode
+    >>> py_private_key = {'modulus': 280789907761334970323210643584308373}
+    >>> private_key = decode(py_private_key, asn1Spec=RSAPrivateKey())
+
+Write it back
+-------------
+
+Possibly not that applicable to the SSH key example, but you can of course modify
+any part of the ASN.1 data structure and serialise it back into the same or other
+wire representation:
+
+.. code-block:: python
+
+    from pyasn1.codec.der.encoder import encode as der_encoder
+
+    # Serialise SSH key data structure into DER stream
+    der_serialisation = der_encoder(private_key)
+
+    # Serialise DER stream into BASE64 stream
+    b64_serialisation = '-----BEGIN RSA PRIVATE KEY-----\n'
+    b64_serialisation += b64encode(der_serialisation)
+    b64_serialisation += '-----END RSA PRIVATE KEY-----\n'
+
+    with open('.ssh/id_rsa.new', 'w') as key_file:
+        key_file.write(b64_serialisation)
+
diff --git a/docs/source/license.rst b/docs/source/license.rst
new file mode 100644
index 0000000..643baf1
--- /dev/null
+++ b/docs/source/license.rst
@@ -0,0 +1,6 @@
+.. _license:
+
+License
+=======
+
+.. include:: ../../LICENSE.rst
diff --git a/docs/source/pyasn1/codec/ber/contents.rst b/docs/source/pyasn1/codec/ber/contents.rst
new file mode 100644
index 0000000..c767625
--- /dev/null
+++ b/docs/source/pyasn1/codec/ber/contents.rst
@@ -0,0 +1,7 @@
+
+Basic Encoding Rules
+--------------------
+
+.. autofunction:: pyasn1.codec.ber.encoder.encode(value, asn1Spec=None, defMode=True, maxChunkSize=0)
+
+.. autofunction:: pyasn1.codec.ber.decoder.decode(substrate, asn1Spec=None)
diff --git a/docs/source/pyasn1/codec/cer/contents.rst b/docs/source/pyasn1/codec/cer/contents.rst
new file mode 100644
index 0000000..8eec842
--- /dev/null
+++ b/docs/source/pyasn1/codec/cer/contents.rst
@@ -0,0 +1,7 @@
+
+Canonical Encoding Rules
+------------------------
+
+.. autofunction:: pyasn1.codec.cer.encoder.encode(value, asn1Spec=None)
+
+.. autofunction:: pyasn1.codec.cer.decoder.decode(substrate, asn1Spec=None)
diff --git a/docs/source/pyasn1/codec/der/contents.rst b/docs/source/pyasn1/codec/der/contents.rst
new file mode 100644
index 0000000..27ef401
--- /dev/null
+++ b/docs/source/pyasn1/codec/der/contents.rst
@@ -0,0 +1,7 @@
+
+Distinguished Encoding Rules
+----------------------------
+
+.. autofunction:: pyasn1.codec.der.encoder.encode(value, asn1Spec=None)
+
+.. autofunction:: pyasn1.codec.der.decoder.decode(substrate, asn1Spec=None)
diff --git a/docs/source/pyasn1/codec/native/contents.rst b/docs/source/pyasn1/codec/native/contents.rst
new file mode 100644
index 0000000..11e96f0
--- /dev/null
+++ b/docs/source/pyasn1/codec/native/contents.rst
@@ -0,0 +1,7 @@
+
+Native Python types
+-------------------
+
+.. autofunction:: pyasn1.codec.native.encoder.encode(asn1Value)
+
+.. autofunction:: pyasn1.codec.native.decoder.decode(pyObject, asn1Spec)
diff --git a/docs/source/pyasn1/contents.rst b/docs/source/pyasn1/contents.rst
new file mode 100644
index 0000000..102ea87
--- /dev/null
+++ b/docs/source/pyasn1/contents.rst
@@ -0,0 +1,214 @@
+
+.. _pyasn1:
+
+Library documentation
+=====================
+
+As of this moment, pyasn1 library implements all ASN.1 data
+types as Python objects in accordance with X.208 standard. Later,
+post-1995, revision (X.680) introduced some changes to the schema
+language which may not be fully supported by pyasn1. Aside from data
+types a collection of data transformation codecs comes with the
+pyasn1 package.
+
+As for ASN.1 schema language, pyasn1 package does
+not ship any compiler for it. However, there's a tool called
+`asn1late <https://github.com/kimgr/asn1ate>`_ which is an ASN.1
+grammar parser paired to code generator capable of generating pyasn1
+code. So this is an alternative (or at least a good start) to manual
+implementation of pyasn1 classes from ASN.1 specification.
+
+Both `pyasn1 <https://github.com/etingof/pyasn1>`_ and
+`pyasn1-modules <https://github.com/etingof/pyasn1-modules>`_ libraries
+can be used out-of-the-box with Python versions 2.4 through 3.6.
+No external dependencies required.
+
+.. _pyasn1-types:
+
+ASN.1 types
+-----------
+
+The ASN.1 data description
+`language <https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.208-198811-W!!PDF-E&type=items>`_
+defines a handful of built-in data types. ASN.1 types exhibit different
+semantics (e.g. number vs string) and can be distinguished from each other by
+:ref:`tags <type.tag>`.
+
+Subtypes can be created on top of base ASN.1 types by adding/overriding the
+:ref:`tags <type.tag>` and/or imposing additional
+:ref:`constraints <type.constraint>` on accepted values.
+
+ASN.1 types in pyasn1 are Python objects. One or more ASN.1 types
+comprise a *schema* describing data structures of unbounded complexity.
+
+.. code-block:: python
+
+   class RSAPublicKey(Sequence):
+       """
+       ASN.1 specification:
+
+       RSAPublicKey ::= SEQUENCE {
+           modulus           INTEGER,  -- n
+           publicExponent    INTEGER   -- e
+       }
+       """
+       componentType = NamedTypes(
+           NamedType('modulus', Integer()),
+           NamedType('publicExponent', Integer())
+       )
+
+ASN.1 schema can be "instantiated" by essentially putting some concrete value
+into the type container. Such instantiated schema object can still be
+used as a schema, but additionally it can play a role of a value in the
+context of any applicable operator (e.g. arithmetic etc.).
+
+.. code-block:: python
+
+   rsaPublicKey = RSAPublicKey()
+
+   # ASN.1 SEQUENCE type quacks like Python dict
+   rsaPublicKey['modulus'] = 280789907761334970323210643584308373
+   rsaPublicKey['publicExponent'] = 65537
+
+Main use of ASN.1 schemas is to guide data transformation. Instantiated
+ASN.1 schemas carry concrete data to/from data transformation services.
+
+.. _isValue:
+
+To tell instantiated schema object from just a schema, the *.isValue*
+property can come in handy:
+
+.. code-block:: python
+
+   schema = RSAPublicKey()
+
+   # non-instantiated schema
+   assert schema.isValue == False
+
+   rsaPublicKey['modulus'] = 280789907761334970323210643584308373
+
+   # partially instantiated schema
+   assert schema['modulus'].isValue == True
+   assert schema.isValue == False
+
+   rsaPublicKey['publicExponent'] = 65537
+
+   # fully instantiated schema
+   assert schema.isValue == True
+
+Copies of existing ASN.1 types can be created with *.clone()* method.
+All the existing properties of the prototype ASN.1 object get copied
+over the new type unless the replacements are given. Main use-case
+for *.clone()* is to instantiate a schema.
+
+.. _clone:
+
+.. code-block:: python
+
+   instantiated_schema_A = Integer(1)
+
+   # ASN.1 INTEGER type quacks like Python int
+   assert instantiated_schema_A == 1
+
+   instantiated_schema_B = instantiated_schema_A.clone(2)
+
+   assert instantiated_schema_B == 2
+
+.. _subtype:
+
+New ASN.1 types can be created on top of existing ASN.1 types with
+the *subtype()* method. Desired properties of the new type get
+merged with the corresponding properties of the old type. Main use-case
+for *.subtype()* is to assemble new ASN.1 types by :ref:`tagging <type.tag>`
+or applying additional :ref:`constraints <type.constraint>` to accepted
+type's values.
+
+.. code-block:: python
+
+   parent_type_schema = Integer()
+
+   child_type_schema = parent_type_schema.subtype(
+       explicitTag=Tag(tag.tagClassApplication, tag.tagFormatSimple, 0x06)
+   )
+
+   # test ASN.1 type relationships
+   assert child_type_schema.isSubtypeOf(parent_type_schema) == True
+   assert child_type_schema.isSameTypeWith(parent_type_schema) == False
+
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/univ/contents
+   /pyasn1/type/char/contents
+   /pyasn1/type/useful/contents
+
+ASN.1 type harness
+++++++++++++++++++
+
+The identification and behaviour of ASN.1 types is determined by
+:ref:`tags <type.tag>` and :ref:`constraints <type.constraint>`.
+The inner structure of *constructed* ASN.1 types is defined by
+its :ref:`fields <type.namedtype>` specification.
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/tag/contents
+   /pyasn1/type/constraint/contents
+   /pyasn1/type/namedtype/contents
+   /pyasn1/type/opentype/contents
+   /pyasn1/type/namedval/contents
+
+.. _pyasn1-codecs:
+
+Serialisation codecs
+--------------------
+
+Common use-case for pyasn1 is to instantiate ASN.1 schema with
+user-supplied values and pass instantiated schema to the encoder.
+The encoder will then turn the data structure into serialised form
+(stream of bytes) suitable for storing into a file or sending over
+the network.
+
+.. code-block:: python
+
+    value = 1
+    instantiated_schema = Integer(value)
+
+    serialised = encode(instantiated_schema)
+
+Alternatively, value and schema can be passed separately:
+
+.. code-block:: python
+
+    value = 1
+    schema = Integer()
+
+    serialised = encode(value, asn1Spec=schema)
+
+At the receiving end, a decoder would be invoked and given the
+serialised data as received from the network along with the ASN.1
+schema describing the layout of the data structures. The outcome
+would be an instance of ASN.1 schema filled with values as supplied
+by the sender.
+
+.. code-block:: python
+
+    serialised = b'\x01\x01\x01'
+    schema = Integer()
+
+    value, _ = decode(serialised, asn1Spec=schema)
+
+    assert value == 1
+
+Many distinct serialisation protocols exist for ASN.1, some are
+implemented in pyasn1.
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/codec/ber/contents
+   /pyasn1/codec/cer/contents
+   /pyasn1/codec/der/contents
+   /pyasn1/codec/native/contents
diff --git a/docs/source/pyasn1/type/char/bmpstring.rst b/docs/source/pyasn1/type/char/bmpstring.rst
new file mode 100644
index 0000000..066d03a
--- /dev/null
+++ b/docs/source/pyasn1/type/char/bmpstring.rst
@@ -0,0 +1,19 @@
+
+.. _char.BMPString:
+
+.. |ASN.1| replace:: BMPString
+
+.. |encoding| replace:: utf-16-be
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.BMPString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type models a Unicode (ISO10646-1) character string implicitly serialised into UTF-16 big endian.
+       
+   .. automethod:: pyasn1.type.char.BMPString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.BMPString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/contents.rst b/docs/source/pyasn1/type/char/contents.rst
new file mode 100644
index 0000000..e73c959
--- /dev/null
+++ b/docs/source/pyasn1/type/char/contents.rst
@@ -0,0 +1,30 @@
+
+.. _type.char:
+
+Character types
+---------------
+
+Besides :ref:`universal types <type.univ>` also defines a collection
+of text types. Most of these types come from the past trying to capture
+the fragments of long-forgotten technologies.
+
+These *character* types are all scalars. They are similar to
+:ref:`OctetString <univ.OctetString>` except that they all operate on
+text, not bytes.
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/char/numericstring
+   /pyasn1/type/char/printablestring
+   /pyasn1/type/char/teletexstring
+   /pyasn1/type/char/t61string
+   /pyasn1/type/char/videotexstring
+   /pyasn1/type/char/ia5string
+   /pyasn1/type/char/graphicstring
+   /pyasn1/type/char/visiblestring
+   /pyasn1/type/char/iso646string
+   /pyasn1/type/char/generalstring
+   /pyasn1/type/char/universalstring
+   /pyasn1/type/char/bmpstring
+   /pyasn1/type/char/utf8string
diff --git a/docs/source/pyasn1/type/char/generalstring.rst b/docs/source/pyasn1/type/char/generalstring.rst
new file mode 100644
index 0000000..722c062
--- /dev/null
+++ b/docs/source/pyasn1/type/char/generalstring.rst
@@ -0,0 +1,20 @@
+
+.. _char.GeneralString:
+
+.. |ASN.1| replace:: GeneralString
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.GeneralString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type models a character string similar to :py:class:`GraphicString` but additionally
+       including control characters.
+
+   .. automethod:: pyasn1.type.char.GeneralString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.GeneralString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/graphicstring.rst b/docs/source/pyasn1/type/char/graphicstring.rst
new file mode 100644
index 0000000..9a35a2a
--- /dev/null
+++ b/docs/source/pyasn1/type/char/graphicstring.rst
@@ -0,0 +1,20 @@
+
+.. _char.GraphicString:
+
+.. |ASN.1| replace:: GraphicString
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.GraphicString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type models a character string that can hold any "graphical" characters
+       mixed with control ones to select particular alphabet.
+
+   .. automethod:: pyasn1.type.char.GraphicString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.GraphicString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/ia5string.rst b/docs/source/pyasn1/type/char/ia5string.rst
new file mode 100644
index 0000000..6d057d8
--- /dev/null
+++ b/docs/source/pyasn1/type/char/ia5string.rst
@@ -0,0 +1,19 @@
+
+.. _char.IA5String:
+
+.. |ASN.1| replace:: IA5String
+
+.. |encoding| replace:: us-ascii
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.IA5String(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type models a basic character string first published in 1963 as an ISO/ITU standard, then it turned into ASCII.
+
+   .. automethod:: pyasn1.type.char.IA5String.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.IA5String.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/iso646string.rst b/docs/source/pyasn1/type/char/iso646string.rst
new file mode 100644
index 0000000..3607616
--- /dev/null
+++ b/docs/source/pyasn1/type/char/iso646string.rst
@@ -0,0 +1,19 @@
+
+.. _char.ISO646String:
+
+.. |ASN.1| replace:: ISO646String
+
+.. |encoding| replace:: us-ascii
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.ISO646String(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type is an alias to the :py:class:`VisibleString` type
+       
+   .. automethod:: pyasn1.type.char.ISO646String.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.ISO646String.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/numericstring.rst b/docs/source/pyasn1/type/char/numericstring.rst
new file mode 100644
index 0000000..2f3cfb4
--- /dev/null
+++ b/docs/source/pyasn1/type/char/numericstring.rst
@@ -0,0 +1,19 @@
+
+.. _char.NumericString:
+
+.. |ASN.1| replace:: NumericString
+
+.. |encoding| replace:: us-ascii
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.NumericString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| models character string that can be entered from a telephone handset.
+
+   .. automethod:: pyasn1.type.char.NumericString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.NumericString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/printablestring.rst b/docs/source/pyasn1/type/char/printablestring.rst
new file mode 100644
index 0000000..645c0a1
--- /dev/null
+++ b/docs/source/pyasn1/type/char/printablestring.rst
@@ -0,0 +1,20 @@
+
+.. _char.PrintableString:
+
+.. |ASN.1| replace:: PrintableString
+
+.. |encoding| replace:: us-ascii
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.PrintableString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| models character string that can be entered from a very rudimentary terminals featuring letters,
+       digits and punctuation marks.
+
+   .. automethod:: pyasn1.type.char.PrintableString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.PrintableString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/t61string.rst b/docs/source/pyasn1/type/char/t61string.rst
new file mode 100644
index 0000000..11bf384
--- /dev/null
+++ b/docs/source/pyasn1/type/char/t61string.rst
@@ -0,0 +1,19 @@
+
+.. _char.T61String:
+
+.. |ASN.1| replace:: T61String
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.T61String(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type is an alias to :py:class:`TeletexString` type.
+
+   .. automethod:: pyasn1.type.char.T61String.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.T61String.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/teletexstring.rst b/docs/source/pyasn1/type/char/teletexstring.rst
new file mode 100644
index 0000000..f025d76
--- /dev/null
+++ b/docs/source/pyasn1/type/char/teletexstring.rst
@@ -0,0 +1,21 @@
+
+.. _char.TeletexString:
+
+.. |ASN.1| replace:: TeletexString
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.TeletexString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type models character string that can be entered from a sophisticated text processing machines
+       (by 20-th century standards) featuring letters from multiple alphabets (308 characters!), digits,
+       punctuation marks and escape sequences.
+
+   .. automethod:: pyasn1.type.char.TeletexString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.TeletexString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/universalstring.rst b/docs/source/pyasn1/type/char/universalstring.rst
new file mode 100644
index 0000000..3c947b8
--- /dev/null
+++ b/docs/source/pyasn1/type/char/universalstring.rst
@@ -0,0 +1,19 @@
+
+.. _char.UniversalString:
+
+.. |ASN.1| replace:: UniversalString
+
+.. |encoding| replace:: utf-32-be
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.UniversalString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type models a Unicode (ISO10646-1) character string implicitly serialised into UTF-32 big endian.
+
+   .. automethod:: pyasn1.type.char.UniversalString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.UniversalString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/utf8string.rst b/docs/source/pyasn1/type/char/utf8string.rst
new file mode 100644
index 0000000..cf3a80f
--- /dev/null
+++ b/docs/source/pyasn1/type/char/utf8string.rst
@@ -0,0 +1,19 @@
+
+.. _char.UTF8String:
+
+.. |ASN.1| replace:: UTF8String
+
+.. |encoding| replace:: utf-8
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.UTF8String(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type models a Unicode (ISO10646-1) character string implicitly serialised into UTF-8.
+       
+   .. automethod:: pyasn1.type.char.UTF8String.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.UTF8String.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/videotexstring.rst b/docs/source/pyasn1/type/char/videotexstring.rst
new file mode 100644
index 0000000..27ddf80
--- /dev/null
+++ b/docs/source/pyasn1/type/char/videotexstring.rst
@@ -0,0 +1,20 @@
+
+.. _char.VideotexString:
+
+.. |ASN.1| replace:: VideotexString
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.VideotexString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type models character string that can be consumed by sophisticated video
+       terminals (by 20-th century standards) to render ascii-art style pictures and animations.
+
+   .. automethod:: pyasn1.type.char.VideotexString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.VideotexString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/char/visiblestring.rst b/docs/source/pyasn1/type/char/visiblestring.rst
new file mode 100644
index 0000000..778720a
--- /dev/null
+++ b/docs/source/pyasn1/type/char/visiblestring.rst
@@ -0,0 +1,20 @@
+
+.. _char.VisibleString:
+
+.. |ASN.1| replace:: VisibleString
+
+.. |encoding| replace:: us-ascii
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.char.VisibleString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
+
+   .. note::
+
+       The |ASN.1| type models a character string that can hold any "graphical" characters
+       mixed with control ones to select particular alphabet.
+       
+   .. automethod:: pyasn1.type.char.VisibleString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.char.VisibleString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/constraint/constraintsexclusion.rst b/docs/source/pyasn1/type/constraint/constraintsexclusion.rst
new file mode 100644
index 0000000..ad5cf8a
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/constraintsexclusion.rst
@@ -0,0 +1,10 @@
+
+.. _constrain.ConstraintsExclusion:
+
+.. |Constraint| replace:: ConstraintsExclusion
+
+Constraints exclusion
+---------------------
+
+.. autoclass:: pyasn1.type.constraint.ConstraintsExclusion(constraint)
+   :members:
diff --git a/docs/source/pyasn1/type/constraint/constraintsintersection.rst b/docs/source/pyasn1/type/constraint/constraintsintersection.rst
new file mode 100644
index 0000000..15465b8
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/constraintsintersection.rst
@@ -0,0 +1,10 @@
+
+.. _constrain.ConstraintsIntersection:
+
+.. |Constraint| replace:: ConstraintsIntersection
+
+Constraints intersection
+------------------------
+
+.. autoclass:: pyasn1.type.constraint.ConstraintsIntersection(*constraints)
+   :members:
diff --git a/docs/source/pyasn1/type/constraint/constraintsunion.rst b/docs/source/pyasn1/type/constraint/constraintsunion.rst
new file mode 100644
index 0000000..d42f3d9
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/constraintsunion.rst
@@ -0,0 +1,10 @@
+
+.. _constrain.ConstraintsUnion:
+
+.. |Constraint| replace:: ConstraintsUnion
+
+Constraints union
+-----------------
+
+.. autoclass:: pyasn1.type.constraint.ConstraintsUnion(*constraints)
+   :members:
diff --git a/docs/source/pyasn1/type/constraint/containedsubtype.rst b/docs/source/pyasn1/type/constraint/containedsubtype.rst
new file mode 100644
index 0000000..39fc3a1
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/containedsubtype.rst
@@ -0,0 +1,10 @@
+
+.. _constrain.ContainedSubtypeConstraint:
+
+.. |Constraint| replace:: ContainedSubtypeConstraint
+
+Contained subtype constraint
+----------------------------
+
+.. autoclass:: pyasn1.type.constraint.ContainedSubtypeConstraint
+   :members:
diff --git a/docs/source/pyasn1/type/constraint/contents.rst b/docs/source/pyasn1/type/constraint/contents.rst
new file mode 100644
index 0000000..8e4db7c
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/contents.rst
@@ -0,0 +1,67 @@
+
+.. _type.constraint:
+
+Constraints
+-----------
+
+ASN.1 standard has a built-in way of limiting the set of values
+a type can possibly have. Imposing value constraints on an ASN.1
+type, together with :ref:`tagging <type.tag>`, is a way of creating
+a more specialized subtype of an ASN.1 type.
+
+The pyasn1 implementation represents all flavors of constraints,
+as well as their combinations, as immutable Python objects. Ultimately,
+they get attached to ASN.1 type object at a *.subtypeSpec* attribute.
+
+.. code-block:: python
+
+   class Age(Integer):
+       """
+       ASN.1 specification:
+
+       Age ::= INTEGER (0..120)
+       """
+       subtypeSpec = ValueRangeConstraint(0, 120)
+
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/constraint/singlevalue
+   /pyasn1/type/constraint/containedsubtype
+   /pyasn1/type/constraint/valuerange
+   /pyasn1/type/constraint/valuesize
+   /pyasn1/type/constraint/permittedalphabet
+
+
+Logic operations on constraints
++++++++++++++++++++++++++++++++
+
+Sometimes multiple constraints are applied on an ASN.1 type. To capture
+this situation, individual constraint objects can be glued together
+by the logic operator objects.
+
+The logic operators are Python objects that exhibit similar behaviour
+as the constraint objects do with the only difference that logic operators
+are instantiated on the constraint and logic operator objects, not on the
+bare values.
+
+.. code-block:: python
+
+   class PhoneNumber(NumericString):
+       """
+       ASN.1 specification:
+
+       PhoneNumber ::=
+           NumericString (FROM ("0".."9")) (SIZE (10))
+       """
+       subtypeSpec = ConstraintsIntersection(
+           ValueRangeConstraint('0', '9'), ValueSizeConstraint(10)
+       )
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/constraint/constraintsintersection
+   /pyasn1/type/constraint/constraintsunion
+   /pyasn1/type/constraint/constraintsexclusion
diff --git a/docs/source/pyasn1/type/constraint/permittedalphabet.rst b/docs/source/pyasn1/type/constraint/permittedalphabet.rst
new file mode 100644
index 0000000..86d08c4
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/permittedalphabet.rst
@@ -0,0 +1,10 @@
+
+.. _constrain.PermittedAlphabetConstraint:
+
+.. |Constraint| replace:: PermittedAlphabetConstraint
+
+Permitted alphabet constraint
+-----------------------------
+
+.. autoclass:: pyasn1.type.constraint.PermittedAlphabetConstraint(*alphabet)
+   :members:
diff --git a/docs/source/pyasn1/type/constraint/singlevalue.rst b/docs/source/pyasn1/type/constraint/singlevalue.rst
new file mode 100644
index 0000000..26f2424
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/singlevalue.rst
@@ -0,0 +1,10 @@
+
+.. _constrain.SingleValueConstraint:
+
+.. |Constraint| replace:: SingleValueConstraint
+
+Single value constraint
+-----------------------
+
+.. autoclass:: pyasn1.type.constraint.SingleValueConstraint
+   :members:
diff --git a/docs/source/pyasn1/type/constraint/valuerange.rst b/docs/source/pyasn1/type/constraint/valuerange.rst
new file mode 100644
index 0000000..a3a91ac
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/valuerange.rst
@@ -0,0 +1,10 @@
+
+.. _constrain.ValueRangeConstraint:
+
+.. |Constraint| replace:: ValueRangeConstraint
+
+Value range constraint
+----------------------
+
+.. autoclass:: pyasn1.type.constraint.ValueRangeConstraint(start, end)
+   :members:
diff --git a/docs/source/pyasn1/type/constraint/valuesize.rst b/docs/source/pyasn1/type/constraint/valuesize.rst
new file mode 100644
index 0000000..62444df
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/valuesize.rst
@@ -0,0 +1,10 @@
+
+.. _constrain.ValueSizeConstraint:
+
+.. |Constraint| replace:: ValueSizeConstraint
+
+Value size constraint
+----------------------
+
+.. autoclass:: pyasn1.type.constraint.ValueSizeConstraint(minimum, maximum)
+   :members:
diff --git a/docs/source/pyasn1/type/namedtype/contents.rst b/docs/source/pyasn1/type/namedtype/contents.rst
new file mode 100644
index 0000000..2e553e6
--- /dev/null
+++ b/docs/source/pyasn1/type/namedtype/contents.rst
@@ -0,0 +1,40 @@
+
+.. _type.namedtype:
+
+Fields of constructed types
+---------------------------
+
+The :ref:`Sequence <univ.Sequence>`, :ref:`Set <univ.Set>` and
+:ref:`Choice <univ.Choice>` ASN.1 types embed other ASN.1 types
+as named fields.
+
+Each field can be expressed via the :ref:`NamedType <namedtype.NamedType>`
+object while the individual fields are brought together by the
+:ref:`NamedTypes <namedtype.NamedTypes>` object.
+
+Ultimately, the fields get attached to the ASN.1 type's *.componentType*
+attributes.
+
+.. code-block:: python
+
+   class RSAPublicKey(Sequence):
+       """
+       ASN.1 specification:
+
+       RSAPublicKey ::= SEQUENCE {
+           modulus           INTEGER,  -- n
+           publicExponent    INTEGER   -- e
+       }
+       """
+       componentType = NamedTypes(
+           NamedType('modulus', Integer()),
+           NamedType('publicExponent', Integer())
+       )
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/namedtype/namedtype
+   /pyasn1/type/namedtype/optionalnamedtype
+   /pyasn1/type/namedtype/defaultednamedtype
+   /pyasn1/type/namedtype/namedtypes
diff --git a/docs/source/pyasn1/type/namedtype/defaultednamedtype.rst b/docs/source/pyasn1/type/namedtype/defaultednamedtype.rst
new file mode 100644
index 0000000..f203d08
--- /dev/null
+++ b/docs/source/pyasn1/type/namedtype/defaultednamedtype.rst
@@ -0,0 +1,20 @@
+
+.. _namedtype.DefaultedNamedType:
+
+.. |NamedType| replace:: DefaultedNamedType
+
+DefaultedNamedType
+------------------
+
+.. autoclass:: pyasn1.type.namedtype.DefaultedNamedType
+   :members:
+
+   .. note::
+
+        The *DefaultedNamedType* class models named field of a constructed
+        ASN.1 type which has a default value.
+
+        The *DefaultedNamedType* objects are normally utilized
+        by the :ref:`NamedTypes <namedtype.NamedTypes>` objects
+        to model individual fields of the constructed ASN.1
+        types.
diff --git a/docs/source/pyasn1/type/namedtype/namedtype.rst b/docs/source/pyasn1/type/namedtype/namedtype.rst
new file mode 100644
index 0000000..7429ffa
--- /dev/null
+++ b/docs/source/pyasn1/type/namedtype/namedtype.rst
@@ -0,0 +1,20 @@
+
+.. _namedtype.NamedType:
+
+.. |NamedType| replace:: NamedType
+
+NamedType
+---------
+
+.. autoclass:: pyasn1.type.namedtype.NamedType
+   :members:
+
+   .. note::
+
+        The *NamedType* class models a mandatory field of a constructed
+        ASN.1 type.
+
+        The *NamedType* objects are normally utilized by the
+        the :ref:`NamedTypes <namedtype.NamedTypes>` objects
+        to model individual fields of the constructed ASN.1
+        types.
diff --git a/docs/source/pyasn1/type/namedtype/namedtypes.rst b/docs/source/pyasn1/type/namedtype/namedtypes.rst
new file mode 100644
index 0000000..2e2bb9d
--- /dev/null
+++ b/docs/source/pyasn1/type/namedtype/namedtypes.rst
@@ -0,0 +1,15 @@
+
+.. _namedtype.NamedTypes:
+
+NamedTypes
+----------
+
+.. autoclass:: pyasn1.type.namedtype.NamedTypes
+   :members:
+
+   .. note::
+
+        The *NamedTypes* objects are normally utilized by the
+        constructed ASN.1 types (e.g. :ref:`Sequence <univ.Sequence>`,
+        :ref:`Set <univ.Set>` and :ref:`Choice <univ.Choice>`) to model
+        the set of fields of those types.
diff --git a/docs/source/pyasn1/type/namedtype/optionalnamedtype.rst b/docs/source/pyasn1/type/namedtype/optionalnamedtype.rst
new file mode 100644
index 0000000..2095b05
--- /dev/null
+++ b/docs/source/pyasn1/type/namedtype/optionalnamedtype.rst
@@ -0,0 +1,20 @@
+
+.. _namedtype.OptionalNamedType:
+
+.. |NamedType| replace:: OptionalNamedType
+
+OptionalNamedType
+-----------------
+
+.. autoclass:: pyasn1.type.namedtype.OptionalNamedType
+   :members:
+
+   .. note::
+
+        The *OptionalNamedType* class models an optional field of
+        a constructed ASN.1 type.
+
+        The *OptionalNamedType* objects are normally utilized by
+        the :ref:`NamedTypes <namedtype.NamedTypes>` objects
+        to model individual fields of the constructed ASN.1
+        types.
diff --git a/docs/source/pyasn1/type/namedval/contents.rst b/docs/source/pyasn1/type/namedval/contents.rst
new file mode 100644
index 0000000..73f86c7
--- /dev/null
+++ b/docs/source/pyasn1/type/namedval/contents.rst
@@ -0,0 +1,43 @@
+
+.. _type.namedval:
+
+Enumerating numbers
+-------------------
+
+Some ASN.1 types such as :ref:`Integer <univ.Integer>`,
+:ref:`Enumerated <univ.Enumerated>` and :ref:`BitString <univ.BitString>`
+may enumerate their otherwise numeric values associating them with
+human-friendly labels.
+
+.. code-block:: python
+
+   class ErrorStatus(Integer):
+   """
+   ASN.1 specification:
+
+   error-status
+                 INTEGER {
+                     noError(0),
+                     tooBig(1),
+                     noSuchName(2),
+                     ...
+                  }
+   """
+   namedValues = NamedValues(
+       ('noError', 0), ('tooBig', 1), ('noSuchName', 2)
+   )
+
+The enumerated types behave exactly like the non-enumerated ones but,
+additionally, values can be referred by labels.
+
+.. code-block:: python
+
+   errorStatus = ErrorStatus('tooBig')
+
+   assert errorStatus == 1
+
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/namedval/namedval
diff --git a/docs/source/pyasn1/type/namedval/namedval.rst b/docs/source/pyasn1/type/namedval/namedval.rst
new file mode 100644
index 0000000..0bcfd69
--- /dev/null
+++ b/docs/source/pyasn1/type/namedval/namedval.rst
@@ -0,0 +1,13 @@
+
+.. _namedval.NamedValues:
+
+.. |NamedValues| replace:: NamedValues
+
+|NamedValues|
+-------------
+
+The |NamedValues| class associates human-friendly names to a set of numbers
+or bits.
+
+.. autoclass:: pyasn1.type.namedval.NamedValues
+   :members:
diff --git a/docs/source/pyasn1/type/opentype/contents.rst b/docs/source/pyasn1/type/opentype/contents.rst
new file mode 100644
index 0000000..9ae10d0
--- /dev/null
+++ b/docs/source/pyasn1/type/opentype/contents.rst
@@ -0,0 +1,40 @@
+
+.. _type.opentype:
+
+Untyped fields of constructed types
+-----------------------------------
+
+To aid data structures flexibility, ASN.1 allows the designer to
+leave incomplete field type specification in the
+:ref:`Sequence <univ.Sequence>` and :ref:`Set <univ.Set>` types.
+
+To figure out field's type at the run time, a type selector field
+must accompany the open type field. The open type situation can
+be captured by the :ref:`OpenType <opentype.OpenType>` object.
+
+.. code-block:: python
+
+   algo_map = {
+       ObjectIdentifier('1.2.840.113549.1.1.1'): rsaEncryption(),
+       ObjectIdentifier('1.2.840.113549.1.1.2'): md2WithRSAEncryption()
+   }
+
+
+   class Algorithm(Sequence):
+       """
+       Algorithm ::= SEQUENCE {
+               algorithm OBJECT IDENTIFIER,
+               parameters ANY DEFINED BY algorithm OPTIONAL
+       }
+       """
+       componentType = NamedTypes(
+           NamedType('algorithm', ObjectIdentifier()),
+           OptionalNamedType('parameters', Any(),
+                     openType=OpenType('algorithm', algo_map))
+       )
+
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/opentype/opentype
diff --git a/docs/source/pyasn1/type/opentype/opentype.rst b/docs/source/pyasn1/type/opentype/opentype.rst
new file mode 100644
index 0000000..dc2fa47
--- /dev/null
+++ b/docs/source/pyasn1/type/opentype/opentype.rst
@@ -0,0 +1,17 @@
+
+.. _opentype.OpenType:
+
+.. |OpenType| replace:: OpenType
+
+|OpenType|
+-----------
+
+.. autoclass:: pyasn1.type.opentype.OpenType
+   :members:
+
+   .. note::
+
+        The |OpenType| class models an untyped field of a constructed ASN.1
+        type. In ASN.1 syntax it is usually represented by the
+        `ANY DEFINED BY` clause. Typically used with :ref:`Any <univ.Any>`
+        type.
diff --git a/docs/source/pyasn1/type/tag/contents.rst b/docs/source/pyasn1/type/tag/contents.rst
new file mode 100644
index 0000000..4e5510c
--- /dev/null
+++ b/docs/source/pyasn1/type/tag/contents.rst
@@ -0,0 +1,54 @@
+
+.. _type.tag:
+
+Tags
+----
+
+ASN.1 types formally differ from each other by carrying distinct
+tags. A tag is essentially an integer exhibiting certain inner
+structure.
+
+Individual tags are usually combined into a collection known as
+*TagSet*. Tags and tag sets in pyasn1 are immutable objects assigned
+to ASN.1 types as the *tagSet* attribute.
+
+Tags can be appended to one another (in EXPLICIT tagging mode)
+or overridden (IMPLICIT tagging mode) ultimately creating a new
+ASN.1 subtype.
+
+.. code-block:: python
+
+    class Counter64(Integer):
+       """
+       ASN.1 specification:
+
+       Counter64 ::=
+           [APPLICATION 6]
+               IMPLICIT INTEGER
+       """
+       tagSet = Integer.tagSet.tagImplicitly(
+           Tag(tagClassApplication, tagFormatSimple, 6)
+       )
+
+    # alternatively
+    counter64 = Integer().subtype(
+        implicitTag=Tag(tagClassApplication, tagFormatSimple, 6)
+    )
+
+ASN.1 types can be related to each other via the *.isSameTypeWith()*,
+*.isSuperTypeOf()* and *.isSubTypeOf()* methods. Internally, the *.tagSet*
+of the types are compared along with the value constraints
+(e.g. *.subtypeSpec*).
+
+.. code-block:: python
+
+    assert Counter64().isSubTypeOf(Integer()) == True
+    assert Counter64().isSameTypeWith(Integer()) == False
+
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/tag/tag
+   /pyasn1/type/tag/tagset
+   /pyasn1/type/tag/tagmap
diff --git a/docs/source/pyasn1/type/tag/tag.rst b/docs/source/pyasn1/type/tag/tag.rst
new file mode 100644
index 0000000..3aeb670
--- /dev/null
+++ b/docs/source/pyasn1/type/tag/tag.rst
@@ -0,0 +1,18 @@
+
+.. _tag.tag:
+
+Solitary tag
+------------
+
+.. automodule:: pyasn1.type.tag
+   :members: tagClassUniversal, tagClassApplication, tagClassContext,
+             tagClassPrivate, tagFormatSimple, tagFormatConstructed
+
+.. autoclass:: pyasn1.type.tag.Tag
+   :members:
+
+   .. note::
+
+       The *Tag* objects are normally used by the
+       :ref:`TagSet <tag.TagSet>`, objects to model a collection
+       of ASN.1 tags.
diff --git a/docs/source/pyasn1/type/tag/tagmap.rst b/docs/source/pyasn1/type/tag/tagmap.rst
new file mode 100644
index 0000000..05b8404
--- /dev/null
+++ b/docs/source/pyasn1/type/tag/tagmap.rst
@@ -0,0 +1,14 @@
+
+.. _tag.tagmap:
+
+Tag->type map
+-------------
+
+.. autoclass:: pyasn1.type.tagmap.TagMap
+   :members:
+
+   .. note::
+
+       The *TagMap* objects are used by the
+       :ref:`TagSet <tag.TagSet>`, objects for looking up components
+       of constructed ASN.1 types by :ref:`Tag <tag.Tag>`.
diff --git a/docs/source/pyasn1/type/tag/tagset.rst b/docs/source/pyasn1/type/tag/tagset.rst
new file mode 100644
index 0000000..c4dce2e
--- /dev/null
+++ b/docs/source/pyasn1/type/tag/tagset.rst
@@ -0,0 +1,14 @@
+
+.. _tag.tagset:
+
+Tag set
+-------
+
+.. autoclass:: pyasn1.type.tag.TagSet
+   :members:
+
+   .. note::
+
+       The *TagSet* objects are normally used by all ASN.1 type
+       objects both simple (like :ref:`Integer <univ.Integer>`)
+       and constructed (e.g. :ref:`Sequence <univ.Sequence>`).
diff --git a/docs/source/pyasn1/type/univ/any.rst b/docs/source/pyasn1/type/univ/any.rst
new file mode 100644
index 0000000..56e593e
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/any.rst
@@ -0,0 +1,22 @@
+
+.. _univ.Any:
+
+.. |ASN.1| replace:: Any
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.Any(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='iso-8859-1', binValue=NoValue(),hexValue=NoValue())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec
+
+   .. note::
+
+       The |ASN.1| type models an arbitrary value of an arbitrary type. Typically,
+       a selection of types are defined in form of an
+       :ref:`open type <opentype.OpenType>` . Technically, the ANY
+       value is a serialised representation of some other ASN.1 object.
+
+   .. automethod:: pyasn1.type.univ.Any.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='iso-8859-1')
+   .. automethod:: pyasn1.type.univ.Any.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(),encoding='iso-8859-1')
diff --git a/docs/source/pyasn1/type/univ/bitstring.rst b/docs/source/pyasn1/type/univ/bitstring.rst
new file mode 100644
index 0000000..7d57774
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/bitstring.rst
@@ -0,0 +1,17 @@
+
+.. _univ.BitString:
+
+.. |ASN.1| replace:: BitString
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.BitString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues(),binValue=NoValue(), hexValue=NoValue())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, asInteger, asNumbers, asOctets, asBinary, fromHexString, fromBinaryString, fromOctetString
+
+   .. note::
+
+        The |ASN.1| type models an arbitrary sequence of bits.
+
+   .. automethod:: pyasn1.type.univ.BitString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues(),binValue=NoValue(), hexValue=NoValue())
+   .. automethod:: pyasn1.type.univ.BitString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), , namedValues=NamedValues(),binValue=NoValue(), hexValue=NoValue())
diff --git a/docs/source/pyasn1/type/univ/boolean.rst b/docs/source/pyasn1/type/univ/boolean.rst
new file mode 100644
index 0000000..ab4021a
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/boolean.rst
@@ -0,0 +1,17 @@
+
+.. _univ.Boolean:
+
+.. |ASN.1| replace:: Boolean
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.Boolean(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec
+
+   .. note::
+
+       The |ASN.1| type models a BOOLEAN that can be either TRUE or FALSE.
+
+   .. automethod:: pyasn1.type.univ.Boolean.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
+   .. automethod:: pyasn1.type.univ.Boolean.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(), subtypeSpec=ConstraintsIntersection())
diff --git a/docs/source/pyasn1/type/univ/choice.rst b/docs/source/pyasn1/type/univ/choice.rst
new file mode 100644
index 0000000..9731971
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/choice.rst
@@ -0,0 +1,19 @@
+
+.. _univ.Choice:
+
+.. |ASN.1| replace:: Choice
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.Choice(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
+             getComponentByPosition, setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents,
+             getComponentByType, setComponentByType, getName, getComponent
+
+   .. note::
+
+        The |ASN.1| type can only hold a single component at a time belonging to the list of allowed types.
+
+   .. automethod:: pyasn1.type.univ.Choice.clone(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection())
+   .. automethod:: pyasn1.type.univ.Choice.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())
diff --git a/docs/source/pyasn1/type/univ/contents.rst b/docs/source/pyasn1/type/univ/contents.rst
new file mode 100644
index 0000000..2a5ba25
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/contents.rst
@@ -0,0 +1,38 @@
+
+.. _type.univ:
+
+Universal types
+---------------
+
+The ASN.1 language defines a collection of core data types
+also known as *universal* types.
+
+Some of these types behave like a scalar (e.g. *simple* types) while
+the rest are structured types (the standard calls them *constructed*).
+
+Example of simple types include :ref:`Integer <univ.Integer>` or
+:ref:`OctetString <univ.OctetString>`. Constructed types like
+:ref:`Sequence <univ.Sequence>` embed other types, both simple
+and constructed.
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/univ/integer
+   /pyasn1/type/univ/boolean
+   /pyasn1/type/univ/bitstring
+   /pyasn1/type/univ/octetstring
+   /pyasn1/type/univ/null
+   /pyasn1/type/univ/objectidentifier
+   /pyasn1/type/univ/real
+   /pyasn1/type/univ/enumerated
+   /pyasn1/type/univ/any
+   /pyasn1/type/univ/setof
+   /pyasn1/type/univ/sequenceof
+   /pyasn1/type/univ/set
+   /pyasn1/type/univ/sequence
+   /pyasn1/type/univ/choice
+
+.. _univ.noValue:
+
+.. autoclass:: pyasn1.type.univ.NoValue()
diff --git a/docs/source/pyasn1/type/univ/enumerated.rst b/docs/source/pyasn1/type/univ/enumerated.rst
new file mode 100644
index 0000000..ab225ae
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/enumerated.rst
@@ -0,0 +1,18 @@
+
+.. _univ.Enumerated:
+
+.. |ASN.1| replace:: Enumerated
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.Enumerated(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, namedValues
+
+   .. note::
+
+        The |ASN.1| type models bounded set of named integer values. Other than that, it is identical to
+        the *Integer* class.
+
+   .. automethod:: pyasn1.type.univ.Enumerated.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
+   .. automethod:: pyasn1.type.univ.Enumerated.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
diff --git a/docs/source/pyasn1/type/univ/integer.rst b/docs/source/pyasn1/type/univ/integer.rst
new file mode 100644
index 0000000..4dcfabb
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/integer.rst
@@ -0,0 +1,19 @@
+
+.. _univ.Integer:
+
+.. |ASN.1| replace:: Integer
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.Integer(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap,
+             subtypeSpec, namedValues
+
+   .. note::
+
+        The |ASN.1| type models an arbitrary integer. INTEGER values can be positive, negative,
+        or zero, and can have any magnitude.
+       
+   .. automethod:: pyasn1.type.univ.Integer.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
+   .. automethod:: pyasn1.type.univ.Integer.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
diff --git a/docs/source/pyasn1/type/univ/null.rst b/docs/source/pyasn1/type/univ/null.rst
new file mode 100644
index 0000000..5553fcc
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/null.rst
@@ -0,0 +1,17 @@
+
+.. _univ.Null:
+
+.. |ASN.1| replace:: Null
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.Null(value=NoValue(), tagSet=TagSet())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec
+
+   .. note::
+
+       The |ASN.1| type models ASN.1 NULL.
+
+   .. automethod:: pyasn1.type.univ.Null.clone(value=NoValue(), tagSet=TagSet())
+   .. automethod:: pyasn1.type.univ.Null.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag())
diff --git a/docs/source/pyasn1/type/univ/objectidentifier.rst b/docs/source/pyasn1/type/univ/objectidentifier.rst
new file mode 100644
index 0000000..cc4ae0b
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/objectidentifier.rst
@@ -0,0 +1,17 @@
+
+.. _univ.ObjectIdentifier:
+
+.. |ASN.1| replace:: ObjectIdentifier
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.ObjectIdentifier(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, isPrefixOf
+
+   .. note::
+
+        The |ASN.1| type models ASN.1 OBJECT IDENTIFIER as a sequence of integer numbers.
+
+   .. automethod:: pyasn1.type.univ.ObjectIdentifier.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
+   .. automethod:: pyasn1.type.univ.ObjectIdentifier.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(), subtypeSpec=ConstraintsIntersection())
diff --git a/docs/source/pyasn1/type/univ/octetstring.rst b/docs/source/pyasn1/type/univ/octetstring.rst
new file mode 100644
index 0000000..a984a34
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/octetstring.rst
@@ -0,0 +1,19 @@
+
+.. _univ.OctetString:
+
+.. |ASN.1| replace:: OctetString
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.OctetString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='iso-8859-1', binValue=NoValue(),hexValue=NoValue())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, fromHexString, fromBinaryString
+
+   .. note::
+
+        The |ASN.1| type models an arbitrary string of octets (eight-bit numbers), not printable text string.
+
+   .. automethod:: pyasn1.type.univ.OctetString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='iso-8859-1')
+   .. automethod:: pyasn1.type.univ.OctetString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(),encoding='iso-8859-1')
diff --git a/docs/source/pyasn1/type/univ/real.rst b/docs/source/pyasn1/type/univ/real.rst
new file mode 100644
index 0000000..31ea07b
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/real.rst
@@ -0,0 +1,17 @@
+
+.. _univ.Real:
+
+.. |ASN.1| replace:: Real
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.Real(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, isInf, isPlusInf, isMinusInf
+
+   .. note::
+
+       The |ASN.1| type models a rational number of arbitrary precision.
+
+   .. automethod:: pyasn1.type.univ.Real.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
+   .. automethod:: pyasn1.type.univ.Real.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(), subtypeSpec=ConstraintsIntersection())
diff --git a/docs/source/pyasn1/type/univ/sequence.rst b/docs/source/pyasn1/type/univ/sequence.rst
new file mode 100644
index 0000000..6d2c6b5
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/sequence.rst
@@ -0,0 +1,19 @@
+
+.. _univ.Sequence:
+
+.. |ASN.1| replace:: Sequence
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.Sequence(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec, getComponentByPosition,
+             setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents
+
+   .. note::
+
+        The |ASN.1| type models a collection of named ASN.1 components.
+        Ordering of the components **is** preserved upon de/serialisation.
+
+   .. automethod:: pyasn1.type.univ.Sequence.clone(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection())
+   .. automethod:: pyasn1.type.univ.Sequence.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())
diff --git a/docs/source/pyasn1/type/univ/sequenceof.rst b/docs/source/pyasn1/type/univ/sequenceof.rst
new file mode 100644
index 0000000..dadef67
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/sequenceof.rst
@@ -0,0 +1,19 @@
+
+.. _univ.SequenceOf:
+
+.. |ASN.1| replace:: SequenceOf
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.SequenceOf(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
+             getComponentByPosition, setComponentByPosition
+
+   .. note::
+
+       The |ASN.1| type models a collection of elements of a single ASN.1 type.
+       Ordering of the components **is** preserved upon de/serialisation.
+        
+   .. automethod:: pyasn1.type.univ.SequenceOf.clone(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
+   .. automethod:: pyasn1.type.univ.SequenceOf.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())
diff --git a/docs/source/pyasn1/type/univ/set.rst b/docs/source/pyasn1/type/univ/set.rst
new file mode 100644
index 0000000..5c75938
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/set.rst
@@ -0,0 +1,20 @@
+
+.. _univ.Set:
+
+.. |ASN.1| replace:: Set
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.Set(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
+             getComponentByPosition, setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents,
+             getComponentByType, setComponentByType
+
+   .. note::
+
+        The |ASN.1| type models a collection of named ASN.1 components.
+        Ordering of the components **is not** preserved upon de/serialisation.
+
+   .. automethod:: pyasn1.type.univ.Set.clone(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
+   .. automethod:: pyasn1.type.univ.Set.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())
diff --git a/docs/source/pyasn1/type/univ/setof.rst b/docs/source/pyasn1/type/univ/setof.rst
new file mode 100644
index 0000000..0317f4a
--- /dev/null
+++ b/docs/source/pyasn1/type/univ/setof.rst
@@ -0,0 +1,19 @@
+
+.. _univ.SetOf:
+
+.. |ASN.1| replace:: SetOf
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.univ.SetOf(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
+             getComponentByPosition, setComponentByPosition
+
+   .. note::
+
+        The |ASN.1| type models a collection of elements of a single ASN.1 type.
+        Ordering of the components **is not** preserved upon de/serialisation.
+
+   .. automethod:: pyasn1.type.univ.SetOf.clone(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
+   .. automethod:: pyasn1.type.univ.SetOf.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())
diff --git a/docs/source/pyasn1/type/useful/contents.rst b/docs/source/pyasn1/type/useful/contents.rst
new file mode 100644
index 0000000..2e4ae7b
--- /dev/null
+++ b/docs/source/pyasn1/type/useful/contents.rst
@@ -0,0 +1,15 @@
+
+.. _type.useful:
+
+Useful types
+------------
+
+Some assorted utility ASN.1 types belong to the *useful* group.
+These types are all scalar.
+
+.. toctree::
+   :maxdepth: 2
+
+   /pyasn1/type/useful/objectdescriptor
+   /pyasn1/type/useful/generalizedtime
+   /pyasn1/type/useful/utctime
diff --git a/docs/source/pyasn1/type/useful/generalizedtime.rst b/docs/source/pyasn1/type/useful/generalizedtime.rst
new file mode 100644
index 0000000..d1f5b85
--- /dev/null
+++ b/docs/source/pyasn1/type/useful/generalizedtime.rst
@@ -0,0 +1,37 @@
+
+.. _useful.GeneralizedTime:
+
+.. |ASN.1| replace:: GeneralizedTime
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.useful.GeneralizedTime(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, asDateTime, fromDateTime
+
+   .. note::
+
+
+       The |ASN.1| type models a character string representing date and time
+       in many different formats.
+
+       Formal syntax for the *GeneralizedTime* value is:
+
+       * **YYYYMMDDhh[mm[ss[(.|,)ffff]]]** standing for a local time, four
+         digits for the year, two for the month, two for the day and two
+         for the hour, followed by two digits for the minutes and two
+         for the seconds if required, then a dot (or a comma), and a
+         number for the fractions of second or
+
+       * a string as above followed by the letter “Z” (denoting a UTC
+         time) or
+
+       * a string as above followed by a string **(+|-)hh[mm]** denoting
+         time zone offset relative to UTC
+
+       For example, *20170126120000Z* stands for YYYYMMDDHHMMSSZ.
+
+   .. automethod:: pyasn1.type.useful.GeneralizedTime.clone(self, value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.useful.GeneralizedTime.subtype(self, value=NoValue(), implicitTag=TagSet(), explicitTag=TagSet(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/useful/objectdescriptor.rst b/docs/source/pyasn1/type/useful/objectdescriptor.rst
new file mode 100644
index 0000000..f7bf4f6
--- /dev/null
+++ b/docs/source/pyasn1/type/useful/objectdescriptor.rst
@@ -0,0 +1,20 @@
+
+.. _useful.ObjectDescriptor:
+
+.. |ASN.1| replace:: ObjectDescriptor
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.useful.ObjectDescriptor(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet
+
+   .. note::
+
+       The |ASN.1| type models a character string that can accompany the *ObjectIdentifier* type
+       to serve as a human-friendly annotation for an OBJECT IDENTIFIER.
+
+   .. automethod:: pyasn1.type.useful.ObjectDescriptor.clone(self, value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.useful.ObjectDescriptor.subtype(self, value=NoValue(), implicitTag=TagSet(), explicitTag=TagSet(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/docs/source/pyasn1/type/useful/utctime.rst b/docs/source/pyasn1/type/useful/utctime.rst
new file mode 100644
index 0000000..7bdfa8a
--- /dev/null
+++ b/docs/source/pyasn1/type/useful/utctime.rst
@@ -0,0 +1,34 @@
+
+.. _useful.UTCTime:
+
+.. |ASN.1| replace:: UTCTime
+
+.. |encoding| replace:: iso-8859-1
+
+|ASN.1| type
+------------
+
+.. autoclass:: pyasn1.type.useful.UTCTime(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, asDateTime, fromDateTime
+
+   .. note::
+
+       The |ASN.1| type models a character string representing date and time.
+
+       Formal syntax for the *UTCTime* value is:
+
+       * **YYMMDDhhmm[ss]** standing for UTC time, two
+         digits for the year, two for the month, two for the day and two
+         for the hour, followed by two digits for the minutes and two
+         for the seconds if required or
+
+       * a string as above followed by the letter “Z” (denoting a UTC
+         time) or
+
+       * a string as above followed by a string **(+|-)hhmm** denoting
+         time zone offset relative to UTC
+
+       For example, *170126120000Z* which stands for YYMMDDHHMMSSZ.
+
+   .. automethod:: pyasn1.type.useful.UTCTime.clone(self, value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
+   .. automethod:: pyasn1.type.useful.UTCTime.subtype(self, value=NoValue(), implicitTag=TagSet(), explicitTag=TagSet(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/pyasn1/Android.bp b/pyasn1/Android.bp
new file mode 100644
index 0000000..b46e8f2
--- /dev/null
+++ b/pyasn1/Android.bp
@@ -0,0 +1,36 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// 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.
+python_library {
+    name: "py-pyasn1",
+    host_supported: true,
+    srcs: [
+        "*.py",
+        "codec/*.py",
+        "codec/ber/*.py",
+        "codec/cer/*.py",
+        "codec/der/*.py",
+        "codec/native/*.py",
+        "compat/*.py",
+        "type/*.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+    pkg_path: "pyasn1",
+}
diff --git a/pyasn1/__init__.py b/pyasn1/__init__.py
new file mode 100644
index 0000000..71bb22f
--- /dev/null
+++ b/pyasn1/__init__.py
@@ -0,0 +1,7 @@
+import sys
+
+# https://www.python.org/dev/peps/pep-0396/
+__version__ = '0.4.3'
+
+if sys.version_info[:2] < (2, 4):
+    raise RuntimeError('PyASN1 requires Python 2.4 or later')
diff --git a/pyasn1/codec/__init__.py b/pyasn1/codec/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/pyasn1/codec/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/pyasn1/codec/ber/__init__.py b/pyasn1/codec/ber/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/pyasn1/codec/ber/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py
new file mode 100644
index 0000000..a27b3e0
--- /dev/null
+++ b/pyasn1/codec/ber/decoder.py
@@ -0,0 +1,1379 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import debug
+from pyasn1 import error
+from pyasn1.codec.ber import eoo
+from pyasn1.compat.integer import from_bytes
+from pyasn1.compat.octets import oct2int, octs2ints, ints2octs, null
+from pyasn1.type import base
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['decode']
+
+noValue = base.noValue
+
+
+class AbstractDecoder(object):
+    protoComponent = None
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,))
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,))
+
+
+class AbstractSimpleDecoder(AbstractDecoder):
+    @staticmethod
+    def substrateCollector(asn1Object, substrate, length):
+        return substrate[:length], substrate[length:]
+
+    def _createComponent(self, asn1Spec, tagSet, value, **options):
+        if options.get('native'):
+            return value
+        elif asn1Spec is None:
+            return self.protoComponent.clone(value, tagSet=tagSet)
+        elif value is noValue:
+            return asn1Spec
+        else:
+            return asn1Spec.clone(value)
+
+
+class ExplicitTagDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.Any('')
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if substrateFun:
+            return substrateFun(
+                self._createComponent(asn1Spec, tagSet, '', **options),
+                substrate, length
+            )
+
+        head, tail = substrate[:length], substrate[length:]
+
+        value, _ = decodeFun(head, asn1Spec, tagSet, length, **options)
+
+        return value, tail
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if substrateFun:
+            return substrateFun(
+                self._createComponent(asn1Spec, tagSet, '', **options),
+                substrate, length
+            )
+
+        value, substrate = decodeFun(substrate, asn1Spec, tagSet, length, **options)
+
+        eooMarker, substrate = decodeFun(substrate, allowEoo=True, **options)
+
+        if eooMarker is eoo.endOfOctets:
+            return value, substrate
+        else:
+            raise error.PyAsn1Error('Missing end-of-octets terminator')
+
+
+explicitTagDecoder = ExplicitTagDecoder()
+
+
+class IntegerDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.Integer(0)
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+
+        if tagSet[0].tagFormat != tag.tagFormatSimple:
+            raise error.PyAsn1Error('Simple tag format expected')
+
+        head, tail = substrate[:length], substrate[length:]
+
+        if not head:
+            return self._createComponent(asn1Spec, tagSet, 0, **options), tail
+
+        value = from_bytes(head, signed=True)
+
+        return self._createComponent(asn1Spec, tagSet, value, **options), tail
+
+
+class BooleanDecoder(IntegerDecoder):
+    protoComponent = univ.Boolean(0)
+
+    def _createComponent(self, asn1Spec, tagSet, value, **options):
+        return IntegerDecoder._createComponent(self, asn1Spec, tagSet, value and 1 or 0, **options)
+
+
+class BitStringDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.BitString(())
+    supportConstructedForm = True
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        head, tail = substrate[:length], substrate[length:]
+
+        if substrateFun:
+            return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
+                                substrate, length)
+
+        if not head:
+            raise error.PyAsn1Error('Empty BIT STRING substrate')
+
+        if tagSet[0].tagFormat == tag.tagFormatSimple:  # XXX what tag to check?
+
+            trailingBits = oct2int(head[0])
+            if trailingBits > 7:
+                raise error.PyAsn1Error(
+                    'Trailing bits overflow %s' % trailingBits
+                )
+
+            value = self.protoComponent.fromOctetString(head[1:], internalFormat=True, padding=trailingBits)
+
+            return self._createComponent(asn1Spec, tagSet, value, **options), tail
+
+        if not self.supportConstructedForm:
+            raise error.PyAsn1Error('Constructed encoding form prohibited at %s' % self.__class__.__name__)
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        bitString = self.protoComponent.fromOctetString(null, internalFormat=True)
+
+        while head:
+            component, head = decodeFun(head, self.protoComponent,
+                                        substrateFun=substrateFun, **options)
+
+            trailingBits = oct2int(component[0])
+            if trailingBits > 7:
+                raise error.PyAsn1Error(
+                    'Trailing bits overflow %s' % trailingBits
+                )
+
+            bitString = self.protoComponent.fromOctetString(
+                component[1:], internalFormat=True,
+                prepend=bitString, padding=trailingBits
+            )
+
+        return self._createComponent(asn1Spec, tagSet, bitString, **options), tail
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+
+        if substrateFun:
+            return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options), substrate, length)
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        bitString = self.protoComponent.fromOctetString(null, internalFormat=True)
+
+        while substrate:
+            component, substrate = decodeFun(substrate, self.protoComponent,
+                                             substrateFun=substrateFun,
+                                             allowEoo=True, **options)
+            if component is eoo.endOfOctets:
+                break
+
+            trailingBits = oct2int(component[0])
+            if trailingBits > 7:
+                raise error.PyAsn1Error(
+                    'Trailing bits overflow %s' % trailingBits
+                )
+
+            bitString = self.protoComponent.fromOctetString(
+                component[1:], internalFormat=True,
+                prepend=bitString, padding=trailingBits
+            )
+
+        else:
+            raise error.SubstrateUnderrunError('No EOO seen before substrate ends')
+
+        return self._createComponent(asn1Spec, tagSet, bitString, **options), substrate
+
+
+class OctetStringDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.OctetString('')
+    supportConstructedForm = True
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        head, tail = substrate[:length], substrate[length:]
+
+        if substrateFun:
+            return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
+                                substrate, length)
+
+        if tagSet[0].tagFormat == tag.tagFormatSimple:  # XXX what tag to check?
+            return self._createComponent(asn1Spec, tagSet, head, **options), tail
+
+        if not self.supportConstructedForm:
+            raise error.PyAsn1Error('Constructed encoding form prohibited at %s' % self.__class__.__name__)
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        header = null
+
+        while head:
+            component, head = decodeFun(head, self.protoComponent,
+                                        substrateFun=substrateFun,
+                                        **options)
+            header += component
+
+        return self._createComponent(asn1Spec, tagSet, header, **options), tail
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if substrateFun and substrateFun is not self.substrateCollector:
+            asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+            return substrateFun(asn1Object, substrate, length)
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        header = null
+
+        while substrate:
+            component, substrate = decodeFun(substrate,
+                                             self.protoComponent,
+                                             substrateFun=substrateFun,
+                                             allowEoo=True, **options)
+            if component is eoo.endOfOctets:
+                break
+            header += component
+        else:
+            raise error.SubstrateUnderrunError(
+                'No EOO seen before substrate ends'
+            )
+
+        return self._createComponent(asn1Spec, tagSet, header, **options), substrate
+
+
+class NullDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.Null('')
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+
+        if tagSet[0].tagFormat != tag.tagFormatSimple:
+            raise error.PyAsn1Error('Simple tag format expected')
+
+        head, tail = substrate[:length], substrate[length:]
+
+        component = self._createComponent(asn1Spec, tagSet, '', **options)
+
+        if head:
+            raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length)
+
+        return component, tail
+
+
+class ObjectIdentifierDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.ObjectIdentifier(())
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if tagSet[0].tagFormat != tag.tagFormatSimple:
+            raise error.PyAsn1Error('Simple tag format expected')
+
+        head, tail = substrate[:length], substrate[length:]
+        if not head:
+            raise error.PyAsn1Error('Empty substrate')
+
+        head = octs2ints(head)
+
+        oid = ()
+        index = 0
+        substrateLen = len(head)
+        while index < substrateLen:
+            subId = head[index]
+            index += 1
+            if subId < 128:
+                oid += (subId,)
+            elif subId > 128:
+                # Construct subid from a number of octets
+                nextSubId = subId
+                subId = 0
+                while nextSubId >= 128:
+                    subId = (subId << 7) + (nextSubId & 0x7F)
+                    if index >= substrateLen:
+                        raise error.SubstrateUnderrunError(
+                            'Short substrate for sub-OID past %s' % (oid,)
+                        )
+                    nextSubId = head[index]
+                    index += 1
+                oid += ((subId << 7) + nextSubId,)
+            elif subId == 128:
+                # ASN.1 spec forbids leading zeros (0x80) in OID
+                # encoding, tolerating it opens a vulnerability. See
+                # https://www.esat.kuleuven.be/cosic/publications/article-1432.pdf
+                # page 7
+                raise error.PyAsn1Error('Invalid octet 0x80 in OID encoding')
+
+        # Decode two leading arcs
+        if 0 <= oid[0] <= 39:
+            oid = (0,) + oid
+        elif 40 <= oid[0] <= 79:
+            oid = (1, oid[0] - 40) + oid[1:]
+        elif oid[0] >= 80:
+            oid = (2, oid[0] - 80) + oid[1:]
+        else:
+            raise error.PyAsn1Error('Malformed first OID octet: %s' % head[0])
+
+        return self._createComponent(asn1Spec, tagSet, oid, **options), tail
+
+
+class RealDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.Real()
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if tagSet[0].tagFormat != tag.tagFormatSimple:
+            raise error.PyAsn1Error('Simple tag format expected')
+
+        head, tail = substrate[:length], substrate[length:]
+
+        if not head:
+            return self._createComponent(asn1Spec, tagSet, 0.0, **options), tail
+
+        fo = oct2int(head[0])
+        head = head[1:]
+        if fo & 0x80:  # binary encoding
+            if not head:
+                raise error.PyAsn1Error("Incomplete floating-point value")
+            n = (fo & 0x03) + 1
+            if n == 4:
+                n = oct2int(head[0])
+                head = head[1:]
+            eo, head = head[:n], head[n:]
+            if not eo or not head:
+                raise error.PyAsn1Error('Real exponent screwed')
+            e = oct2int(eo[0]) & 0x80 and -1 or 0
+            while eo:  # exponent
+                e <<= 8
+                e |= oct2int(eo[0])
+                eo = eo[1:]
+            b = fo >> 4 & 0x03  # base bits
+            if b > 2:
+                raise error.PyAsn1Error('Illegal Real base')
+            if b == 1:  # encbase = 8
+                e *= 3
+            elif b == 2:  # encbase = 16
+                e *= 4
+            p = 0
+            while head:  # value
+                p <<= 8
+                p |= oct2int(head[0])
+                head = head[1:]
+            if fo & 0x40:  # sign bit
+                p = -p
+            sf = fo >> 2 & 0x03  # scale bits
+            p *= 2 ** sf
+            value = (p, 2, e)
+        elif fo & 0x40:  # infinite value
+            value = fo & 0x01 and '-inf' or 'inf'
+        elif fo & 0xc0 == 0:  # character encoding
+            if not head:
+                raise error.PyAsn1Error("Incomplete floating-point value")
+            try:
+                if fo & 0x3 == 0x1:  # NR1
+                    value = (int(head), 10, 0)
+                elif fo & 0x3 == 0x2:  # NR2
+                    value = float(head)
+                elif fo & 0x3 == 0x3:  # NR3
+                    value = float(head)
+                else:
+                    raise error.SubstrateUnderrunError(
+                        'Unknown NR (tag %s)' % fo
+                    )
+            except ValueError:
+                raise error.SubstrateUnderrunError(
+                    'Bad character Real syntax'
+                )
+        else:
+            raise error.SubstrateUnderrunError(
+                'Unknown encoding (tag %s)' % fo
+            )
+        return self._createComponent(asn1Spec, tagSet, value, **options), tail
+
+
+class AbstractConstructedDecoder(AbstractDecoder):
+    protoComponent = None
+
+
+class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
+    protoRecordComponent = None
+    protoSequenceComponent = None
+
+    def _getComponentTagMap(self, asn1Object, idx):
+        raise NotImplementedError()
+
+    def _getComponentPositionByType(self, asn1Object, tagSet, idx):
+        raise NotImplementedError()
+
+    def _decodeComponents(self, substrate, tagSet=None, decodeFun=None, **options):
+        components = []
+        componentTypes = set()
+        while substrate:
+            component, substrate = decodeFun(substrate, **options)
+            if component is eoo.endOfOctets:
+                break
+            components.append(component)
+            componentTypes.add(component.tagSet)
+
+        # Now we have to guess is it SEQUENCE/SET or SEQUENCE OF/SET OF
+        # The heuristics is:
+        # * 1+ components of different types -> likely SEQUENCE/SET
+        # * otherwise -> likely SEQUENCE OF/SET OF
+        if len(componentTypes) > 1:
+            protoComponent = self.protoRecordComponent
+        else:
+            protoComponent = self.protoSequenceComponent
+
+        asn1Object = protoComponent.clone(
+            # construct tagSet from base tag from prototype ASN.1 object
+            # and additional tags recovered from the substrate
+            tagSet=tag.TagSet(protoComponent.tagSet.baseTag, *tagSet.superTags)
+        )
+
+        for idx, component in enumerate(components):
+            asn1Object.setComponentByPosition(
+                idx, component,
+                verifyConstraints=False,
+                matchTags=False, matchConstraints=False
+            )
+
+        return asn1Object, substrate
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if tagSet[0].tagFormat != tag.tagFormatConstructed:
+            raise error.PyAsn1Error('Constructed tag format expected')
+
+        head, tail = substrate[:length], substrate[length:]
+
+        if substrateFun is not None:
+            if asn1Spec is not None:
+                asn1Object = asn1Spec.clone()
+            elif self.protoComponent is not None:
+                asn1Object = self.protoComponent.clone(tagSet=tagSet)
+            else:
+                asn1Object = self.protoRecordComponent, self.protoSequenceComponent
+
+            return substrateFun(asn1Object, substrate, length)
+
+        if asn1Spec is None:
+            asn1Object, trailing = self._decodeComponents(
+                head, tagSet=tagSet, decodeFun=decodeFun, **options
+            )
+            if trailing:
+                raise error.PyAsn1Error('Unused trailing %d octets encountered' % len(trailing))
+            return asn1Object, tail
+
+        asn1Object = asn1Spec.clone()
+
+        if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
+
+            namedTypes = asn1Spec.componentType
+
+            isSetType = asn1Spec.typeId == univ.Set.typeId
+            isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault
+
+            seenIndices = set()
+            idx = 0
+            while head:
+                if not namedTypes:
+                    componentType = None
+                elif isSetType:
+                    componentType = namedTypes.tagMapUnique
+                else:
+                    try:
+                        if isDeterministic:
+                            componentType = namedTypes[idx].asn1Object
+                        elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+                            componentType = namedTypes.getTagMapNearPosition(idx)
+                        else:
+                            componentType = namedTypes[idx].asn1Object
+                    except IndexError:
+                        raise error.PyAsn1Error(
+                            'Excessive components decoded at %r' % (asn1Spec,)
+                        )
+
+                component, head = decodeFun(head, componentType, **options)
+
+                if not isDeterministic and namedTypes:
+                    if isSetType:
+                        idx = namedTypes.getPositionByType(component.effectiveTagSet)
+                    elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+                        idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx)
+
+                asn1Object.setComponentByPosition(
+                    idx, component,
+                    verifyConstraints=False,
+                    matchTags=False, matchConstraints=False
+                )
+
+                seenIndices.add(idx)
+                idx += 1
+
+            if namedTypes:
+                if not namedTypes.requiredComponents.issubset(seenIndices):
+                    raise error.PyAsn1Error('ASN.1 object %s has uninitialized components' % asn1Object.__class__.__name__)
+
+                if  namedTypes.hasOpenTypes:
+
+                    openTypes = options.get('openTypes', {})
+
+                    if openTypes or options.get('decodeOpenTypes', False):
+
+                        for idx, namedType in enumerate(namedTypes.namedTypes):
+                            if not namedType.openType:
+                                continue
+
+                            if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue:
+                                continue
+
+                            governingValue = asn1Object.getComponentByName(
+                                namedType.openType.name
+                            )
+
+                            try:
+                                openType = openTypes[governingValue]
+
+                            except KeyError:
+
+                                try:
+                                    openType = namedType.openType[governingValue]
+
+                                except KeyError:
+                                    continue
+
+                            component, rest = decodeFun(
+                                asn1Object.getComponentByPosition(idx).asOctets(),
+                                asn1Spec=openType
+                            )
+
+                            asn1Object.setComponentByPosition(idx, component)
+
+            else:
+                asn1Object.verifySizeSpec()
+
+        else:
+            asn1Object = asn1Spec.clone()
+
+            componentType = asn1Spec.componentType
+
+            idx = 0
+
+            while head:
+                component, head = decodeFun(head, componentType, **options)
+                asn1Object.setComponentByPosition(
+                    idx, component,
+                    verifyConstraints=False,
+                    matchTags=False, matchConstraints=False
+                )
+                idx += 1
+
+        return asn1Object, tail
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if tagSet[0].tagFormat != tag.tagFormatConstructed:
+            raise error.PyAsn1Error('Constructed tag format expected')
+
+        if substrateFun is not None:
+            if asn1Spec is not None:
+                asn1Object = asn1Spec.clone()
+            elif self.protoComponent is not None:
+                asn1Object = self.protoComponent.clone(tagSet=tagSet)
+            else:
+                asn1Object = self.protoRecordComponent, self.protoSequenceComponent
+
+            return substrateFun(asn1Object, substrate, length)
+
+        if asn1Spec is None:
+            return self._decodeComponents(
+                substrate, tagSet=tagSet, decodeFun=decodeFun, allowEoo=True, **options
+            )
+
+        asn1Object = asn1Spec.clone()
+
+        if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
+
+            namedTypes = asn1Object.componentType
+
+            isSetType = asn1Object.typeId == univ.Set.typeId
+            isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault
+
+            seenIndices = set()
+            idx = 0
+            while substrate:
+                if len(namedTypes) <= idx:
+                    asn1Spec = None
+                elif isSetType:
+                    asn1Spec = namedTypes.tagMapUnique
+                else:
+                    try:
+                        if isDeterministic:
+                            asn1Spec = namedTypes[idx].asn1Object
+                        elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+                            asn1Spec = namedTypes.getTagMapNearPosition(idx)
+                        else:
+                            asn1Spec = namedTypes[idx].asn1Object
+                    except IndexError:
+                        raise error.PyAsn1Error(
+                            'Excessive components decoded at %r' % (asn1Object,)
+                        )
+
+                component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True, **options)
+                if component is eoo.endOfOctets:
+                    break
+
+                if not isDeterministic and namedTypes:
+                    if isSetType:
+                        idx = namedTypes.getPositionByType(component.effectiveTagSet)
+                    elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+                        idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx)
+
+                asn1Object.setComponentByPosition(
+                    idx, component,
+                    verifyConstraints=False,
+                    matchTags=False, matchConstraints=False
+                )
+
+                seenIndices.add(idx)
+                idx += 1
+
+            else:
+                raise error.SubstrateUnderrunError(
+                    'No EOO seen before substrate ends'
+                )
+
+            if namedTypes:
+                if not namedTypes.requiredComponents.issubset(seenIndices):
+                    raise error.PyAsn1Error('ASN.1 object %s has uninitialized components' % asn1Object.__class__.__name__)
+
+                if  namedTypes.hasOpenTypes:
+
+                    openTypes = options.get('openTypes', None)
+
+                    if openTypes or options.get('decodeOpenTypes', False):
+
+                        for idx, namedType in enumerate(namedTypes.namedTypes):
+                            if not namedType.openType:
+                                continue
+
+                            if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue:
+                                continue
+
+                            governingValue = asn1Object.getComponentByName(
+                                namedType.openType.name
+                            )
+
+                            try:
+                                openType = openTypes[governingValue]
+
+                            except KeyError:
+
+                                try:
+                                    openType = namedType.openType[governingValue]
+
+                                except KeyError:
+                                    continue
+
+                            component, rest = decodeFun(
+                                asn1Object.getComponentByPosition(idx).asOctets(),
+                                asn1Spec=openType, allowEoo=True
+                            )
+
+                            if component is not eoo.endOfOctets:
+                                asn1Object.setComponentByPosition(idx, component)
+
+                else:
+                    asn1Object.verifySizeSpec()
+
+        else:
+            asn1Object = asn1Spec.clone()
+
+            componentType = asn1Spec.componentType
+
+            idx = 0
+
+            while substrate:
+                component, substrate = decodeFun(substrate, componentType, allowEoo=True, **options)
+
+                if component is eoo.endOfOctets:
+                    break
+
+                asn1Object.setComponentByPosition(
+                    idx, component,
+                    verifyConstraints=False,
+                    matchTags=False, matchConstraints=False
+                )
+                idx += 1
+            else:
+                raise error.SubstrateUnderrunError(
+                    'No EOO seen before substrate ends'
+                )
+
+        return asn1Object, substrate
+
+
+class SequenceOrSequenceOfDecoder(UniversalConstructedTypeDecoder):
+    protoRecordComponent = univ.Sequence()
+    protoSequenceComponent = univ.SequenceOf()
+
+
+class SequenceDecoder(SequenceOrSequenceOfDecoder):
+    protoComponent = univ.Sequence()
+
+
+class SequenceOfDecoder(SequenceOrSequenceOfDecoder):
+    protoComponent = univ.SequenceOf()
+
+
+class SetOrSetOfDecoder(UniversalConstructedTypeDecoder):
+    protoRecordComponent = univ.Set()
+    protoSequenceComponent = univ.SetOf()
+
+
+class SetDecoder(SetOrSetOfDecoder):
+    protoComponent = univ.Set()
+
+
+
+class SetOfDecoder(SetOrSetOfDecoder):
+    protoComponent = univ.SetOf()
+
+
+class ChoiceDecoder(AbstractConstructedDecoder):
+    protoComponent = univ.Choice()
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        head, tail = substrate[:length], substrate[length:]
+
+        if asn1Spec is None:
+            asn1Object = self.protoComponent.clone(tagSet=tagSet)
+        else:
+            asn1Object = asn1Spec.clone()
+
+        if substrateFun:
+            return substrateFun(asn1Object, substrate, length)
+
+        if asn1Object.tagSet == tagSet:  # explicitly tagged Choice
+            component, head = decodeFun(
+                head, asn1Object.componentTagMap, **options
+            )
+
+        else:
+            component, head = decodeFun(
+                head, asn1Object.componentTagMap,
+                tagSet, length, state, **options
+            )
+
+        effectiveTagSet = component.effectiveTagSet
+
+        asn1Object.setComponentByType(
+            effectiveTagSet, component,
+            verifyConstraints=False,
+            matchTags=False, matchConstraints=False,
+            innerFlag=False
+        )
+
+        return asn1Object, tail
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if asn1Spec is None:
+            asn1Object = self.protoComponent.clone(tagSet=tagSet)
+        else:
+            asn1Object = asn1Spec.clone()
+
+        if substrateFun:
+            return substrateFun(asn1Object, substrate, length)
+
+        if asn1Object.tagSet == tagSet:  # explicitly tagged Choice
+            component, substrate = decodeFun(
+                substrate, asn1Object.componentType.tagMapUnique, **options
+            )
+            # eat up EOO marker
+            eooMarker, substrate = decodeFun(
+                substrate, allowEoo=True, **options
+            )
+            if eooMarker is not eoo.endOfOctets:
+                raise error.PyAsn1Error('No EOO seen before substrate ends')
+
+        else:
+            component, substrate = decodeFun(
+                substrate, asn1Object.componentType.tagMapUnique,
+                tagSet, length, state, **options
+            )
+
+        effectiveTagSet = component.effectiveTagSet
+
+        asn1Object.setComponentByType(
+            effectiveTagSet, component,
+            verifyConstraints=False,
+            matchTags=False, matchConstraints=False,
+            innerFlag=False
+        )
+
+        return asn1Object, substrate
+
+
+class AnyDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.Any()
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if asn1Spec is None or asn1Spec is not None and tagSet != asn1Spec.tagSet:
+            fullSubstrate = options['fullSubstrate']
+
+            # untagged Any container, recover inner header substrate
+            length += len(fullSubstrate) - len(substrate)
+            substrate = fullSubstrate
+
+        if substrateFun:
+            return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
+                                substrate, length)
+
+        head, tail = substrate[:length], substrate[length:]
+
+        return self._createComponent(asn1Spec, tagSet, head, **options), tail
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if asn1Spec is not None and tagSet == asn1Spec.tagSet:
+            # tagged Any type -- consume header substrate
+            header = null
+        else:
+            fullSubstrate = options['fullSubstrate']
+
+            # untagged Any, recover header substrate
+            header = fullSubstrate[:-len(substrate)]
+
+        # Any components do not inherit initial tag
+        asn1Spec = self.protoComponent
+
+        if substrateFun and substrateFun is not self.substrateCollector:
+            asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+            return substrateFun(asn1Object, header + substrate, length + len(header))
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        while substrate:
+            component, substrate = decodeFun(substrate, asn1Spec,
+                                             substrateFun=substrateFun,
+                                             allowEoo=True, **options)
+            if component is eoo.endOfOctets:
+                break
+            header += component
+        else:
+            raise error.SubstrateUnderrunError(
+                'No EOO seen before substrate ends'
+            )
+        if substrateFun:
+            return header, substrate
+        else:
+            return self._createComponent(asn1Spec, tagSet, header, **options), substrate
+
+
+# character string types
+class UTF8StringDecoder(OctetStringDecoder):
+    protoComponent = char.UTF8String()
+
+
+class NumericStringDecoder(OctetStringDecoder):
+    protoComponent = char.NumericString()
+
+
+class PrintableStringDecoder(OctetStringDecoder):
+    protoComponent = char.PrintableString()
+
+
+class TeletexStringDecoder(OctetStringDecoder):
+    protoComponent = char.TeletexString()
+
+
+class VideotexStringDecoder(OctetStringDecoder):
+    protoComponent = char.VideotexString()
+
+
+class IA5StringDecoder(OctetStringDecoder):
+    protoComponent = char.IA5String()
+
+
+class GraphicStringDecoder(OctetStringDecoder):
+    protoComponent = char.GraphicString()
+
+
+class VisibleStringDecoder(OctetStringDecoder):
+    protoComponent = char.VisibleString()
+
+
+class GeneralStringDecoder(OctetStringDecoder):
+    protoComponent = char.GeneralString()
+
+
+class UniversalStringDecoder(OctetStringDecoder):
+    protoComponent = char.UniversalString()
+
+
+class BMPStringDecoder(OctetStringDecoder):
+    protoComponent = char.BMPString()
+
+
+# "useful" types
+class ObjectDescriptorDecoder(OctetStringDecoder):
+    protoComponent = useful.ObjectDescriptor()
+
+
+class GeneralizedTimeDecoder(OctetStringDecoder):
+    protoComponent = useful.GeneralizedTime()
+
+
+class UTCTimeDecoder(OctetStringDecoder):
+    protoComponent = useful.UTCTime()
+
+
+tagMap = {
+    univ.Integer.tagSet: IntegerDecoder(),
+    univ.Boolean.tagSet: BooleanDecoder(),
+    univ.BitString.tagSet: BitStringDecoder(),
+    univ.OctetString.tagSet: OctetStringDecoder(),
+    univ.Null.tagSet: NullDecoder(),
+    univ.ObjectIdentifier.tagSet: ObjectIdentifierDecoder(),
+    univ.Enumerated.tagSet: IntegerDecoder(),
+    univ.Real.tagSet: RealDecoder(),
+    univ.Sequence.tagSet: SequenceOrSequenceOfDecoder(),  # conflicts with SequenceOf
+    univ.Set.tagSet: SetOrSetOfDecoder(),  # conflicts with SetOf
+    univ.Choice.tagSet: ChoiceDecoder(),  # conflicts with Any
+    # character string types
+    char.UTF8String.tagSet: UTF8StringDecoder(),
+    char.NumericString.tagSet: NumericStringDecoder(),
+    char.PrintableString.tagSet: PrintableStringDecoder(),
+    char.TeletexString.tagSet: TeletexStringDecoder(),
+    char.VideotexString.tagSet: VideotexStringDecoder(),
+    char.IA5String.tagSet: IA5StringDecoder(),
+    char.GraphicString.tagSet: GraphicStringDecoder(),
+    char.VisibleString.tagSet: VisibleStringDecoder(),
+    char.GeneralString.tagSet: GeneralStringDecoder(),
+    char.UniversalString.tagSet: UniversalStringDecoder(),
+    char.BMPString.tagSet: BMPStringDecoder(),
+    # useful types
+    useful.ObjectDescriptor.tagSet: ObjectDescriptorDecoder(),
+    useful.GeneralizedTime.tagSet: GeneralizedTimeDecoder(),
+    useful.UTCTime.tagSet: UTCTimeDecoder()
+}
+
+# Type-to-codec map for ambiguous ASN.1 types
+typeMap = {
+    univ.Set.typeId: SetDecoder(),
+    univ.SetOf.typeId: SetOfDecoder(),
+    univ.Sequence.typeId: SequenceDecoder(),
+    univ.SequenceOf.typeId: SequenceOfDecoder(),
+    univ.Choice.typeId: ChoiceDecoder(),
+    univ.Any.typeId: AnyDecoder()
+}
+
+# Put in non-ambiguous types for faster codec lookup
+for typeDecoder in tagMap.values():
+    if typeDecoder.protoComponent is not None:
+        typeId = typeDecoder.protoComponent.__class__.typeId
+        if typeId is not None and typeId not in typeMap:
+            typeMap[typeId] = typeDecoder
+
+
+(stDecodeTag,
+ stDecodeLength,
+ stGetValueDecoder,
+ stGetValueDecoderByAsn1Spec,
+ stGetValueDecoderByTag,
+ stTryAsExplicitTag,
+ stDecodeValue,
+ stDumpRawValue,
+ stErrorCondition,
+ stStop) = [x for x in range(10)]
+
+
+class Decoder(object):
+    defaultErrorState = stErrorCondition
+    #    defaultErrorState = stDumpRawValue
+    defaultRawDecoder = AnyDecoder()
+    supportIndefLength = True
+
+    # noinspection PyDefaultArgument
+    def __init__(self, tagMap, typeMap={}):
+        self.__tagMap = tagMap
+        self.__typeMap = typeMap
+        # Tag & TagSet objects caches
+        self.__tagCache = {}
+        self.__tagSetCache = {}
+        self.__eooSentinel = ints2octs((0, 0))
+
+    def __call__(self, substrate, asn1Spec=None,
+                 tagSet=None, length=None, state=stDecodeTag,
+                 decodeFun=None, substrateFun=None,
+                 **options):
+
+        if debug.logger & debug.flagDecoder:
+            logger = debug.logger
+        else:
+            logger = None
+
+        if logger:
+            logger('decoder called at scope %s with state %d, working with up to %d octets of substrate: %s' % (debug.scope, state, len(substrate), debug.hexdump(substrate)))
+
+        allowEoo = options.pop('allowEoo', False)
+
+        # Look for end-of-octets sentinel
+        if allowEoo and self.supportIndefLength:
+            if substrate[:2] == self.__eooSentinel:
+                if logger:
+                    logger('end-of-octets sentinel found')
+                return eoo.endOfOctets, substrate[2:]
+
+        value = noValue
+
+        tagMap = self.__tagMap
+        typeMap = self.__typeMap
+        tagCache = self.__tagCache
+        tagSetCache = self.__tagSetCache
+
+        fullSubstrate = substrate
+
+        while state is not stStop:
+            if state is stDecodeTag:
+                if not substrate:
+                    raise error.SubstrateUnderrunError(
+                        'Short octet stream on tag decoding'
+                    )
+                # Decode tag
+                isShortTag = True
+                firstOctet = substrate[0]
+                substrate = substrate[1:]
+                try:
+                    lastTag = tagCache[firstOctet]
+                except KeyError:
+                    integerTag = oct2int(firstOctet)
+                    tagClass = integerTag & 0xC0
+                    tagFormat = integerTag & 0x20
+                    tagId = integerTag & 0x1F
+                    if tagId == 0x1F:
+                        isShortTag = False
+                        lengthOctetIdx = 0
+                        tagId = 0
+                        try:
+                            while True:
+                                integerTag = oct2int(substrate[lengthOctetIdx])
+                                lengthOctetIdx += 1
+                                tagId <<= 7
+                                tagId |= (integerTag & 0x7F)
+                                if not integerTag & 0x80:
+                                    break
+                            substrate = substrate[lengthOctetIdx:]
+                        except IndexError:
+                            raise error.SubstrateUnderrunError(
+                                'Short octet stream on long tag decoding'
+                            )
+                    lastTag = tag.Tag(
+                        tagClass=tagClass, tagFormat=tagFormat, tagId=tagId
+                    )
+                    if isShortTag:
+                        # cache short tags
+                        tagCache[firstOctet] = lastTag
+                if tagSet is None:
+                    if isShortTag:
+                        try:
+                            tagSet = tagSetCache[firstOctet]
+                        except KeyError:
+                            # base tag not recovered
+                            tagSet = tag.TagSet((), lastTag)
+                            tagSetCache[firstOctet] = tagSet
+                    else:
+                        tagSet = tag.TagSet((), lastTag)
+                else:
+                    tagSet = lastTag + tagSet
+                state = stDecodeLength
+                if logger:
+                    logger('tag decoded into %s, decoding length' % tagSet)
+            if state is stDecodeLength:
+                # Decode length
+                if not substrate:
+                    raise error.SubstrateUnderrunError(
+                        'Short octet stream on length decoding'
+                    )
+                firstOctet = oct2int(substrate[0])
+                if firstOctet < 128:
+                    size = 1
+                    length = firstOctet
+                elif firstOctet > 128:
+                    size = firstOctet & 0x7F
+                    # encoded in size bytes
+                    encodedLength = octs2ints(substrate[1:size + 1])
+                    # missing check on maximum size, which shouldn't be a
+                    # problem, we can handle more than is possible
+                    if len(encodedLength) != size:
+                        raise error.SubstrateUnderrunError(
+                            '%s<%s at %s' % (size, len(encodedLength), tagSet)
+                        )
+                    length = 0
+                    for lengthOctet in encodedLength:
+                        length <<= 8
+                        length |= lengthOctet
+                    size += 1
+                else:
+                    size = 1
+                    length = -1
+
+                substrate = substrate[size:]
+                if length == -1:
+                    if not self.supportIndefLength:
+                        raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
+                else:
+                    if len(substrate) < length:
+                        raise error.SubstrateUnderrunError('%d-octet short' % (length - len(substrate)))
+                state = stGetValueDecoder
+                if logger:
+                    logger('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length])))
+            if state is stGetValueDecoder:
+                if asn1Spec is None:
+                    state = stGetValueDecoderByTag
+                else:
+                    state = stGetValueDecoderByAsn1Spec
+            #
+            # There're two ways of creating subtypes in ASN.1 what influences
+            # decoder operation. These methods are:
+            # 1) Either base types used in or no IMPLICIT tagging has been
+            #    applied on subtyping.
+            # 2) Subtype syntax drops base type information (by means of
+            #    IMPLICIT tagging.
+            # The first case allows for complete tag recovery from substrate
+            # while the second one requires original ASN.1 type spec for
+            # decoding.
+            #
+            # In either case a set of tags (tagSet) is coming from substrate
+            # in an incremental, tag-by-tag fashion (this is the case of
+            # EXPLICIT tag which is most basic). Outermost tag comes first
+            # from the wire.
+            #
+            if state is stGetValueDecoderByTag:
+                try:
+                    concreteDecoder = tagMap[tagSet]
+                except KeyError:
+                    concreteDecoder = None
+                if concreteDecoder:
+                    state = stDecodeValue
+                else:
+                    try:
+                        concreteDecoder = tagMap[tagSet[:1]]
+                    except KeyError:
+                        concreteDecoder = None
+                    if concreteDecoder:
+                        state = stDecodeValue
+                    else:
+                        state = stTryAsExplicitTag
+                if logger:
+                    logger('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag'))
+                    debug.scope.push(concreteDecoder is None and '?' or concreteDecoder.protoComponent.__class__.__name__)
+            if state is stGetValueDecoderByAsn1Spec:
+                if asn1Spec.__class__ is tagmap.TagMap:
+                    try:
+                        chosenSpec = asn1Spec[tagSet]
+                    except KeyError:
+                        chosenSpec = None
+                    if logger:
+                        logger('candidate ASN.1 spec is a map of:')
+                        for firstOctet, v in asn1Spec.presentTypes.items():
+                            logger('  %s -> %s' % (firstOctet, v.__class__.__name__))
+                        if asn1Spec.skipTypes:
+                            logger('but neither of: ')
+                            for firstOctet, v in asn1Spec.skipTypes.items():
+                                logger('  %s -> %s' % (firstOctet, v.__class__.__name__))
+                        logger('new candidate ASN.1 spec is %s, chosen by %s' % (chosenSpec is None and '<none>' or chosenSpec.prettyPrintType(), tagSet))
+                elif tagSet == asn1Spec.tagSet or tagSet in asn1Spec.tagMap:
+                    chosenSpec = asn1Spec
+                    if logger:
+                        logger('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__)
+                else:
+                    chosenSpec = None
+
+                if chosenSpec is not None:
+                    try:
+                        # ambiguous type or just faster codec lookup
+                        concreteDecoder = typeMap[chosenSpec.typeId]
+                        if logger:
+                            logger('value decoder chosen for an ambiguous type by type ID %s' % (chosenSpec.typeId,))
+                    except KeyError:
+                        # use base type for codec lookup to recover untagged types
+                        baseTagSet = tag.TagSet(chosenSpec.tagSet.baseTag,  chosenSpec.tagSet.baseTag)
+                        try:
+                            # base type or tagged subtype
+                            concreteDecoder = tagMap[baseTagSet]
+                            if logger:
+                                logger('value decoder chosen by base %s' % (baseTagSet,))
+                        except KeyError:
+                            concreteDecoder = None
+                    if concreteDecoder:
+                        asn1Spec = chosenSpec
+                        state = stDecodeValue
+                    else:
+                        state = stTryAsExplicitTag
+                else:
+                    concreteDecoder = None
+                    state = stTryAsExplicitTag
+                if logger:
+                    logger('codec %s chosen by ASN.1 spec, decoding %s' % (state is stDecodeValue and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag'))
+                    debug.scope.push(chosenSpec is None and '?' or chosenSpec.__class__.__name__)
+            if state is stDecodeValue:
+                if not options.get('recursiveFlag', True) and not substrateFun:  # deprecate this
+                    substrateFun = lambda a, b, c: (a, b[:c])
+
+                options.update(fullSubstrate=fullSubstrate)
+
+                if length == -1:  # indef length
+                    value, substrate = concreteDecoder.indefLenValueDecoder(
+                        substrate, asn1Spec,
+                        tagSet, length, stGetValueDecoder,
+                        self, substrateFun,
+                        **options
+                    )
+                else:
+                    value, substrate = concreteDecoder.valueDecoder(
+                        substrate, asn1Spec,
+                        tagSet, length, stGetValueDecoder,
+                        self, substrateFun,
+                        **options
+                    )
+
+                if logger:
+                    logger('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, isinstance(value, base.Asn1Item) and value.prettyPrint() or value, substrate and debug.hexdump(substrate) or '<none>'))
+
+                state = stStop
+                break
+            if state is stTryAsExplicitTag:
+                if tagSet and tagSet[0].tagFormat == tag.tagFormatConstructed and tagSet[0].tagClass != tag.tagClassUniversal:
+                    # Assume explicit tagging
+                    concreteDecoder = explicitTagDecoder
+                    state = stDecodeValue
+                else:
+                    concreteDecoder = None
+                    state = self.defaultErrorState
+                if logger:
+                    logger('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as failure'))
+            if state is stDumpRawValue:
+                concreteDecoder = self.defaultRawDecoder
+                if logger:
+                    logger('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__)
+                state = stDecodeValue
+            if state is stErrorCondition:
+                raise error.PyAsn1Error(
+                    '%s not in asn1Spec: %r' % (tagSet, asn1Spec)
+                )
+        if logger:
+            debug.scope.pop()
+            logger('decoder left scope %s, call completed' % debug.scope)
+        return value, substrate
+
+
+#: Turns BER octet stream into an ASN.1 object.
+#:
+#: Takes BER octet-stream and decode it into an ASN.1 object
+#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#:     BER octet-stream
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
+#:     being decoded, *asn1Spec* may or may not be required. Most common reason for
+#:     it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+#:
+#: Returns
+#: -------
+#: : :py:class:`tuple`
+#:     A tuple of pyasn1 object recovered from BER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     and the unprocessed trailing portion of the *substrate* (may be empty)
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#:     On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode BER serialisation without ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+#: Decode BER serialisation with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+decode = Decoder(tagMap, typeMap)
+
+# XXX
+# non-recursive decoding; return position rather than substrate
diff --git a/pyasn1/codec/ber/encoder.py b/pyasn1/codec/ber/encoder.py
new file mode 100644
index 0000000..0094b22
--- /dev/null
+++ b/pyasn1/codec/ber/encoder.py
@@ -0,0 +1,721 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import debug
+from pyasn1 import error
+from pyasn1.codec.ber import eoo
+from pyasn1.compat.integer import to_bytes
+from pyasn1.compat.octets import (int2oct, oct2int, ints2octs, null,
+                                  str2octs, isOctetsType)
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['encode']
+
+
+class AbstractItemEncoder(object):
+    supportIndefLenMode = True
+
+    # An outcome of otherwise legit call `encodeFun(eoo.endOfOctets)`
+    eooIntegerSubstrate = (0, 0)
+    eooOctetsSubstrate = ints2octs(eooIntegerSubstrate)
+
+    # noinspection PyMethodMayBeStatic
+    def encodeTag(self, singleTag, isConstructed):
+        tagClass, tagFormat, tagId = singleTag
+        encodedTag = tagClass | tagFormat
+        if isConstructed:
+            encodedTag |= tag.tagFormatConstructed
+        if tagId < 31:
+            return encodedTag | tagId,
+        else:
+            substrate = tagId & 0x7f,
+            tagId >>= 7
+            while tagId:
+                substrate = (0x80 | (tagId & 0x7f),) + substrate
+                tagId >>= 7
+            return (encodedTag | 0x1F,) + substrate
+
+    def encodeLength(self, length, defMode):
+        if not defMode and self.supportIndefLenMode:
+            return (0x80,)
+        if length < 0x80:
+            return length,
+        else:
+            substrate = ()
+            while length:
+                substrate = (length & 0xff,) + substrate
+                length >>= 8
+            substrateLen = len(substrate)
+            if substrateLen > 126:
+                raise error.PyAsn1Error('Length octets overflow (%d)' % substrateLen)
+            return (0x80 | substrateLen,) + substrate
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        raise error.PyAsn1Error('Not implemented')
+
+    def encode(self, value, asn1Spec=None, encodeFun=None, **options):
+
+        if asn1Spec is None:
+            tagSet = value.tagSet
+        else:
+            tagSet = asn1Spec.tagSet
+
+        # untagged item?
+        if not tagSet:
+            substrate, isConstructed, isOctets = self.encodeValue(
+                value, asn1Spec, encodeFun, **options
+            )
+            return substrate
+
+        defMode = options.get('defMode', True)
+
+        for idx, singleTag in enumerate(tagSet.superTags):
+
+            defModeOverride = defMode
+
+            # base tag?
+            if not idx:
+                substrate, isConstructed, isOctets = self.encodeValue(
+                    value, asn1Spec, encodeFun, **options
+                )
+
+                if not substrate and isConstructed and options.get('ifNotEmpty', False):
+                    return substrate
+
+                # primitive form implies definite mode
+                if not isConstructed:
+                    defModeOverride = True
+
+            header = self.encodeTag(singleTag, isConstructed)
+            header += self.encodeLength(len(substrate), defModeOverride)
+
+            if isOctets:
+                substrate = ints2octs(header) + substrate
+
+                if not defModeOverride:
+                    substrate += self.eooOctetsSubstrate
+
+            else:
+                substrate = header + substrate
+
+                if not defModeOverride:
+                    substrate += self.eooIntegerSubstrate
+
+        if not isOctets:
+            substrate = ints2octs(substrate)
+
+        return substrate
+
+
+class EndOfOctetsEncoder(AbstractItemEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        return null, False, True
+
+
+class BooleanEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        return value and (1,) or (0,), False, False
+
+
+class IntegerEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+    supportCompactZero = False
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if value == 0:
+            # de-facto way to encode zero
+            if self.supportCompactZero:
+                return (), False, False
+            else:
+                return (0,), False, False
+
+        return to_bytes(int(value), signed=True), False, True
+
+
+class BitStringEncoder(AbstractItemEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is not None:
+            # TODO: try to avoid ASN.1 schema instantiation
+            value = asn1Spec.clone(value)
+
+        valueLength = len(value)
+        if valueLength % 8:
+            alignedValue = value << (8 - valueLength % 8)
+        else:
+            alignedValue = value
+
+        maxChunkSize = options.get('maxChunkSize', 0)
+        if not maxChunkSize or len(alignedValue) <= maxChunkSize * 8:
+            substrate = alignedValue.asOctets()
+            return int2oct(len(substrate) * 8 - valueLength) + substrate, False, True
+
+        baseTag = value.tagSet.baseTag
+
+        # strip off explicit tags
+        if baseTag:
+            tagSet = tag.TagSet(baseTag, baseTag)
+        else:
+            tagSet = tag.TagSet()
+
+        alignedValue = alignedValue.clone(tagSet=tagSet)
+
+        stop = 0
+        substrate = null
+        while stop < valueLength:
+            start = stop
+            stop = min(start + maxChunkSize * 8, valueLength)
+            substrate += encodeFun(alignedValue[start:stop], asn1Spec, **options)
+
+        return substrate, True, True
+
+
+class OctetStringEncoder(AbstractItemEncoder):
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+        if asn1Spec is None:
+            substrate = value.asOctets()
+
+        elif not isOctetsType(value):
+            substrate = asn1Spec.clone(value).asOctets()
+
+        else:
+            substrate = value
+
+        maxChunkSize = options.get('maxChunkSize', 0)
+
+        if not maxChunkSize or len(substrate) <= maxChunkSize:
+            return substrate, False, True
+
+        else:
+
+            # strip off explicit tags for inner chunks
+
+            if asn1Spec is None:
+                baseTag = value.tagSet.baseTag
+
+                # strip off explicit tags
+                if baseTag:
+                    tagSet = tag.TagSet(baseTag, baseTag)
+                else:
+                    tagSet = tag.TagSet()
+
+                asn1Spec = value.clone(tagSet=tagSet)
+
+            elif not isOctetsType(value):
+                baseTag = asn1Spec.tagSet.baseTag
+
+                # strip off explicit tags
+                if baseTag:
+                    tagSet = tag.TagSet(baseTag, baseTag)
+                else:
+                    tagSet = tag.TagSet()
+
+                asn1Spec = asn1Spec.clone(tagSet=tagSet)
+
+            pos = 0
+            substrate = null
+
+            while True:
+                chunk = value[pos:pos + maxChunkSize]
+                if not chunk:
+                    break
+
+                substrate += encodeFun(chunk, asn1Spec, **options)
+                pos += maxChunkSize
+
+            return substrate, True, True
+
+
+class NullEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        return null, False, True
+
+
+class ObjectIdentifierEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is not None:
+            value = asn1Spec.clone(value)
+
+        oid = value.asTuple()
+
+        # Build the first pair
+        try:
+            first = oid[0]
+            second = oid[1]
+
+        except IndexError:
+            raise error.PyAsn1Error('Short OID %s' % (value,))
+
+        if 0 <= second <= 39:
+            if first == 1:
+                oid = (second + 40,) + oid[2:]
+            elif first == 0:
+                oid = (second,) + oid[2:]
+            elif first == 2:
+                oid = (second + 80,) + oid[2:]
+            else:
+                raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
+        elif first == 2:
+            oid = (second + 80,) + oid[2:]
+        else:
+            raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
+
+        octets = ()
+
+        # Cycle through subIds
+        for subOid in oid:
+            if 0 <= subOid <= 127:
+                # Optimize for the common case
+                octets += (subOid,)
+            elif subOid > 127:
+                # Pack large Sub-Object IDs
+                res = (subOid & 0x7f,)
+                subOid >>= 7
+                while subOid:
+                    res = (0x80 | (subOid & 0x7f),) + res
+                    subOid >>= 7
+                # Add packed Sub-Object ID to resulted Object ID
+                octets += res
+            else:
+                raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value))
+
+        return octets, False, False
+
+
+class RealEncoder(AbstractItemEncoder):
+    supportIndefLenMode = 0
+    binEncBase = 2  # set to None to choose encoding base automatically
+
+    @staticmethod
+    def _dropFloatingPoint(m, encbase, e):
+        ms, es = 1, 1
+        if m < 0:
+            ms = -1  # mantissa sign
+        if e < 0:
+            es = -1  # exponenta sign 
+        m *= ms
+        if encbase == 8:
+            m *= 2 ** (abs(e) % 3 * es)
+            e = abs(e) // 3 * es
+        elif encbase == 16:
+            m *= 2 ** (abs(e) % 4 * es)
+            e = abs(e) // 4 * es
+
+        while True:
+            if int(m) != m:
+                m *= encbase
+                e -= 1
+                continue
+            break
+        return ms, int(m), encbase, e
+
+    def _chooseEncBase(self, value):
+        m, b, e = value
+        encBase = [2, 8, 16]
+        if value.binEncBase in encBase:
+            return self._dropFloatingPoint(m, value.binEncBase, e)
+        elif self.binEncBase in encBase:
+            return self._dropFloatingPoint(m, self.binEncBase, e)
+        # auto choosing base 2/8/16 
+        mantissa = [m, m, m]
+        exponenta = [e, e, e]
+        sign = 1
+        encbase = 2
+        e = float('inf')
+        for i in range(3):
+            (sign,
+             mantissa[i],
+             encBase[i],
+             exponenta[i]) = self._dropFloatingPoint(mantissa[i], encBase[i], exponenta[i])
+            if abs(exponenta[i]) < abs(e) or (abs(exponenta[i]) == abs(e) and mantissa[i] < m):
+                e = exponenta[i]
+                m = int(mantissa[i])
+                encbase = encBase[i]
+        return sign, m, encbase, e
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is not None:
+            value = asn1Spec.clone(value)
+
+        if value.isPlusInf:
+            return (0x40,), False, False
+        if value.isMinusInf:
+            return (0x41,), False, False
+        m, b, e = value
+        if not m:
+            return null, False, True
+        if b == 10:
+            return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), False, True
+        elif b == 2:
+            fo = 0x80  # binary encoding
+            ms, m, encbase, e = self._chooseEncBase(value)
+            if ms < 0:  # mantissa sign
+                fo |= 0x40  # sign bit
+            # exponenta & mantissa normalization
+            if encbase == 2:
+                while m & 0x1 == 0:
+                    m >>= 1
+                    e += 1
+            elif encbase == 8:
+                while m & 0x7 == 0:
+                    m >>= 3
+                    e += 1
+                fo |= 0x10
+            else:  # encbase = 16
+                while m & 0xf == 0:
+                    m >>= 4
+                    e += 1
+                fo |= 0x20
+            sf = 0  # scale factor
+            while m & 0x1 == 0:
+                m >>= 1
+                sf += 1
+            if sf > 3:
+                raise error.PyAsn1Error('Scale factor overflow')  # bug if raised
+            fo |= sf << 2
+            eo = null
+            if e == 0 or e == -1:
+                eo = int2oct(e & 0xff)
+            else:
+                while e not in (0, -1):
+                    eo = int2oct(e & 0xff) + eo
+                    e >>= 8
+                if e == 0 and eo and oct2int(eo[0]) & 0x80:
+                    eo = int2oct(0) + eo
+                if e == -1 and eo and not (oct2int(eo[0]) & 0x80):
+                    eo = int2oct(0xff) + eo
+            n = len(eo)
+            if n > 0xff:
+                raise error.PyAsn1Error('Real exponent overflow')
+            if n == 1:
+                pass
+            elif n == 2:
+                fo |= 1
+            elif n == 3:
+                fo |= 2
+            else:
+                fo |= 3
+                eo = int2oct(n & 0xff) + eo
+            po = null
+            while m:
+                po = int2oct(m & 0xff) + po
+                m >>= 8
+            substrate = int2oct(fo) + eo + po
+            return substrate, False, True
+        else:
+            raise error.PyAsn1Error('Prohibited Real base %s' % b)
+
+
+class SequenceEncoder(AbstractItemEncoder):
+    omitEmptyOptionals = False
+
+    # TODO: handling three flavors of input is too much -- split over codecs
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+        substrate = null
+
+        if asn1Spec is None:
+            # instance of ASN.1 schema
+            value.verifySizeSpec()
+
+            namedTypes = value.componentType
+
+            for idx, component in enumerate(value.values()):
+                if namedTypes:
+                    namedType = namedTypes[idx]
+
+                    if namedType.isOptional and not component.isValue:
+                            continue
+
+                    if namedType.isDefaulted and component == namedType.asn1Object:
+                            continue
+
+                    if self.omitEmptyOptionals:
+                        options.update(ifNotEmpty=namedType.isOptional)
+
+                chunk = encodeFun(component, asn1Spec, **options)
+
+                # wrap open type blob if needed
+                if namedTypes and namedType.openType:
+                    wrapType = namedType.asn1Object
+                    if wrapType.tagSet and not wrapType.isSameTypeWith(component):
+                        chunk = encodeFun(chunk, wrapType, **options)
+
+                substrate += chunk
+
+        else:
+            # bare Python value + ASN.1 schema
+            for idx, namedType in enumerate(asn1Spec.componentType.namedTypes):
+
+                try:
+                    component = value[namedType.name]
+
+                except KeyError:
+                    raise error.PyAsn1Error('Component name "%s" not found in %r' % (namedType.name, value))
+
+                if namedType.isOptional and namedType.name not in value:
+                    continue
+
+                if namedType.isDefaulted and component == namedType.asn1Object:
+                    continue
+
+                if self.omitEmptyOptionals:
+                    options.update(ifNotEmpty=namedType.isOptional)
+
+                chunk = encodeFun(component, asn1Spec[idx], **options)
+
+                # wrap open type blob if needed
+                if namedType.openType:
+                    wrapType = namedType.asn1Object
+                    if wrapType.tagSet and not wrapType.isSameTypeWith(component):
+                        chunk = encodeFun(chunk, wrapType, **options)
+
+                substrate += chunk
+
+        return substrate, True, True
+
+
+class SequenceOfEncoder(AbstractItemEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is None:
+            value.verifySizeSpec()
+        else:
+            asn1Spec = asn1Spec.componentType
+
+        substrate = null
+
+        for idx, component in enumerate(value):
+            substrate += encodeFun(value[idx], asn1Spec, **options)
+
+        return substrate, True, True
+
+
+class ChoiceEncoder(AbstractItemEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is None:
+            component = value.getComponent()
+        else:
+            names = [namedType.name for namedType in asn1Spec.componentType.namedTypes
+                     if namedType.name in value]
+            if len(names) != 1:
+                raise error.PyAsn1Error('%s components for Choice at %r' % (len(names) and 'Multiple ' or 'None ', value))
+
+            name = names[0]
+
+            component = value[name]
+            asn1Spec = asn1Spec[name]
+
+        return encodeFun(component, asn1Spec, **options), True, True
+
+
+class AnyEncoder(OctetStringEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is None:
+            value = value.asOctets()
+        elif not isOctetsType(value):
+            value = asn1Spec.clone(value).asOctets()
+
+        return value, not options.get('defMode', True), True
+
+
+tagMap = {
+    eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
+    univ.Boolean.tagSet: BooleanEncoder(),
+    univ.Integer.tagSet: IntegerEncoder(),
+    univ.BitString.tagSet: BitStringEncoder(),
+    univ.OctetString.tagSet: OctetStringEncoder(),
+    univ.Null.tagSet: NullEncoder(),
+    univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(),
+    univ.Enumerated.tagSet: IntegerEncoder(),
+    univ.Real.tagSet: RealEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.SequenceOf.tagSet: SequenceOfEncoder(),
+    univ.SetOf.tagSet: SequenceOfEncoder(),
+    univ.Choice.tagSet: ChoiceEncoder(),
+    # character string types
+    char.UTF8String.tagSet: OctetStringEncoder(),
+    char.NumericString.tagSet: OctetStringEncoder(),
+    char.PrintableString.tagSet: OctetStringEncoder(),
+    char.TeletexString.tagSet: OctetStringEncoder(),
+    char.VideotexString.tagSet: OctetStringEncoder(),
+    char.IA5String.tagSet: OctetStringEncoder(),
+    char.GraphicString.tagSet: OctetStringEncoder(),
+    char.VisibleString.tagSet: OctetStringEncoder(),
+    char.GeneralString.tagSet: OctetStringEncoder(),
+    char.UniversalString.tagSet: OctetStringEncoder(),
+    char.BMPString.tagSet: OctetStringEncoder(),
+    # useful types
+    useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
+    useful.GeneralizedTime.tagSet: OctetStringEncoder(),
+    useful.UTCTime.tagSet: OctetStringEncoder()
+}
+
+# Put in ambiguous & non-ambiguous types for faster codec lookup
+typeMap = {
+    univ.Boolean.typeId: BooleanEncoder(),
+    univ.Integer.typeId: IntegerEncoder(),
+    univ.BitString.typeId: BitStringEncoder(),
+    univ.OctetString.typeId: OctetStringEncoder(),
+    univ.Null.typeId: NullEncoder(),
+    univ.ObjectIdentifier.typeId: ObjectIdentifierEncoder(),
+    univ.Enumerated.typeId: IntegerEncoder(),
+    univ.Real.typeId: RealEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.Set.typeId: SequenceEncoder(),
+    univ.SetOf.typeId: SequenceOfEncoder(),
+    univ.Sequence.typeId: SequenceEncoder(),
+    univ.SequenceOf.typeId: SequenceOfEncoder(),
+    univ.Choice.typeId: ChoiceEncoder(),
+    univ.Any.typeId: AnyEncoder(),
+    # character string types
+    char.UTF8String.typeId: OctetStringEncoder(),
+    char.NumericString.typeId: OctetStringEncoder(),
+    char.PrintableString.typeId: OctetStringEncoder(),
+    char.TeletexString.typeId: OctetStringEncoder(),
+    char.VideotexString.typeId: OctetStringEncoder(),
+    char.IA5String.typeId: OctetStringEncoder(),
+    char.GraphicString.typeId: OctetStringEncoder(),
+    char.VisibleString.typeId: OctetStringEncoder(),
+    char.GeneralString.typeId: OctetStringEncoder(),
+    char.UniversalString.typeId: OctetStringEncoder(),
+    char.BMPString.typeId: OctetStringEncoder(),
+    # useful types
+    useful.ObjectDescriptor.typeId: OctetStringEncoder(),
+    useful.GeneralizedTime.typeId: OctetStringEncoder(),
+    useful.UTCTime.typeId: OctetStringEncoder()
+}
+
+
+class Encoder(object):
+    fixedDefLengthMode = None
+    fixedChunkSize = None
+
+    # noinspection PyDefaultArgument
+    def __init__(self, tagMap, typeMap={}):
+        self.__tagMap = tagMap
+        self.__typeMap = typeMap
+
+    def __call__(self, value, asn1Spec=None, **options):
+        try:
+            if asn1Spec is None:
+                typeId = value.typeId
+            else:
+                typeId = asn1Spec.typeId
+
+        except AttributeError:
+            raise error.PyAsn1Error('Value %r is not ASN.1 type instance '
+                                    'and "asn1Spec" not given' % (value,))
+
+        if debug.logger & debug.flagEncoder:
+            logger = debug.logger
+        else:
+            logger = None
+
+        if logger:
+            logger('encoder called in %sdef mode, chunk size %s for '
+                   'type %s, value:\n%s' % (not options.get('defMode', True) and 'in' or '', options.get('maxChunkSize', 0), asn1Spec is None and value.prettyPrintType() or asn1Spec.prettyPrintType(), value))
+
+        if self.fixedDefLengthMode is not None:
+            options.update(defMode=self.fixedDefLengthMode)
+
+        if self.fixedChunkSize is not None:
+            options.update(maxChunkSize=self.fixedChunkSize)
+
+
+        try:
+            concreteEncoder = self.__typeMap[typeId]
+
+            if logger:
+                logger('using value codec %s chosen by type ID %s' % (concreteEncoder.__class__.__name__, typeId))
+
+        except KeyError:
+            if asn1Spec is None:
+                tagSet = value.tagSet
+            else:
+                tagSet = asn1Spec.tagSet
+
+            # use base type for codec lookup to recover untagged types
+            baseTagSet = tag.TagSet(tagSet.baseTag, tagSet.baseTag)
+
+            try:
+                concreteEncoder = self.__tagMap[baseTagSet]
+
+            except KeyError:
+                raise error.PyAsn1Error('No encoder for %r (%s)' % (value, tagSet))
+
+            if logger:
+                logger('using value codec %s chosen by tagSet %s' % (concreteEncoder.__class__.__name__, tagSet))
+
+        substrate = concreteEncoder.encode(value, asn1Spec, self, **options)
+
+        if logger:
+            logger('codec %s built %s octets of substrate: %s\nencoder completed' % (concreteEncoder, len(substrate), debug.hexdump(substrate)))
+
+        return substrate
+
+#: Turns ASN.1 object into BER octet stream.
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a BER octet stream.
+#:
+#: Parameters
+#: ----------
+#: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
+#:     parameter is required to guide the encoding process.
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec:
+#:     Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:
+#: defMode: :py:class:`bool`
+#:     If `False`, produces indefinite length encoding
+#:
+#: maxChunkSize: :py:class:`int`
+#:     Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
+#:
+#: Returns
+#: -------
+#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#:     Given ASN.1 object encoded into BER octetstream
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#:     On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode Python value into BER with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> encode([1, 2, 3], asn1Spec=seq)
+#:    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+#: Encode ASN.1 value object into BER
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> seq.extend([1, 2, 3])
+#:    >>> encode(seq)
+#:    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+encode = Encoder(tagMap, typeMap)
diff --git a/pyasn1/codec/ber/eoo.py b/pyasn1/codec/ber/eoo.py
new file mode 100644
index 0000000..d4cd827
--- /dev/null
+++ b/pyasn1/codec/ber/eoo.py
@@ -0,0 +1,28 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1.type import base
+from pyasn1.type import tag
+
+__all__ = ['endOfOctets']
+
+
+class EndOfOctets(base.AbstractSimpleAsn1Item):
+    defaultValue = 0
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00)
+    )
+
+    _instance = None
+
+    def __new__(cls, *args, **kwargs):
+        if cls._instance is None:
+            cls._instance = object.__new__(cls, *args, **kwargs)
+
+        return cls._instance
+
+
+endOfOctets = EndOfOctets()
diff --git a/pyasn1/codec/cer/__init__.py b/pyasn1/codec/cer/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/pyasn1/codec/cer/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/pyasn1/codec/cer/decoder.py b/pyasn1/codec/cer/decoder.py
new file mode 100644
index 0000000..66572ec
--- /dev/null
+++ b/pyasn1/codec/cer/decoder.py
@@ -0,0 +1,114 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import error
+from pyasn1.codec.ber import decoder
+from pyasn1.compat.octets import oct2int
+from pyasn1.type import univ
+
+__all__ = ['decode']
+
+
+class BooleanDecoder(decoder.AbstractSimpleDecoder):
+    protoComponent = univ.Boolean(0)
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        head, tail = substrate[:length], substrate[length:]
+        if not head or length != 1:
+            raise error.PyAsn1Error('Not single-octet Boolean payload')
+        byte = oct2int(head[0])
+        # CER/DER specifies encoding of TRUE as 0xFF and FALSE as 0x0, while
+        # BER allows any non-zero value as TRUE; cf. sections 8.2.2. and 11.1 
+        # in https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
+        if byte == 0xff:
+            value = 1
+        elif byte == 0x00:
+            value = 0
+        else:
+            raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte)
+        return self._createComponent(asn1Spec, tagSet, value, **options), tail
+
+# TODO: prohibit non-canonical encoding
+BitStringDecoder = decoder.BitStringDecoder
+OctetStringDecoder = decoder.OctetStringDecoder
+RealDecoder = decoder.RealDecoder
+
+tagMap = decoder.tagMap.copy()
+tagMap.update(
+    {univ.Boolean.tagSet: BooleanDecoder(),
+     univ.BitString.tagSet: BitStringDecoder(),
+     univ.OctetString.tagSet: OctetStringDecoder(),
+     univ.Real.tagSet: RealDecoder()}
+)
+
+typeMap = decoder.typeMap.copy()
+
+# Put in non-ambiguous types for faster codec lookup
+for typeDecoder in tagMap.values():
+    if typeDecoder.protoComponent is not None:
+        typeId = typeDecoder.protoComponent.__class__.typeId
+        if typeId is not None and typeId not in typeMap:
+            typeMap[typeId] = typeDecoder
+
+
+class Decoder(decoder.Decoder):
+    pass
+
+
+#: Turns CER octet stream into an ASN.1 object.
+#:
+#: Takes CER octet-stream and decode it into an ASN.1 object
+#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#:     CER octet-stream
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
+#:     being decoded, *asn1Spec* may or may not be required. Most common reason for
+#:     it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+#:
+#: Returns
+#: -------
+#: : :py:class:`tuple`
+#:     A tuple of pyasn1 object recovered from CER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     and the unprocessed trailing portion of the *substrate* (may be empty)
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#:     On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode CER serialisation without ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00')
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+#: Decode CER serialisation with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00', asn1Spec=seq)
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+decode = Decoder(tagMap, decoder.typeMap)
diff --git a/pyasn1/codec/cer/encoder.py b/pyasn1/codec/cer/encoder.py
new file mode 100644
index 0000000..768d3c1
--- /dev/null
+++ b/pyasn1/codec/cer/encoder.py
@@ -0,0 +1,296 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import error
+from pyasn1.codec.ber import encoder
+from pyasn1.compat.octets import str2octs, null
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['encode']
+
+
+class BooleanEncoder(encoder.IntegerEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if value == 0:
+            substrate = (0,)
+        else:
+            substrate = (255,)
+        return substrate, False, False
+
+
+class RealEncoder(encoder.RealEncoder):
+    def _chooseEncBase(self, value):
+        m, b, e = value
+        return self._dropFloatingPoint(m, b, e)
+
+
+# specialized GeneralStringEncoder here
+
+class TimeEncoderMixIn(object):
+    zchar, = str2octs('Z')
+    pluschar, = str2octs('+')
+    minuschar, = str2octs('-')
+    commachar, = str2octs(',')
+    minLength = 12
+    maxLength = 19
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        # Encoding constraints:
+        # - minutes are mandatory, seconds are optional
+        # - subseconds must NOT be zero
+        # - no hanging fraction dot
+        # - time in UTC (Z)
+        # - only dot is allowed for fractions
+
+        if asn1Spec is not None:
+            value = asn1Spec.clone(value)
+
+        octets = value.asOctets()
+
+        if not self.minLength < len(octets) < self.maxLength:
+            raise error.PyAsn1Error('Length constraint violated: %r' % value)
+
+        if self.pluschar in octets or self.minuschar in octets:
+            raise error.PyAsn1Error('Must be UTC time: %r' % octets)
+
+        if octets[-1] != self.zchar:
+            raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % octets)
+
+        if self.commachar in octets:
+            raise error.PyAsn1Error('Comma in fractions disallowed: %r' % value)
+
+        options.update(maxChunkSize=1000)
+
+        return encoder.OctetStringEncoder.encodeValue(
+            self, value, asn1Spec, encodeFun, **options
+        )
+
+
+class GeneralizedTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
+    minLength = 12
+    maxLength = 19
+
+
+class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
+    minLength = 10
+    maxLength = 14
+
+
+class SetEncoder(encoder.SequenceEncoder):
+    @staticmethod
+    def _componentSortKey(componentAndType):
+        """Sort SET components by tag
+
+        Sort regardless of the Choice value (static sort)
+        """
+        component, asn1Spec = componentAndType
+
+        if asn1Spec is None:
+            asn1Spec = component
+
+        if asn1Spec.typeId == univ.Choice.typeId and not asn1Spec.tagSet:
+            if asn1Spec.tagSet:
+                return asn1Spec.tagSet
+            else:
+                return asn1Spec.componentType.minTagSet
+        else:
+            return asn1Spec.tagSet
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+        substrate = null
+
+        comps = []
+        compsMap = {}
+
+        if asn1Spec is None:
+            # instance of ASN.1 schema
+            value.verifySizeSpec()
+
+            namedTypes = value.componentType
+
+            for idx, component in enumerate(value.values()):
+                if namedTypes:
+                    namedType = namedTypes[idx]
+
+                    if namedType.isOptional and not component.isValue:
+                            continue
+
+                    if namedType.isDefaulted and component == namedType.asn1Object:
+                            continue
+
+                    compsMap[id(component)] = namedType
+
+                else:
+                    compsMap[id(component)] = None
+
+                comps.append((component, asn1Spec))
+
+        else:
+            # bare Python value + ASN.1 schema
+            for idx, namedType in enumerate(asn1Spec.componentType.namedTypes):
+
+                try:
+                    component = value[namedType.name]
+
+                except KeyError:
+                    raise error.PyAsn1Error('Component name "%s" not found in %r' % (namedType.name, value))
+
+                if namedType.isOptional and namedType.name not in value:
+                    continue
+
+                if namedType.isDefaulted and component == namedType.asn1Object:
+                    continue
+
+                compsMap[id(component)] = namedType
+                comps.append((component, asn1Spec[idx]))
+
+        for comp, compType in sorted(comps, key=self._componentSortKey):
+            namedType = compsMap[id(comp)]
+
+            if namedType:
+                options.update(ifNotEmpty=namedType.isOptional)
+
+            chunk = encodeFun(comp, compType, **options)
+
+            # wrap open type blob if needed
+            if namedType and namedType.openType:
+                wrapType = namedType.asn1Object
+                if wrapType.tagSet and not wrapType.isSameTypeWith(comp):
+                    chunk = encodeFun(chunk, wrapType, **options)
+
+            substrate += chunk
+
+        return substrate, True, True
+
+
+class SetOfEncoder(encoder.SequenceOfEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is None:
+            value.verifySizeSpec()
+        else:
+            asn1Spec = asn1Spec.componentType
+
+        components = [encodeFun(x, asn1Spec, **options)
+                      for x in value]
+
+        # sort by serialised and padded components
+        if len(components) > 1:
+            zero = str2octs('\x00')
+            maxLen = max(map(len, components))
+            paddedComponents = [
+                (x.ljust(maxLen, zero), x) for x in components
+                ]
+            paddedComponents.sort(key=lambda x: x[0])
+
+            components = [x[1] for x in paddedComponents]
+
+        substrate = null.join(components)
+
+        return substrate, True, True
+
+
+class SequenceEncoder(encoder.SequenceEncoder):
+    omitEmptyOptionals = True
+
+
+class SequenceOfEncoder(encoder.SequenceOfEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+        if options.get('ifNotEmpty', False) and not len(value):
+            return null, True, True
+
+        if asn1Spec is None:
+            value.verifySizeSpec()
+        else:
+            asn1Spec = asn1Spec.componentType
+
+        substrate = null
+
+        for idx, component in enumerate(value):
+            substrate += encodeFun(value[idx], asn1Spec, **options)
+
+        return substrate, True, True
+
+
+tagMap = encoder.tagMap.copy()
+tagMap.update({
+    univ.Boolean.tagSet: BooleanEncoder(),
+    univ.Real.tagSet: RealEncoder(),
+    useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
+    useful.UTCTime.tagSet: UTCTimeEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.SetOf.tagSet: SetOfEncoder(),
+    univ.Sequence.typeId: SequenceEncoder()
+})
+
+typeMap = encoder.typeMap.copy()
+typeMap.update({
+    univ.Boolean.typeId: BooleanEncoder(),
+    univ.Real.typeId: RealEncoder(),
+    useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(),
+    useful.UTCTime.typeId: UTCTimeEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.Set.typeId: SetEncoder(),
+    univ.SetOf.typeId: SetOfEncoder(),
+    univ.Sequence.typeId: SequenceEncoder(),
+    univ.SequenceOf.typeId: SequenceOfEncoder()
+})
+
+
+class Encoder(encoder.Encoder):
+    fixedDefLengthMode = False
+    fixedChunkSize = 1000
+
+#: Turns ASN.1 object into CER octet stream.
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a CER octet stream.
+#:
+#: Parameters
+#: ----------
+#: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
+#:     parameter is required to guide the encoding process.
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec:
+#:     Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:
+#: Returns
+#: -------
+#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#:     Given ASN.1 object encoded into BER octet-stream
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#:     On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode Python value into CER with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> encode([1, 2, 3], asn1Spec=seq)
+#:    b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00'
+#:
+#: Encode ASN.1 value object into CER
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> seq.extend([1, 2, 3])
+#:    >>> encode(seq)
+#:    b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00'
+#:
+encode = Encoder(tagMap, typeMap)
+
+# EncoderFactory queries class instance and builds a map of tags -> encoders
diff --git a/pyasn1/codec/der/__init__.py b/pyasn1/codec/der/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/pyasn1/codec/der/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/pyasn1/codec/der/decoder.py b/pyasn1/codec/der/decoder.py
new file mode 100644
index 0000000..f67d025
--- /dev/null
+++ b/pyasn1/codec/der/decoder.py
@@ -0,0 +1,94 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1.codec.cer import decoder
+from pyasn1.type import univ
+
+__all__ = ['decode']
+
+
+class BitStringDecoder(decoder.BitStringDecoder):
+    supportConstructedForm = False
+
+
+class OctetStringDecoder(decoder.OctetStringDecoder):
+    supportConstructedForm = False
+
+# TODO: prohibit non-canonical encoding
+RealDecoder = decoder.RealDecoder
+
+tagMap = decoder.tagMap.copy()
+tagMap.update(
+    {univ.BitString.tagSet: BitStringDecoder(),
+     univ.OctetString.tagSet: OctetStringDecoder(),
+     univ.Real.tagSet: RealDecoder()}
+)
+
+typeMap = decoder.typeMap.copy()
+
+# Put in non-ambiguous types for faster codec lookup
+for typeDecoder in tagMap.values():
+    if typeDecoder.protoComponent is not None:
+        typeId = typeDecoder.protoComponent.__class__.typeId
+        if typeId is not None and typeId not in typeMap:
+            typeMap[typeId] = typeDecoder
+
+
+class Decoder(decoder.Decoder):
+    supportIndefLength = False
+
+
+#: Turns DER octet stream into an ASN.1 object.
+#:
+#: Takes DER octet-stream and decode it into an ASN.1 object
+#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#:     DER octet-stream
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
+#:     being decoded, *asn1Spec* may or may not be required. Most common reason for
+#:     it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+#:
+#: Returns
+#: -------
+#: : :py:class:`tuple`
+#:     A tuple of pyasn1 object recovered from DER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     and the unprocessed trailing portion of the *substrate* (may be empty)
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#:     On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode DER serialisation without ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+#: Decode DER serialisation with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+decode = Decoder(tagMap, typeMap)
diff --git a/pyasn1/codec/der/encoder.py b/pyasn1/codec/der/encoder.py
new file mode 100644
index 0000000..756d9fe
--- /dev/null
+++ b/pyasn1/codec/der/encoder.py
@@ -0,0 +1,107 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import error
+from pyasn1.codec.cer import encoder
+from pyasn1.type import univ
+
+__all__ = ['encode']
+
+
+class SetEncoder(encoder.SetEncoder):
+    @staticmethod
+    def _componentSortKey(componentAndType):
+        """Sort SET components by tag
+
+        Sort depending on the actual Choice value (dynamic sort)
+        """
+        component, asn1Spec = componentAndType
+
+        if asn1Spec is None:
+            compType = component
+        else:
+            compType = asn1Spec
+
+        if compType.typeId == univ.Choice.typeId and not compType.tagSet:
+            if asn1Spec is None:
+                return component.getComponent().tagSet
+            else:
+                # TODO: move out of sorting key function
+                names = [namedType.name for namedType in asn1Spec.componentType.namedTypes
+                         if namedType.name in component]
+                if len(names) != 1:
+                    raise error.PyAsn1Error(
+                        '%s components for Choice at %r' % (len(names) and 'Multiple ' or 'None ', component))
+
+                # TODO: support nested CHOICE ordering
+                return asn1Spec[names[0]].tagSet
+
+        else:
+            return compType.tagSet
+
+tagMap = encoder.tagMap.copy()
+tagMap.update({
+    # Set & SetOf have same tags
+    univ.Set.tagSet: SetEncoder()
+})
+
+typeMap = encoder.typeMap.copy()
+typeMap.update({
+    # Set & SetOf have same tags
+    univ.Set.typeId: SetEncoder()
+})
+
+
+class Encoder(encoder.Encoder):
+    fixedDefLengthMode = True
+    fixedChunkSize = 0
+
+#: Turns ASN.1 object into DER octet stream.
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a DER octet stream.
+#:
+#: Parameters
+#: ----------
+#: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
+#:     parameter is required to guide the encoding process.
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec:
+#:     Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:
+#: Returns
+#: -------
+#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#:     Given ASN.1 object encoded into BER octet-stream
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#:     On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode Python value into DER with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> encode([1, 2, 3], asn1Spec=seq)
+#:    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+#: Encode ASN.1 value object into DER
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> seq.extend([1, 2, 3])
+#:    >>> encode(seq)
+#:    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+encode = Encoder(tagMap, typeMap)
diff --git a/pyasn1/codec/native/__init__.py b/pyasn1/codec/native/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/pyasn1/codec/native/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/pyasn1/codec/native/decoder.py b/pyasn1/codec/native/decoder.py
new file mode 100644
index 0000000..78fcda6
--- /dev/null
+++ b/pyasn1/codec/native/decoder.py
@@ -0,0 +1,214 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import debug
+from pyasn1 import error
+from pyasn1.type import base
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['decode']
+
+
+class AbstractScalarDecoder(object):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        return asn1Spec.clone(pyObject)
+
+
+class BitStringDecoder(AbstractScalarDecoder):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        return asn1Spec.clone(univ.BitString.fromBinaryString(pyObject))
+
+
+class SequenceOrSetDecoder(object):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        asn1Value = asn1Spec.clone()
+
+        componentsTypes = asn1Spec.componentType
+
+        for field in asn1Value:
+            if field in pyObject:
+                asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options)
+
+        return asn1Value
+
+
+class SequenceOfOrSetOfDecoder(object):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        asn1Value = asn1Spec.clone()
+
+        for pyValue in pyObject:
+            asn1Value.append(decodeFun(pyValue, asn1Spec.componentType), **options)
+
+        return asn1Value
+
+
+class ChoiceDecoder(object):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        asn1Value = asn1Spec.clone()
+
+        componentsTypes = asn1Spec.componentType
+
+        for field in pyObject:
+            if field in componentsTypes:
+                asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options)
+                break
+
+        return asn1Value
+
+
+tagMap = {
+    univ.Integer.tagSet: AbstractScalarDecoder(),
+    univ.Boolean.tagSet: AbstractScalarDecoder(),
+    univ.BitString.tagSet: BitStringDecoder(),
+    univ.OctetString.tagSet: AbstractScalarDecoder(),
+    univ.Null.tagSet: AbstractScalarDecoder(),
+    univ.ObjectIdentifier.tagSet: AbstractScalarDecoder(),
+    univ.Enumerated.tagSet: AbstractScalarDecoder(),
+    univ.Real.tagSet: AbstractScalarDecoder(),
+    univ.Sequence.tagSet: SequenceOrSetDecoder(),  # conflicts with SequenceOf
+    univ.Set.tagSet: SequenceOrSetDecoder(),  # conflicts with SetOf
+    univ.Choice.tagSet: ChoiceDecoder(),  # conflicts with Any
+    # character string types
+    char.UTF8String.tagSet: AbstractScalarDecoder(),
+    char.NumericString.tagSet: AbstractScalarDecoder(),
+    char.PrintableString.tagSet: AbstractScalarDecoder(),
+    char.TeletexString.tagSet: AbstractScalarDecoder(),
+    char.VideotexString.tagSet: AbstractScalarDecoder(),
+    char.IA5String.tagSet: AbstractScalarDecoder(),
+    char.GraphicString.tagSet: AbstractScalarDecoder(),
+    char.VisibleString.tagSet: AbstractScalarDecoder(),
+    char.GeneralString.tagSet: AbstractScalarDecoder(),
+    char.UniversalString.tagSet: AbstractScalarDecoder(),
+    char.BMPString.tagSet: AbstractScalarDecoder(),
+    # useful types
+    useful.ObjectDescriptor.tagSet: AbstractScalarDecoder(),
+    useful.GeneralizedTime.tagSet: AbstractScalarDecoder(),
+    useful.UTCTime.tagSet: AbstractScalarDecoder()
+}
+
+# Put in ambiguous & non-ambiguous types for faster codec lookup
+typeMap = {
+    univ.Integer.typeId: AbstractScalarDecoder(),
+    univ.Boolean.typeId: AbstractScalarDecoder(),
+    univ.BitString.typeId: BitStringDecoder(),
+    univ.OctetString.typeId: AbstractScalarDecoder(),
+    univ.Null.typeId: AbstractScalarDecoder(),
+    univ.ObjectIdentifier.typeId: AbstractScalarDecoder(),
+    univ.Enumerated.typeId: AbstractScalarDecoder(),
+    univ.Real.typeId: AbstractScalarDecoder(),
+    # ambiguous base types
+    univ.Set.typeId: SequenceOrSetDecoder(),
+    univ.SetOf.typeId: SequenceOfOrSetOfDecoder(),
+    univ.Sequence.typeId: SequenceOrSetDecoder(),
+    univ.SequenceOf.typeId: SequenceOfOrSetOfDecoder(),
+    univ.Choice.typeId: ChoiceDecoder(),
+    univ.Any.typeId: AbstractScalarDecoder(),
+    # character string types
+    char.UTF8String.typeId: AbstractScalarDecoder(),
+    char.NumericString.typeId: AbstractScalarDecoder(),
+    char.PrintableString.typeId: AbstractScalarDecoder(),
+    char.TeletexString.typeId: AbstractScalarDecoder(),
+    char.VideotexString.typeId: AbstractScalarDecoder(),
+    char.IA5String.typeId: AbstractScalarDecoder(),
+    char.GraphicString.typeId: AbstractScalarDecoder(),
+    char.VisibleString.typeId: AbstractScalarDecoder(),
+    char.GeneralString.typeId: AbstractScalarDecoder(),
+    char.UniversalString.typeId: AbstractScalarDecoder(),
+    char.BMPString.typeId: AbstractScalarDecoder(),
+    # useful types
+    useful.ObjectDescriptor.typeId: AbstractScalarDecoder(),
+    useful.GeneralizedTime.typeId: AbstractScalarDecoder(),
+    useful.UTCTime.typeId: AbstractScalarDecoder()
+}
+
+
+class Decoder(object):
+
+    # noinspection PyDefaultArgument
+    def __init__(self, tagMap, typeMap):
+        self.__tagMap = tagMap
+        self.__typeMap = typeMap
+
+    def __call__(self, pyObject, asn1Spec, **options):
+        if debug.logger & debug.flagDecoder:
+            logger = debug.logger
+        else:
+            logger = None
+        if logger:
+            debug.scope.push(type(pyObject).__name__)
+            logger('decoder called at scope %s, working with type %s' % (debug.scope, type(pyObject).__name__))
+
+        if asn1Spec is None or not isinstance(asn1Spec, base.Asn1Item):
+            raise error.PyAsn1Error('asn1Spec is not valid (should be an instance of an ASN.1 Item, not %s)' % asn1Spec.__class__.__name__)
+
+        try:
+            valueDecoder = self.__typeMap[asn1Spec.typeId]
+
+        except KeyError:
+            # use base type for codec lookup to recover untagged types
+            baseTagSet = tag.TagSet(asn1Spec.tagSet.baseTag, asn1Spec.tagSet.baseTag)
+
+            try:
+                valueDecoder = self.__tagMap[baseTagSet]
+            except KeyError:
+                raise error.PyAsn1Error('Unknown ASN.1 tag %s' % asn1Spec.tagSet)
+
+        if logger:
+            logger('calling decoder %s on Python type %s <%s>' % (type(valueDecoder).__name__, type(pyObject).__name__, repr(pyObject)))
+
+        value = valueDecoder(pyObject, asn1Spec, self, **options)
+
+        if logger:
+            logger('decoder %s produced ASN.1 type %s <%s>' % (type(valueDecoder).__name__, type(value).__name__, repr(value)))
+            debug.scope.pop()
+
+        return value
+
+
+#: Turns Python objects of built-in types into ASN.1 objects.
+#:
+#: Takes Python objects of built-in types and turns them into a tree of
+#: ASN.1 objects (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: pyObject: :py:class:`object`
+#:     A scalar or nested Python objects
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A pyasn1 type object to act as a template guiding the decoder. It is required
+#:     for successful interpretation of Python objects mapping into their ASN.1
+#:     representations.
+#:
+#: Returns
+#: -------
+#: : :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A scalar or constructed pyasn1 object
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#:     On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode native Python object into ASN.1 objects with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> s, _ = decode([1, 2, 3], asn1Spec=seq)
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+decode = Decoder(tagMap, typeMap)
diff --git a/pyasn1/codec/native/encoder.py b/pyasn1/codec/native/encoder.py
new file mode 100644
index 0000000..87e50f2
--- /dev/null
+++ b/pyasn1/codec/native/encoder.py
@@ -0,0 +1,229 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+try:
+    from collections import OrderedDict
+
+except ImportError:
+    OrderedDict = dict
+
+from pyasn1 import debug
+from pyasn1 import error
+from pyasn1.type import base
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['encode']
+
+
+class AbstractItemEncoder(object):
+    def encode(self, value, encodeFun, **options):
+        raise error.PyAsn1Error('Not implemented')
+
+
+class BooleanEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return bool(value)
+
+
+class IntegerEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return int(value)
+
+
+class BitStringEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return str(value)
+
+
+class OctetStringEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return value.asOctets()
+
+
+class TextStringEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return str(value)
+
+
+class NullEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return None
+
+
+class ObjectIdentifierEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return str(value)
+
+
+class RealEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return float(value)
+
+
+class SetEncoder(AbstractItemEncoder):
+    protoDict = dict
+
+    def encode(self, value, encodeFun, **options):
+        value.verifySizeSpec()
+
+        namedTypes = value.componentType
+        substrate = self.protoDict()
+
+        for idx, (key, subValue) in enumerate(value.items()):
+            if namedTypes and namedTypes[idx].isOptional and not value[idx].isValue:
+                continue
+            substrate[key] = encodeFun(subValue, **options)
+        return substrate
+
+
+class SequenceEncoder(SetEncoder):
+    protoDict = OrderedDict
+
+
+class SequenceOfEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        value.verifySizeSpec()
+        return [encodeFun(x, **options) for x in value]
+
+
+class ChoiceEncoder(SequenceEncoder):
+    pass
+
+
+class AnyEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return value.asOctets()
+
+
+tagMap = {
+    univ.Boolean.tagSet: BooleanEncoder(),
+    univ.Integer.tagSet: IntegerEncoder(),
+    univ.BitString.tagSet: BitStringEncoder(),
+    univ.OctetString.tagSet: OctetStringEncoder(),
+    univ.Null.tagSet: NullEncoder(),
+    univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(),
+    univ.Enumerated.tagSet: IntegerEncoder(),
+    univ.Real.tagSet: RealEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.SequenceOf.tagSet: SequenceOfEncoder(),
+    univ.SetOf.tagSet: SequenceOfEncoder(),
+    univ.Choice.tagSet: ChoiceEncoder(),
+    # character string types
+    char.UTF8String.tagSet: TextStringEncoder(),
+    char.NumericString.tagSet: TextStringEncoder(),
+    char.PrintableString.tagSet: TextStringEncoder(),
+    char.TeletexString.tagSet: TextStringEncoder(),
+    char.VideotexString.tagSet: TextStringEncoder(),
+    char.IA5String.tagSet: TextStringEncoder(),
+    char.GraphicString.tagSet: TextStringEncoder(),
+    char.VisibleString.tagSet: TextStringEncoder(),
+    char.GeneralString.tagSet: TextStringEncoder(),
+    char.UniversalString.tagSet: TextStringEncoder(),
+    char.BMPString.tagSet: TextStringEncoder(),
+    # useful types
+    useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
+    useful.GeneralizedTime.tagSet: OctetStringEncoder(),
+    useful.UTCTime.tagSet: OctetStringEncoder()
+}
+
+# Type-to-codec map for ambiguous ASN.1 types
+typeMap = {
+    univ.Set.typeId: SetEncoder(),
+    univ.SetOf.typeId: SequenceOfEncoder(),
+    univ.Sequence.typeId: SequenceEncoder(),
+    univ.SequenceOf.typeId: SequenceOfEncoder(),
+    univ.Choice.typeId: ChoiceEncoder(),
+    univ.Any.typeId: AnyEncoder()
+}
+
+
+class Encoder(object):
+
+    # noinspection PyDefaultArgument
+    def __init__(self, tagMap, typeMap={}):
+        self.__tagMap = tagMap
+        self.__typeMap = typeMap
+
+    def __call__(self, value, **options):
+        if not isinstance(value, base.Asn1Item):
+            raise error.PyAsn1Error('value is not valid (should be an instance of an ASN.1 Item)')
+
+        if debug.logger & debug.flagEncoder:
+            logger = debug.logger
+        else:
+            logger = None
+
+        if logger:
+            debug.scope.push(type(value).__name__)
+            logger('encoder called for type %s <%s>' % (type(value).__name__, value.prettyPrint()))
+
+        tagSet = value.tagSet
+
+        try:
+            concreteEncoder = self.__typeMap[value.typeId]
+
+        except KeyError:
+            # use base type for codec lookup to recover untagged types
+            baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
+
+            try:
+                concreteEncoder = self.__tagMap[baseTagSet]
+
+            except KeyError:
+                raise error.PyAsn1Error('No encoder for %s' % (value,))
+
+        if logger:
+            logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
+
+        pyObject = concreteEncoder.encode(value, self, **options)
+
+        if logger:
+            logger('encoder %s produced: %s' % (type(concreteEncoder).__name__, repr(pyObject)))
+            debug.scope.pop()
+
+        return pyObject
+
+
+#: Turns ASN.1 object into a Python built-in type object(s).
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a Python built-in type or a tree
+#: of those.
+#:
+#: One exception is that instead of :py:class:`dict`, the :py:class:`OrderedDict`
+#: can be produced (whenever available) to preserve ordering of the components
+#: in ASN.1 SEQUENCE.
+#:
+#: Parameters
+#: ----------
+#  asn1Value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     pyasn1 object to encode (or a tree of them)
+#:
+#: Returns
+#: -------
+#: : :py:class:`object`
+#:     Python built-in type instance (or a tree of them)
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#:     On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode ASN.1 value object into native Python types
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> seq.extend([1, 2, 3])
+#:    >>> encode(seq)
+#:    [1, 2, 3]
+#:
+encode = Encoder(tagMap, typeMap)
diff --git a/pyasn1/compat/__init__.py b/pyasn1/compat/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/pyasn1/compat/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/pyasn1/compat/binary.py b/pyasn1/compat/binary.py
new file mode 100644
index 0000000..c38a650
--- /dev/null
+++ b/pyasn1/compat/binary.py
@@ -0,0 +1,33 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from sys import version_info
+
+if version_info[0:2] < (2, 6):
+    def bin(value):
+        bitstring = []
+
+        if value > 0:
+            prefix = '0b'
+        elif value < 0:
+            prefix = '-0b'
+            value = abs(value)
+        else:
+            prefix = '0b0'
+
+        while value:
+            if value & 1 == 1:
+                bitstring.append('1')
+            else:
+                bitstring.append('0')
+
+            value >>= 1
+
+        bitstring.reverse()
+
+        return prefix + ''.join(bitstring)
+else:
+    bin = bin
diff --git a/pyasn1/compat/calling.py b/pyasn1/compat/calling.py
new file mode 100644
index 0000000..c60b50d
--- /dev/null
+++ b/pyasn1/compat/calling.py
@@ -0,0 +1,20 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from sys import version_info
+
+__all__ = ['callable']
+
+
+if (2, 7) < version_info[:2] < (3, 2):
+    import collections
+
+    def callable(x):
+        return isinstance(x, collections.Callable)
+
+else:
+
+    callable = callable
diff --git a/pyasn1/compat/dateandtime.py b/pyasn1/compat/dateandtime.py
new file mode 100644
index 0000000..27526ad
--- /dev/null
+++ b/pyasn1/compat/dateandtime.py
@@ -0,0 +1,22 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import time
+from datetime import datetime
+from sys import version_info
+
+__all__ = ['strptime']
+
+
+if version_info[:2] <= (2, 4):
+
+    def strptime(text, dateFormat):
+        return datetime(*(time.strptime(text, dateFormat)[0:6]))
+
+else:
+
+    def strptime(text, dateFormat):
+        return datetime.strptime(text, dateFormat)
diff --git a/pyasn1/compat/integer.py b/pyasn1/compat/integer.py
new file mode 100644
index 0000000..bb3d099
--- /dev/null
+++ b/pyasn1/compat/integer.py
@@ -0,0 +1,110 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import platform
+
+    implementation = platform.python_implementation()
+
+except (ImportError, AttributeError):
+    implementation = 'CPython'
+
+from pyasn1.compat.octets import oct2int, null, ensureString
+
+if sys.version_info[0:2] < (3, 2) or implementation != 'CPython':
+    from binascii import a2b_hex, b2a_hex
+
+    if sys.version_info[0] > 2:
+        long = int
+
+    def from_bytes(octets, signed=False):
+        if not octets:
+            return 0
+
+        value = long(b2a_hex(ensureString(octets)), 16)
+
+        if signed and oct2int(octets[0]) & 0x80:
+            return value - (1 << len(octets) * 8)
+
+        return value
+
+    def to_bytes(value, signed=False, length=0):
+        if value < 0:
+            if signed:
+                bits = bitLength(value)
+
+                # two's complement form
+                maxValue = 1 << bits
+                valueToEncode = (value + maxValue) % maxValue
+
+            else:
+                raise OverflowError('can\'t convert negative int to unsigned')
+        elif value == 0 and length == 0:
+            return null
+        else:
+            bits = 0
+            valueToEncode = value
+
+        hexValue = hex(valueToEncode)[2:]
+        if hexValue.endswith('L'):
+            hexValue = hexValue[:-1]
+
+        if len(hexValue) & 1:
+            hexValue = '0' + hexValue
+
+        # padding may be needed for two's complement encoding
+        if value != valueToEncode or length:
+            hexLength = len(hexValue) * 4
+
+            padLength = max(length, bits)
+
+            if padLength > hexLength:
+                hexValue = '00' * ((padLength - hexLength - 1) // 8 + 1) + hexValue
+            elif length and hexLength - length > 7:
+                raise OverflowError('int too big to convert')
+
+        firstOctet = int(hexValue[:2], 16)
+
+        if signed:
+            if firstOctet & 0x80:
+                if value >= 0:
+                    hexValue = '00' + hexValue
+            elif value < 0:
+                hexValue = 'ff' + hexValue
+
+        octets_value = a2b_hex(hexValue)
+
+        return octets_value
+
+    def bitLength(number):
+        # bits in unsigned number
+        hexValue = hex(abs(number))
+        bits = len(hexValue) - 2
+        if hexValue.endswith('L'):
+            bits -= 1
+        if bits & 1:
+            bits += 1
+        bits *= 4
+        # TODO: strip lhs zeros
+        return bits
+
+else:
+
+    def from_bytes(octets, signed=False):
+        return int.from_bytes(bytes(octets), 'big', signed=signed)
+
+    def to_bytes(value, signed=False, length=0):
+        length = max(value.bit_length(), length)
+
+        if signed and length % 8 == 0:
+            length += 1
+
+        return value.to_bytes(length // 8 + (length % 8 and 1 or 0), 'big', signed=signed)
+
+    def bitLength(number):
+        return int(number).bit_length()
diff --git a/pyasn1/compat/octets.py b/pyasn1/compat/octets.py
new file mode 100644
index 0000000..a06db5d
--- /dev/null
+++ b/pyasn1/compat/octets.py
@@ -0,0 +1,46 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from sys import version_info
+
+if version_info[0] <= 2:
+    int2oct = chr
+    # noinspection PyPep8
+    ints2octs = lambda s: ''.join([int2oct(x) for x in s])
+    null = ''
+    oct2int = ord
+    # TODO: refactor to return a sequence of ints
+    # noinspection PyPep8
+    octs2ints = lambda s: [oct2int(x) for x in s]
+    # noinspection PyPep8
+    str2octs = lambda x: x
+    # noinspection PyPep8
+    octs2str = lambda x: x
+    # noinspection PyPep8
+    isOctetsType = lambda s: isinstance(s, str)
+    # noinspection PyPep8
+    isStringType = lambda s: isinstance(s, (str, unicode))
+    # noinspection PyPep8
+    ensureString = str
+else:
+    ints2octs = bytes
+    # noinspection PyPep8
+    int2oct = lambda x: ints2octs((x,))
+    null = ints2octs()
+    # noinspection PyPep8
+    oct2int = lambda x: x
+    # noinspection PyPep8
+    octs2ints = lambda x: x
+    # noinspection PyPep8
+    str2octs = lambda x: x.encode('iso-8859-1')
+    # noinspection PyPep8
+    octs2str = lambda x: x.decode('iso-8859-1')
+    # noinspection PyPep8
+    isOctetsType = lambda s: isinstance(s, bytes)
+    # noinspection PyPep8
+    isStringType = lambda s: isinstance(s, str)
+    # noinspection PyPep8
+    ensureString = bytes
diff --git a/pyasn1/compat/string.py b/pyasn1/compat/string.py
new file mode 100644
index 0000000..4d8a045
--- /dev/null
+++ b/pyasn1/compat/string.py
@@ -0,0 +1,26 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from sys import version_info
+
+if version_info[:2] <= (2, 5):
+
+    def partition(string, sep):
+        try:
+            a, c = string.split(sep, 1)
+
+        except ValueError:
+            a, b, c = string, '', ''
+
+        else:
+            b = sep
+
+        return a, b, c
+
+else:
+
+    def partition(string, sep):
+        return string.partition(sep)
diff --git a/pyasn1/debug.py b/pyasn1/debug.py
new file mode 100644
index 0000000..ab72fa8
--- /dev/null
+++ b/pyasn1/debug.py
@@ -0,0 +1,145 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import logging
+
+from pyasn1 import __version__
+from pyasn1 import error
+from pyasn1.compat.octets import octs2ints
+
+__all__ = ['Debug', 'setLogger', 'hexdump']
+
+flagNone = 0x0000
+flagEncoder = 0x0001
+flagDecoder = 0x0002
+flagAll = 0xffff
+
+flagMap = {
+    'none': flagNone,
+    'encoder': flagEncoder,
+    'decoder': flagDecoder,
+    'all': flagAll
+}
+
+
+class Printer(object):
+    # noinspection PyShadowingNames
+    def __init__(self, logger=None, handler=None, formatter=None):
+        if logger is None:
+            logger = logging.getLogger('pyasn1')
+
+        logger.setLevel(logging.DEBUG)
+
+        if handler is None:
+            handler = logging.StreamHandler()
+
+        if formatter is None:
+            formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s')
+
+        handler.setFormatter(formatter)
+        handler.setLevel(logging.DEBUG)
+        logger.addHandler(handler)
+
+        self.__logger = logger
+
+    def __call__(self, msg):
+        self.__logger.debug(msg)
+
+    def __str__(self):
+        return '<python logging>'
+
+
+if hasattr(logging, 'NullHandler'):
+    NullHandler = logging.NullHandler
+
+else:
+    # Python 2.6 and older
+    class NullHandler(logging.Handler):
+        def emit(self, record):
+            pass
+
+
+class Debug(object):
+    defaultPrinter = Printer()
+
+    def __init__(self, *flags, **options):
+        self._flags = flagNone
+
+        if 'loggerName' in options:
+            # route our logs to parent logger
+            self._printer = Printer(
+                logger=logging.getLogger(options['loggerName']),
+                handler=NullHandler()
+            )
+
+        elif 'printer' in options:
+            self._printer = options.get('printer')
+
+        else:
+            self._printer = self.defaultPrinter
+
+        self._printer('running pyasn1 %s, debug flags %s' % (__version__, ', '.join(flags)))
+
+        for flag in flags:
+            inverse = flag and flag[0] in ('!', '~')
+            if inverse:
+                flag = flag[1:]
+            try:
+                if inverse:
+                    self._flags &= ~flagMap[flag]
+                else:
+                    self._flags |= flagMap[flag]
+            except KeyError:
+                raise error.PyAsn1Error('bad debug flag %s' % flag)
+
+            self._printer("debug category '%s' %s" % (flag, inverse and 'disabled' or 'enabled'))
+
+    def __str__(self):
+        return 'logger %s, flags %x' % (self._printer, self._flags)
+
+    def __call__(self, msg):
+        self._printer(msg)
+
+    def __and__(self, flag):
+        return self._flags & flag
+
+    def __rand__(self, flag):
+        return flag & self._flags
+
+
+logger = 0
+
+
+def setLogger(userLogger):
+    global logger
+
+    if userLogger:
+        logger = userLogger
+    else:
+        logger = 0
+
+
+def hexdump(octets):
+    return ' '.join(
+        ['%s%.2X' % (n % 16 == 0 and ('\n%.5d: ' % n) or '', x)
+         for n, x in zip(range(len(octets)), octs2ints(octets))]
+    )
+
+
+class Scope(object):
+    def __init__(self):
+        self._list = []
+
+    def __str__(self): return '.'.join(self._list)
+
+    def push(self, token):
+        self._list.append(token)
+
+    def pop(self):
+        return self._list.pop()
+
+
+scope = Scope()
diff --git a/pyasn1/error.py b/pyasn1/error.py
new file mode 100644
index 0000000..c05e65c
--- /dev/null
+++ b/pyasn1/error.py
@@ -0,0 +1,29 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+
+
+class PyAsn1Error(Exception):
+    """Create pyasn1 exception object
+
+    The `PyAsn1Error` exception represents generic, usually fatal, error.
+    """
+
+
+class ValueConstraintError(PyAsn1Error):
+    """Create pyasn1 exception object
+
+    The `ValueConstraintError` exception indicates an ASN.1 value
+    constraint violation.
+    """
+
+
+class SubstrateUnderrunError(PyAsn1Error):
+    """Create pyasn1 exception object
+
+    The `SubstrateUnderrunError` exception indicates insufficient serialised
+    data on input of a deserialisation routine.
+    """
diff --git a/pyasn1/type/__init__.py b/pyasn1/type/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/pyasn1/type/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/pyasn1/type/base.py b/pyasn1/type/base.py
new file mode 100644
index 0000000..adaab22
--- /dev/null
+++ b/pyasn1/type/base.py
@@ -0,0 +1,643 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+from pyasn1 import error
+from pyasn1.compat import calling
+from pyasn1.type import constraint
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+
+__all__ = ['Asn1Item', 'Asn1ItemBase', 'AbstractSimpleAsn1Item', 'AbstractConstructedAsn1Item']
+
+
+class Asn1Item(object):
+    @classmethod
+    def getTypeId(cls, increment=1):
+        try:
+            Asn1Item._typeCounter += increment
+        except AttributeError:
+            Asn1Item._typeCounter = increment
+        return Asn1Item._typeCounter
+
+
+class Asn1ItemBase(Asn1Item):
+    #: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing
+    #: ASN.1 tag(s) associated with |ASN.1| type.
+    tagSet = tag.TagSet()
+
+    #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+    #: object imposing constraints on initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = None
+
+    def __init__(self, **kwargs):
+        readOnly = {
+            'tagSet': self.tagSet,
+            'subtypeSpec': self.subtypeSpec
+        }
+
+        readOnly.update(kwargs)
+
+        self.__dict__.update(readOnly)
+
+        self._readOnly = readOnly
+
+    def __setattr__(self, name, value):
+        if name[0] != '_' and name in self._readOnly:
+            raise error.PyAsn1Error('read-only instance attribute "%s"' % name)
+
+        self.__dict__[name] = value
+
+    def __str__(self):
+        return self.prettyPrint()
+
+    @property
+    def readOnly(self):
+        return self._readOnly
+
+    @property
+    def effectiveTagSet(self):
+        """For |ASN.1| type is equivalent to *tagSet*
+        """
+        return self.tagSet  # used by untagged types
+
+    @property
+    def tagMap(self):
+        """Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object.
+        """
+        return tagmap.TagMap({self.tagSet: self})
+
+    def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
+        """Examine |ASN.1| type for equality with other ASN.1 type.
+
+        ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
+        (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
+        out ASN.1 types comparison.
+
+        Python class inheritance relationship is NOT considered.
+
+        Parameters
+        ----------
+        other: a pyasn1 type object
+            Class instance representing ASN.1 type.
+
+        Returns
+        -------
+        : :class:`bool`
+            :class:`True` if *other* is |ASN.1| type,
+            :class:`False` otherwise.
+        """
+        return (self is other or
+                (not matchTags or self.tagSet == other.tagSet) and
+                (not matchConstraints or self.subtypeSpec == other.subtypeSpec))
+
+    def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True):
+        """Examine |ASN.1| type for subtype relationship with other ASN.1 type.
+
+        ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
+        (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
+        out ASN.1 types comparison.
+
+        Python class inheritance relationship is NOT considered.
+
+        Parameters
+        ----------
+            other: a pyasn1 type object
+                Class instance representing ASN.1 type.
+
+        Returns
+        -------
+            : :class:`bool`
+                :class:`True` if *other* is a subtype of |ASN.1| type,
+                :class:`False` otherwise.
+        """
+        return (not matchTags or
+                (self.tagSet.isSuperTagSetOf(other.tagSet)) and
+                 (not matchConstraints or self.subtypeSpec.isSuperTypeOf(other.subtypeSpec)))
+
+    @staticmethod
+    def isNoValue(*values):
+        for value in values:
+            if value is not noValue:
+                return False
+        return True
+
+    def prettyPrint(self, scope=0):
+        raise NotImplementedError()
+
+    # backward compatibility
+
+    def getTagSet(self):
+        return self.tagSet
+
+    def getEffectiveTagSet(self):
+        return self.effectiveTagSet
+
+    def getTagMap(self):
+        return self.tagMap
+
+    def getSubtypeSpec(self):
+        return self.subtypeSpec
+
+    def hasValue(self):
+        return self.isValue
+
+
+class NoValue(object):
+    """Create a singleton instance of NoValue class.
+
+    The *NoValue* sentinel object represents an instance of ASN.1 schema
+    object as opposed to ASN.1 value object.
+
+    Only ASN.1 schema-related operations can be performed on ASN.1
+    schema objects.
+
+    Warning
+    -------
+    Any operation attempted on the *noValue* object will raise the
+    *PyAsn1Error* exception.
+    """
+    skipMethods = set(
+        ('__slots__',
+         # attributes
+         '__getattribute__',
+         '__getattr__',
+         '__setattr__',
+         '__delattr__',
+         # class instance
+         '__class__',
+         '__init__',
+         '__del__',
+         '__new__',
+         '__repr__',
+         '__qualname__',
+         '__objclass__',
+         'im_class',
+         '__sizeof__',
+         # pickle protocol
+         '__reduce__',
+         '__reduce_ex__',
+         '__getnewargs__',
+         '__getinitargs__',
+         '__getstate__',
+         '__setstate__')
+    )
+
+    _instance = None
+
+    def __new__(cls):
+        if cls._instance is None:
+            def getPlug(name):
+                def plug(self, *args, **kw):
+                    raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % name)
+                return plug
+
+            op_names = [name
+                        for typ in (str, int, list, dict)
+                        for name in dir(typ)
+                        if (name not in cls.skipMethods and
+                            name.startswith('__') and
+                            name.endswith('__') and
+                            calling.callable(getattr(typ, name)))]
+
+            for name in set(op_names):
+                setattr(cls, name, getPlug(name))
+
+            cls._instance = object.__new__(cls)
+
+        return cls._instance
+
+    def __getattr__(self, attr):
+        if attr in self.skipMethods:
+            raise AttributeError('Attribute %s not present' % attr)
+
+        raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % attr)
+
+    def __repr__(self):
+        return '<%s object at 0x%x>' % (self.__class__.__name__, id(self))
+
+
+noValue = NoValue()
+
+
+# Base class for "simple" ASN.1 objects. These are immutable.
+class AbstractSimpleAsn1Item(Asn1ItemBase):
+    #: Default payload value
+    defaultValue = noValue
+
+    def __init__(self, value=noValue, **kwargs):
+        Asn1ItemBase.__init__(self, **kwargs)
+        if value is noValue:
+            value = self.defaultValue
+        else:
+            value = self.prettyIn(value)
+            try:
+                self.subtypeSpec(value)
+
+            except error.PyAsn1Error:
+                exType, exValue, exTb = sys.exc_info()
+                raise exType('%s at %s' % (exValue, self.__class__.__name__))
+
+        self._value = value
+
+    def __repr__(self):
+        representation = '%s %s object at 0x%x' % (
+            self.__class__.__name__, self.isValue and 'value' or 'schema', id(self)
+        )
+
+        for attr, value in self.readOnly.items():
+            if value:
+                representation += ' %s %s' % (attr, value)
+
+        if self.isValue:
+            value = self.prettyPrint()
+            if len(value) > 32:
+                value = value[:16] + '...' + value[-16:]
+            representation += ' payload [%s]' % value
+
+        return '<%s>' % representation
+
+    def __eq__(self, other):
+        return self is other and True or self._value == other
+
+    def __ne__(self, other):
+        return self._value != other
+
+    def __lt__(self, other):
+        return self._value < other
+
+    def __le__(self, other):
+        return self._value <= other
+
+    def __gt__(self, other):
+        return self._value > other
+
+    def __ge__(self, other):
+        return self._value >= other
+
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self):
+            return self._value and True or False
+    else:
+        def __bool__(self):
+            return self._value and True or False
+
+    def __hash__(self):
+        return hash(self._value)
+
+    @property
+    def isValue(self):
+        """Indicate that |ASN.1| object represents ASN.1 value.
+
+        If *isValue* is `False` then this object represents just ASN.1 schema.
+
+        If *isValue* is `True` then, in addition to its ASN.1 schema features,
+        this object can also be used like a Python built-in object (e.g. `int`,
+        `str`, `dict` etc.).
+
+        Returns
+        -------
+        : :class:`bool`
+            :class:`False` if object represents just ASN.1 schema.
+            :class:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+        Note
+        ----
+        There is an important distinction between PyASN1 schema and value objects.
+        The PyASN1 schema objects can only participate in ASN.1 schema-related
+        operations (e.g. defining or testing the structure of the data). Most
+        obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+        encoding/decoding serialised ASN.1 contents.
+
+        The PyASN1 value objects can **additionally** participate in many operations
+        involving regular Python objects (e.g. arithmetic, comprehension etc).
+        """
+        return self._value is not noValue
+
+    def clone(self, value=noValue, **kwargs):
+        """Create a modified version of |ASN.1| schema or value object.
+
+        The `clone()` method accepts the same set arguments as |ASN.1|
+        class takes on instantiation except that all arguments
+        of the `clone()` method are optional.
+
+        Whatever arguments are supplied, they are used to create a copy
+        of `self` taking precedence over the ones used to instantiate `self`.
+
+        Note
+        ----
+        Due to the immutable nature of the |ASN.1| object, if no arguments
+        are supplied, no new |ASN.1| object will be created and `self` will
+        be returned instead.
+        """
+        if value is noValue:
+            if not kwargs:
+                return self
+
+            value = self._value
+
+        initilaizers = self.readOnly.copy()
+        initilaizers.update(kwargs)
+
+        return self.__class__(value, **initilaizers)
+
+    def subtype(self, value=noValue, **kwargs):
+        """Create a specialization of |ASN.1| schema or value object.
+
+        The subtype relationship between ASN.1 types has no correlation with
+        subtype relationship between Python types. ASN.1 type is mainly identified
+        by its tag(s) (:py:class:`~pyasn1.type.tag.TagSet`) and value range
+        constraints (:py:class:`~pyasn1.type.constraint.ConstraintsIntersection`).
+        These ASN.1 type properties are implemented as |ASN.1| attributes.  
+
+        The `subtype()` method accepts the same set arguments as |ASN.1|
+        class takes on instantiation except that all parameters
+        of the `subtype()` method are optional.
+
+        With the exception of the arguments described below, the rest of
+        supplied arguments they are used to create a copy of `self` taking
+        precedence over the ones used to instantiate `self`.
+
+        The following arguments to `subtype()` create a ASN.1 subtype out of
+        |ASN.1| type:
+
+        Other Parameters
+        ----------------
+        implicitTag: :py:class:`~pyasn1.type.tag.Tag`
+            Implicitly apply given ASN.1 tag object to `self`'s
+            :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+            new object's ASN.1 tag(s).
+
+        explicitTag: :py:class:`~pyasn1.type.tag.Tag`
+            Explicitly apply given ASN.1 tag object to `self`'s
+            :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+            new object's ASN.1 tag(s).
+
+        subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+            Add ASN.1 constraints object to one of the `self`'s, then
+            use the result as new object's ASN.1 constraints.
+
+        Returns
+        -------
+        :
+            new instance of |ASN.1| schema or value object
+
+        Note
+        ----
+        Due to the immutable nature of the |ASN.1| object, if no arguments
+        are supplied, no new |ASN.1| object will be created and `self` will
+        be returned instead.
+        """
+        if value is noValue:
+            if not kwargs:
+                return self
+
+            value = self._value
+
+        initializers = self.readOnly.copy()
+
+        implicitTag = kwargs.pop('implicitTag', None)
+        if implicitTag is not None:
+            initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
+
+        explicitTag = kwargs.pop('explicitTag', None)
+        if explicitTag is not None:
+            initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
+
+        for arg, option in kwargs.items():
+            initializers[arg] += option
+
+        return self.__class__(value, **initializers)
+
+    def prettyIn(self, value):
+        return value
+
+    def prettyOut(self, value):
+        return str(value)
+
+    def prettyPrint(self, scope=0):
+        return self.prettyOut(self._value)
+
+    # noinspection PyUnusedLocal
+    def prettyPrintType(self, scope=0):
+        return '%s -> %s' % (self.tagSet, self.__class__.__name__)
+
+#
+# Constructed types:
+# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
+# * ASN1 types and values are represened by Python class instances
+# * Value initialization is made for defaulted components only
+# * Primary method of component addressing is by-position. Data model for base
+#   type is Python sequence. Additional type-specific addressing methods
+#   may be implemented for particular types.
+# * SequenceOf and SetOf types do not implement any additional methods
+# * Sequence, Set and Choice types also implement by-identifier addressing
+# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing
+# * Sequence and Set types may include optional and defaulted
+#   components
+# * Constructed types hold a reference to component types used for value
+#   verification and ordering.
+# * Component type is a scalar type for SequenceOf/SetOf types and a list
+#   of types for Sequence/Set/Choice.
+#
+
+
+class AbstractConstructedAsn1Item(Asn1ItemBase):
+
+    #: If `True`, requires exact component type matching,
+    #: otherwise subtype relation is only enforced
+    strictConstraints = False
+
+    componentType = None
+    sizeSpec = None
+
+    def __init__(self, **kwargs):
+        readOnly = {
+            'componentType': self.componentType,
+            'sizeSpec': self.sizeSpec
+        }
+        readOnly.update(kwargs)
+
+        Asn1ItemBase.__init__(self, **readOnly)
+
+        self._componentValues = []
+
+    def __repr__(self):
+        representation = '%s %s object at 0x%x' % (
+            self.__class__.__name__, self.isValue and 'value' or 'schema', id(self)
+        )
+
+        for attr, value in self.readOnly.items():
+            if value is not noValue:
+                representation += ' %s=%r' % (attr, value)
+
+        if self.isValue and self._componentValues:
+            representation += ' payload [%s]' % ', '.join([repr(x) for x in self._componentValues])
+
+        return '<%s>' % representation
+
+    def __eq__(self, other):
+        return self is other and True or self._componentValues == other
+
+    def __ne__(self, other):
+        return self._componentValues != other
+
+    def __lt__(self, other):
+        return self._componentValues < other
+
+    def __le__(self, other):
+        return self._componentValues <= other
+
+    def __gt__(self, other):
+        return self._componentValues > other
+
+    def __ge__(self, other):
+        return self._componentValues >= other
+
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self):
+            return self._componentValues and True or False
+    else:
+        def __bool__(self):
+            return self._componentValues and True or False
+
+    def __len__(self):
+        return len(self._componentValues)
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        pass
+
+    def clone(self, **kwargs):
+        """Create a modified version of |ASN.1| schema object.
+
+        The `clone()` method accepts the same set arguments as |ASN.1|
+        class takes on instantiation except that all arguments
+        of the `clone()` method are optional.
+
+        Whatever arguments are supplied, they are used to create a copy
+        of `self` taking precedence over the ones used to instantiate `self`.
+
+        Possible values of `self` are never copied over thus `clone()` can
+        only create a new schema object.
+
+        Returns
+        -------
+        :
+            new instance of |ASN.1| type/value
+
+        Note
+        ----
+        Due to the mutable nature of the |ASN.1| object, even if no arguments
+        are supplied, new |ASN.1| object will always be created as a shallow
+        copy of `self`.
+        """
+        cloneValueFlag = kwargs.pop('cloneValueFlag', False)
+
+        initilaizers = self.readOnly.copy()
+        initilaizers.update(kwargs)
+
+        clone = self.__class__(**initilaizers)
+
+        if cloneValueFlag:
+            self._cloneComponentValues(clone, cloneValueFlag)
+
+        return clone
+
+    def subtype(self, **kwargs):
+        """Create a specialization of |ASN.1| schema object.
+
+        The `subtype()` method accepts the same set arguments as |ASN.1|
+        class takes on instantiation except that all parameters
+        of the `subtype()` method are optional.
+
+        With the exception of the arguments described below, the rest of
+        supplied arguments they are used to create a copy of `self` taking
+        precedence over the ones used to instantiate `self`.
+
+        The following arguments to `subtype()` create a ASN.1 subtype out of
+        |ASN.1| type.
+
+        Other Parameters
+        ----------------
+        implicitTag: :py:class:`~pyasn1.type.tag.Tag`
+            Implicitly apply given ASN.1 tag object to `self`'s
+            :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+            new object's ASN.1 tag(s).
+
+        explicitTag: :py:class:`~pyasn1.type.tag.Tag`
+            Explicitly apply given ASN.1 tag object to `self`'s
+            :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+            new object's ASN.1 tag(s).
+
+        subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+            Add ASN.1 constraints object to one of the `self`'s, then
+            use the result as new object's ASN.1 constraints.
+
+
+        Returns
+        -------
+        :
+            new instance of |ASN.1| type/value
+
+        Note
+        ----
+        Due to the immutable nature of the |ASN.1| object, if no arguments
+        are supplied, no new |ASN.1| object will be created and `self` will
+        be returned instead.
+        """
+
+        initializers = self.readOnly.copy()
+
+        cloneValueFlag = kwargs.pop('cloneValueFlag', False)
+
+        implicitTag = kwargs.pop('implicitTag', None)
+        if implicitTag is not None:
+            initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
+
+        explicitTag = kwargs.pop('explicitTag', None)
+        if explicitTag is not None:
+            initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
+
+        for arg, option in kwargs.items():
+            initializers[arg] += option
+
+        clone = self.__class__(**initializers)
+
+        if cloneValueFlag:
+            self._cloneComponentValues(clone, cloneValueFlag)
+
+        return clone
+
+    def verifySizeSpec(self):
+        self.sizeSpec(self)
+
+    def getComponentByPosition(self, idx):
+        raise error.PyAsn1Error('Method not implemented')
+
+    def setComponentByPosition(self, idx, value, verifyConstraints=True):
+        raise error.PyAsn1Error('Method not implemented')
+
+    def setComponents(self, *args, **kwargs):
+        for idx, value in enumerate(args):
+            self[idx] = value
+        for k in kwargs:
+            self[k] = kwargs[k]
+        return self
+
+    def clear(self):
+        self._componentValues = []
+
+    # backward compatibility
+
+    def setDefaultComponents(self):
+        pass
+
+    def getComponentType(self):
+        return self.componentType
diff --git a/pyasn1/type/char.py b/pyasn1/type/char.py
new file mode 100644
index 0000000..493badb
--- /dev/null
+++ b/pyasn1/type/char.py
@@ -0,0 +1,321 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+from pyasn1 import error
+from pyasn1.type import tag
+from pyasn1.type import univ
+
+__all__ = ['NumericString', 'PrintableString', 'TeletexString', 'T61String', 'VideotexString',
+           'IA5String', 'GraphicString', 'VisibleString', 'ISO646String',
+           'GeneralString', 'UniversalString', 'BMPString', 'UTF8String']
+
+NoValue = univ.NoValue
+noValue = univ.noValue
+
+
+class AbstractCharacterString(univ.OctetString):
+    """Creates |ASN.1| schema or value object.
+
+    |ASN.1| objects are immutable and duck-type Python 2 :class:`unicode` or Python 3 :class:`str`.
+    When used in octet-stream context, |ASN.1| type assumes "|encoding|" encoding.
+
+    Keyword Args
+    ------------
+    value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
+        unicode object (Python 2) or string (Python 3), alternatively string
+        (Python 2) or bytes (Python 3) representing octet-stream of serialised
+        unicode string (note `encoding` parameter) or |ASN.1| class instance.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    encoding: :py:class:`str`
+        Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
+        :class:`str` (Python 3) the payload when |ASN.1| object is used
+        in octet-stream context.
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+    """
+
+    if sys.version_info[0] <= 2:
+        def __str__(self):
+            try:
+                # `str` is Py2 text representation
+                return self._value.encode(self.encoding)
+
+            except UnicodeEncodeError:
+                raise error.PyAsn1Error(
+                    "Can't encode string '%s' with codec %s" % (self._value, self.encoding)
+                )
+
+        def __unicode__(self):
+            return unicode(self._value)
+
+        def prettyIn(self, value):
+            try:
+                if isinstance(value, unicode):
+                    return value
+                elif isinstance(value, str):
+                    return value.decode(self.encoding)
+                elif isinstance(value, (tuple, list)):
+                    return self.prettyIn(''.join([chr(x) for x in value]))
+                elif isinstance(value, univ.OctetString):
+                    return value.asOctets().decode(self.encoding)
+                else:
+                    return unicode(value)
+
+            except (UnicodeDecodeError, LookupError):
+                raise error.PyAsn1Error(
+                    "Can't decode string '%s' with codec %s" % (value, self.encoding)
+                )
+
+        def asOctets(self, padding=True):
+            return str(self)
+
+        def asNumbers(self, padding=True):
+            return tuple([ord(x) for x in str(self)])
+
+    else:
+        def __str__(self):
+            # `unicode` is Py3 text representation
+            return str(self._value)
+
+        def __bytes__(self):
+            try:
+                return self._value.encode(self.encoding)
+            except UnicodeEncodeError:
+                raise error.PyAsn1Error(
+                    "Can't encode string '%s' with codec %s" % (self._value, self.encoding)
+                )
+
+        def prettyIn(self, value):
+            try:
+                if isinstance(value, str):
+                    return value
+                elif isinstance(value, bytes):
+                    return value.decode(self.encoding)
+                elif isinstance(value, (tuple, list)):
+                    return self.prettyIn(bytes(value))
+                elif isinstance(value, univ.OctetString):
+                    return value.asOctets().decode(self.encoding)
+                else:
+                    return str(value)
+
+            except (UnicodeDecodeError, LookupError):
+                raise error.PyAsn1Error(
+                    "Can't decode string '%s' with codec %s" % (value, self.encoding)
+                )
+
+        def asOctets(self, padding=True):
+            return bytes(self)
+
+        def asNumbers(self, padding=True):
+            return tuple(bytes(self))
+
+    #
+    # See OctetString.prettyPrint() for the explanation
+    #
+
+    def prettyOut(self, value):
+        return value
+
+    def prettyPrint(self, scope=0):
+        # first see if subclass has its own .prettyOut()
+        value = self.prettyOut(self._value)
+
+        if value is not self._value:
+            return value
+
+        return AbstractCharacterString.__str__(self)
+
+    def __reversed__(self):
+        return reversed(self._value)
+
+
+class NumericString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
+    )
+    encoding = 'us-ascii'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class PrintableString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
+    )
+    encoding = 'us-ascii'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class TeletexString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
+    )
+    encoding = 'iso-8859-1'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class T61String(TeletexString):
+    __doc__ = TeletexString.__doc__
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class VideotexString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
+    )
+    encoding = 'iso-8859-1'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class IA5String(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
+    )
+    encoding = 'us-ascii'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class GraphicString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
+    )
+    encoding = 'iso-8859-1'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class VisibleString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
+    )
+    encoding = 'us-ascii'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class ISO646String(VisibleString):
+    __doc__ = VisibleString.__doc__
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+class GeneralString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
+    )
+    encoding = 'iso-8859-1'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class UniversalString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
+    )
+    encoding = "utf-32-be"
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class BMPString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
+    )
+    encoding = "utf-16-be"
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class UTF8String(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+    )
+    encoding = "utf-8"
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
diff --git a/pyasn1/type/constraint.py b/pyasn1/type/constraint.py
new file mode 100644
index 0000000..a704331
--- /dev/null
+++ b/pyasn1/type/constraint.py
@@ -0,0 +1,556 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+# Original concept and code by Mike C. Fletcher.
+#
+import sys
+
+from pyasn1.type import error
+
+__all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint',
+           'ValueRangeConstraint', 'ValueSizeConstraint',
+           'PermittedAlphabetConstraint', 'InnerTypeConstraint',
+           'ConstraintsExclusion', 'ConstraintsIntersection',
+           'ConstraintsUnion']
+
+
+class AbstractConstraint(object):
+
+    def __init__(self, *values):
+        self._valueMap = set()
+        self._setValues(values)
+        self.__hash = hash((self.__class__.__name__, self._values))
+
+    def __call__(self, value, idx=None):
+        if not self._values:
+            return
+
+        try:
+            self._testValue(value, idx)
+
+        except error.ValueConstraintError:
+            raise error.ValueConstraintError(
+                '%s failed at: %r' % (self, sys.exc_info()[1])
+            )
+
+    def __repr__(self):
+        representation = '%s object at 0x%x' % (self.__class__.__name__, id(self))
+
+        if self._values:
+            representation += ' consts %s' % ', '.join([repr(x) for x in self._values])
+
+        return '<%s>' % representation
+
+    def __eq__(self, other):
+        return self is other and True or self._values == other
+
+    def __ne__(self, other):
+        return self._values != other
+
+    def __lt__(self, other):
+        return self._values < other
+
+    def __le__(self, other):
+        return self._values <= other
+
+    def __gt__(self, other):
+        return self._values > other
+
+    def __ge__(self, other):
+        return self._values >= other
+
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self):
+            return self._values and True or False
+    else:
+        def __bool__(self):
+            return self._values and True or False
+
+    def __hash__(self):
+        return self.__hash
+
+    def _setValues(self, values):
+        self._values = values
+
+    def _testValue(self, value, idx):
+        raise error.ValueConstraintError(value)
+
+    # Constraints derivation logic
+    def getValueMap(self):
+        return self._valueMap
+
+    def isSuperTypeOf(self, otherConstraint):
+        # TODO: fix possible comparison of set vs scalars here
+        return (otherConstraint is self or
+                not self._values or
+                otherConstraint == self or
+                self in otherConstraint.getValueMap())
+
+    def isSubTypeOf(self, otherConstraint):
+        return (otherConstraint is self or
+                not self or
+                otherConstraint == self or
+                otherConstraint in self._valueMap)
+
+
+class SingleValueConstraint(AbstractConstraint):
+    """Create a SingleValueConstraint object.
+
+    The SingleValueConstraint satisfies any value that
+    is present in the set of permitted values.
+
+    The SingleValueConstraint object can be applied to
+    any ASN.1 type.
+
+    Parameters
+    ----------
+    \*values: :class:`int`
+        Full set of values permitted by this constraint object.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class DivisorOfSix(Integer):
+            '''
+            ASN.1 specification:
+
+            Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6)
+            '''
+            subtypeSpec = SingleValueConstraint(1, 2, 3, 6)
+
+        # this will succeed
+        divisor_of_six = DivisorOfSix(1)
+
+        # this will raise ValueConstraintError
+        divisor_of_six = DivisorOfSix(7)
+    """
+    def _setValues(self, values):
+        self._values = values
+        self._set = set(values)
+
+    def _testValue(self, value, idx):
+        if value not in self._set:
+            raise error.ValueConstraintError(value)
+
+
+class ContainedSubtypeConstraint(AbstractConstraint):
+    """Create a ContainedSubtypeConstraint object.
+
+    The ContainedSubtypeConstraint satisfies any value that
+    is present in the set of permitted values and also
+    satisfies included constraints.
+
+    The ContainedSubtypeConstraint object can be applied to
+    any ASN.1 type.
+
+    Parameters
+    ----------
+    \*values:
+        Full set of values and constraint objects permitted
+        by this constraint object.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class DivisorOfEighteen(Integer):
+            '''
+            ASN.1 specification:
+
+            Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18)
+            '''
+            subtypeSpec = ContainedSubtypeConstraint(
+                SingleValueConstraint(1, 2, 3, 6), 9, 18
+            )
+
+        # this will succeed
+        divisor_of_eighteen = DivisorOfEighteen(9)
+
+        # this will raise ValueConstraintError
+        divisor_of_eighteen = DivisorOfEighteen(10)
+    """
+    def _testValue(self, value, idx):
+        for constraint in self._values:
+            if isinstance(constraint, AbstractConstraint):
+                constraint(value, idx)
+            elif value not in self._set:
+                raise error.ValueConstraintError(value)
+
+
+class ValueRangeConstraint(AbstractConstraint):
+    """Create a ValueRangeConstraint object.
+
+    The ValueRangeConstraint satisfies any value that
+    falls in the range of permitted values.
+
+    The ValueRangeConstraint object can only be applied
+    to :class:`~pyasn1.type.univ.Integer` and
+    :class:`~pyasn1.type.univ.Real` types.
+
+    Parameters
+    ----------
+    start: :class:`int`
+        Minimum permitted value in the range (inclusive)
+
+    end: :class:`int`
+        Maximum permitted value in the range (inclusive)
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class TeenAgeYears(Integer):
+            '''
+            ASN.1 specification:
+
+            TeenAgeYears ::= INTEGER (13 .. 19)
+            '''
+            subtypeSpec = ValueRangeConstraint(13, 19)
+
+        # this will succeed
+        teen_year = TeenAgeYears(18)
+
+        # this will raise ValueConstraintError
+        teen_year = TeenAgeYears(20)
+    """
+    def _testValue(self, value, idx):
+        if value < self.start or value > self.stop:
+            raise error.ValueConstraintError(value)
+
+    def _setValues(self, values):
+        if len(values) != 2:
+            raise error.PyAsn1Error(
+                '%s: bad constraint values' % (self.__class__.__name__,)
+            )
+        self.start, self.stop = values
+        if self.start > self.stop:
+            raise error.PyAsn1Error(
+                '%s: screwed constraint values (start > stop): %s > %s' % (
+                    self.__class__.__name__,
+                    self.start, self.stop
+                )
+            )
+        AbstractConstraint._setValues(self, values)
+
+
+class ValueSizeConstraint(ValueRangeConstraint):
+    """Create a ValueSizeConstraint object.
+
+    The ValueSizeConstraint satisfies any value for
+    as long as its size falls within the range of
+    permitted sizes.
+
+    The ValueSizeConstraint object can be applied
+    to :class:`~pyasn1.type.univ.BitString`,
+    :class:`~pyasn1.type.univ.OctetString` (including
+    all :ref:`character ASN.1 types <type.char>`),
+    :class:`~pyasn1.type.univ.SequenceOf`
+    and :class:`~pyasn1.type.univ.SetOf` types.
+
+    Parameters
+    ----------
+    minimum: :class:`int`
+        Minimum permitted size of the value (inclusive)
+
+    maximum: :class:`int`
+        Maximum permitted size of the value (inclusive)
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class BaseballTeamRoster(SetOf):
+            '''
+            ASN.1 specification:
+
+            BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames
+            '''
+            componentType = PlayerNames()
+            subtypeSpec = ValueSizeConstraint(1, 25)
+
+        # this will succeed
+        team = BaseballTeamRoster()
+        team.extend(['Jan', 'Matej'])
+        encode(team)
+
+        # this will raise ValueConstraintError
+        team = BaseballTeamRoster()
+        team.extend(['Jan'] * 26)
+        encode(team)
+
+    Note
+    ----
+    Whenever ValueSizeConstraint is applied to mutable types
+    (e.g. :class:`~pyasn1.type.univ.SequenceOf`,
+    :class:`~pyasn1.type.univ.SetOf`), constraint
+    validation only happens at the serialisation phase rather
+    than schema instantiation phase (as it is with immutable
+    types).
+    """
+    def _testValue(self, value, idx):
+        valueSize = len(value)
+        if valueSize < self.start or valueSize > self.stop:
+            raise error.ValueConstraintError(value)
+
+
+class PermittedAlphabetConstraint(SingleValueConstraint):
+    """Create a PermittedAlphabetConstraint object.
+
+    The PermittedAlphabetConstraint satisfies any character
+    string for as long as all its characters are present in
+    the set of permitted characters.
+
+    The PermittedAlphabetConstraint object can only be applied
+    to the :ref:`character ASN.1 types <type.char>` such as
+    :class:`~pyasn1.type.char.IA5String`.
+
+    Parameters
+    ----------
+    \*alphabet: :class:`str`
+        Full set of characters permitted by this constraint object.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class BooleanValue(IA5String):
+            '''
+            ASN.1 specification:
+
+            BooleanValue ::= IA5String (FROM ('T' | 'F'))
+            '''
+            subtypeSpec = PermittedAlphabetConstraint('T', 'F')
+
+        # this will succeed
+        truth = BooleanValue('T')
+        truth = BooleanValue('TF')
+
+        # this will raise ValueConstraintError
+        garbage = BooleanValue('TAF')
+    """
+    def _setValues(self, values):
+        self._values = values
+        self._set = set(values)
+
+    def _testValue(self, value, idx):
+        if not self._set.issuperset(value):
+            raise error.ValueConstraintError(value)
+
+
+# This is a bit kludgy, meaning two op modes within a single constraint
+class InnerTypeConstraint(AbstractConstraint):
+    """Value must satisfy the type and presence constraints"""
+
+    def _testValue(self, value, idx):
+        if self.__singleTypeConstraint:
+            self.__singleTypeConstraint(value)
+        elif self.__multipleTypeConstraint:
+            if idx not in self.__multipleTypeConstraint:
+                raise error.ValueConstraintError(value)
+            constraint, status = self.__multipleTypeConstraint[idx]
+            if status == 'ABSENT':  # XXX presense is not checked!
+                raise error.ValueConstraintError(value)
+            constraint(value)
+
+    def _setValues(self, values):
+        self.__multipleTypeConstraint = {}
+        self.__singleTypeConstraint = None
+        for v in values:
+            if isinstance(v, tuple):
+                self.__multipleTypeConstraint[v[0]] = v[1], v[2]
+            else:
+                self.__singleTypeConstraint = v
+        AbstractConstraint._setValues(self, values)
+
+
+# Logic operations on constraints
+
+class ConstraintsExclusion(AbstractConstraint):
+    """Create a ConstraintsExclusion logic operator object.
+
+    The ConstraintsExclusion logic operator succeeds when the
+    value does *not* satisfy the operand constraint.
+
+    The ConstraintsExclusion object can be applied to
+    any constraint and logic operator object.
+
+    Parameters
+    ----------
+    constraint:
+        Constraint or logic operator object.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Lipogramme(IA5STRING):
+            '''
+            ASN.1 specification:
+
+            Lipogramme ::=
+                IA5String (FROM (ALL EXCEPT ("e"|"E")))
+            '''
+            subtypeSpec = ConstraintsExclusion(
+                PermittedAlphabetConstraint('e', 'E')
+            )
+
+        # this will succeed
+        lipogramme = Lipogramme('A work of fiction?')
+
+        # this will raise ValueConstraintError
+        lipogramme = Lipogramme('Eel')
+
+    Warning
+    -------
+    The above example involving PermittedAlphabetConstraint might
+    not work due to the way how PermittedAlphabetConstraint works.
+    The other constraints might work with ConstraintsExclusion
+    though.
+    """
+    def _testValue(self, value, idx):
+        try:
+            self._values[0](value, idx)
+        except error.ValueConstraintError:
+            return
+        else:
+            raise error.ValueConstraintError(value)
+
+    def _setValues(self, values):
+        if len(values) != 1:
+            raise error.PyAsn1Error('Single constraint expected')
+
+        AbstractConstraint._setValues(self, values)
+
+
+class AbstractConstraintSet(AbstractConstraint):
+
+    def __getitem__(self, idx):
+        return self._values[idx]
+
+    def __iter__(self):
+        return iter(self._values)
+
+    def __add__(self, value):
+        return self.__class__(*(self._values + (value,)))
+
+    def __radd__(self, value):
+        return self.__class__(*((value,) + self._values))
+
+    def __len__(self):
+        return len(self._values)
+
+    # Constraints inclusion in sets
+
+    def _setValues(self, values):
+        self._values = values
+        for constraint in values:
+            if constraint:
+                self._valueMap.add(constraint)
+                self._valueMap.update(constraint.getValueMap())
+
+
+class ConstraintsIntersection(AbstractConstraintSet):
+    """Create a ConstraintsIntersection logic operator object.
+
+    The ConstraintsIntersection logic operator only succeeds
+    if *all* its operands succeed.
+
+    The ConstraintsIntersection object can be applied to
+    any constraint and logic operator objects.
+
+    The ConstraintsIntersection object duck-types the immutable
+    container object like Python :py:class:`tuple`.
+
+    Parameters
+    ----------
+    \*constraints:
+        Constraint or logic operator objects.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class CapitalAndSmall(IA5String):
+            '''
+            ASN.1 specification:
+
+            CapitalAndSmall ::=
+                IA5String (FROM ("A".."Z"|"a".."z"))
+            '''
+            subtypeSpec = ConstraintsIntersection(
+                PermittedAlphabetConstraint('A', 'Z'),
+                PermittedAlphabetConstraint('a', 'z')
+            )
+
+        # this will succeed
+        capital_and_small = CapitalAndSmall('Hello')
+
+        # this will raise ValueConstraintError
+        capital_and_small = CapitalAndSmall('hello')
+    """
+    def _testValue(self, value, idx):
+        for constraint in self._values:
+            constraint(value, idx)
+
+
+class ConstraintsUnion(AbstractConstraintSet):
+    """Create a ConstraintsUnion logic operator object.
+
+    The ConstraintsUnion logic operator only succeeds if
+    *at least a single* operand succeeds.
+
+    The ConstraintsUnion object can be applied to
+    any constraint and logic operator objects.
+
+    The ConstraintsUnion object duck-types the immutable
+    container object like Python :py:class:`tuple`.
+
+    Parameters
+    ----------
+    \*constraints:
+        Constraint or logic operator objects.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class CapitalOrSmall(IA5String):
+            '''
+            ASN.1 specification:
+
+            CapitalOrSmall ::=
+                IA5String (FROM ("A".."Z") | FROM ("a".."z"))
+            '''
+            subtypeSpec = ConstraintsIntersection(
+                PermittedAlphabetConstraint('A', 'Z'),
+                PermittedAlphabetConstraint('a', 'z')
+            )
+
+        # this will succeed
+        capital_or_small = CapitalAndSmall('Hello')
+
+        # this will raise ValueConstraintError
+        capital_or_small = CapitalOrSmall('hello!')
+    """
+    def _testValue(self, value, idx):
+        for constraint in self._values:
+            try:
+                constraint(value, idx)
+            except error.ValueConstraintError:
+                pass
+            else:
+                return
+
+        raise error.ValueConstraintError(
+            'all of %s failed for "%s"' % (self._values, value)
+        )
+
+# TODO:
+# refactor InnerTypeConstraint
+# add tests for type check
+# implement other constraint types
+# make constraint validation easy to skip
diff --git a/pyasn1/type/error.py b/pyasn1/type/error.py
new file mode 100644
index 0000000..b2056bd
--- /dev/null
+++ b/pyasn1/type/error.py
@@ -0,0 +1,11 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1.error import PyAsn1Error
+
+
+class ValueConstraintError(PyAsn1Error):
+    pass
diff --git a/pyasn1/type/namedtype.py b/pyasn1/type/namedtype.py
new file mode 100644
index 0000000..f162d19
--- /dev/null
+++ b/pyasn1/type/namedtype.py
@@ -0,0 +1,559 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+from pyasn1 import error
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+
+__all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType',
+           'NamedTypes']
+
+try:
+    any
+
+except NameError:
+    any = lambda x: bool(filter(bool, x))
+
+
+class NamedType(object):
+    """Create named field object for a constructed ASN.1 type.
+
+    The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type.
+
+    |NamedType| objects are immutable and duck-type Python :class:`tuple` objects
+    holding *name* and *asn1Object* components.
+
+    Parameters
+    ----------
+    name: :py:class:`str`
+        Field name
+
+    asn1Object:
+        ASN.1 type object
+    """
+    isOptional = False
+    isDefaulted = False
+
+    def __init__(self, name, asn1Object, openType=None):
+        self.__name = name
+        self.__type = asn1Object
+        self.__nameAndType = name, asn1Object
+        self.__openType = openType
+
+    def __repr__(self):
+        representation = '%s=%r' % (self.name, self.asn1Object)
+
+        if self.openType:
+            representation += ' openType: %r' % self.openType
+
+        return '<%s object at 0x%x type %s>' % (self.__class__.__name__, id(self), representation)
+
+    def __eq__(self, other):
+        return self.__nameAndType == other
+
+    def __ne__(self, other):
+        return self.__nameAndType != other
+
+    def __lt__(self, other):
+        return self.__nameAndType < other
+
+    def __le__(self, other):
+        return self.__nameAndType <= other
+
+    def __gt__(self, other):
+        return self.__nameAndType > other
+
+    def __ge__(self, other):
+        return self.__nameAndType >= other
+
+    def __hash__(self):
+        return hash(self.__nameAndType)
+
+    def __getitem__(self, idx):
+        return self.__nameAndType[idx]
+
+    def __iter__(self):
+        return iter(self.__nameAndType)
+
+    @property
+    def name(self):
+        return self.__name
+
+    @property
+    def asn1Object(self):
+        return self.__type
+
+    @property
+    def openType(self):
+        return self.__openType
+
+    # Backward compatibility
+
+    def getName(self):
+        return self.name
+
+    def getType(self):
+        return self.asn1Object
+
+
+class OptionalNamedType(NamedType):
+    __doc__ = NamedType.__doc__
+
+    isOptional = True
+
+
+class DefaultedNamedType(NamedType):
+    __doc__ = NamedType.__doc__
+
+    isDefaulted = True
+
+
+class NamedTypes(object):
+    """Create a collection of named fields for a constructed ASN.1 type.
+
+    The NamedTypes object represents a collection of named fields of a constructed ASN.1 type.
+
+    *NamedTypes* objects are immutable and duck-type Python :class:`dict` objects
+    holding *name* as keys and ASN.1 type object as values.
+
+    Parameters
+    ----------
+    *namedTypes: :class:`~pyasn1.type.namedtype.NamedType`
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class Description(Sequence):
+            '''
+            ASN.1 specification:
+
+            Description ::= SEQUENCE {
+                surname    IA5String,
+                first-name IA5String OPTIONAL,
+                age        INTEGER DEFAULT 40
+            }
+            '''
+            componentType = NamedTypes(
+                NamedType('surname', IA5String()),
+                OptionalNamedType('first-name', IA5String()),
+                DefaultedNamedType('age', Integer(40))
+            )
+
+        descr = Description()
+        descr['surname'] = 'Smith'
+        descr['first-name'] = 'John'
+    """
+    def __init__(self, *namedTypes, **kwargs):
+        self.__namedTypes = namedTypes
+        self.__namedTypesLen = len(self.__namedTypes)
+        self.__minTagSet = self.__computeMinTagSet()
+        self.__nameToPosMap = self.__computeNameToPosMap()
+        self.__tagToPosMap = self.__computeTagToPosMap()
+        self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {}
+        self.__uniqueTagMap = self.__computeTagMaps(unique=True)
+        self.__nonUniqueTagMap = self.__computeTagMaps(unique=False)
+        self.__hasOptionalOrDefault = any([True for namedType in self.__namedTypes
+                                           if namedType.isDefaulted or namedType.isOptional])
+        self.__hasOpenTypes = any([True for namedType in self.__namedTypes
+                                   if namedType.openType])
+
+        self.__requiredComponents = frozenset(
+                [idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
+            )
+        self.__keys = frozenset([namedType.name for namedType in self.__namedTypes])
+        self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes])
+        self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes])
+
+    def __repr__(self):
+        representation = ', '.join(['%r' % x for x in self.__namedTypes])
+        return '<%s object at 0x%x types %s>' % (self.__class__.__name__, id(self), representation)
+
+    def __eq__(self, other):
+        return self.__namedTypes == other
+
+    def __ne__(self, other):
+        return self.__namedTypes != other
+
+    def __lt__(self, other):
+        return self.__namedTypes < other
+
+    def __le__(self, other):
+        return self.__namedTypes <= other
+
+    def __gt__(self, other):
+        return self.__namedTypes > other
+
+    def __ge__(self, other):
+        return self.__namedTypes >= other
+
+    def __hash__(self):
+        return hash(self.__namedTypes)
+
+    def __getitem__(self, idx):
+        try:
+            return self.__namedTypes[idx]
+
+        except TypeError:
+            return self.__namedTypes[self.__nameToPosMap[idx]]
+
+    def __contains__(self, key):
+        return key in self.__nameToPosMap
+
+    def __iter__(self):
+        return (x[0] for x in self.__namedTypes)
+
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self):
+            return self.__namedTypesLen > 0
+    else:
+        def __bool__(self):
+            return self.__namedTypesLen > 0
+
+    def __len__(self):
+        return self.__namedTypesLen
+
+    # Python dict protocol
+
+    def values(self):
+        return self.__values
+
+    def keys(self):
+        return self.__keys
+
+    def items(self):
+        return self.__items
+
+    def clone(self):
+        return self.__class__(*self.__namedTypes)
+
+    class PostponedError(object):
+        def __init__(self, errorMsg):
+            self.__errorMsg = errorMsg
+
+        def __getitem__(self, item):
+            raise  error.PyAsn1Error(self.__errorMsg)
+
+    def __computeTagToPosMap(self):
+        tagToPosMap = {}
+        for idx, namedType in enumerate(self.__namedTypes):
+            tagMap = namedType.asn1Object.tagMap
+            if isinstance(tagMap, NamedTypes.PostponedError):
+                return tagMap
+            if not tagMap:
+                continue
+            for _tagSet in tagMap.presentTypes:
+                if _tagSet in tagToPosMap:
+                    return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType))
+                tagToPosMap[_tagSet] = idx
+
+        return tagToPosMap
+
+    def __computeNameToPosMap(self):
+        nameToPosMap = {}
+        for idx, namedType in enumerate(self.__namedTypes):
+            if namedType.name in nameToPosMap:
+                return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType))
+            nameToPosMap[namedType.name] = idx
+
+        return nameToPosMap
+
+    def __computeAmbiguousTypes(self):
+        ambigiousTypes = {}
+        partialAmbigiousTypes = ()
+        for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
+            if namedType.isOptional or namedType.isDefaulted:
+                partialAmbigiousTypes = (namedType,) + partialAmbigiousTypes
+            else:
+                partialAmbigiousTypes = (namedType,)
+            if len(partialAmbigiousTypes) == len(self.__namedTypes):
+                ambigiousTypes[idx] = self
+            else:
+                ambigiousTypes[idx] = NamedTypes(*partialAmbigiousTypes, **dict(terminal=True))
+        return ambigiousTypes
+
+    def getTypeByPosition(self, idx):
+        """Return ASN.1 type object by its position in fields set.
+
+        Parameters
+        ----------
+        idx: :py:class:`int`
+            Field index
+
+        Returns
+        -------
+        :
+            ASN.1 type
+
+        Raises
+        ------
+        : :class:`~pyasn1.error.PyAsn1Error`
+            If given position is out of fields range
+        """
+        try:
+            return self.__namedTypes[idx].asn1Object
+
+        except IndexError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def getPositionByType(self, tagSet):
+        """Return field position by its ASN.1 type.
+
+        Parameters
+        ----------
+        tagSet: :class:`~pysnmp.type.tag.TagSet`
+            ASN.1 tag set distinguishing one ASN.1 type from others.
+
+        Returns
+        -------
+        : :py:class:`int`
+            ASN.1 type position in fields set
+
+        Raises
+        ------
+        : :class:`~pyasn1.error.PyAsn1Error`
+            If *tagSet* is not present or ASN.1 types are not unique within callee *NamedTypes*
+        """
+        try:
+            return self.__tagToPosMap[tagSet]
+
+        except KeyError:
+            raise error.PyAsn1Error('Type %s not found' % (tagSet,))
+
+    def getNameByPosition(self, idx):
+        """Return field name by its position in fields set.
+
+        Parameters
+        ----------
+        idx: :py:class:`idx`
+            Field index
+
+        Returns
+        -------
+        : :py:class:`str`
+            Field name
+
+        Raises
+        ------
+        : :class:`~pyasn1.error.PyAsn1Error`
+            If given field name is not present in callee *NamedTypes*
+        """
+        try:
+            return self.__namedTypes[idx].name
+
+        except IndexError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def getPositionByName(self, name):
+        """Return field position by filed name.
+
+        Parameters
+        ----------
+        name: :py:class:`str`
+            Field name
+
+        Returns
+        -------
+        : :py:class:`int`
+            Field position in fields set
+
+        Raises
+        ------
+        : :class:`~pyasn1.error.PyAsn1Error`
+            If *name* is not present or not unique within callee *NamedTypes*
+        """
+        try:
+            return self.__nameToPosMap[name]
+
+        except KeyError:
+            raise error.PyAsn1Error('Name %s not found' % (name,))
+
+    def getTagMapNearPosition(self, idx):
+        """Return ASN.1 types that are allowed at or past given field position.
+
+        Some ASN.1 serialisation allow for skipping optional and defaulted fields.
+        Some constructed ASN.1 types allow reordering of the fields. When recovering
+        such objects it may be important to know which types can possibly be
+        present at any given position in the field sets.
+
+        Parameters
+        ----------
+        idx: :py:class:`int`
+            Field index
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tagmap.TagMap`
+            Map if ASN.1 types allowed at given field position
+
+        Raises
+        ------
+        : :class:`~pyasn1.error.PyAsn1Error`
+            If given position is out of fields range
+        """
+        try:
+            return self.__ambiguousTypes[idx].tagMap
+
+        except KeyError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def getPositionNearType(self, tagSet, idx):
+        """Return the closest field position where given ASN.1 type is allowed.
+
+        Some ASN.1 serialisation allow for skipping optional and defaulted fields.
+        Some constructed ASN.1 types allow reordering of the fields. When recovering
+        such objects it may be important to know at which field position, in field set,
+        given *tagSet* is allowed at or past *idx* position.
+
+        Parameters
+        ----------
+        tagSet: :class:`~pyasn1.type.tag.TagSet`
+           ASN.1 type which field position to look up
+
+        idx: :py:class:`int`
+            Field position at or past which to perform ASN.1 type look up
+
+        Returns
+        -------
+        : :py:class:`int`
+            Field position in fields set
+
+        Raises
+        ------
+        : :class:`~pyasn1.error.PyAsn1Error`
+            If *tagSet* is not present or not unique within callee *NamedTypes*
+            or *idx* is out of fields range
+        """
+        try:
+            return idx + self.__ambiguousTypes[idx].getPositionByType(tagSet)
+
+        except KeyError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def __computeMinTagSet(self):
+        minTagSet = None
+        for namedType in self.__namedTypes:
+            asn1Object = namedType.asn1Object
+
+            try:
+                tagSet = asn1Object.minTagSet
+
+            except AttributeError:
+                tagSet = asn1Object.tagSet
+
+            if minTagSet is None or tagSet < minTagSet:
+                minTagSet = tagSet
+
+        return minTagSet or tag.TagSet()
+
+    @property
+    def minTagSet(self):
+        """Return the minimal TagSet among ASN.1 type in callee *NamedTypes*.
+
+        Some ASN.1 types/serialisation protocols require ASN.1 types to be
+        arranged based on their numerical tag value. The *minTagSet* property
+        returns that.
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tagset.TagSet`
+            Minimal TagSet among ASN.1 types in callee *NamedTypes*
+        """
+        return self.__minTagSet
+
+    def __computeTagMaps(self, unique):
+        presentTypes = {}
+        skipTypes = {}
+        defaultType = None
+        for namedType in self.__namedTypes:
+            tagMap = namedType.asn1Object.tagMap
+            if isinstance(tagMap, NamedTypes.PostponedError):
+                return tagMap
+            for tagSet in tagMap:
+                if unique and tagSet in presentTypes:
+                    return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self))
+                presentTypes[tagSet] = namedType.asn1Object
+            skipTypes.update(tagMap.skipTypes)
+
+            if defaultType is None:
+                defaultType = tagMap.defaultType
+            elif tagMap.defaultType is not None:
+                return NamedTypes.PostponedError('Duplicate default ASN.1 type at %s' % (self,))
+
+        return tagmap.TagMap(presentTypes, skipTypes, defaultType)
+
+    @property
+    def tagMap(self):
+        """Return a *TagMap* object from tags and types recursively.
+
+        Return a :class:`~pyasn1.type.tagmap.TagMap` object by
+        combining tags from *TagMap* objects of children types and
+        associating them with their immediate child type.
+
+        Example
+        -------
+        .. code-block:: python
+
+           OuterType ::= CHOICE {
+               innerType INTEGER
+           }
+
+        Calling *.tagMap* on *OuterType* will yield a map like this:
+
+        .. code-block:: python
+
+           Integer.tagSet -> Choice
+        """
+        return self.__nonUniqueTagMap
+
+    @property
+    def tagMapUnique(self):
+        """Return a *TagMap* object from unique tags and types recursively.
+
+        Return a :class:`~pyasn1.type.tagmap.TagMap` object by
+        combining tags from *TagMap* objects of children types and
+        associating them with their immediate child type.
+
+        Example
+        -------
+        .. code-block:: python
+
+           OuterType ::= CHOICE {
+               innerType INTEGER
+           }
+
+        Calling *.tagMapUnique* on *OuterType* will yield a map like this:
+
+        .. code-block:: python
+
+           Integer.tagSet -> Choice
+
+        Note
+        ----
+
+        Duplicate *TagSet* objects found in the tree of children
+        types would cause error.
+        """
+        return self.__uniqueTagMap
+
+    @property
+    def hasOptionalOrDefault(self):
+        return self.__hasOptionalOrDefault
+
+    @property
+    def hasOpenTypes(self):
+        return self.__hasOpenTypes
+
+    @property
+    def namedTypes(self):
+        return tuple(self.__namedTypes)
+
+    @property
+    def requiredComponents(self):
+        return self.__requiredComponents
diff --git a/pyasn1/type/namedval.py b/pyasn1/type/namedval.py
new file mode 100644
index 0000000..59257e4
--- /dev/null
+++ b/pyasn1/type/namedval.py
@@ -0,0 +1,191 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+# ASN.1 named integers
+#
+from pyasn1 import error
+
+__all__ = ['NamedValues']
+
+
+class NamedValues(object):
+    """Create named values object.
+
+    The |NamedValues| object represents a collection of string names
+    associated with numeric IDs. These objects are used for giving
+    names to otherwise numerical values.
+
+    |NamedValues| objects are immutable and duck-type Python
+    :class:`dict` object mapping ID to name and vice-versa.
+
+    Parameters
+    ----------
+    \*args: variable number of two-element :py:class:`tuple`
+
+        name: :py:class:`str`
+            Value label
+
+        value: :py:class:`int`
+            Numeric value
+
+    Keyword Args
+    ------------
+    name: :py:class:`str`
+        Value label
+
+    value: :py:class:`int`
+        Numeric value
+
+    Examples
+    --------
+
+    .. code-block:: pycon
+
+        >>> nv = NamedValues('a', 'b', ('c', 0), d=1)
+        >>> nv
+        >>> {'c': 0, 'd': 1, 'a': 2, 'b': 3}
+        >>> nv[0]
+        'c'
+        >>> nv['a']
+        2
+    """
+    def __init__(self, *args, **kwargs):
+        self.__names = {}
+        self.__numbers = {}
+
+        anonymousNames = []
+
+        for namedValue in args:
+            if isinstance(namedValue, (tuple, list)):
+                try:
+                    name, number = namedValue
+
+                except ValueError:
+                    raise error.PyAsn1Error('Not a proper attribute-value pair %r' % (namedValue,))
+
+            else:
+                anonymousNames.append(namedValue)
+                continue
+
+            if name in self.__names:
+                raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+            if number in self.__numbers:
+                raise error.PyAsn1Error('Duplicate number  %s=%s' % (name, number))
+
+            self.__names[name] = number
+            self.__numbers[number] = name
+
+        for name, number in kwargs.items():
+            if name in self.__names:
+                raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+            if number in self.__numbers:
+                raise error.PyAsn1Error('Duplicate number  %s=%s' % (name, number))
+
+            self.__names[name] = number
+            self.__numbers[number] = name
+
+        if anonymousNames:
+
+            number = self.__numbers and max(self.__numbers) + 1 or 0
+
+            for name in anonymousNames:
+
+                if name in self.__names:
+                    raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+                self.__names[name] = number
+                self.__numbers[number] = name
+
+                number += 1
+
+    def __repr__(self):
+        representation = ', '.join(['%s=%d' % x for x in self.items()])
+
+        if len(representation) > 64:
+            representation = representation[:32] + '...' + representation[-32:]
+
+        return '<%s object 0x%x enums %s>' % (self.__class__.__name__, id(self), representation)
+
+    def __eq__(self, other):
+        return dict(self) == other
+
+    def __ne__(self, other):
+        return dict(self) != other
+
+    def __lt__(self, other):
+        return dict(self) < other
+
+    def __le__(self, other):
+        return dict(self) <= other
+
+    def __gt__(self, other):
+        return dict(self) > other
+
+    def __ge__(self, other):
+        return dict(self) >= other
+
+    def __hash__(self):
+        return hash(self.items())
+
+    # Python dict protocol (read-only)
+
+    def __getitem__(self, key):
+        try:
+            return self.__numbers[key]
+
+        except KeyError:
+            return self.__names[key]
+
+    def __len__(self):
+        return len(self.__names)
+
+    def __contains__(self, key):
+        return key in self.__names or key in self.__numbers
+
+    def __iter__(self):
+        return iter(self.__names)
+
+    def values(self):
+        return iter(self.__numbers)
+
+    def keys(self):
+        return iter(self.__names)
+
+    def items(self):
+        for name in self.__names:
+            yield name, self.__names[name]
+
+    # support merging
+
+    def __add__(self, namedValues):
+        return self.__class__(*tuple(self.items()) + tuple(namedValues.items()))
+
+    # XXX clone/subtype?
+
+    def clone(self, *args, **kwargs):
+        new = self.__class__(*args, **kwargs)
+        return self + new
+
+    # legacy protocol
+
+    def getName(self, value):
+        if value in self.__numbers:
+            return self.__numbers[value]
+
+    def getValue(self, name):
+        if name in self.__names:
+            return self.__names[name]
+
+    def getValues(self, *names):
+        try:
+            return [self.__names[name] for name in names]
+
+        except KeyError:
+            raise error.PyAsn1Error(
+                'Unknown bit identifier(s): %s' % (set(names).difference(self.__names),)
+            )
diff --git a/pyasn1/type/opentype.py b/pyasn1/type/opentype.py
new file mode 100644
index 0000000..d14ab34
--- /dev/null
+++ b/pyasn1/type/opentype.py
@@ -0,0 +1,75 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+
+__all__ = ['OpenType']
+
+
+class OpenType(object):
+    """Create ASN.1 type map indexed by a value
+
+    The *DefinedBy* object models the ASN.1 *DEFINED BY* clause which maps
+    values to ASN.1 types in the context of the ASN.1 SEQUENCE/SET type.
+
+    OpenType objects are duck-type a read-only Python :class:`dict` objects,
+    however the passed `typeMap` is stored by reference.
+
+    Parameters
+    ----------
+    name: :py:class:`str`
+        Field name
+
+    typeMap: :py:class:`dict`
+        A map of value->ASN.1 type. It's stored by reference and can be
+        mutated later to register new mappings.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        openType = OpenType(
+            'id',
+            {1: Integer(),
+             2: OctetString()}
+        )
+        Sequence(
+            componentType=NamedTypes(
+                NamedType('id', Integer()),
+                NamedType('blob', Any(), openType=openType)
+            )
+        )
+    """
+
+    def __init__(self, name, typeMap=None):
+        self.__name = name
+        if typeMap is None:
+            self.__typeMap = {}
+        else:
+            self.__typeMap = typeMap
+
+    @property
+    def name(self):
+        return self.__name
+
+    # Python dict protocol
+
+    def values(self):
+        return self.__typeMap.values()
+
+    def keys(self):
+        return self.__typeMap.keys()
+
+    def items(self):
+        return self.__typeMap.items()
+
+    def __contains__(self, key):
+        return key in self.__typeMap
+
+    def __getitem__(self, key):
+        return self.__typeMap[key]
+
+    def __iter__(self):
+        return iter(self.__typeMap)
diff --git a/pyasn1/type/tag.py b/pyasn1/type/tag.py
new file mode 100644
index 0000000..95c226f
--- /dev/null
+++ b/pyasn1/type/tag.py
@@ -0,0 +1,333 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import error
+
+__all__ = ['tagClassUniversal', 'tagClassApplication', 'tagClassContext',
+           'tagClassPrivate', 'tagFormatSimple', 'tagFormatConstructed',
+           'tagCategoryImplicit', 'tagCategoryExplicit',
+           'tagCategoryUntagged', 'Tag', 'TagSet']
+
+#: Identifier for ASN.1 class UNIVERSAL
+tagClassUniversal = 0x00
+
+#: Identifier for ASN.1 class APPLICATION
+tagClassApplication = 0x40
+
+#: Identifier for ASN.1 class context-specific
+tagClassContext = 0x80
+
+#: Identifier for ASN.1 class private
+tagClassPrivate = 0xC0
+
+#: Identifier for "simple" ASN.1 structure (e.g. scalar)
+tagFormatSimple = 0x00
+
+#: Identifier for "constructed" ASN.1 structure (e.g. may have inner components)
+tagFormatConstructed = 0x20
+
+tagCategoryImplicit = 0x01
+tagCategoryExplicit = 0x02
+tagCategoryUntagged = 0x04
+
+
+class Tag(object):
+    """Create ASN.1 tag
+
+    Represents ASN.1 tag that can be attached to a ASN.1 type to make
+    types distinguishable from each other.
+
+    *Tag* objects are immutable and duck-type Python :class:`tuple` objects
+    holding three integer components of a tag.
+
+    Parameters
+    ----------
+    tagClass: :py:class:`int`
+        Tag *class* value
+
+    tagFormat: :py:class:`int`
+        Tag *format* value
+
+    tagId: :py:class:`int`
+        Tag ID value
+    """
+    def __init__(self, tagClass, tagFormat, tagId):
+        if tagId < 0:
+            raise error.PyAsn1Error('Negative tag ID (%s) not allowed' % tagId)
+        self.__tagClass = tagClass
+        self.__tagFormat = tagFormat
+        self.__tagId = tagId
+        self.__tagClassId = tagClass, tagId
+        self.__hash = hash(self.__tagClassId)
+
+    def __repr__(self):
+        representation = '[%s:%s:%s]' % (self.__tagClass, self.__tagFormat, self.__tagId)
+        return '<%s object at 0x%x tag %s>' % (self.__class__.__name__, id(self), representation)
+
+    def __eq__(self, other):
+        return self.__tagClassId == other
+
+    def __ne__(self, other):
+        return self.__tagClassId != other
+
+    def __lt__(self, other):
+        return self.__tagClassId < other
+
+    def __le__(self, other):
+        return self.__tagClassId <= other
+
+    def __gt__(self, other):
+        return self.__tagClassId > other
+
+    def __ge__(self, other):
+        return self.__tagClassId >= other
+
+    def __hash__(self):
+        return self.__hash
+
+    def __getitem__(self, idx):
+        if idx == 0:
+            return self.__tagClass
+        elif idx == 1:
+            return self.__tagFormat
+        elif idx == 2:
+            return self.__tagId
+        else:
+            raise IndexError()
+
+    def __iter__(self):
+        yield self.__tagClass
+        yield self.__tagFormat
+        yield self.__tagId
+
+    def __and__(self, otherTag):
+        return self.__class__(self.__tagClass & otherTag.tagClass,
+                              self.__tagFormat & otherTag.tagFormat,
+                              self.__tagId & otherTag.tagId)
+
+    def __or__(self, otherTag):
+        return self.__class__(self.__tagClass | otherTag.tagClass,
+                              self.__tagFormat | otherTag.tagFormat,
+                              self.__tagId | otherTag.tagId)
+
+    @property
+    def tagClass(self):
+        """ASN.1 tag class
+
+        Returns
+        -------
+        : :py:class:`int`
+            Tag class
+        """
+        return self.__tagClass
+
+    @property
+    def tagFormat(self):
+        """ASN.1 tag format
+
+        Returns
+        -------
+        : :py:class:`int`
+            Tag format
+        """
+        return self.__tagFormat
+
+    @property
+    def tagId(self):
+        """ASN.1 tag ID
+
+        Returns
+        -------
+        : :py:class:`int`
+            Tag ID
+        """
+        return self.__tagId
+
+
+class TagSet(object):
+    """Create a collection of ASN.1 tags
+
+    Represents a combination of :class:`~pyasn1.type.tag.Tag` objects
+    that can be attached to a ASN.1 type to make types distinguishable
+    from each other.
+
+    *TagSet* objects are immutable and duck-type Python :class:`tuple` objects
+    holding arbitrary number of :class:`~pyasn1.type.tag.Tag` objects.
+
+    Parameters
+    ----------
+    baseTag: :class:`~pyasn1.type.tag.Tag`
+        Base *Tag* object. This tag survives IMPLICIT tagging.
+
+    *superTags: :class:`~pyasn1.type.tag.Tag`
+        Additional *Tag* objects taking part in subtyping.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class OrderNumber(NumericString):
+            '''
+            ASN.1 specification
+
+            Order-number ::=
+                [APPLICATION 5] IMPLICIT NumericString
+            '''
+            tagSet = NumericString.tagSet.tagImplicitly(
+                Tag(tagClassApplication, tagFormatSimple, 5)
+            )
+
+        orderNumber = OrderNumber('1234')
+    """
+    def __init__(self, baseTag=(), *superTags):
+        self.__baseTag = baseTag
+        self.__superTags = superTags
+        self.__superTagsClassId = tuple(
+            [(superTag.tagClass, superTag.tagId) for superTag in superTags]
+        )
+        self.__lenOfSuperTags = len(superTags)
+        self.__hash = hash(self.__superTagsClassId)
+
+    def __repr__(self):
+        representation = '-'.join(['%s:%s:%s' % (x.tagClass, x.tagFormat, x.tagId)
+                                   for x in self.__superTags])
+        if representation:
+            representation = 'tags ' + representation
+        else:
+            representation = 'untagged'
+
+        return '<%s object at 0x%x %s>' % (self.__class__.__name__, id(self), representation)
+
+    def __add__(self, superTag):
+        return self.__class__(self.__baseTag, *self.__superTags + (superTag,))
+
+    def __radd__(self, superTag):
+        return self.__class__(self.__baseTag, *(superTag,) + self.__superTags)
+
+    def __getitem__(self, i):
+        if i.__class__ is slice:
+            return self.__class__(self.__baseTag, *self.__superTags[i])
+        else:
+            return self.__superTags[i]
+
+    def __eq__(self, other):
+        return self.__superTagsClassId == other
+
+    def __ne__(self, other):
+        return self.__superTagsClassId != other
+
+    def __lt__(self, other):
+        return self.__superTagsClassId < other
+
+    def __le__(self, other):
+        return self.__superTagsClassId <= other
+
+    def __gt__(self, other):
+        return self.__superTagsClassId > other
+
+    def __ge__(self, other):
+        return self.__superTagsClassId >= other
+
+    def __hash__(self):
+        return self.__hash
+
+    def __len__(self):
+        return self.__lenOfSuperTags
+
+    @property
+    def baseTag(self):
+        """Return base ASN.1 tag
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tag.Tag`
+            Base tag of this *TagSet*
+        """
+        return self.__baseTag
+
+    @property
+    def superTags(self):
+        """Return ASN.1 tags
+
+        Returns
+        -------
+        : :py:class:`tuple`
+            Tuple of :class:`~pyasn1.type.tag.Tag` objects that this *TagSet* contains
+        """
+        return self.__superTags
+
+    def tagExplicitly(self, superTag):
+        """Return explicitly tagged *TagSet*
+
+        Create a new *TagSet* representing callee *TagSet* explicitly tagged
+        with passed tag(s). With explicit tagging mode, new tags are appended
+        to existing tag(s).
+
+        Parameters
+        ----------
+        superTag: :class:`~pyasn1.type.tag.Tag`
+            *Tag* object to tag this *TagSet*
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tag.TagSet`
+            New *TagSet* object
+        """
+        if superTag.tagClass == tagClassUniversal:
+            raise error.PyAsn1Error("Can't tag with UNIVERSAL class tag")
+        if superTag.tagFormat != tagFormatConstructed:
+            superTag = Tag(superTag.tagClass, tagFormatConstructed, superTag.tagId)
+        return self + superTag
+
+    def tagImplicitly(self, superTag):
+        """Return implicitly tagged *TagSet*
+
+        Create a new *TagSet* representing callee *TagSet* implicitly tagged
+        with passed tag(s). With implicit tagging mode, new tag(s) replace the
+        last existing tag.
+
+        Parameters
+        ----------
+        superTag: :class:`~pyasn1.type.tag.Tag`
+            *Tag* object to tag this *TagSet*
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tag.TagSet`
+            New *TagSet* object
+        """
+        if self.__superTags:
+            superTag = Tag(superTag.tagClass, self.__superTags[-1].tagFormat, superTag.tagId)
+        return self[:-1] + superTag
+
+    def isSuperTagSetOf(self, tagSet):
+        """Test type relationship against given *TagSet*
+
+        The callee is considered to be a supertype of given *TagSet*
+        tag-wise if all tags in *TagSet* are present in the callee and
+        they are in the same order.
+
+        Parameters
+        ----------
+        tagSet: :class:`~pyasn1.type.tag.TagSet`
+            *TagSet* object to evaluate against the callee
+
+        Returns
+        -------
+        : :py:class:`bool`
+            `True` if callee is a supertype of *tagSet*
+        """
+        if len(tagSet) < self.__lenOfSuperTags:
+            return False
+        return self.__superTags == tagSet[:self.__lenOfSuperTags]
+
+    # Backward compatibility
+
+    def getBaseTag(self):
+        return self.__baseTag
+
+def initTagSet(tag):
+    return TagSet(tag, tag)
diff --git a/pyasn1/type/tagmap.py b/pyasn1/type/tagmap.py
new file mode 100644
index 0000000..a9d237f
--- /dev/null
+++ b/pyasn1/type/tagmap.py
@@ -0,0 +1,96 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import error
+
+__all__ = ['TagMap']
+
+
+class TagMap(object):
+    """Map *TagSet* objects to ASN.1 types
+
+    Create an object mapping *TagSet* object to ASN.1 type.
+
+    *TagMap* objects are immutable and duck-type read-only Python
+    :class:`dict` objects holding *TagSet* objects as keys and ASN.1
+    type objects as values.
+
+    Parameters
+    ----------
+    presentTypes: :py:class:`dict`
+        Map of :class:`~pyasn1.type.tag.TagSet` to ASN.1 objects considered
+        as being unconditionally present in the *TagMap*.
+
+    skipTypes: :py:class:`dict`
+        A collection of :class:`~pyasn1.type.tag.TagSet` objects considered
+        as absent in the *TagMap* even when *defaultType* is present.
+
+    defaultType: ASN.1 type object
+        An ASN.1 type object callee *TagMap* returns for any *TagSet* key not present
+        in *presentTypes* (unless given key is present in *skipTypes*).
+    """
+    def __init__(self, presentTypes=None, skipTypes=None, defaultType=None):
+        self.__presentTypes = presentTypes or {}
+        self.__skipTypes = skipTypes or {}
+        self.__defaultType = defaultType
+
+    def __contains__(self, tagSet):
+        return (tagSet in self.__presentTypes or
+                self.__defaultType is not None and tagSet not in self.__skipTypes)
+
+    def __getitem__(self, tagSet):
+        try:
+            return self.__presentTypes[tagSet]
+        except KeyError:
+            if self.__defaultType is None:
+                raise KeyError()
+            elif tagSet in self.__skipTypes:
+                raise error.PyAsn1Error('Key in negative map')
+            else:
+                return self.__defaultType
+
+    def __iter__(self):
+        return iter(self.__presentTypes)
+
+    def __repr__(self):
+        representation = '%s object at 0x%x' % (self.__class__.__name__, id(self))
+
+        if self.__presentTypes:
+            representation += ' present %s' % repr(self.__presentTypes)
+
+        if self.__skipTypes:
+            representation += ' skip %s' % repr(self.__skipTypes)
+
+        if self.__defaultType is not None:
+            representation += ' default %s' % repr(self.__defaultType)
+
+        return '<%s>' % representation
+
+    @property
+    def presentTypes(self):
+        """Return *TagSet* to ASN.1 type map present in callee *TagMap*"""
+        return self.__presentTypes
+
+    @property
+    def skipTypes(self):
+        """Return *TagSet* collection unconditionally absent in callee *TagMap*"""
+        return self.__skipTypes
+
+    @property
+    def defaultType(self):
+        """Return default ASN.1 type being returned for any missing *TagSet*"""
+        return self.__defaultType
+
+    # Backward compatibility
+
+    def getPosMap(self):
+        return self.presentTypes
+
+    def getNegMap(self):
+        return self.skipTypes
+
+    def getDef(self):
+        return self.defaultType
diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py
new file mode 100644
index 0000000..a19f6ba
--- /dev/null
+++ b/pyasn1/type/univ.py
@@ -0,0 +1,3061 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import math
+import sys
+
+from pyasn1 import error
+from pyasn1.codec.ber import eoo
+from pyasn1.compat import binary
+from pyasn1.compat import integer
+from pyasn1.compat import octets
+from pyasn1.type import base
+from pyasn1.type import constraint
+from pyasn1.type import namedtype
+from pyasn1.type import namedval
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+
+NoValue = base.NoValue
+noValue = NoValue()
+
+__all__ = ['Integer', 'Boolean', 'BitString', 'OctetString', 'Null',
+           'ObjectIdentifier', 'Real', 'Enumerated',
+           'SequenceOfAndSetOfBase', 'SequenceOf', 'SetOf',
+           'SequenceAndSetBase', 'Sequence', 'Set', 'Choice', 'Any',
+           'NoValue', 'noValue']
+
+# "Simple" ASN.1 types (yet incomplete)
+
+
+class Integer(base.AbstractSimpleAsn1Item):
+    """Create |ASN.1| type or object.
+
+    |ASN.1| objects are immutable and duck-type Python :class:`int` objects.
+
+    Keyword Args
+    ------------
+    value: :class:`int`, :class:`str` or |ASN.1| object
+        Python integer or string literal or |ASN.1| class instance.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+        Object representing non-default symbolic aliases for numbers
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class ErrorCode(Integer):
+            '''
+            ASN.1 specification:
+
+            ErrorCode ::=
+                INTEGER { disk-full(1), no-disk(-1),
+                          disk-not-formatted(2) }
+
+            error ErrorCode ::= disk-full
+            '''
+            namedValues = NamedValues(
+                ('disk-full', 1), ('no-disk', -1),
+                ('disk-not-formatted', 2)
+            )
+
+        error = ErrorCode('disk-full')
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+    #: representing symbolic aliases for numbers
+    namedValues = namedval.NamedValues()
+
+    # Optimization for faster codec lookup
+    typeId = base.AbstractSimpleAsn1Item.getTypeId()
+
+    def __init__(self, value=noValue, **kwargs):
+        if 'namedValues' not in kwargs:
+            kwargs['namedValues'] = self.namedValues
+
+        base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs)
+
+    def __and__(self, value):
+        return self.clone(self._value & value)
+
+    def __rand__(self, value):
+        return self.clone(value & self._value)
+
+    def __or__(self, value):
+        return self.clone(self._value | value)
+
+    def __ror__(self, value):
+        return self.clone(value | self._value)
+
+    def __xor__(self, value):
+        return self.clone(self._value ^ value)
+
+    def __rxor__(self, value):
+        return self.clone(value ^ self._value)
+
+    def __lshift__(self, value):
+        return self.clone(self._value << value)
+
+    def __rshift__(self, value):
+        return self.clone(self._value >> value)
+
+    def __add__(self, value):
+        return self.clone(self._value + value)
+
+    def __radd__(self, value):
+        return self.clone(value + self._value)
+
+    def __sub__(self, value):
+        return self.clone(self._value - value)
+
+    def __rsub__(self, value):
+        return self.clone(value - self._value)
+
+    def __mul__(self, value):
+        return self.clone(self._value * value)
+
+    def __rmul__(self, value):
+        return self.clone(value * self._value)
+
+    def __mod__(self, value):
+        return self.clone(self._value % value)
+
+    def __rmod__(self, value):
+        return self.clone(value % self._value)
+
+    def __pow__(self, value, modulo=None):
+        return self.clone(pow(self._value, value, modulo))
+
+    def __rpow__(self, value):
+        return self.clone(pow(value, self._value))
+
+    def __floordiv__(self, value):
+        return self.clone(self._value // value)
+
+    def __rfloordiv__(self, value):
+        return self.clone(value // self._value)
+
+    if sys.version_info[0] <= 2:
+        def __div__(self, value):
+            if isinstance(value, float):
+                return Real(self._value / value)
+            else:
+                return self.clone(self._value / value)
+
+        def __rdiv__(self, value):
+            if isinstance(value, float):
+                return Real(value / self._value)
+            else:
+                return self.clone(value / self._value)
+    else:
+        def __truediv__(self, value):
+            return Real(self._value / value)
+
+        def __rtruediv__(self, value):
+            return Real(value / self._value)
+
+        def __divmod__(self, value):
+            return self.clone(divmod(self._value, value))
+
+        def __rdivmod__(self, value):
+            return self.clone(divmod(value, self._value))
+
+        __hash__ = base.AbstractSimpleAsn1Item.__hash__
+
+    def __int__(self):
+        return int(self._value)
+
+    if sys.version_info[0] <= 2:
+        def __long__(self):
+            return long(self._value)
+
+    def __float__(self):
+        return float(self._value)
+
+    def __abs__(self):
+        return self.clone(abs(self._value))
+
+    def __index__(self):
+        return int(self._value)
+
+    def __pos__(self):
+        return self.clone(+self._value)
+
+    def __neg__(self):
+        return self.clone(-self._value)
+
+    def __invert__(self):
+        return self.clone(~self._value)
+
+    def __round__(self, n=0):
+        r = round(self._value, n)
+        if n:
+            return self.clone(r)
+        else:
+            return r
+
+    def __floor__(self):
+        return math.floor(self._value)
+
+    def __ceil__(self):
+        return math.ceil(self._value)
+
+    if sys.version_info[0:2] > (2, 5):
+        def __trunc__(self):
+            return self.clone(math.trunc(self._value))
+
+    def __lt__(self, value):
+        return self._value < value
+
+    def __le__(self, value):
+        return self._value <= value
+
+    def __eq__(self, value):
+        return self._value == value
+
+    def __ne__(self, value):
+        return self._value != value
+
+    def __gt__(self, value):
+        return self._value > value
+
+    def __ge__(self, value):
+        return self._value >= value
+
+    def prettyIn(self, value):
+        try:
+            return int(value)
+
+        except ValueError:
+            try:
+                return self.namedValues[value]
+
+            except KeyError:
+                raise error.PyAsn1Error(
+                    'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1])
+                )
+
+    def prettyOut(self, value):
+        try:
+            return str(self.namedValues[value])
+
+        except KeyError:
+            return str(value)
+
+    # backward compatibility
+
+    def getNamedValues(self):
+        return self.namedValues
+
+
+class Boolean(Integer):
+    """Create |ASN.1| type or object.
+
+    |ASN.1| objects are immutable and duck-type Python :class:`int` objects.
+
+    Keyword Args
+    ------------
+    value: :class:`int`, :class:`str` or |ASN.1| object
+        Python integer or boolean or string literal or |ASN.1| class instance.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+        Object representing non-default symbolic aliases for numbers
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class RoundResult(Boolean):
+            '''
+            ASN.1 specification:
+
+            RoundResult ::= BOOLEAN
+
+            ok RoundResult ::= TRUE
+            ko RoundResult ::= FALSE
+            '''
+        ok = RoundResult(True)
+        ko = RoundResult(False)
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01),
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = Integer.subtypeSpec + constraint.SingleValueConstraint(0, 1)
+
+    #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+    #: representing symbolic aliases for numbers
+    namedValues = namedval.NamedValues(('False', 0), ('True', 1))
+
+    # Optimization for faster codec lookup
+    typeId = Integer.getTypeId()
+
+if sys.version_info[0] < 3:
+    SizedIntegerBase = long
+else:
+    SizedIntegerBase = int
+
+
+class SizedInteger(SizedIntegerBase):
+    bitLength = leadingZeroBits = None
+
+    def setBitLength(self, bitLength):
+        self.bitLength = bitLength
+        self.leadingZeroBits = max(bitLength - integer.bitLength(self), 0)
+        return self
+
+    def __len__(self):
+        if self.bitLength is None:
+            self.setBitLength(integer.bitLength(self))
+
+        return self.bitLength
+
+
+class BitString(base.AbstractSimpleAsn1Item):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| objects are immutable and duck-type both Python :class:`tuple` (as a tuple
+    of bits) and :class:`int` objects.
+
+    Keyword Args
+    ------------
+    value: :class:`int`, :class:`str` or |ASN.1| object
+        Python integer or string literal representing binary or hexadecimal
+        number or sequence of integer bits or |ASN.1| object.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+        Object representing non-default symbolic aliases for numbers
+
+    binValue: :py:class:`str`
+        Binary string initializer to use instead of the *value*.
+        Example: '10110011'.
+
+    hexValue: :py:class:`str`
+        Hexadecimal string initializer to use instead of the *value*.
+        Example: 'DEADBEEF'.
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Rights(BitString):
+            '''
+            ASN.1 specification:
+
+            Rights ::= BIT STRING { user-read(0), user-write(1),
+                                    group-read(2), group-write(3),
+                                    other-read(4), other-write(5) }
+
+            group1 Rights ::= { group-read, group-write }
+            group2 Rights ::= '0011'B
+            group3 Rights ::= '3'H
+            '''
+            namedValues = NamedValues(
+                ('user-read', 0), ('user-write', 1),
+                ('group-read', 2), ('group-write', 3),
+                ('other-read', 4), ('other-write', 5)
+            )
+
+        group1 = Rights(('group-read', 'group-write'))
+        group2 = Rights('0011')
+        group3 = Rights(0x3)
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+    #: representing symbolic aliases for numbers
+    namedValues = namedval.NamedValues()
+
+    # Optimization for faster codec lookup
+    typeId = base.AbstractSimpleAsn1Item.getTypeId()
+
+    defaultBinValue = defaultHexValue = noValue
+
+    def __init__(self, value=noValue, **kwargs):
+        if value is noValue:
+            if kwargs:
+                try:
+                    value = self.fromBinaryString(kwargs.pop('binValue'), internalFormat=True)
+
+                except KeyError:
+                    pass
+
+                try:
+                    value = self.fromHexString(kwargs.pop('hexValue'), internalFormat=True)
+
+                except KeyError:
+                    pass
+
+        if value is noValue:
+            if self.defaultBinValue is not noValue:
+                value = self.fromBinaryString(self.defaultBinValue, internalFormat=True)
+
+            elif self.defaultHexValue is not noValue:
+                value = self.fromHexString(self.defaultHexValue, internalFormat=True)
+
+        if 'namedValues' not in kwargs:
+            kwargs['namedValues'] = self.namedValues
+
+        base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs)
+
+    def __str__(self):
+        return self.asBinary()
+
+    def __eq__(self, other):
+        other = self.prettyIn(other)
+        return self is other or self._value == other and len(self._value) == len(other)
+
+    def __ne__(self, other):
+        other = self.prettyIn(other)
+        return self._value != other or len(self._value) != len(other)
+
+    def __lt__(self, other):
+        other = self.prettyIn(other)
+        return len(self._value) < len(other) or len(self._value) == len(other) and self._value < other
+
+    def __le__(self, other):
+        other = self.prettyIn(other)
+        return len(self._value) <= len(other) or len(self._value) == len(other) and self._value <= other
+
+    def __gt__(self, other):
+        other = self.prettyIn(other)
+        return len(self._value) > len(other) or len(self._value) == len(other) and self._value > other
+
+    def __ge__(self, other):
+        other = self.prettyIn(other)
+        return len(self._value) >= len(other) or len(self._value) == len(other) and self._value >= other
+
+    # Immutable sequence object protocol
+
+    def __len__(self):
+        return len(self._value)
+
+    def __getitem__(self, i):
+        if i.__class__ is slice:
+            return self.clone([self[x] for x in range(*i.indices(len(self)))])
+        else:
+            length = len(self._value) - 1
+            if i > length or i < 0:
+                raise IndexError('bit index out of range')
+            return (self._value >> (length - i)) & 1
+
+    def __iter__(self):
+        length = len(self._value)
+        while length:
+            length -= 1
+            yield (self._value >> length) & 1
+
+    def __reversed__(self):
+        return reversed(tuple(self))
+
+    # arithmetic operators
+
+    def __add__(self, value):
+        value = self.prettyIn(value)
+        return self.clone(SizedInteger(self._value << len(value) | value).setBitLength(len(self._value) + len(value)))
+
+    def __radd__(self, value):
+        value = self.prettyIn(value)
+        return self.clone(SizedInteger(value << len(self._value) | self._value).setBitLength(len(self._value) + len(value)))
+
+    def __mul__(self, value):
+        bitString = self._value
+        while value > 1:
+            bitString <<= len(self._value)
+            bitString |= self._value
+            value -= 1
+        return self.clone(bitString)
+
+    def __rmul__(self, value):
+        return self * value
+
+    def __lshift__(self, count):
+        return self.clone(SizedInteger(self._value << count).setBitLength(len(self._value) + count))
+
+    def __rshift__(self, count):
+        return self.clone(SizedInteger(self._value >> count).setBitLength(max(0, len(self._value) - count)))
+
+    def __int__(self):
+        return self._value
+
+    def __float__(self):
+        return float(self._value)
+
+    if sys.version_info[0] < 3:
+        def __long__(self):
+            return self._value
+
+    def asNumbers(self):
+        """Get |ASN.1| value as a sequence of 8-bit integers.
+
+        If |ASN.1| object length is not a multiple of 8, result
+        will be left-padded with zeros.
+        """
+        return tuple(octets.octs2ints(self.asOctets()))
+
+    def asOctets(self):
+        """Get |ASN.1| value as a sequence of octets.
+
+        If |ASN.1| object length is not a multiple of 8, result
+        will be left-padded with zeros.
+        """
+        return integer.to_bytes(self._value, length=len(self))
+
+    def asInteger(self):
+        """Get |ASN.1| value as a single integer value.
+        """
+        return self._value
+
+    def asBinary(self):
+        """Get |ASN.1| value as a text string of bits.
+        """
+        binString = binary.bin(self._value)[2:]
+        return '0' * (len(self._value) - len(binString)) + binString
+
+    @classmethod
+    def fromHexString(cls, value, internalFormat=False, prepend=None):
+        """Create a |ASN.1| object initialized from the hex string.
+
+        Parameters
+        ----------
+        value: :class:`str`
+            Text string like 'DEADBEEF'
+        """
+        try:
+            value = SizedInteger(value, 16).setBitLength(len(value) * 4)
+
+        except ValueError:
+            raise error.PyAsn1Error('%s.fromHexString() error: %s' % (cls.__name__, sys.exc_info()[1]))
+
+        if prepend is not None:
+            value = SizedInteger(
+                (SizedInteger(prepend) << len(value)) | value
+            ).setBitLength(len(prepend) + len(value))
+
+        if not internalFormat:
+            value = cls(value)
+
+        return value
+
+    @classmethod
+    def fromBinaryString(cls, value, internalFormat=False, prepend=None):
+        """Create a |ASN.1| object initialized from a string of '0' and '1'.
+
+        Parameters
+        ----------
+        value: :class:`str`
+            Text string like '1010111'
+        """
+        try:
+            value = SizedInteger(value or '0', 2).setBitLength(len(value))
+
+        except ValueError:
+            raise error.PyAsn1Error('%s.fromBinaryString() error: %s' % (cls.__name__, sys.exc_info()[1]))
+
+        if prepend is not None:
+            value = SizedInteger(
+                (SizedInteger(prepend) << len(value)) | value
+            ).setBitLength(len(prepend) + len(value))
+
+        if not internalFormat:
+            value = cls(value)
+
+        return value
+
+    @classmethod
+    def fromOctetString(cls, value, internalFormat=False, prepend=None, padding=0):
+        """Create a |ASN.1| object initialized from a string.
+
+        Parameters
+        ----------
+        value: :class:`str` (Py2) or :class:`bytes` (Py3)
+            Text string like '\\\\x01\\\\xff' (Py2) or b'\\\\x01\\\\xff' (Py3)
+        """
+        value = SizedInteger(integer.from_bytes(value) >> padding).setBitLength(len(value) * 8 - padding)
+
+        if prepend is not None:
+            value = SizedInteger(
+                (SizedInteger(prepend) << len(value)) | value
+            ).setBitLength(len(prepend) + len(value))
+
+        if not internalFormat:
+            value = cls(value)
+
+        return value
+
+    def prettyIn(self, value):
+        if isinstance(value, SizedInteger):
+            return value
+        elif octets.isStringType(value):
+            if not value:
+                return SizedInteger(0).setBitLength(0)
+
+            elif value[0] == '\'':  # "'1011'B" -- ASN.1 schema representation (deprecated)
+                if value[-2:] == '\'B':
+                    return self.fromBinaryString(value[1:-2], internalFormat=True)
+                elif value[-2:] == '\'H':
+                    return self.fromHexString(value[1:-2], internalFormat=True)
+                else:
+                    raise error.PyAsn1Error(
+                        'Bad BIT STRING value notation %s' % (value,)
+                    )
+
+            elif self.namedValues and not value.isdigit():  # named bits like 'Urgent, Active'
+                names = [x.strip() for x in value.split(',')]
+
+                try:
+
+                    bitPositions = [self.namedValues[name] for name in names]
+
+                except KeyError:
+                    raise error.PyAsn1Error('unknown bit name(s) in %r' % (names,))
+
+                rightmostPosition = max(bitPositions)
+
+                number = 0
+                for bitPosition in bitPositions:
+                    number |= 1 << (rightmostPosition - bitPosition)
+
+                return SizedInteger(number).setBitLength(rightmostPosition + 1)
+
+            elif value.startswith('0x'):
+                return self.fromHexString(value[2:], internalFormat=True)
+
+            elif value.startswith('0b'):
+                return self.fromBinaryString(value[2:], internalFormat=True)
+
+            else:  # assume plain binary string like '1011'
+                return self.fromBinaryString(value, internalFormat=True)
+
+        elif isinstance(value, (tuple, list)):
+            return self.fromBinaryString(''.join([b and '1' or '0' for b in value]), internalFormat=True)
+
+        elif isinstance(value, BitString):
+            return SizedInteger(value).setBitLength(len(value))
+
+        elif isinstance(value, intTypes):
+            return SizedInteger(value)
+
+        else:
+            raise error.PyAsn1Error(
+                'Bad BitString initializer type \'%s\'' % (value,)
+            )
+
+
+try:
+    # noinspection PyStatementEffect
+    all
+
+except NameError:  # Python 2.4
+    # noinspection PyShadowingBuiltins
+    def all(iterable):
+        for element in iterable:
+            if not element:
+                return False
+        return True
+
+
+class OctetString(base.AbstractSimpleAsn1Item):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| objects are immutable and duck-type Python 2 :class:`str` or Python 3 :class:`bytes`.
+    When used in Unicode context, |ASN.1| type assumes "|encoding|" serialisation.
+
+    Keyword Args
+    ------------
+    value: :class:`str`, :class:`bytes` or |ASN.1| object
+        string (Python 2) or bytes (Python 3), alternatively unicode object
+        (Python 2) or string (Python 3) representing character string to be
+        serialised into octets (note `encoding` parameter) or |ASN.1| object.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    encoding: :py:class:`str`
+        Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
+        :class:`str` (Python 3) the payload when |ASN.1| object is used
+        in text string context.
+
+    binValue: :py:class:`str`
+        Binary string initializer to use instead of the *value*.
+        Example: '10110011'.
+
+    hexValue: :py:class:`str`
+        Hexadecimal string initializer to use instead of the *value*.
+        Example: 'DEADBEEF'.
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Icon(OctetString):
+            '''
+            ASN.1 specification:
+
+            Icon ::= OCTET STRING
+
+            icon1 Icon ::= '001100010011001000110011'B
+            icon2 Icon ::= '313233'H
+            '''
+        icon1 = Icon.fromBinaryString('001100010011001000110011')
+        icon2 = Icon.fromHexString('313233')
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Optimization for faster codec lookup
+    typeId = base.AbstractSimpleAsn1Item.getTypeId()
+
+    defaultBinValue = defaultHexValue = noValue
+    encoding = 'iso-8859-1'
+
+    def __init__(self, value=noValue, **kwargs):
+        if kwargs:
+            if value is noValue:
+                try:
+                    value = self.fromBinaryString(kwargs.pop('binValue'))
+
+                except KeyError:
+                    pass
+
+                try:
+                    value = self.fromHexString(kwargs.pop('hexValue'))
+
+                except KeyError:
+                    pass
+
+        if value is noValue:
+            if self.defaultBinValue is not noValue:
+                value = self.fromBinaryString(self.defaultBinValue)
+
+            elif self.defaultHexValue is not noValue:
+                value = self.fromHexString(self.defaultHexValue)
+
+        if 'encoding' not in kwargs:
+            kwargs['encoding'] = self.encoding
+
+        base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs)
+
+    if sys.version_info[0] <= 2:
+        def prettyIn(self, value):
+            if isinstance(value, str):
+                return value
+            elif isinstance(value, unicode):
+                try:
+                    return value.encode(self.encoding)
+                except (LookupError, UnicodeEncodeError):
+                    raise error.PyAsn1Error(
+                        "Can't encode string '%s' with codec %s" % (value, self.encoding)
+                    )
+            elif isinstance(value, (tuple, list)):
+                try:
+                    return ''.join([chr(x) for x in value])
+                except ValueError:
+                    raise error.PyAsn1Error(
+                        "Bad %s initializer '%s'" % (self.__class__.__name__, value)
+                    )
+            else:
+                return str(value)
+
+        def __str__(self):
+            return str(self._value)
+
+        def __unicode__(self):
+            try:
+                return self._value.decode(self.encoding)
+
+            except UnicodeDecodeError:
+                raise error.PyAsn1Error(
+                    "Can't decode string '%s' with codec %s" % (self._value, self.encoding)
+                )
+
+        def asOctets(self):
+            return str(self._value)
+
+        def asNumbers(self):
+            return tuple([ord(x) for x in self._value])
+
+    else:
+        def prettyIn(self, value):
+            if isinstance(value, bytes):
+                return value
+            elif isinstance(value, str):
+                try:
+                    return value.encode(self.encoding)
+                except UnicodeEncodeError:
+                    raise error.PyAsn1Error(
+                        "Can't encode string '%s' with '%s' codec" % (value, self.encoding)
+                    )
+            elif isinstance(value, OctetString):  # a shortcut, bytes() would work the same way
+                return value.asOctets()
+            elif isinstance(value, base.AbstractSimpleAsn1Item):  # this mostly targets Integer objects
+                return self.prettyIn(str(value))
+            elif isinstance(value, (tuple, list)):
+                return self.prettyIn(bytes(value))
+            else:
+                return bytes(value)
+
+        def __str__(self):
+            try:
+                return self._value.decode(self.encoding)
+
+            except UnicodeDecodeError:
+                raise error.PyAsn1Error(
+                    "Can't decode string '%s' with '%s' codec at '%s'" % (self._value, self.encoding, self.__class__.__name__)
+                )
+
+        def __bytes__(self):
+            return bytes(self._value)
+
+        def asOctets(self):
+            return bytes(self._value)
+
+        def asNumbers(self):
+            return tuple(self._value)
+
+    #
+    # Normally, `.prettyPrint()` is called from `__str__()`. Historically,
+    # OctetString.prettyPrint() used to return hexified payload
+    # representation in cases when non-printable content is present. At the
+    # same time `str()` used to produce either octet-stream (Py2) or
+    # text (Py3) representations.
+    #
+    # Therefore `OctetString.__str__()` -> `.prettyPrint()` call chain is
+    # reversed to preserve the original behaviour.
+    #
+    # Eventually we should deprecate `.prettyPrint()` / `.prettyOut()` harness
+    # and end up with just `__str__()` producing hexified representation while
+    # both text and octet-stream representation should only be requested via
+    # the `.asOctets()` method.
+    #
+    # Note: ASN.1 OCTET STRING is never mean to contain text!
+    #
+
+    def prettyOut(self, value):
+        return value
+
+    def prettyPrint(self, scope=0):
+        # first see if subclass has its own .prettyOut()
+        value = self.prettyOut(self._value)
+
+        if value is not self._value:
+            return value
+
+        numbers = self.asNumbers()
+
+        for x in numbers:
+            # hexify if needed
+            if x < 32 or x > 126:
+                return '0x' + ''.join(('%.2x' % x for x in numbers))
+        else:
+            # this prevents infinite recursion
+            return OctetString.__str__(self)
+
+    @staticmethod
+    def fromBinaryString(value):
+        """Create a |ASN.1| object initialized from a string of '0' and '1'.
+
+        Parameters
+        ----------
+        value: :class:`str`
+            Text string like '1010111'
+        """
+        bitNo = 8
+        byte = 0
+        r = []
+        for v in value:
+            if bitNo:
+                bitNo -= 1
+            else:
+                bitNo = 7
+                r.append(byte)
+                byte = 0
+            if v in ('0', '1'):
+                v = int(v)
+            else:
+                raise error.PyAsn1Error(
+                    'Non-binary OCTET STRING initializer %s' % (v,)
+                )
+            byte |= v << bitNo
+
+        r.append(byte)
+
+        return octets.ints2octs(r)
+
+    @staticmethod
+    def fromHexString(value):
+        """Create a |ASN.1| object initialized from the hex string.
+
+        Parameters
+        ----------
+        value: :class:`str`
+            Text string like 'DEADBEEF'
+        """
+        r = []
+        p = []
+        for v in value:
+            if p:
+                r.append(int(p + v, 16))
+                p = None
+            else:
+                p = v
+        if p:
+            r.append(int(p + '0', 16))
+
+        return octets.ints2octs(r)
+
+    # Immutable sequence object protocol
+
+    def __len__(self):
+        return len(self._value)
+
+    def __getitem__(self, i):
+        if i.__class__ is slice:
+            return self.clone(self._value[i])
+        else:
+            return self._value[i]
+
+    def __iter__(self):
+        return iter(self._value)
+
+    def __contains__(self, value):
+        return value in self._value
+
+    def __add__(self, value):
+        return self.clone(self._value + self.prettyIn(value))
+
+    def __radd__(self, value):
+        return self.clone(self.prettyIn(value) + self._value)
+
+    def __mul__(self, value):
+        return self.clone(self._value * value)
+
+    def __rmul__(self, value):
+        return self * value
+
+    def __int__(self):
+        return int(self._value)
+
+    def __float__(self):
+        return float(self._value)
+
+    def __reversed__(self):
+        return reversed(self._value)
+
+
+class Null(OctetString):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| objects are immutable and duck-type Python :class:`str` objects (always empty).
+
+    Keyword Args
+    ------------
+    value: :class:`str` or :py:class:`~pyasn1.type.univ.Null` object
+        Python empty string literal or any object that evaluates to `False`
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Ack(Null):
+            '''
+            ASN.1 specification:
+
+            Ack ::= NULL
+            '''
+        ack = Ack('')
+    """
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05)
+    )
+    subtypeSpec = OctetString.subtypeSpec + constraint.SingleValueConstraint(octets.str2octs(''))
+
+    # Optimization for faster codec lookup
+    typeId = OctetString.getTypeId()
+
+    def prettyIn(self, value):
+        if value:
+            return value
+
+        return octets.str2octs('')
+
+if sys.version_info[0] <= 2:
+    intTypes = (int, long)
+else:
+    intTypes = (int,)
+
+numericTypes = intTypes + (float,)
+
+
+class ObjectIdentifier(base.AbstractSimpleAsn1Item):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| objects are immutable and duck-type Python :class:`tuple` objects (tuple of non-negative integers).
+
+    Keyword Args
+    ------------
+    value: :class:`tuple`, :class:`str` or |ASN.1| object
+        Python sequence of :class:`int` or string literal or |ASN.1| object.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class ID(ObjectIdentifier):
+            '''
+            ASN.1 specification:
+
+            ID ::= OBJECT IDENTIFIER
+
+            id-edims ID ::= { joint-iso-itu-t mhs-motif(6) edims(7) }
+            id-bp ID ::= { id-edims 11 }
+            '''
+        id_edims = ID('2.6.7')
+        id_bp = id_edims + (11,)
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Optimization for faster codec lookup
+    typeId = base.AbstractSimpleAsn1Item.getTypeId()
+
+    def __add__(self, other):
+        return self.clone(self._value + other)
+
+    def __radd__(self, other):
+        return self.clone(other + self._value)
+
+    def asTuple(self):
+        return self._value
+
+    # Sequence object protocol
+
+    def __len__(self):
+        return len(self._value)
+
+    def __getitem__(self, i):
+        if i.__class__ is slice:
+            return self.clone(self._value[i])
+        else:
+            return self._value[i]
+
+    def __iter__(self):
+        return iter(self._value)
+
+    def __contains__(self, value):
+        return value in self._value
+
+    def index(self, suboid):
+        return self._value.index(suboid)
+
+    def isPrefixOf(self, other):
+        """Indicate if this |ASN.1| object is a prefix of other |ASN.1| object.
+
+        Parameters
+        ----------
+        other: |ASN.1| object
+            |ASN.1| object
+
+        Returns
+        -------
+        : :class:`bool`
+            :class:`True` if this |ASN.1| object is a parent (e.g. prefix) of the other |ASN.1| object
+            or :class:`False` otherwise.
+        """
+        l = len(self)
+        if l <= len(other):
+            if self._value[:l] == other[:l]:
+                return True
+        return False
+
+    def prettyIn(self, value):
+        if isinstance(value, ObjectIdentifier):
+            return tuple(value)
+        elif octets.isStringType(value):
+            if '-' in value:
+                raise error.PyAsn1Error(
+                    'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1])
+                )
+            try:
+                return tuple([int(subOid) for subOid in value.split('.') if subOid])
+            except ValueError:
+                raise error.PyAsn1Error(
+                    'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1])
+                )
+
+        try:
+            tupleOfInts = tuple([int(subOid) for subOid in value if subOid >= 0])
+
+        except (ValueError, TypeError):
+            raise error.PyAsn1Error(
+                'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1])
+            )
+
+        if len(tupleOfInts) == len(value):
+            return tupleOfInts
+
+        raise error.PyAsn1Error('Malformed Object ID %s at %s' % (value, self.__class__.__name__))
+
+    def prettyOut(self, value):
+        return '.'.join([str(x) for x in value])
+
+
+class Real(base.AbstractSimpleAsn1Item):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| objects are immutable and duck-type Python :class:`float` objects.
+    Additionally, |ASN.1| objects behave like a :class:`tuple` in which case its
+    elements are mantissa, base and exponent.
+
+    Keyword Args
+    ------------
+    value: :class:`tuple`, :class:`float` or |ASN.1| object
+        Python sequence of :class:`int` (representing mantissa, base and
+        exponent) or float instance or *Real* class instance.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Pi(Real):
+            '''
+            ASN.1 specification:
+
+            Pi ::= REAL
+
+            pi Pi ::= { mantissa 314159, base 10, exponent -5 }
+
+            '''
+        pi = Pi((314159, 10, -5))
+    """
+    binEncBase = None  # binEncBase = 16 is recommended for large numbers
+
+    try:
+        _plusInf = float('inf')
+        _minusInf = float('-inf')
+        _inf = _plusInf, _minusInf
+
+    except ValueError:
+        # Infinity support is platform and Python dependent
+        _plusInf = _minusInf = None
+        _inf = ()
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Optimization for faster codec lookup
+    typeId = base.AbstractSimpleAsn1Item.getTypeId()
+
+    @staticmethod
+    def __normalizeBase10(value):
+        m, b, e = value
+        while m and m % 10 == 0:
+            m /= 10
+            e += 1
+        return m, b, e
+
+    def prettyIn(self, value):
+        if isinstance(value, tuple) and len(value) == 3:
+            if (not isinstance(value[0], numericTypes) or
+                    not isinstance(value[1], intTypes) or
+                    not isinstance(value[2], intTypes)):
+                raise error.PyAsn1Error('Lame Real value syntax: %s' % (value,))
+            if (isinstance(value[0], float) and
+                    self._inf and value[0] in self._inf):
+                return value[0]
+            if value[1] not in (2, 10):
+                raise error.PyAsn1Error(
+                    'Prohibited base for Real value: %s' % (value[1],)
+                )
+            if value[1] == 10:
+                value = self.__normalizeBase10(value)
+            return value
+        elif isinstance(value, intTypes):
+            return self.__normalizeBase10((value, 10, 0))
+        elif isinstance(value, float) or octets.isStringType(value):
+            if octets.isStringType(value):
+                try:
+                    value = float(value)
+                except ValueError:
+                    raise error.PyAsn1Error(
+                        'Bad real value syntax: %s' % (value,)
+                    )
+            if self._inf and value in self._inf:
+                return value
+            else:
+                e = 0
+                while int(value) != value:
+                    value *= 10
+                    e -= 1
+                return self.__normalizeBase10((int(value), 10, e))
+        elif isinstance(value, Real):
+            return tuple(value)
+        raise error.PyAsn1Error(
+            'Bad real value syntax: %s' % (value,)
+        )
+
+    def prettyPrint(self, scope=0):
+        try:
+            return self.prettyOut(float(self))
+
+        except OverflowError:
+            return '<overflow>'
+
+    @property
+    def isPlusInf(self):
+        """Indicate PLUS-INFINITY object value
+
+        Returns
+        -------
+        : :class:`bool`
+            :class:`True` if calling object represents plus infinity
+            or :class:`False` otherwise.
+
+        """
+        return self._value == self._plusInf
+
+    @property
+    def isMinusInf(self):
+        """Indicate MINUS-INFINITY object value
+
+        Returns
+        -------
+        : :class:`bool`
+            :class:`True` if calling object represents minus infinity
+            or :class:`False` otherwise.
+        """
+        return self._value == self._minusInf
+
+    @property
+    def isInf(self):
+        return self._value in self._inf
+
+    def __add__(self, value):
+        return self.clone(float(self) + value)
+
+    def __radd__(self, value):
+        return self + value
+
+    def __mul__(self, value):
+        return self.clone(float(self) * value)
+
+    def __rmul__(self, value):
+        return self * value
+
+    def __sub__(self, value):
+        return self.clone(float(self) - value)
+
+    def __rsub__(self, value):
+        return self.clone(value - float(self))
+
+    def __mod__(self, value):
+        return self.clone(float(self) % value)
+
+    def __rmod__(self, value):
+        return self.clone(value % float(self))
+
+    def __pow__(self, value, modulo=None):
+        return self.clone(pow(float(self), value, modulo))
+
+    def __rpow__(self, value):
+        return self.clone(pow(value, float(self)))
+
+    if sys.version_info[0] <= 2:
+        def __div__(self, value):
+            return self.clone(float(self) / value)
+
+        def __rdiv__(self, value):
+            return self.clone(value / float(self))
+    else:
+        def __truediv__(self, value):
+            return self.clone(float(self) / value)
+
+        def __rtruediv__(self, value):
+            return self.clone(value / float(self))
+
+        def __divmod__(self, value):
+            return self.clone(float(self) // value)
+
+        def __rdivmod__(self, value):
+            return self.clone(value // float(self))
+
+    def __int__(self):
+        return int(float(self))
+
+    if sys.version_info[0] <= 2:
+        def __long__(self):
+            return long(float(self))
+
+    def __float__(self):
+        if self._value in self._inf:
+            return self._value
+        else:
+            return float(
+                self._value[0] * pow(self._value[1], self._value[2])
+            )
+
+    def __abs__(self):
+        return self.clone(abs(float(self)))
+
+    def __pos__(self):
+        return self.clone(+float(self))
+
+    def __neg__(self):
+        return self.clone(-float(self))
+
+    def __round__(self, n=0):
+        r = round(float(self), n)
+        if n:
+            return self.clone(r)
+        else:
+            return r
+
+    def __floor__(self):
+        return self.clone(math.floor(float(self)))
+
+    def __ceil__(self):
+        return self.clone(math.ceil(float(self)))
+
+    if sys.version_info[0:2] > (2, 5):
+        def __trunc__(self):
+            return self.clone(math.trunc(float(self)))
+
+    def __lt__(self, value):
+        return float(self) < value
+
+    def __le__(self, value):
+        return float(self) <= value
+
+    def __eq__(self, value):
+        return float(self) == value
+
+    def __ne__(self, value):
+        return float(self) != value
+
+    def __gt__(self, value):
+        return float(self) > value
+
+    def __ge__(self, value):
+        return float(self) >= value
+
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self):
+            return bool(float(self))
+    else:
+        def __bool__(self):
+            return bool(float(self))
+
+        __hash__ = base.AbstractSimpleAsn1Item.__hash__
+
+    def __getitem__(self, idx):
+        if self._value in self._inf:
+            raise error.PyAsn1Error('Invalid infinite value operation')
+        else:
+            return self._value[idx]
+
+    # compatibility stubs
+
+    def isPlusInfinity(self):
+        return self.isPlusInf
+
+    def isMinusInfinity(self):
+        return self.isMinusInf
+
+    def isInfinity(self):
+        return self.isInf
+
+
+class Enumerated(Integer):
+    """Create |ASN.1| type or object.
+
+    |ASN.1| objects are immutable and duck-type Python :class:`int` objects.
+
+    Keyword Args
+    ------------
+    value: :class:`int`, :class:`str` or |ASN.1| object
+        Python integer or string literal or |ASN.1| class instance.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+        Object representing non-default symbolic aliases for numbers
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class RadioButton(Enumerated):
+            '''
+            ASN.1 specification:
+
+            RadioButton ::= ENUMERATED { button1(0), button2(1),
+                                         button3(2) }
+
+            selected-by-default RadioButton ::= button1
+            '''
+            namedValues = NamedValues(
+                ('button1', 0), ('button2', 1),
+                ('button3', 2)
+            )
+
+        selected_by_default = RadioButton('button1')
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0A)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Optimization for faster codec lookup
+    typeId = Integer.getTypeId()
+
+    #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+    #: representing symbolic aliases for numbers
+    namedValues = namedval.NamedValues()
+
+
+# "Structured" ASN.1 types
+
+class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
+    """Create |ASN.1| type.
+
+    |ASN.1| objects are mutable and duck-type Python :class:`list` objects.
+
+    Keyword Args
+    ------------
+    componentType : :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+        A pyasn1 object representing ASN.1 type allowed within |ASN.1| type
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing collection size constraint
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class LotteryDraw(SequenceOf):  #  SetOf is similar
+            '''
+            ASN.1 specification:
+
+            LotteryDraw ::= SEQUENCE OF INTEGER
+            '''
+            componentType = Integer()
+
+        lotteryDraw = LotteryDraw()
+        lotteryDraw.extend([123, 456, 789])
+    """
+    def __init__(self, *args, **kwargs):
+        # support positional params for backward compatibility
+        if args:
+            for key, value in zip(('componentType', 'tagSet',
+                                   'subtypeSpec', 'sizeSpec'), args):
+                if key in kwargs:
+                    raise error.PyAsn1Error('Conflicting positional and keyword params!')
+                kwargs['componentType'] = value
+
+        base.AbstractConstructedAsn1Item.__init__(self, **kwargs)
+
+    # Python list protocol
+
+    def __getitem__(self, idx):
+        try:
+            return self.getComponentByPosition(idx)
+
+        except error.PyAsn1Error:
+            raise IndexError(sys.exc_info()[1])
+
+    def __setitem__(self, idx, value):
+        try:
+            self.setComponentByPosition(idx, value)
+
+        except error.PyAsn1Error:
+            raise IndexError(sys.exc_info()[1])
+
+    def clear(self):
+        self._componentValues = []
+
+    def append(self, value):
+        self[len(self)] = value
+
+    def count(self, value):
+        return self._componentValues.count(value)
+
+    def extend(self, values):
+        for value in values:
+            self.append(value)
+
+    def index(self, value, start=0, stop=None):
+        if stop is None:
+            stop = len(self)
+        try:
+            return self._componentValues.index(value, start, stop)
+
+        except error.PyAsn1Error:
+            raise ValueError(sys.exc_info()[1])
+
+    def reverse(self):
+        self._componentValues.reverse()
+
+    def sort(self, key=None, reverse=False):
+        self._componentValues.sort(key=key, reverse=reverse)
+
+    def __iter__(self):
+        return iter(self._componentValues)
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        for idx, componentValue in enumerate(self._componentValues):
+            if componentValue is not noValue:
+                if isinstance(componentValue, base.AbstractConstructedAsn1Item):
+                    myClone.setComponentByPosition(
+                        idx, componentValue.clone(cloneValueFlag=cloneValueFlag)
+                    )
+                else:
+                    myClone.setComponentByPosition(idx, componentValue.clone())
+
+    def getComponentByPosition(self, idx, default=noValue, instantiate=True):
+        """Return |ASN.1| type component value by position.
+
+        Equivalent to Python sequence subscription operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        idx : :class:`int`
+            Component index (zero-based). Must either refer to an existing
+            component or to N+1 component (if *componentType* is set). In the latter
+            case a new component type gets instantiated and appended to the |ASN.1|
+            sequence.
+
+        Keyword Args
+        ------------
+        default: :class:`object`
+            If set and requested component is a schema object, return the `default`
+            object instead of the requested component.
+
+        instantiate: :class:`bool`
+            If `True` (default), inner component will be automatically instantiated.
+            If 'False' either existing component or the `noValue` object will be
+            returned.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            Instantiate |ASN.1| component type or return existing component value
+
+        Examples
+        --------
+
+        .. code-block:: python
+
+            # can also be SetOf
+            class MySequenceOf(SequenceOf):
+                componentType = OctetString()
+
+            s = MySequenceOf()
+
+            # returns component #0 with `.isValue` property False
+            s.getComponentByPosition(0)
+
+            # returns None
+            s.getComponentByPosition(0, default=None)
+
+            s.clear()
+
+            # returns noValue
+            s.getComponentByPosition(0, instantiate=False)
+
+            # sets component #0 to OctetString() ASN.1 schema
+            # object and returns it
+            s.getComponentByPosition(0, instantiate=True)
+
+            # sets component #0 to ASN.1 value object
+            s.setComponentByPosition(0, 'ABCD')
+
+            # returns OctetString('ABCD') value object
+            s.getComponentByPosition(0, instantiate=False)
+
+            s.clear()
+
+            # returns noValue
+            s.getComponentByPosition(0, instantiate=False)
+        """
+        try:
+            componentValue = self._componentValues[idx]
+
+        except IndexError:
+            if not instantiate:
+                return default
+
+            self.setComponentByPosition(idx)
+
+            componentValue = self._componentValues[idx]
+
+        if default is noValue or componentValue.isValue:
+            return componentValue
+        else:
+            return default
+
+    def setComponentByPosition(self, idx, value=noValue,
+                               verifyConstraints=True,
+                               matchTags=True,
+                               matchConstraints=True):
+        """Assign |ASN.1| type component by position.
+
+        Equivalent to Python sequence item assignment operation (e.g. `[]`)
+        or list.append() (when idx == len(self)).
+
+        Parameters
+        ----------
+        idx: :class:`int`
+            Component index (zero-based). Must either refer to existing
+            component or to N+1 component. In the latter case a new component
+            type gets instantiated (if *componentType* is set, or given ASN.1
+            object is taken otherwise) and appended to the |ASN.1| sequence.
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component.
+
+        verifyConstraints: :class:`bool`
+             If `False`, skip constraints validation
+
+        matchTags: :class:`bool`
+             If `False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+             If `False`, skip component constraints matching
+
+        Returns
+        -------
+        self
+
+        Raises
+        ------
+        IndexError:
+            When idx > len(self)
+        """
+        componentType = self.componentType
+
+        try:
+            currentValue = self._componentValues[idx]
+        except IndexError:
+            currentValue = noValue
+
+            if len(self._componentValues) < idx:
+                raise error.PyAsn1Error('Component index out of range')
+
+        if value is noValue:
+            if componentType is not None:
+                value = componentType.clone()
+            elif currentValue is noValue:
+                raise error.PyAsn1Error('Component type not defined')
+        elif not isinstance(value, base.Asn1Item):
+            if componentType is not None and isinstance(componentType, base.AbstractSimpleAsn1Item):
+                value = componentType.clone(value=value)
+            elif currentValue is not noValue and isinstance(currentValue, base.AbstractSimpleAsn1Item):
+                value = currentValue.clone(value=value)
+            else:
+                raise error.PyAsn1Error('Non-ASN.1 value %r and undefined component type at %r' % (value, self))
+        elif componentType is not None:
+            if self.strictConstraints:
+                if not componentType.isSameTypeWith(value, matchTags, matchConstraints):
+                    raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType))
+            else:
+                if not componentType.isSuperTypeOf(value, matchTags, matchConstraints):
+                    raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType))
+
+        if verifyConstraints and value.isValue:
+            try:
+                self.subtypeSpec(value, idx)
+
+            except error.PyAsn1Error:
+                exType, exValue, exTb = sys.exc_info()
+                raise exType('%s at %s' % (exValue, self.__class__.__name__))
+
+        if currentValue is noValue:
+            self._componentValues.append(value)
+        else:
+            self._componentValues[idx] = value
+
+        return self
+
+    @property
+    def componentTagMap(self):
+        if self.componentType is not None:
+            return self.componentType.tagMap
+
+    def prettyPrint(self, scope=0):
+        scope += 1
+        representation = self.__class__.__name__ + ':\n'
+        for idx, componentValue in enumerate(self._componentValues):
+            representation += ' ' * scope
+            if (componentValue is noValue and
+                    self.componentType is not None):
+                representation += '<empty>'
+            else:
+                representation += componentValue.prettyPrint(scope)
+        return representation
+
+    def prettyPrintType(self, scope=0):
+        scope += 1
+        representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__)
+        if self.componentType is not None:
+            representation += ' ' * scope
+            representation += self.componentType.prettyPrintType(scope)
+        return representation + '\n' + ' ' * (scope - 1) + '}'
+
+
+    @property
+    def isValue(self):
+        """Indicate that |ASN.1| object represents ASN.1 value.
+
+        If *isValue* is `False` then this object represents just ASN.1 schema.
+
+        If *isValue* is `True` then, in addition to its ASN.1 schema features,
+        this object can also be used like a Python built-in object (e.g. `int`,
+        `str`, `dict` etc.).
+
+        Returns
+        -------
+        : :class:`bool`
+            :class:`False` if object represents just ASN.1 schema.
+            :class:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+        Note
+        ----
+        There is an important distinction between PyASN1 schema and value objects.
+        The PyASN1 schema objects can only participate in ASN.1 schema-related
+        operations (e.g. defining or testing the structure of the data). Most
+        obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+        encoding/decoding serialised ASN.1 contents.
+
+        The PyASN1 value objects can **additionally** participate in many operations
+        involving regular Python objects (e.g. arithmetic, comprehension etc).
+        """
+        for componentValue in self._componentValues:
+            if componentValue is noValue or not componentValue.isValue:
+                return False
+
+        return True
+
+
+class SequenceOf(SequenceOfAndSetOfBase):
+    __doc__ = SequenceOfAndSetOfBase.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+    )
+
+    #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+    #: object representing ASN.1 type allowed within |ASN.1| type
+    componentType = None
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+    #: object imposing size constraint on |ASN.1| objects
+    sizeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = SequenceOfAndSetOfBase.getTypeId()
+
+
+class SetOf(SequenceOfAndSetOfBase):
+    __doc__ = SequenceOfAndSetOfBase.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+    )
+
+    #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+    #: object representing ASN.1 type allowed within |ASN.1| type
+    componentType = None
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+    #: object imposing size constraint on |ASN.1| objects
+    sizeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = SequenceOfAndSetOfBase.getTypeId()
+
+
+class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
+    """Create |ASN.1| type.
+
+    |ASN.1| objects are mutable and duck-type Python :class:`dict` objects.
+
+    Keyword Args
+    ------------
+    componentType: :py:class:`~pyasn1.type.namedtype.NamedType`
+        Object holding named ASN.1 types allowed within this collection
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing collection size constraint
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class Description(Sequence):  #  Set is similar
+            '''
+            ASN.1 specification:
+
+            Description ::= SEQUENCE {
+                surname    IA5String,
+                first-name IA5String OPTIONAL,
+                age        INTEGER DEFAULT 40
+            }
+            '''
+            componentType = NamedTypes(
+                NamedType('surname', IA5String()),
+                OptionalNamedType('first-name', IA5String()),
+                DefaultedNamedType('age', Integer(40))
+            )
+
+        descr = Description()
+        descr['surname'] = 'Smith'
+        descr['first-name'] = 'John'
+    """
+    #: Default :py:class:`~pyasn1.type.namedtype.NamedTypes`
+    #: object representing named ASN.1 types allowed within |ASN.1| type
+    componentType = namedtype.NamedTypes()
+
+
+    class DynamicNames(object):
+        """Fields names/positions mapping for component-less objects"""
+        def __init__(self):
+            self._keyToIdxMap = {}
+            self._idxToKeyMap = {}
+
+        def __len__(self):
+            return len(self._keyToIdxMap)
+
+        def __contains__(self, item):
+            return item in self._keyToIdxMap or item in self._idxToKeyMap
+
+        def __iter__(self):
+            return (self._idxToKeyMap[idx] for idx in range(len(self._idxToKeyMap)))
+
+        def __getitem__(self, item):
+            try:
+                return self._keyToIdxMap[item]
+
+            except KeyError:
+                return self._idxToKeyMap[item]
+
+        def getNameByPosition(self, idx):
+            try:
+                return self._idxToKeyMap[idx]
+
+            except KeyError:
+                raise error.PyAsn1Error('Type position out of range')
+
+        def getPositionByName(self, name):
+            try:
+                return self._keyToIdxMap[name]
+
+            except KeyError:
+                raise error.PyAsn1Error('Name %s not found' % (name,))
+
+        def addField(self, idx):
+            self._keyToIdxMap['field-%d' % idx] = idx
+            self._idxToKeyMap[idx] = 'field-%d' % idx
+
+
+    def __init__(self, **kwargs):
+        base.AbstractConstructedAsn1Item.__init__(self, **kwargs)
+        self._componentTypeLen = len(self.componentType)
+        self._dynamicNames = self._componentTypeLen or self.DynamicNames()
+
+    def __getitem__(self, idx):
+        if octets.isStringType(idx):
+            try:
+                return self.getComponentByName(idx)
+
+            except error.PyAsn1Error:
+                # duck-typing dict
+                raise KeyError(sys.exc_info()[1])
+
+        else:
+            try:
+                return self.getComponentByPosition(idx)
+
+            except error.PyAsn1Error:
+                # duck-typing list
+                raise IndexError(sys.exc_info()[1])
+
+    def __setitem__(self, idx, value):
+        if octets.isStringType(idx):
+            try:
+                self.setComponentByName(idx, value)
+
+            except error.PyAsn1Error:
+                # duck-typing dict
+                raise KeyError(sys.exc_info()[1])
+
+        else:
+            try:
+                self.setComponentByPosition(idx, value)
+
+            except error.PyAsn1Error:
+                # duck-typing list
+                raise IndexError(sys.exc_info()[1])
+
+    def __contains__(self, key):
+        if self._componentTypeLen:
+            return key in self.componentType
+        else:
+            return key in self._dynamicNames
+
+    def __iter__(self):
+        return iter(self.componentType or self._dynamicNames)
+
+    # Python dict protocol
+
+    def values(self):
+        for idx in range(self._componentTypeLen or len(self._dynamicNames)):
+            yield self[idx]
+
+    def keys(self):
+        return iter(self)
+
+    def items(self):
+        for idx in range(self._componentTypeLen or len(self._dynamicNames)):
+            if self._componentTypeLen:
+                yield self.componentType[idx].name, self[idx]
+            else:
+                yield self._dynamicNames[idx], self[idx]
+
+    def update(self, *iterValue, **mappingValue):
+        for k, v in iterValue:
+            self[k] = v
+        for k in mappingValue:
+            self[k] = mappingValue[k]
+
+    def clear(self):
+        self._componentValues = []
+        self._dynamicNames = self.DynamicNames()
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        for idx, componentValue in enumerate(self._componentValues):
+            if componentValue is not noValue:
+                if isinstance(componentValue, base.AbstractConstructedAsn1Item):
+                    myClone.setComponentByPosition(
+                        idx, componentValue.clone(cloneValueFlag=cloneValueFlag)
+                    )
+                else:
+                    myClone.setComponentByPosition(idx, componentValue.clone())
+
+    def getComponentByName(self, name, default=noValue, instantiate=True):
+        """Returns |ASN.1| type component by name.
+
+        Equivalent to Python :class:`dict` subscription operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        name: :class:`str`
+            |ASN.1| type component name
+
+        Keyword Args
+        ------------
+        default: :class:`object`
+            If set and requested component is a schema object, return the `default`
+            object instead of the requested component.
+
+        instantiate: :class:`bool`
+            If `True` (default), inner component will be automatically instantiated.
+            If 'False' either existing component or the `noValue` object will be
+            returned.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            Instantiate |ASN.1| component type or return existing component value
+        """
+        if self._componentTypeLen:
+            idx = self.componentType.getPositionByName(name)
+        else:
+            try:
+                idx = self._dynamicNames.getPositionByName(name)
+
+            except KeyError:
+                raise error.PyAsn1Error('Name %s not found' % (name,))
+
+        return self.getComponentByPosition(idx, default=default, instantiate=instantiate)
+
+    def setComponentByName(self, name, value=noValue,
+                           verifyConstraints=True,
+                           matchTags=True,
+                           matchConstraints=True):
+        """Assign |ASN.1| type component by name.
+
+        Equivalent to Python :class:`dict` item assignment operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        name: :class:`str`
+            |ASN.1| type component name
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component.
+
+        verifyConstraints: :class:`bool`
+             If `False`, skip constraints validation
+
+        matchTags: :class:`bool`
+             If `False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+             If `False`, skip component constraints matching
+
+        Returns
+        -------
+        self
+        """
+        if self._componentTypeLen:
+            idx = self.componentType.getPositionByName(name)
+        else:
+            try:
+                idx = self._dynamicNames.getPositionByName(name)
+
+            except KeyError:
+                raise error.PyAsn1Error('Name %s not found' % (name,))
+
+        return self.setComponentByPosition(
+            idx, value, verifyConstraints, matchTags, matchConstraints
+        )
+
+    def getComponentByPosition(self, idx, default=noValue, instantiate=True):
+        """Returns |ASN.1| type component by index.
+
+        Equivalent to Python sequence subscription operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        idx: :class:`int`
+            Component index (zero-based). Must either refer to an existing
+            component or (if *componentType* is set) new ASN.1 schema object gets
+            instantiated.
+
+        Keyword Args
+        ------------
+        default: :class:`object`
+            If set and requested component is a schema object, return the `default`
+            object instead of the requested component.
+
+        instantiate: :class:`bool`
+            If `True` (default), inner component will be automatically instantiated.
+            If 'False' either existing component or the `noValue` object will be
+            returned.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            a PyASN1 object
+
+        Examples
+        --------
+
+        .. code-block:: python
+
+            # can also be Set
+            class MySequence(Sequence):
+                componentType = NamedTypes(
+                    NamedType('id', OctetString())
+                )
+
+            s = MySequence()
+
+            # returns component #0 with `.isValue` property False
+            s.getComponentByPosition(0)
+
+            # returns None
+            s.getComponentByPosition(0, default=None)
+
+            s.clear()
+
+            # returns noValue
+            s.getComponentByPosition(0, instantiate=False)
+
+            # sets component #0 to OctetString() ASN.1 schema
+            # object and returns it
+            s.getComponentByPosition(0, instantiate=True)
+
+            # sets component #0 to ASN.1 value object
+            s.setComponentByPosition(0, 'ABCD')
+
+            # returns OctetString('ABCD') value object
+            s.getComponentByPosition(0, instantiate=False)
+
+            s.clear()
+
+            # returns noValue
+            s.getComponentByPosition(0, instantiate=False)
+        """
+        try:
+            componentValue = self._componentValues[idx]
+
+        except IndexError:
+            componentValue = noValue
+
+        if not instantiate:
+            if componentValue is noValue or not componentValue.isValue:
+                return default
+            else:
+                return componentValue
+
+        if componentValue is noValue:
+            self.setComponentByPosition(idx)
+
+        componentValue = self._componentValues[idx]
+
+        if default is noValue or componentValue.isValue:
+            return componentValue
+        else:
+            return default
+
+    def setComponentByPosition(self, idx, value=noValue,
+                               verifyConstraints=True,
+                               matchTags=True,
+                               matchConstraints=True):
+        """Assign |ASN.1| type component by position.
+
+        Equivalent to Python sequence item assignment operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        idx : :class:`int`
+            Component index (zero-based). Must either refer to existing
+            component (if *componentType* is set) or to N+1 component
+            otherwise. In the latter case a new component of given ASN.1
+            type gets instantiated and appended to |ASN.1| sequence.
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component.
+
+        verifyConstraints : :class:`bool`
+             If `False`, skip constraints validation
+
+        matchTags: :class:`bool`
+             If `False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+             If `False`, skip component constraints matching
+
+        Returns
+        -------
+        self
+        """
+        componentType = self.componentType
+        componentTypeLen = self._componentTypeLen
+
+        try:
+            currentValue = self._componentValues[idx]
+
+        except IndexError:
+            currentValue = noValue
+            if componentTypeLen:
+                if componentTypeLen < idx:
+                    raise error.PyAsn1Error('component index out of range')
+
+                self._componentValues = [noValue] * componentTypeLen
+
+        if value is noValue:
+            if componentTypeLen:
+                value = componentType.getTypeByPosition(idx).clone()
+
+            elif currentValue is noValue:
+                raise error.PyAsn1Error('Component type not defined')
+
+        elif not isinstance(value, base.Asn1Item):
+            if componentTypeLen:
+                subComponentType = componentType.getTypeByPosition(idx)
+                if isinstance(subComponentType, base.AbstractSimpleAsn1Item):
+                    value = subComponentType.clone(value=value)
+
+                else:
+                    raise error.PyAsn1Error('%s can cast only scalar values' % componentType.__class__.__name__)
+
+            elif currentValue is not noValue and isinstance(currentValue, base.AbstractSimpleAsn1Item):
+                value = currentValue.clone(value=value)
+
+            else:
+                raise error.PyAsn1Error('%s undefined component type' % componentType.__class__.__name__)
+
+        elif (matchTags or matchConstraints) and componentTypeLen:
+            subComponentType = componentType.getTypeByPosition(idx)
+            if subComponentType is not noValue:
+                subtypeChecker = (self.strictConstraints and
+                                  subComponentType.isSameTypeWith or
+                                  subComponentType.isSuperTypeOf)
+
+                if not subtypeChecker(value, matchTags, matchConstraints):
+                    if not componentType[idx].openType:
+                        raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType))
+
+        if verifyConstraints and value.isValue:
+            try:
+                self.subtypeSpec(value, idx)
+
+            except error.PyAsn1Error:
+                exType, exValue, exTb = sys.exc_info()
+                raise exType('%s at %s' % (exValue, self.__class__.__name__))
+
+        if componentTypeLen or idx in self._dynamicNames:
+            self._componentValues[idx] = value
+
+        elif len(self._componentValues) == idx:
+            self._componentValues.append(value)
+            self._dynamicNames.addField(idx)
+
+        else:
+            raise error.PyAsn1Error('Component index out of range')
+
+        return self
+
+    @property
+    def isValue(self):
+        """Indicate that |ASN.1| object represents ASN.1 value.
+
+        If *isValue* is `False` then this object represents just ASN.1 schema.
+
+        If *isValue* is `True` then, in addition to its ASN.1 schema features,
+        this object can also be used like a Python built-in object (e.g. `int`,
+        `str`, `dict` etc.).
+
+        Returns
+        -------
+        : :class:`bool`
+            :class:`False` if object represents just ASN.1 schema.
+            :class:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+        Note
+        ----
+        There is an important distinction between PyASN1 schema and value objects.
+        The PyASN1 schema objects can only participate in ASN.1 schema-related
+        operations (e.g. defining or testing the structure of the data). Most
+        obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+        encoding/decoding serialised ASN.1 contents.
+
+        The PyASN1 value objects can **additionally** participate in many operations
+        involving regular Python objects (e.g. arithmetic, comprehension etc).
+        """
+        componentType = self.componentType
+
+        if componentType:
+            for idx, subComponentType in enumerate(componentType.namedTypes):
+                if subComponentType.isDefaulted or subComponentType.isOptional:
+                    continue
+
+                if not self._componentValues:
+                    return False
+
+                componentValue = self._componentValues[idx]
+                if componentValue is noValue or not componentValue.isValue:
+                    return False
+
+        else:
+            for componentValue in self._componentValues:
+                if componentValue is noValue or not componentValue.isValue:
+                    return False
+
+        return True
+
+    def prettyPrint(self, scope=0):
+        """Return an object representation string.
+
+        Returns
+        -------
+        : :class:`str`
+            Human-friendly object representation.
+        """
+        scope += 1
+        representation = self.__class__.__name__ + ':\n'
+        for idx, componentValue in enumerate(self._componentValues):
+            if componentValue is not noValue:
+                representation += ' ' * scope
+                if self.componentType:
+                    representation += self.componentType.getNameByPosition(idx)
+                else:
+                    representation += self._dynamicNames.getNameByPosition(idx)
+                representation = '%s=%s\n' % (
+                    representation, componentValue.prettyPrint(scope)
+                )
+        return representation
+
+    def prettyPrintType(self, scope=0):
+        scope += 1
+        representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__)
+        for idx, componentType in enumerate(self.componentType.values() or self._componentValues):
+            representation += ' ' * scope
+            if self.componentType:
+                representation += '"%s"' % self.componentType.getNameByPosition(idx)
+            else:
+                representation += '"%s"' % self._dynamicNames.getNameByPosition(idx)
+            representation = '%s = %s\n' % (
+                representation, componentType.prettyPrintType(scope)
+            )
+        return representation + '\n' + ' ' * (scope - 1) + '}'
+
+    # backward compatibility
+
+    def setDefaultComponents(self):
+        return self
+
+    def getComponentType(self):
+        if self._componentTypeLen:
+            return self.componentType
+
+    def getNameByPosition(self, idx):
+        if self._componentTypeLen:
+            return self.componentType[idx].name
+
+
+class Sequence(SequenceAndSetBase):
+    __doc__ = SequenceAndSetBase.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+    #: object imposing constraints on |ASN.1| objects
+    sizeSpec = constraint.ConstraintsIntersection()
+
+    #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`)
+    #: object imposing size constraint on |ASN.1| objects
+    componentType = namedtype.NamedTypes()
+
+    # Disambiguation ASN.1 types identification
+    typeId = SequenceAndSetBase.getTypeId()
+
+    # backward compatibility
+
+    def getComponentTagMapNearPosition(self, idx):
+        if self.componentType:
+            return self.componentType.getTagMapNearPosition(idx)
+
+    def getComponentPositionNearType(self, tagSet, idx):
+        if self.componentType:
+            return self.componentType.getPositionNearType(tagSet, idx)
+        else:
+            return idx
+
+
+class Set(SequenceAndSetBase):
+    __doc__ = SequenceAndSetBase.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+    )
+
+    #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`)
+    #: object representing ASN.1 type allowed within |ASN.1| type
+    componentType = namedtype.NamedTypes()
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+    #: object imposing constraints on |ASN.1| objects
+    sizeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = SequenceAndSetBase.getTypeId()
+
+    def getComponent(self, innerFlag=False):
+        return self
+
+    def getComponentByType(self, tagSet, default=noValue,
+                           instantiate=True, innerFlag=False):
+        """Returns |ASN.1| type component by ASN.1 tag.
+
+        Parameters
+        ----------
+        tagSet : :py:class:`~pyasn1.type.tag.TagSet`
+            Object representing ASN.1 tags to identify one of
+            |ASN.1| object component
+
+        Keyword Args
+        ------------
+        default: :class:`object`
+            If set and requested component is a schema object, return the `default`
+            object instead of the requested component.
+
+        instantiate: :class:`bool`
+            If `True` (default), inner component will be automatically instantiated.
+            If 'False' either existing component or the `noValue` object will be
+            returned.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            a pyasn1 object
+        """
+        componentValue = self.getComponentByPosition(
+            self.componentType.getPositionByType(tagSet),
+            default=default, instantiate=instantiate
+        )
+        if innerFlag and isinstance(componentValue, Set):
+            # get inner component by inner tagSet
+            return componentValue.getComponent(innerFlag=True)
+        else:
+            # get outer component by inner tagSet
+            return componentValue
+
+    def setComponentByType(self, tagSet, value=noValue,
+                           verifyConstraints=True,
+                           matchTags=True,
+                           matchConstraints=True,
+                           innerFlag=False):
+        """Assign |ASN.1| type component by ASN.1 tag.
+
+        Parameters
+        ----------
+        tagSet : :py:class:`~pyasn1.type.tag.TagSet`
+            Object representing ASN.1 tags to identify one of
+            |ASN.1| object component
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component.
+
+        verifyConstraints : :class:`bool`
+            If `False`, skip constraints validation
+
+        matchTags: :class:`bool`
+            If `False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+            If `False`, skip component constraints matching
+
+        innerFlag: :class:`bool`
+            If `True`, search for matching *tagSet* recursively.
+
+        Returns
+        -------
+        self
+        """
+        idx = self.componentType.getPositionByType(tagSet)
+
+        if innerFlag:  # set inner component by inner tagSet
+            componentType = self.componentType.getTypeByPosition(idx)
+
+            if componentType.tagSet:
+                return self.setComponentByPosition(
+                    idx, value, verifyConstraints, matchTags, matchConstraints
+                )
+            else:
+                componentType = self.getComponentByPosition(idx)
+                return componentType.setComponentByType(
+                    tagSet, value, verifyConstraints, matchTags, matchConstraints, innerFlag=innerFlag
+                )
+        else:  # set outer component by inner tagSet
+            return self.setComponentByPosition(
+                idx, value, verifyConstraints, matchTags, matchConstraints
+            )
+
+    @property
+    def componentTagMap(self):
+        if self.componentType:
+            return self.componentType.tagMapUnique
+
+
+class Choice(Set):
+    """Create |ASN.1| type.
+
+    |ASN.1| objects are mutable and duck-type Python :class:`dict` objects.
+
+    Keyword Args
+    ------------
+    componentType: :py:class:`~pyasn1.type.namedtype.NamedType`
+        Object holding named ASN.1 types allowed within this collection
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing collection size constraint
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class Afters(Choice):
+            '''
+            ASN.1 specification:
+
+            Afters ::= CHOICE {
+                cheese  [0] IA5String,
+                dessert [1] IA5String
+            }
+            '''
+            componentType = NamedTypes(
+                NamedType('cheese', IA5String().subtype(
+                    implicitTag=Tag(tagClassContext, tagFormatSimple, 0)
+                ),
+                NamedType('dessert', IA5String().subtype(
+                    implicitTag=Tag(tagClassContext, tagFormatSimple, 1)
+                )
+            )
+
+        afters = Afters()
+        afters['cheese'] = 'Mascarpone'
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.TagSet()  # untagged
+
+    #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`)
+    #: object representing ASN.1 type allowed within |ASN.1| type
+    componentType = namedtype.NamedTypes()
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+    #: object imposing size constraint on |ASN.1| objects
+    sizeSpec = constraint.ConstraintsIntersection(
+        constraint.ValueSizeConstraint(1, 1)
+    )
+
+    # Disambiguation ASN.1 types identification
+    typeId = Set.getTypeId()
+
+    _currentIdx = None
+
+    def __eq__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] == other
+        return NotImplemented
+
+    def __ne__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] != other
+        return NotImplemented
+
+    def __lt__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] < other
+        return NotImplemented
+
+    def __le__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] <= other
+        return NotImplemented
+
+    def __gt__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] > other
+        return NotImplemented
+
+    def __ge__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] >= other
+        return NotImplemented
+
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self):
+            return self._componentValues and True or False
+    else:
+        def __bool__(self):
+            return self._componentValues and True or False
+
+    def __len__(self):
+        return self._currentIdx is not None and 1 or 0
+
+    def __contains__(self, key):
+        if self._currentIdx is None:
+            return False
+        return key == self.componentType[self._currentIdx].getName()
+
+    def __iter__(self):
+        if self._currentIdx is None:
+            raise StopIteration
+        yield self.componentType[self._currentIdx].getName()
+
+    # Python dict protocol
+
+    def values(self):
+        if self._currentIdx is not None:
+            yield self._componentValues[self._currentIdx]
+
+    def keys(self):
+        if self._currentIdx is not None:
+            yield self.componentType[self._currentIdx].getName()
+
+    def items(self):
+        if self._currentIdx is not None:
+            yield self.componentType[self._currentIdx].getName(), self[self._currentIdx]
+
+    def verifySizeSpec(self):
+        if self._currentIdx is None:
+            raise error.PyAsn1Error('Component not chosen')
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        try:
+            component = self.getComponent()
+        except error.PyAsn1Error:
+            pass
+        else:
+            if isinstance(component, Choice):
+                tagSet = component.effectiveTagSet
+            else:
+                tagSet = component.tagSet
+            if isinstance(component, base.AbstractConstructedAsn1Item):
+                myClone.setComponentByType(
+                    tagSet, component.clone(cloneValueFlag=cloneValueFlag)
+                )
+            else:
+                myClone.setComponentByType(tagSet, component.clone())
+
+    def getComponentByPosition(self, idx, default=noValue, instantiate=True):
+        __doc__ = Set.__doc__
+
+        if self._currentIdx is None or self._currentIdx != idx:
+            return Set.getComponentByPosition(self, idx, default=default,
+                                              instantiate=instantiate)
+
+        return self._componentValues[idx]
+
+    def setComponentByPosition(self, idx, value=noValue,
+                               verifyConstraints=True,
+                               matchTags=True,
+                               matchConstraints=True):
+        """Assign |ASN.1| type component by position.
+
+        Equivalent to Python sequence item assignment operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        idx: :class:`int`
+            Component index (zero-based). Must either refer to existing
+            component or to N+1 component. In the latter case a new component
+            type gets instantiated (if *componentType* is set, or given ASN.1
+            object is taken otherwise) and appended to the |ASN.1| sequence.
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component. Once a new value is
+            set to *idx* component, previous value is dropped.
+
+        verifyConstraints : :class:`bool`
+            If `False`, skip constraints validation
+
+        matchTags: :class:`bool`
+            If `False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+            If `False`, skip component constraints matching
+
+        Returns
+        -------
+        self
+        """
+        oldIdx = self._currentIdx
+        Set.setComponentByPosition(self, idx, value, verifyConstraints, matchTags, matchConstraints)
+        self._currentIdx = idx
+        if oldIdx is not None and oldIdx != idx:
+            self._componentValues[oldIdx] = noValue
+        return self
+
+    @property
+    def effectiveTagSet(self):
+        """Return a :class:`~pyasn1.type.tag.TagSet` object of the currently initialized component or self (if |ASN.1| is tagged)."""
+        if self.tagSet:
+            return self.tagSet
+        else:
+            component = self.getComponent()
+            return component.effectiveTagSet
+
+    @property
+    def tagMap(self):
+        """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping
+            ASN.1 tags to ASN.1 objects contained within callee.
+        """
+        if self.tagSet:
+            return Set.tagMap.fget(self)
+        else:
+            return self.componentType.tagMapUnique
+
+    def getComponent(self, innerFlag=False):
+        """Return currently assigned component of the |ASN.1| object.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            a PyASN1 object
+        """
+        if self._currentIdx is None:
+            raise error.PyAsn1Error('Component not chosen')
+        else:
+            c = self._componentValues[self._currentIdx]
+            if innerFlag and isinstance(c, Choice):
+                return c.getComponent(innerFlag)
+            else:
+                return c
+
+    def getName(self, innerFlag=False):
+        """Return the name of currently assigned component of the |ASN.1| object.
+
+        Returns
+        -------
+        : :py:class:`str`
+            |ASN.1| component name
+        """
+        if self._currentIdx is None:
+            raise error.PyAsn1Error('Component not chosen')
+        else:
+            if innerFlag:
+                c = self._componentValues[self._currentIdx]
+                if isinstance(c, Choice):
+                    return c.getName(innerFlag)
+            return self.componentType.getNameByPosition(self._currentIdx)
+
+    @property
+    def isValue(self):
+        """Indicate that |ASN.1| object represents ASN.1 value.
+
+        If *isValue* is `False` then this object represents just ASN.1 schema.
+
+        If *isValue* is `True` then, in addition to its ASN.1 schema features,
+        this object can also be used like a Python built-in object (e.g. `int`,
+        `str`, `dict` etc.).
+
+        Returns
+        -------
+        : :class:`bool`
+            :class:`False` if object represents just ASN.1 schema.
+            :class:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+        Note
+        ----
+        There is an important distinction between PyASN1 schema and value objects.
+        The PyASN1 schema objects can only participate in ASN.1 schema-related
+        operations (e.g. defining or testing the structure of the data). Most
+        obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+        encoding/decoding serialised ASN.1 contents.
+
+        The PyASN1 value objects can **additionally** participate in many operations
+        involving regular Python objects (e.g. arithmetic, comprehension etc).
+        """
+        if self._currentIdx is None:
+            return False
+
+        componentValue = self._componentValues[self._currentIdx]
+
+        return componentValue is not noValue and componentValue.isValue
+
+    def clear(self):
+        self._currentIdx = None
+        Set.clear(self)
+
+    # compatibility stubs
+
+    def getMinTagSet(self):
+        return self.minTagSet
+
+
+class Any(OctetString):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| objects are immutable and duck-type Python 2 :class:`str` or Python 3
+    :class:`bytes`. When used in Unicode context, |ASN.1| type assumes "|encoding|"
+    serialisation.
+
+    Keyword Args
+    ------------
+    value: :class:`str`, :class:`bytes` or |ASN.1| object
+        string (Python 2) or bytes (Python 3), alternatively unicode object
+        (Python 2) or string (Python 3) representing character string to be
+        serialised into octets (note `encoding` parameter) or |ASN.1| object.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s)
+
+    encoding: :py:class:`str`
+        Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
+        :class:`str` (Python 3) the payload when |ASN.1| object is used
+        in text string context.
+
+    binValue: :py:class:`str`
+        Binary string initializer to use instead of the *value*.
+        Example: '10110011'.
+
+    hexValue: :py:class:`str`
+        Hexadecimal string initializer to use instead of the *value*.
+        Example: 'DEADBEEF'.
+
+    Raises
+    ------
+    :py:class:`~pyasn1.error.PyAsn1Error`
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Error(Sequence):
+            '''
+            ASN.1 specification:
+
+            Error ::= SEQUENCE {
+                code      INTEGER,
+                parameter ANY DEFINED BY code  -- Either INTEGER or REAL
+            }
+            '''
+            componentType=NamedTypes(
+                NamedType('code', Integer()),
+                NamedType('parameter', Any(),
+                          openType=OpenType('code', {1: Integer(),
+                                                     2: Real()}))
+            )
+
+        error = Error()
+        error['code'] = 1
+        error['parameter'] = Integer(1234)
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.TagSet()  # untagged
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = OctetString.getTypeId()
+
+    @property
+    def tagMap(self):
+        """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping
+            ASN.1 tags to ASN.1 objects contained within callee.
+        """
+        try:
+            return self._tagMap
+
+        except AttributeError:
+            self._tagMap = tagmap.TagMap(
+                {self.tagSet: self},
+                {eoo.endOfOctets.tagSet: eoo.endOfOctets},
+                self
+            )
+
+            return self._tagMap
+
+# XXX
+# coercion rules?
diff --git a/pyasn1/type/useful.py b/pyasn1/type/useful.py
new file mode 100644
index 0000000..146916d
--- /dev/null
+++ b/pyasn1/type/useful.py
@@ -0,0 +1,191 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import datetime
+
+from pyasn1 import error
+from pyasn1.compat import dateandtime
+from pyasn1.compat import string
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+
+__all__ = ['ObjectDescriptor', 'GeneralizedTime', 'UTCTime']
+
+NoValue = univ.NoValue
+noValue = univ.noValue
+
+
+class ObjectDescriptor(char.GraphicString):
+    __doc__ = char.GraphicString.__doc__
+
+    #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
+    tagSet = char.GraphicString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7)
+    )
+
+    # Optimization for faster codec lookup
+    typeId = char.GraphicString.getTypeId()
+
+
+class TimeMixIn(object):
+
+    _yearsDigits = 4
+    _hasSubsecond = False
+    _optionalMinutes = False
+    _shortTZ = False
+
+    class FixedOffset(datetime.tzinfo):
+        """Fixed offset in minutes east from UTC."""
+
+        # defaulted arguments required
+        # https: // docs.python.org / 2.3 / lib / datetime - tzinfo.html
+        def __init__(self, offset=0, name='UTC'):
+            self.__offset = datetime.timedelta(minutes=offset)
+            self.__name = name
+
+        def utcoffset(self, dt):
+            return self.__offset
+
+        def tzname(self, dt):
+            return self.__name
+
+        def dst(self, dt):
+            return datetime.timedelta(0)
+
+    UTC = FixedOffset()
+
+    @property
+    def asDateTime(self):
+        """Create :py:class:`datetime.datetime` object from a |ASN.1| object.
+
+        Returns
+        -------
+        :
+            new instance of :py:class:`datetime.datetime` object
+        """
+        text = str(self)
+        if text.endswith('Z'):
+            tzinfo = TimeMixIn.UTC
+            text = text[:-1]
+
+        elif '-' in text or '+' in text:
+            if '+' in text:
+                text, plusminus, tz = string.partition(text, '+')
+            else:
+                text, plusminus, tz = string.partition(text, '-')
+
+            if self._shortTZ and len(tz) == 2:
+                tz += '00'
+
+            if len(tz) != 4:
+                raise error.PyAsn1Error('malformed time zone offset %s' % tz)
+
+            try:
+                minutes = int(tz[:2]) * 60 + int(tz[2:])
+                if plusminus == '-':
+                    minutes *= -1
+
+            except ValueError:
+                raise error.PyAsn1Error('unknown time specification %s' % self)
+
+            tzinfo = TimeMixIn.FixedOffset(minutes, '?')
+
+        else:
+            tzinfo = None
+
+        if '.' in text or ',' in text:
+            if '.' in text:
+                text, _, ms = string.partition(text, '.')
+            else:
+                text, _, ms = string.partition(text, ',')
+
+            try:
+                ms = int(ms) * 1000
+
+            except ValueError:
+                raise error.PyAsn1Error('bad sub-second time specification %s' % self)
+
+        else:
+            ms = 0
+
+        if self._optionalMinutes and len(text) - self._yearsDigits == 6:
+            text += '0000'
+        elif len(text) - self._yearsDigits == 8:
+            text += '00'
+
+        try:
+            dt = dateandtime.strptime(text, self._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
+
+        except ValueError:
+            raise error.PyAsn1Error('malformed datetime format %s' % self)
+
+        return dt.replace(microsecond=ms, tzinfo=tzinfo)
+
+    @classmethod
+    def fromDateTime(cls, dt):
+        """Create |ASN.1| object from a :py:class:`datetime.datetime` object.
+
+        Parameters
+        ----------
+        dt: :py:class:`datetime.datetime` object
+            The `datetime.datetime` object to initialize the |ASN.1| object
+            from
+
+        Returns
+        -------
+        :
+            new instance of |ASN.1| value
+        """
+        text = dt.strftime(cls._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
+        if cls._hasSubsecond:
+            text += '.%d' % (dt.microsecond // 1000)
+
+        if dt.utcoffset():
+            seconds = dt.utcoffset().seconds
+            if seconds < 0:
+                text += '-'
+            else:
+                text += '+'
+            text += '%.2d%.2d' % (seconds // 3600, seconds % 3600)
+        else:
+            text += 'Z'
+
+        return cls(text)
+
+
+class GeneralizedTime(char.VisibleString, TimeMixIn):
+    __doc__ = char.VisibleString.__doc__
+
+    #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
+    tagSet = char.VisibleString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
+    )
+
+    # Optimization for faster codec lookup
+    typeId = char.VideotexString.getTypeId()
+
+    _yearsDigits = 4
+    _hasSubsecond = True
+    _optionalMinutes = True
+    _shortTZ = True
+
+
+class UTCTime(char.VisibleString, TimeMixIn):
+    __doc__ = char.VisibleString.__doc__
+
+    #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
+    tagSet = char.VisibleString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
+    )
+
+    # Optimization for faster codec lookup
+    typeId = char.VideotexString.getTypeId()
+
+    _yearsDigits = 2
+    _hasSubsecond = False
+    _optionalMinutes = False
+    _shortTZ = False
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/requirements.txt
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..13b523c
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[bdist_wheel]
+universal = 1
+
+[metadata]
+license_file = LICENSE.rst
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..cea48b9
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import os
+import sys
+
+classifiers = """\
+Development Status :: 5 - Production/Stable
+Environment :: Console
+Intended Audience :: Developers
+Intended Audience :: Education
+Intended Audience :: Information Technology
+Intended Audience :: System Administrators
+Intended Audience :: Telecommunications Industry
+License :: OSI Approved :: BSD License
+Natural Language :: English
+Operating System :: OS Independent
+Programming Language :: Python :: 2
+Programming Language :: Python :: 2.4
+Programming Language :: Python :: 2.5
+Programming Language :: Python :: 2.6
+Programming Language :: Python :: 2.7
+Programming Language :: Python :: 3
+Programming Language :: Python :: 3.2
+Programming Language :: Python :: 3.3
+Programming Language :: Python :: 3.4
+Programming Language :: Python :: 3.5
+Programming Language :: Python :: 3.6
+Topic :: Communications
+Topic :: Software Development :: Libraries :: Python Modules
+"""
+
+
+def howto_install_setuptools():
+    print("""
+   Error: You need setuptools Python package!
+
+   It's very easy to install it, just type:
+
+   wget https://bootstrap.pypa.io/ez_setup.py
+   python ez_setup.py
+
+   Then you could make eggs from this package.
+""")
+
+
+if sys.version_info[:2] < (2, 4):
+    print("ERROR: this package requires Python 2.4 or later!")
+    sys.exit(1)
+
+try:
+    from setuptools import setup, Command
+
+    params = {
+        'zip_safe': True
+    }
+
+except ImportError:
+    for arg in sys.argv:
+        if 'egg' in arg:
+            howto_install_setuptools()
+            sys.exit(1)
+    from distutils.core import setup, Command
+
+    params = {}
+
+params.update({
+    'name': 'pyasn1',
+    'version': open(os.path.join('pyasn1', '__init__.py')).read().split('\'')[1],
+    'description': 'ASN.1 types and codecs',
+    'long_description': 'Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)',
+    'maintainer': 'Ilya Etingof <etingof@gmail.com>',
+    'author': 'Ilya Etingof',
+    'author_email': 'etingof@gmail.com',
+    'url': 'https://github.com/etingof/pyasn1',
+    'platforms': ['any'],
+    'classifiers': [x for x in classifiers.split('\n') if x],
+    'license': 'BSD',
+    'packages': ['pyasn1',
+                 'pyasn1.type',
+                 'pyasn1.compat',
+                 'pyasn1.codec',
+                 'pyasn1.codec.ber',
+                 'pyasn1.codec.cer',
+                 'pyasn1.codec.der',
+                 'pyasn1.codec.native']})
+
+# handle unittest discovery feature
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
+
+class PyTest(Command):
+    user_options = []
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        suite = unittest.TestLoader().loadTestsFromNames(
+            ['tests.__main__.suite']
+        )
+
+        unittest.TextTestRunner(verbosity=2).run(suite)
+
+params['cmdclass'] = {
+    'test': PyTest,
+    'tests': PyTest,
+}
+
+setup(**params)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/tests/__main__.py b/tests/__main__.py
new file mode 100644
index 0000000..55ccf39
--- /dev/null
+++ b/tests/__main__.py
@@ -0,0 +1,22 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+suite = unittest.TestLoader().loadTestsFromNames(
+    ['tests.test_debug.suite',
+     'tests.type.__main__.suite',
+     'tests.codec.__main__.suite',
+     'tests.compat.__main__.suite']
+)
+
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/base.py b/tests/base.py
new file mode 100644
index 0000000..645b256
--- /dev/null
+++ b/tests/base.py
@@ -0,0 +1,23 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from pyasn1 import debug
+
+
+class BaseTestCase(unittest.TestCase):
+
+    def setUp(self):
+        debug.setLogger(debug.Debug('all', printer=lambda *x: None))
+
+    def tearDown(self):
+        debug.setLogger(None)
diff --git a/tests/codec/__init__.py b/tests/codec/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/tests/codec/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/tests/codec/__main__.py b/tests/codec/__main__.py
new file mode 100644
index 0000000..8ca201a
--- /dev/null
+++ b/tests/codec/__main__.py
@@ -0,0 +1,22 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+suite = unittest.TestLoader().loadTestsFromNames(
+    ['tests.codec.ber.__main__.suite',
+     'tests.codec.cer.__main__.suite',
+     'tests.codec.der.__main__.suite',
+     'tests.codec.native.__main__.suite']
+)
+
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/ber/__init__.py b/tests/codec/ber/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/tests/codec/ber/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/tests/codec/ber/__main__.py b/tests/codec/ber/__main__.py
new file mode 100644
index 0000000..aca8493
--- /dev/null
+++ b/tests/codec/ber/__main__.py
@@ -0,0 +1,20 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+suite = unittest.TestLoader().loadTestsFromNames(
+    ['tests.codec.ber.test_encoder.suite',
+     'tests.codec.ber.test_decoder.suite']
+)
+
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/ber/test_decoder.py b/tests/codec/ber/test_decoder.py
new file mode 100644
index 0000000..d456949
--- /dev/null
+++ b/tests/codec/ber/test_decoder.py
@@ -0,0 +1,1435 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import tag
+from pyasn1.type import namedtype
+from pyasn1.type import opentype
+from pyasn1.type import univ
+from pyasn1.type import char
+from pyasn1.codec.ber import decoder
+from pyasn1.codec.ber import eoo
+from pyasn1.compat.octets import ints2octs, str2octs, null
+from pyasn1.error import PyAsn1Error
+
+
+class LargeTagDecoderTestCase(BaseTestCase):
+    def testLargeTag(self):
+        assert decoder.decode(ints2octs((127, 141, 245, 182, 253, 47, 3, 2, 1, 1))) == (1, null)
+
+    def testLongTag(self):
+        assert decoder.decode(ints2octs((0x1f, 2, 1, 0)))[0].tagSet == univ.Integer.tagSet
+
+    def testTagsEquivalence(self):
+        integer = univ.Integer(2).subtype(implicitTag=tag.Tag(tag.tagClassContext, 0, 0))
+        assert decoder.decode(ints2octs((0x9f, 0x80, 0x00, 0x02, 0x01, 0x02)), asn1Spec=integer) == decoder.decode(
+            ints2octs((0x9f, 0x00, 0x02, 0x01, 0x02)), asn1Spec=integer)
+
+
+class DecoderCacheTestCase(BaseTestCase):
+    def testCache(self):
+        assert decoder.decode(ints2octs((0x1f, 2, 1, 0))) == decoder.decode(ints2octs((0x1f, 2, 1, 0)))
+
+
+class IntegerDecoderTestCase(BaseTestCase):
+    def testPosInt(self):
+        assert decoder.decode(ints2octs((2, 1, 12))) == (12, null)
+
+    def testNegInt(self):
+        assert decoder.decode(ints2octs((2, 1, 244))) == (-12, null)
+
+    def testZero(self):
+        assert decoder.decode(ints2octs((2, 0))) == (0, null)
+
+    def testZeroLong(self):
+        assert decoder.decode(ints2octs((2, 1, 0))) == (0, null)
+
+    def testMinusOne(self):
+        assert decoder.decode(ints2octs((2, 1, 255))) == (-1, null)
+
+    def testPosLong(self):
+        assert decoder.decode(
+            ints2octs((2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255))
+        ) == (0xffffffffffffffff, null)
+
+    def testNegLong(self):
+        assert decoder.decode(
+            ints2octs((2, 9, 255, 0, 0, 0, 0, 0, 0, 0, 1))
+        ) == (-0xffffffffffffffff, null)
+
+    def testSpec(self):
+        try:
+            decoder.decode(
+                ints2octs((2, 1, 12)), asn1Spec=univ.Null()
+            ) == (12, null)
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'wrong asn1Spec worked out'
+        assert decoder.decode(
+            ints2octs((2, 1, 12)), asn1Spec=univ.Integer()
+        ) == (12, null)
+
+    def testTagFormat(self):
+        try:
+            decoder.decode(ints2octs((34, 1, 12)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'wrong tagFormat worked out'
+
+
+class BooleanDecoderTestCase(BaseTestCase):
+    def testTrue(self):
+        assert decoder.decode(ints2octs((1, 1, 1))) == (1, null)
+
+    def testTrueNeg(self):
+        assert decoder.decode(ints2octs((1, 1, 255))) == (1, null)
+
+    def testExtraTrue(self):
+        assert decoder.decode(ints2octs((1, 1, 1, 0, 120, 50, 50))) == (1, ints2octs((0, 120, 50, 50)))
+
+    def testFalse(self):
+        assert decoder.decode(ints2octs((1, 1, 0))) == (0, null)
+
+    def testTagFormat(self):
+        try:
+            decoder.decode(ints2octs((33, 1, 1)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'wrong tagFormat worked out'
+
+
+class BitStringDecoderTestCase(BaseTestCase):
+    def testDefMode(self):
+        assert decoder.decode(
+            ints2octs((3, 3, 1, 169, 138))
+        ) == ((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), null)
+
+    def testIndefMode(self):
+        assert decoder.decode(
+            ints2octs((3, 3, 1, 169, 138))
+        ) == ((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), null)
+
+    def testDefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138))
+        ) == ((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), null)
+
+    def testIndefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))
+        ) == ((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), null)
+
+    def testDefModeChunkedSubst(self):
+        assert decoder.decode(
+            ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138)),
+            substrateFun=lambda a, b, c: (b, b[c:])
+        ) == (ints2octs((3, 2, 0, 169, 3, 2, 1, 138)), str2octs(''))
+
+    def testIndefModeChunkedSubst(self):
+        assert decoder.decode(
+            ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)),
+            substrateFun=lambda a, b, c: (b, str2octs(''))
+        ) == (ints2octs((3, 2, 0, 169, 3, 2, 1, 138, 0, 0)), str2octs(''))
+
+    def testTypeChecking(self):
+        try:
+            decoder.decode(ints2octs((35, 4, 2, 2, 42, 42)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'accepted mis-encoded bit-string constructed out of an integer'
+
+
+class OctetStringDecoderTestCase(BaseTestCase):
+    def testDefMode(self):
+        assert decoder.decode(
+            ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+        ) == (str2octs('Quick brown fox'), null)
+
+    def testIndefMode(self):
+        assert decoder.decode(
+            ints2octs((36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0))
+        ) == (str2octs('Quick brown fox'), null)
+
+    def testDefModeChunked(self):
+        assert decoder.decode(
+            ints2octs(
+                (36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120))
+        ) == (str2octs('Quick brown fox'), null)
+
+    def testIndefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0))
+        ) == (str2octs('Quick brown fox'), null)
+
+    def testDefModeChunkedSubst(self):
+        assert decoder.decode(
+            ints2octs(
+                (36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120)),
+            substrateFun=lambda a, b, c: (b, b[c:])
+        ) == (ints2octs((4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120)), str2octs(''))
+
+    def testIndefModeChunkedSubst(self):
+        assert decoder.decode(
+            ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111,
+                       120, 0, 0)),
+            substrateFun=lambda a, b, c: (b, str2octs(''))
+        ) == (ints2octs(
+            (4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0)), str2octs(''))
+
+
+class ExpTaggedOctetStringDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.o = univ.OctetString(
+            'Quick brown fox',
+            tagSet=univ.OctetString.tagSet.tagExplicitly(
+                tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 5)
+            ))
+
+    def testDefMode(self):
+        o, r = decoder.decode(
+            ints2octs((101, 17, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+        )
+        assert not r
+        assert self.o == o
+        assert self.o.tagSet == o.tagSet
+        assert self.o.isSameTypeWith(o)
+
+    def testIndefMode(self):
+        o, r = decoder.decode(
+            ints2octs((101, 128, 36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0, 0, 0))
+        )
+        assert not r
+        assert self.o == o
+        assert self.o.tagSet == o.tagSet
+        assert self.o.isSameTypeWith(o)
+
+    def testDefModeChunked(self):
+        o, r = decoder.decode(
+            ints2octs((101, 25, 36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120))
+        )
+        assert not r
+        assert self.o == o
+        assert self.o.tagSet == o.tagSet
+        assert self.o.isSameTypeWith(o)
+
+    def testIndefModeChunked(self):
+        o, r = decoder.decode(
+            ints2octs((101, 128, 36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0, 0, 0))
+        )
+        assert not r
+        assert self.o == o
+        assert self.o.tagSet == o.tagSet
+        assert self.o.isSameTypeWith(o)
+
+    def testDefModeSubst(self):
+        assert decoder.decode(
+            ints2octs((101, 17, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)),
+            substrateFun=lambda a, b, c: (b, b[c:])
+        ) == (ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)), str2octs(''))
+
+    def testIndefModeSubst(self):
+        assert decoder.decode(
+            ints2octs((
+                      101, 128, 36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0,
+                      0, 0, 0)),
+            substrateFun=lambda a, b, c: (b, str2octs(''))
+        ) == (ints2octs(
+            (36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0, 0, 0)), str2octs(''))
+
+
+class NullDecoderTestCase(BaseTestCase):
+    def testNull(self):
+        assert decoder.decode(ints2octs((5, 0))) == (null, null)
+
+    def testTagFormat(self):
+        try:
+            decoder.decode(ints2octs((37, 0)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'wrong tagFormat worked out'
+
+
+# Useful analysis of OID encoding issues could be found here:
+# https://misc.daniel-marschall.de/asn.1/oid_facts.html
+class ObjectIdentifierDecoderTestCase(BaseTestCase):
+    def testOne(self):
+        assert decoder.decode(
+            ints2octs((6, 6, 43, 6, 0, 191, 255, 126))
+        ) == ((1, 3, 6, 0, 0xffffe), null)
+
+    def testEdge1(self):
+        assert decoder.decode(
+            ints2octs((6, 1, 39))
+        ) == ((0, 39), null)
+
+    def testEdge2(self):
+        assert decoder.decode(
+            ints2octs((6, 1, 79))
+        ) == ((1, 39), null)
+
+    def testEdge3(self):
+        assert decoder.decode(
+            ints2octs((6, 1, 120))
+        ) == ((2, 40), null)
+
+    def testEdge4(self):
+        assert decoder.decode(
+            ints2octs((6, 5, 0x90, 0x80, 0x80, 0x80, 0x4F))
+        ) == ((2, 0xffffffff), null)
+
+    def testEdge5(self):
+        assert decoder.decode(
+            ints2octs((6, 1, 0x7F))
+        ) == ((2, 47), null)
+
+    def testEdge6(self):
+        assert decoder.decode(
+            ints2octs((6, 2, 0x81, 0x00))
+        ) == ((2, 48), null)
+
+    def testEdge7(self):
+        assert decoder.decode(
+            ints2octs((6, 3, 0x81, 0x34, 0x03))
+        ) == ((2, 100, 3), null)
+
+    def testEdge8(self):
+        assert decoder.decode(
+            ints2octs((6, 2, 133, 0))
+        ) == ((2, 560), null)
+
+    def testEdge9(self):
+        assert decoder.decode(
+            ints2octs((6, 4, 0x88, 0x84, 0x87, 0x02))
+        ) == ((2, 16843570), null)
+
+    def testNonLeading0x80(self):
+        assert decoder.decode(
+            ints2octs((6, 5, 85, 4, 129, 128, 0)),
+        ) == ((2, 5, 4, 16384), null)
+
+    def testLeading0x80Case1(self):
+        try:
+            decoder.decode(
+                ints2octs((6, 5, 85, 4, 128, 129, 0))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Leading 0x80 tolarated'
+
+    def testLeading0x80Case2(self):
+        try:
+            decoder.decode(
+                ints2octs((6, 7, 1, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Leading 0x80 tolarated'
+
+    def testLeading0x80Case3(self):
+        try:
+            decoder.decode(
+                ints2octs((6, 2, 0x80, 1))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Leading 0x80 tolarated'
+
+    def testLeading0x80Case4(self):
+        try:
+            decoder.decode(
+                ints2octs((6, 2, 0x80, 0x7F))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Leading 0x80 tolarated'
+
+    def testTagFormat(self):
+        try:
+            decoder.decode(ints2octs((38, 1, 239)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'wrong tagFormat worked out'
+
+    def testZeroLength(self):
+        try:
+            decoder.decode(ints2octs((6, 0, 0)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'zero length tolarated'
+
+    def testIndefiniteLength(self):
+        try:
+            decoder.decode(ints2octs((6, 128, 0)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'indefinite length tolerated'
+
+    def testReservedLength(self):
+        try:
+            decoder.decode(ints2octs((6, 255, 0)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'reserved length tolerated'
+
+    def testLarge1(self):
+        assert decoder.decode(
+            ints2octs((0x06, 0x11, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB7, 0x17))
+        ) == ((2, 18446744073709551535184467440737095), null)
+
+    def testLarge2(self):
+        assert decoder.decode(
+            ints2octs((0x06, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB6, 0x47))
+        ) == ((2, 999, 18446744073709551535184467440737095), null)
+
+
+class RealDecoderTestCase(BaseTestCase):
+    def testChar(self):
+        assert decoder.decode(
+            ints2octs((9, 7, 3, 49, 50, 51, 69, 49, 49))
+        ) == (univ.Real((123, 10, 11)), null)
+
+    def testBin1(self):  # check base = 2
+        assert decoder.decode(  # (0.5, 2, 0) encoded with base = 2
+            ints2octs((9, 3, 128, 255, 1))
+        ) == (univ.Real((1, 2, -1)), null)
+
+    def testBin2(self):  # check base = 2 and scale factor
+        assert decoder.decode(  # (3.25, 2, 0) encoded with base = 8
+            ints2octs((9, 3, 148, 255, 13))
+        ) == (univ.Real((26, 2, -3)), null)
+
+    def testBin3(self):  # check base = 16
+        assert decoder.decode(  # (0.00390625, 2, 0) encoded with base = 16
+            ints2octs((9, 3, 160, 254, 1))
+        ) == (univ.Real((1, 2, -8)), null)
+
+    def testBin4(self):  # check exponenta = 0
+        assert decoder.decode(  # (1, 2, 0) encoded with base = 2
+            ints2octs((9, 3, 128, 0, 1))
+        ) == (univ.Real((1, 2, 0)), null)
+
+    def testBin5(self):  # case of 2 octs for exponenta and negative exponenta
+        assert decoder.decode(  # (3, 2, -1020) encoded with base = 16
+            ints2octs((9, 4, 161, 255, 1, 3))
+        ) == (univ.Real((3, 2, -1020)), null)
+
+# TODO: this requires Real type comparison fix
+
+#    def testBin6(self):
+#        assert decoder.decode(
+#            ints2octs((9, 5, 162, 0, 255, 255, 1))
+#        ) == (univ.Real((1, 2, 262140)), null)
+
+#    def testBin7(self):
+#        assert decoder.decode(
+#            ints2octs((9, 7, 227, 4, 1, 35, 69, 103, 1))
+#        ) == (univ.Real((-1, 2, 76354972)), null)
+
+    def testPlusInf(self):
+        assert decoder.decode(
+            ints2octs((9, 1, 64))
+        ) == (univ.Real('inf'), null)
+
+    def testMinusInf(self):
+        assert decoder.decode(
+            ints2octs((9, 1, 65))
+        ) == (univ.Real('-inf'), null)
+
+    def testEmpty(self):
+        assert decoder.decode(
+            ints2octs((9, 0))
+        ) == (univ.Real(0.0), null)
+
+    def testTagFormat(self):
+        try:
+            decoder.decode(ints2octs((41, 0)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'wrong tagFormat worked out'
+
+    def testShortEncoding(self):
+        try:
+            decoder.decode(ints2octs((9, 1, 131)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'accepted too-short real'
+
+
+if sys.version_info[0:2] > (2, 5):
+    class UniversalStringDecoderTestCase(BaseTestCase):
+        def testDecoder(self):
+            assert decoder.decode(ints2octs((28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99))) == (char.UniversalString(sys.version_info[0] == 3 and 'abc' or unicode('abc')), null)
+
+
+class BMPStringDecoderTestCase(BaseTestCase):
+    def testDecoder(self):
+        assert decoder.decode(ints2octs((30, 6, 0, 97, 0, 98, 0, 99))) == (char.BMPString(sys.version_info[0] == 3 and 'abc' or unicode('abc')), null)
+
+
+class UTF8StringDecoderTestCase(BaseTestCase):
+    def testDecoder(self):
+        assert decoder.decode(ints2octs((12, 3, 97, 98, 99))) == (char.UTF8String(sys.version_info[0] == 3 and 'abc' or unicode('abc')), null)
+
+
+class SequenceOfDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.s = univ.SequenceOf(componentType=univ.OctetString())
+        self.s.setComponentByPosition(0, univ.OctetString('quick brown'))
+
+    def testDefMode(self):
+        assert decoder.decode(
+            ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+        ) == (self.s, null)
+
+    def testIndefMode(self):
+        assert decoder.decode(
+            ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+        ) == (self.s, null)
+
+    def testDefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+        ) == (self.s, null)
+
+    def testIndefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+        ) == (self.s, null)
+
+    def testSchemalessDecoder(self):
+        assert decoder.decode(
+            ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=univ.SequenceOf()
+        ) == (self.s, null)
+
+
+class ExpTaggedSequenceOfDecoderTestCase(BaseTestCase):
+
+    def testWithSchema(self):
+        s = univ.SequenceOf().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))
+        s2, r = decoder.decode(
+            ints2octs((163, 15, 48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=s
+        )
+        assert not r
+        assert s2 == [str2octs('quick brown')]
+        assert s.tagSet == s2.tagSet
+
+    def testWithoutSchema(self):
+        s = univ.SequenceOf().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))
+        s2, r = decoder.decode(
+            ints2octs((163, 15, 48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+        )
+        assert not r
+        assert s2 == [str2octs('quick brown')]
+        assert s.tagSet == s2.tagSet
+
+
+class SequenceOfDecoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SequenceOf(componentType=univ.OctetString())
+        self.s.setComponentByPosition(0, univ.OctetString('quick brown'))
+
+    def testDefMode(self):
+        assert decoder.decode(
+            ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testIndefMode(self):
+        assert decoder.decode(
+            ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testDefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testIndefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+
+class SetOfDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SetOf(componentType=univ.OctetString())
+        self.s.setComponentByPosition(0, univ.OctetString('quick brown'))
+
+    def testDefMode(self):
+        assert decoder.decode(
+            ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+        ) == (self.s, null)
+
+    def testIndefMode(self):
+        assert decoder.decode(
+            ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+        ) == (self.s, null)
+
+    def testDefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+        ) == (self.s, null)
+
+    def testIndefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+        ) == (self.s, null)
+
+    def testSchemalessDecoder(self):
+        assert decoder.decode(
+            ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=univ.SetOf()
+        ) == (self.s, null)
+
+
+class SetOfDecoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SetOf(componentType=univ.OctetString())
+        self.s.setComponentByPosition(0, univ.OctetString('quick brown'))
+
+    def testDefMode(self):
+        assert decoder.decode(
+            ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testIndefMode(self):
+        assert decoder.decode(
+            ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testDefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testIndefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+
+class SequenceDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null(null)),
+                namedtype.NamedType('first-name', univ.OctetString(null)),
+                namedtype.NamedType('age', univ.Integer(33))
+            )
+        )
+        self.s.setComponentByPosition(0, univ.Null(null))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testWithOptionalAndDefaultedDefMode(self):
+        assert decoder.decode(
+            ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedDefModeChunked(self):
+        assert decoder.decode(
+            ints2octs(
+                (48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedIndefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedDefModeSubst(self):
+        assert decoder.decode(
+            ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)),
+            substrateFun=lambda a, b, c: (b, b[c:])
+        ) == (ints2octs((5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), str2octs(''))
+
+    def testWithOptionalAndDefaultedIndefModeSubst(self):
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)),
+            substrateFun=lambda a, b, c: (b, str2octs(''))
+        ) == (ints2octs(
+            (5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), str2octs(''))
+
+    def testTagFormat(self):
+        try:
+            decoder.decode(
+                ints2octs((16, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'wrong tagFormat worked out'
+
+
+class SequenceDecoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null(null)),
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+
+    def __init(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(null))
+
+    def __initWithOptional(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(null))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+
+    def __initWithDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(null))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def __initWithOptionalAndDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(null))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testDefMode(self):
+        self.__init()
+        assert decoder.decode(
+            ints2octs((48, 2, 5, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testIndefMode(self):
+        self.__init()
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testDefModeChunked(self):
+        self.__init()
+        assert decoder.decode(
+            ints2octs((48, 2, 5, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testIndefModeChunked(self):
+        self.__init()
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalDefMode(self):
+        self.__initWithOptional()
+        assert decoder.decode(
+            ints2octs((48, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionaIndefMode(self):
+        self.__initWithOptional()
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 0, 0)),
+            asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalDefModeChunked(self):
+        self.__initWithOptional()
+        assert decoder.decode(
+            ints2octs((48, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)),
+            asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalIndefModeChunked(self):
+        self.__initWithOptional()
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0,
+                       0, 0, 0)),
+            asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithDefaultedDefMode(self):
+        self.__initWithDefaulted()
+        assert decoder.decode(
+            ints2octs((48, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithDefaultedIndefMode(self):
+        self.__initWithDefaulted()
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithDefaultedDefModeChunked(self):
+        self.__initWithDefaulted()
+        assert decoder.decode(
+            ints2octs((48, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithDefaultedIndefModeChunked(self):
+        self.__initWithDefaulted()
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedDefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)),
+            asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1,
+                       0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedDefModeChunked(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs(
+                (48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)),
+            asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedIndefModeChunked(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0,
+                       0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+
+class SequenceDecoderWithUnaggedOpenTypesTestCase(BaseTestCase):
+    def setUp(self):
+        openType = opentype.OpenType(
+            'id',
+            {1: univ.Integer(),
+             2: univ.OctetString()}
+        )
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer()),
+                namedtype.NamedType('blob', univ.Any(), openType=openType)
+            )
+        )
+
+    def testDecodeOpenTypesChoiceOne(self):
+        s, r = decoder.decode(
+            ints2octs((48, 6, 2, 1, 1, 2, 1, 12)), asn1Spec=self.s,
+            decodeOpenTypes=True
+        )
+        assert not r
+        assert s[0] == 1
+        assert s[1] == 12
+
+    def testDecodeOpenTypesChoiceTwo(self):
+        s, r = decoder.decode(
+            ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s,
+            decodeOpenTypes=True
+        )
+        assert not r
+        assert s[0] == 2
+        assert s[1] == univ.OctetString('quick brown')
+
+    def testDecodeOpenTypesUnknownType(self):
+        try:
+            s, r = decoder.decode(
+                ints2octs((48, 6, 2, 1, 2, 6, 1, 39)), asn1Spec=self.s,
+                decodeOpenTypes=True
+            )
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'unknown open type tolerated'
+
+    def testDecodeOpenTypesUnknownId(self):
+        s, r = decoder.decode(
+            ints2octs((48, 6, 2, 1, 3, 6, 1, 39)), asn1Spec=self.s,
+            decodeOpenTypes=True
+        )
+        assert not r
+        assert s[0] == 3
+        assert s[1] == univ.OctetString(hexValue='060127')
+
+    def testDontDecodeOpenTypesChoiceOne(self):
+        s, r = decoder.decode(
+            ints2octs((48, 6, 2, 1, 1, 2, 1, 12)), asn1Spec=self.s
+        )
+        assert not r
+        assert s[0] == 1
+        assert s[1] == ints2octs((2, 1, 12))
+
+    def testDontDecodeOpenTypesChoiceTwo(self):
+        s, r = decoder.decode(
+            ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s
+        )
+        assert not r
+        assert s[0] == 2
+        assert s[1] == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+
+class SequenceDecoderWithImplicitlyTaggedOpenTypesTestCase(BaseTestCase):
+    def setUp(self):
+        openType = opentype.OpenType(
+            'id',
+            {1: univ.Integer(),
+             2: univ.OctetString()}
+        )
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer()),
+                namedtype.NamedType(
+                    'blob', univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType
+                )
+            )
+        )
+
+    def testDecodeOpenTypesChoiceOne(self):
+        s, r = decoder.decode(
+            ints2octs((48, 8, 2, 1, 1, 131, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True
+        )
+        assert not r
+        assert s[0] == 1
+        assert s[1] == 12
+
+    def testDecodeOpenTypesUnknownId(self):
+        s, r = decoder.decode(
+            ints2octs((48, 8, 2, 1, 3, 131, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True
+        )
+        assert not r
+        assert s[0] == 3
+        assert s[1] == univ.OctetString(hexValue='02010C')
+
+
+class SequenceDecoderWithExplicitlyTaggedOpenTypesTestCase(BaseTestCase):
+    def setUp(self):
+        openType = opentype.OpenType(
+            'id',
+            {1: univ.Integer(),
+             2: univ.OctetString()}
+        )
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer()),
+                namedtype.NamedType(
+                    'blob', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType
+                )
+            )
+        )
+
+    def testDecodeOpenTypesChoiceOne(self):
+        s, r = decoder.decode(
+            ints2octs((48, 8, 2, 1, 1, 163, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True
+        )
+        assert not r
+        assert s[0] == 1
+        assert s[1] == 12
+
+    def testDecodeOpenTypesUnknownId(self):
+        s, r = decoder.decode(
+            ints2octs((48, 8, 2, 1, 3, 163, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True
+        )
+        assert not r
+        assert s[0] == 3
+        assert s[1] == univ.OctetString(hexValue='02010C')
+
+
+class SetDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null(null)),
+                namedtype.NamedType('first-name', univ.OctetString(null)),
+                namedtype.NamedType('age', univ.Integer(33))
+            )
+        )
+        self.s.setComponentByPosition(0, univ.Null(null))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testWithOptionalAndDefaultedDefMode(self):
+        assert decoder.decode(
+            ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedDefModeChunked(self):
+        assert decoder.decode(
+            ints2octs(
+                (49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedIndefModeChunked(self):
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedDefModeSubst(self):
+        assert decoder.decode(
+            ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)),
+            substrateFun=lambda a, b, c: (b, b[c:])
+        ) == (ints2octs((5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), str2octs(''))
+
+    def testWithOptionalAndDefaultedIndefModeSubst(self):
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)),
+            substrateFun=lambda a, b, c: (b, str2octs(''))
+        ) == (ints2octs(
+            (5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), str2octs(''))
+
+    def testTagFormat(self):
+        try:
+            decoder.decode(
+                ints2octs((16, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'wrong tagFormat worked out'
+
+
+class SetDecoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null(null)),
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+
+    def __init(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(null))
+
+    def __initWithOptional(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(null))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+
+    def __initWithDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(null))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def __initWithOptionalAndDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(null))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testDefMode(self):
+        self.__init()
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testIndefMode(self):
+        self.__init()
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testDefModeChunked(self):
+        self.__init()
+        assert decoder.decode(
+            ints2octs((49, 2, 5, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testIndefModeChunked(self):
+        self.__init()
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalDefMode(self):
+        self.__initWithOptional()
+        assert decoder.decode(
+            ints2octs((49, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionaIndefMode(self):
+        self.__initWithOptional()
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalDefModeChunked(self):
+        self.__initWithOptional()
+        assert decoder.decode(
+            ints2octs((49, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalIndefModeChunked(self):
+        self.__initWithOptional()
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithDefaultedDefMode(self):
+        self.__initWithDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithDefaultedIndefMode(self):
+        self.__initWithDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithDefaultedDefModeChunked(self):
+        self.__initWithDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithDefaultedIndefModeChunked(self):
+        self.__initWithDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedDefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedDefModeReordered(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 18, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedIndefModeReordered(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 128, 2, 1, 1, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0,  0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedDefModeChunked(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithOptionalAndDefaultedIndefModeChunked(self):
+        self.__initWithOptionalAndDefaulted()
+        assert decoder.decode(
+            ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+
+class SequenceOfWithExpTaggedOctetStringDecoder(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SequenceOf(
+            componentType=univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
+        )
+        self.s.setComponentByPosition(0, 'q')
+        self.s2 = univ.SequenceOf()
+
+    def testDefModeSchema(self):
+        s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)), asn1Spec=self.s)
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testIndefModeSchema(self):
+        s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)), asn1Spec=self.s)
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testDefModeNoComponent(self):
+        s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)), asn1Spec=self.s2)
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testIndefModeNoComponent(self):
+        s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)), asn1Spec=self.s2)
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testDefModeSchemaless(self):
+        s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)))
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testIndefModeSchemaless(self):
+        s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)))
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+
+class SequenceWithExpTaggedOctetStringDecoder(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType(
+                    'x', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
+                )
+            )
+        )
+        self.s.setComponentByPosition(0, 'q')
+        self.s2 = univ.Sequence()
+
+    def testDefModeSchema(self):
+        s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)), asn1Spec=self.s)
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testIndefModeSchema(self):
+        s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)), asn1Spec=self.s)
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testDefModeNoComponent(self):
+        s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)), asn1Spec=self.s2)
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testIndefModeNoComponent(self):
+        s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)), asn1Spec=self.s2)
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testDefModeSchemaless(self):
+        s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)))
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+    def testIndefModeSchemaless(self):
+        s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)))
+        assert not r
+        assert s == self.s
+        assert s.tagSet == self.s.tagSet
+
+
+class ChoiceDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null(null)),
+                namedtype.NamedType('number', univ.Integer(0)),
+                namedtype.NamedType('string', univ.OctetString())
+            )
+        )
+
+    def testBySpec(self):
+        self.s.setComponentByPosition(0, univ.Null(null))
+        assert decoder.decode(
+            ints2octs((5, 0)), asn1Spec=self.s
+        ) == (self.s, null)
+
+    def testWithoutSpec(self):
+        self.s.setComponentByPosition(0, univ.Null(null))
+        assert decoder.decode(ints2octs((5, 0))) == (self.s, null)
+        assert decoder.decode(ints2octs((5, 0))) == (univ.Null(null), null)
+
+    def testUndefLength(self):
+        self.s.setComponentByPosition(2, univ.OctetString('abcdefgh'))
+        assert decoder.decode(ints2octs((36, 128, 4, 3, 97, 98, 99, 4, 3, 100, 101, 102, 4, 2, 103, 104, 0, 0)),
+                              asn1Spec=self.s) == (self.s, null)
+
+    def testExplicitTag(self):
+        s = self.s.subtype(explicitTag=tag.Tag(tag.tagClassContext,
+                                               tag.tagFormatConstructed, 4))
+        s.setComponentByPosition(0, univ.Null(null))
+        assert decoder.decode(ints2octs((164, 2, 5, 0)), asn1Spec=s) == (s, null)
+
+    def testExplicitTagUndefLength(self):
+        s = self.s.subtype(explicitTag=tag.Tag(tag.tagClassContext,
+                                               tag.tagFormatConstructed, 4))
+        s.setComponentByPosition(0, univ.Null(null))
+        assert decoder.decode(ints2octs((164, 128, 5, 0, 0, 0)), asn1Spec=s) == (s, null)
+
+
+class AnyDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Any()
+
+    def testByUntagged(self):
+        assert decoder.decode(
+            ints2octs((4, 3, 102, 111, 120)), asn1Spec=self.s
+        ) == (univ.Any('\004\003fox'), null)
+
+    def testTaggedEx(self):
+        s = univ.Any('\004\003fox').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
+        assert decoder.decode(ints2octs((164, 5, 4, 3, 102, 111, 120)), asn1Spec=s) == (s, null)
+
+    def testTaggedIm(self):
+        s = univ.Any('\004\003fox').subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
+        assert decoder.decode(ints2octs((132, 5, 4, 3, 102, 111, 120)), asn1Spec=s) == (s, null)
+
+    def testByUntaggedIndefMode(self):
+        assert decoder.decode(
+            ints2octs((4, 3, 102, 111, 120)), asn1Spec=self.s
+        ) == (univ.Any('\004\003fox'), null)
+
+    def testTaggedExIndefMode(self):
+        s = univ.Any('\004\003fox').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
+        assert decoder.decode(ints2octs((164, 128, 4, 3, 102, 111, 120, 0, 0)), asn1Spec=s) == (s, null)
+
+    def testTaggedImIndefMode(self):
+        s = univ.Any('\004\003fox').subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
+        assert decoder.decode(ints2octs((164, 128, 4, 3, 102, 111, 120, 0, 0)), asn1Spec=s) == (s, null)
+
+    def testByUntaggedSubst(self):
+        assert decoder.decode(
+            ints2octs((4, 3, 102, 111, 120)),
+            asn1Spec=self.s,
+            substrateFun=lambda a, b, c: (b, b[c:])
+        ) == (ints2octs((4, 3, 102, 111, 120)), str2octs(''))
+
+    def testTaggedExSubst(self):
+        assert decoder.decode(
+            ints2octs((164, 5, 4, 3, 102, 111, 120)),
+            asn1Spec=self.s,
+            substrateFun=lambda a, b, c: (b, b[c:])
+        ) == (ints2octs((164, 5, 4, 3, 102, 111, 120)), str2octs(''))
+
+
+class EndOfOctetsTestCase(BaseTestCase):
+    def testUnexpectedEoo(self):
+        try:
+            decoder.decode(ints2octs((0, 0)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'end-of-contents octets accepted at top level'
+
+    def testExpectedEoo(self):
+        result, remainder = decoder.decode(ints2octs((0, 0)), allowEoo=True)
+        assert eoo.endOfOctets.isSameTypeWith(result) and result == eoo.endOfOctets and result is eoo.endOfOctets
+        assert remainder == null
+
+    def testDefiniteNoEoo(self):
+        try:
+            decoder.decode(ints2octs((0x23, 0x02, 0x00, 0x00)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'end-of-contents octets accepted inside definite-length encoding'
+
+    def testIndefiniteEoo(self):
+        result, remainder = decoder.decode(ints2octs((0x23, 0x80, 0x00, 0x00)))
+        assert result == () and remainder == null, 'incorrect decoding of indefinite length end-of-octets'
+
+    def testNoLongFormEoo(self):
+        try:
+            decoder.decode(ints2octs((0x23, 0x80, 0x00, 0x81, 0x00)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'end-of-contents octets accepted with invalid long-form length'
+
+    def testNoConstructedEoo(self):
+        try:
+            decoder.decode(ints2octs((0x23, 0x80, 0x20, 0x00)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'end-of-contents octets accepted with invalid constructed encoding'
+
+    def testNoEooData(self):
+        try:
+            decoder.decode(ints2octs((0x23, 0x80, 0x00, 0x01, 0x00)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'end-of-contents octets accepted with unexpected data'
+
+
+class NonStringDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null(null)),
+                namedtype.NamedType('first-name', univ.OctetString(null)),
+                namedtype.NamedType('age', univ.Integer(33))
+            )
+        )
+        self.s.setComponentByPosition(0, univ.Null(null))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+        self.substrate = ints2octs([48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1])
+
+    def testOctetString(self):
+        s, _ = decoder.decode(univ.OctetString(self.substrate), asn1Spec=self.s)
+        assert self.s == s
+
+    def testAny(self):
+        s, _ = decoder.decode(univ.Any(self.substrate), asn1Spec=self.s)
+        assert self.s == s
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/ber/test_encoder.py b/tests/codec/ber/test_encoder.py
new file mode 100644
index 0000000..81368a9
--- /dev/null
+++ b/tests/codec/ber/test_encoder.py
@@ -0,0 +1,1377 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import tag
+from pyasn1.type import namedtype
+from pyasn1.type import opentype
+from pyasn1.type import univ
+from pyasn1.type import char
+from pyasn1.codec.ber import encoder
+from pyasn1.compat.octets import ints2octs
+from pyasn1.error import PyAsn1Error
+
+
+class LargeTagEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.o = univ.Integer().subtype(
+            value=1, explicitTag=tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0xdeadbeaf)
+        )
+
+    def testEncoder(self):
+        assert encoder.encode(self.o) == ints2octs((127, 141, 245, 182, 253, 47, 3, 2, 1, 1))
+
+
+class IntegerEncoderTestCase(BaseTestCase):
+    def testPosInt(self):
+        assert encoder.encode(univ.Integer(12)) == ints2octs((2, 1, 12))
+
+    def testNegInt(self):
+        assert encoder.encode(univ.Integer(-12)) == ints2octs((2, 1, 244))
+
+    def testZero(self):
+        assert encoder.encode(univ.Integer(0)) == ints2octs((2, 1, 0))
+
+    def testCompactZero(self):
+        encoder.IntegerEncoder.supportCompactZero = True
+        substrate = encoder.encode(univ.Integer(0))
+        encoder.IntegerEncoder.supportCompactZero = False
+        assert substrate == ints2octs((2, 0))
+
+    def testMinusOne(self):
+        assert encoder.encode(univ.Integer(-1)) == ints2octs((2, 1, 255))
+
+    def testPosLong(self):
+        assert encoder.encode(
+            univ.Integer(0xffffffffffffffff)
+        ) == ints2octs((2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255))
+
+    def testNegLong(self):
+        assert encoder.encode(
+            univ.Integer(-0xffffffffffffffff)
+        ) == ints2octs((2, 9, 255, 0, 0, 0, 0, 0, 0, 0, 1))
+
+
+class IntegerEncoderWithSchemaTestCase(BaseTestCase):
+    def testPosInt(self):
+        assert encoder.encode(12, asn1Spec=univ.Integer()) == ints2octs((2, 1, 12))
+
+    def testNegInt(self):
+        assert encoder.encode(-12, asn1Spec=univ.Integer()) == ints2octs((2, 1, 244))
+
+    def testZero(self):
+        assert encoder.encode(0, asn1Spec=univ.Integer()) == ints2octs((2, 1, 0))
+
+    def testPosLong(self):
+        assert encoder.encode(
+            0xffffffffffffffff, asn1Spec=univ.Integer()
+        ) == ints2octs((2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255))
+
+
+class BooleanEncoderTestCase(BaseTestCase):
+    def testTrue(self):
+        assert encoder.encode(univ.Boolean(1)) == ints2octs((1, 1, 1))
+
+    def testFalse(self):
+        assert encoder.encode(univ.Boolean(0)) == ints2octs((1, 1, 0))
+
+
+class BooleanEncoderWithSchemaTestCase(BaseTestCase):
+    def testTrue(self):
+        assert encoder.encode(True, asn1Spec=univ.Boolean()) == ints2octs((1, 1, 1))
+
+    def testFalse(self):
+        assert encoder.encode(False, asn1Spec=univ.Boolean()) == ints2octs((1, 1, 0))
+
+
+class BitStringEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.b = univ.BitString((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1))
+
+    def testDefMode(self):
+        assert encoder.encode(self.b) == ints2octs((3, 3, 1, 169, 138))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.b, defMode=False
+        ) == ints2octs((3, 3, 1, 169, 138))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.b, maxChunkSize=1
+        ) == ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.b, defMode=False, maxChunkSize=1
+        ) == ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))
+
+    def testEmptyValue(self):
+        assert encoder.encode(univ.BitString([])) == ints2octs((3, 1, 0))
+
+
+class BitStringEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.b = (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1)
+        self.s = univ.BitString()
+
+    def testDefMode(self):
+        assert encoder.encode(self.b, asn1Spec=self.s) == ints2octs((3, 3, 1, 169, 138))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.b, asn1Spec=self.s, defMode=False
+        ) == ints2octs((3, 3, 1, 169, 138))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.b, asn1Spec=self.s, maxChunkSize=1
+        ) == ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.b, asn1Spec=self.s, defMode=False, maxChunkSize=1
+        ) == ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))
+
+    def testEmptyValue(self):
+        assert encoder.encode([],  asn1Spec=self.s) == ints2octs((3, 1, 0))
+
+
+class OctetStringEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.o = univ.OctetString('Quick brown fox')
+
+    def testDefMode(self):
+        assert encoder.encode(self.o) == ints2octs(
+            (4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.o, defMode=False
+        ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.o, maxChunkSize=4
+        ) == ints2octs((36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119,
+                        110, 32, 4, 3, 102, 111, 120))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.o, defMode=False, maxChunkSize=4
+        ) == ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110,
+                        32, 4, 3, 102,  111, 120, 0, 0))
+
+
+class OctetStringEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.OctetString()
+        self.o = 'Quick brown fox'
+
+    def testDefMode(self):
+        assert encoder.encode(self.o, asn1Spec=self.s) == ints2octs(
+            (4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.o, asn1Spec=self.s, defMode=False
+        ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.o, asn1Spec=self.s, maxChunkSize=4
+        ) == ints2octs((36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119,
+                        110, 32, 4, 3, 102, 111, 120))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.o, asn1Spec=self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110,
+                        32, 4, 3, 102,  111, 120, 0, 0))
+
+
+class ExpTaggedOctetStringEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.o = univ.OctetString().subtype(
+            value='Quick brown fox',
+            explicitTag=tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 5)
+        )
+
+    def testDefMode(self):
+        assert encoder.encode(self.o) == ints2octs(
+            (101, 17, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.o, defMode=False
+        ) == ints2octs((101, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.o, defMode=True, maxChunkSize=4
+        ) == ints2octs((101, 25, 36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3,
+                        102, 111, 120))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.o, defMode=False, maxChunkSize=4
+        ) == ints2octs((101, 128, 36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0, 0, 0))
+
+
+class NullEncoderTestCase(BaseTestCase):
+    def testNull(self):
+        assert encoder.encode(univ.Null('')) == ints2octs((5, 0))
+
+
+class NullEncoderWithSchemaTestCase(BaseTestCase):
+    def testNull(self):
+        assert encoder.encode(None, univ.Null()) == ints2octs((5, 0))
+
+
+class ObjectIdentifierEncoderTestCase(BaseTestCase):
+    def testOne(self):
+        assert encoder.encode(
+            univ.ObjectIdentifier((1, 3, 6, 0, 0xffffe))
+        ) == ints2octs((6, 6, 43, 6, 0, 191, 255, 126))
+
+    def testEdge1(self):
+        assert encoder.encode(
+            univ.ObjectIdentifier((0, 39))
+        ) == ints2octs((6, 1, 39))
+
+    def testEdge2(self):
+        assert encoder.encode(
+            univ.ObjectIdentifier((1, 39))
+        ) == ints2octs((6, 1, 79))
+
+    def testEdge3(self):
+        # 01111111
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 40))
+        ) == ints2octs((6, 1, 120))
+
+    def testEdge4(self):
+        # 10010000|10000000|10000000|10000000|01001111
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 0xffffffff))
+        ) == ints2octs((6, 5, 0x90, 0x80, 0x80, 0x80, 0x4F))
+
+    def testEdge5(self):
+        # 01111111
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 47))
+        ) == ints2octs((6, 1, 0x7F))
+
+    def testEdge6(self):
+        # 10000001|00000000
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 48))
+        ) == ints2octs((6, 2, 0x81, 0x00))
+
+    def testEdge7(self):
+        # 10000001|00110100|00000003
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 100, 3))
+        ) == ints2octs((6, 3, 0x81, 0x34, 0x03))
+
+    def testEdge8(self):
+        # 10000101|00000000
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 560))
+        ) == ints2octs((6, 2, 133, 0))
+
+    def testEdge9(self):
+        # 10001000|10000100|10000111|0000010
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 16843570))
+        ) == ints2octs((6, 4, 0x88, 0x84, 0x87, 0x02))
+
+    def testEdgeA(self):
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 5))
+        ) == ints2octs((6, 1, 85))
+
+    def testImpossible1(self):
+        try:
+            encoder.encode(univ.ObjectIdentifier((3, 1, 2)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'impossible leading arc tolerated'
+
+    def testImpossible2(self):
+        try:
+            encoder.encode(univ.ObjectIdentifier((0,)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'single arc OID tolerated'
+
+    def testImpossible3(self):
+        try:
+            encoder.encode(univ.ObjectIdentifier((0, 40)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'second arc overflow tolerated'
+
+    def testImpossible4(self):
+        try:
+            encoder.encode(univ.ObjectIdentifier((1, 40)))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'second arc overflow tolerated'
+
+    def testLarge1(self):
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 18446744073709551535184467440737095))
+        ) == ints2octs((0x06, 0x11, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB,
+                        0xE2, 0xB7, 0x17))
+
+    def testLarge2(self):
+        assert encoder.encode(
+            univ.ObjectIdentifier((2, 999, 18446744073709551535184467440737095))
+        ) == ints2octs((0x06, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6,
+                        0xB8, 0xCB, 0xE2, 0xB6, 0x47))
+
+
+class ObjectIdentifierWithSchemaEncoderTestCase(BaseTestCase):
+    def testOne(self):
+        assert encoder.encode(
+            (1, 3, 6, 0, 0xffffe), asn1Spec=univ.ObjectIdentifier()
+        ) == ints2octs((6, 6, 43, 6, 0, 191, 255, 126))
+
+
+class RealEncoderTestCase(BaseTestCase):
+    def testChar(self):
+        assert encoder.encode(
+            univ.Real((123, 10, 11))
+        ) == ints2octs((9, 7, 3, 49, 50, 51, 69, 49, 49))
+
+    def testBin1(self):
+        assert encoder.encode(  # default binEncBase = 2
+            univ.Real((0.5, 2, 0))  # check encbase = 2 and exponent = -1
+        ) == ints2octs((9, 3, 128, 255, 1))
+
+    def testBin2(self):
+        r = univ.Real((3.25, 2, 0))
+        r.binEncBase = 8  # change binEncBase only for this instance of Real
+        assert encoder.encode(
+            r  # check encbase = 8
+        ) == ints2octs((9, 3, 148, 255, 13))
+
+    def testBin3(self):
+        # change binEncBase in the RealEncoder instance => for all further Real
+        binEncBase, encoder.typeMap[univ.Real.typeId].binEncBase = encoder.typeMap[univ.Real.typeId].binEncBase, 16
+        assert encoder.encode(
+            univ.Real((0.00390625, 2, 0))  # check encbase = 16
+        ) == ints2octs((9, 3, 160, 254, 1))
+        encoder.typeMap[univ.Real.typeId].binEncBase = binEncBase
+
+    def testBin4(self):
+        # choose binEncBase automatically for all further Real (testBin[4-7])
+        binEncBase, encoder.typeMap[univ.Real.typeId].binEncBase = encoder.typeMap[univ.Real.typeId].binEncBase, None
+        assert encoder.encode(
+            univ.Real((1, 2, 0))  # check exponenta = 0
+        ) == ints2octs((9, 3, 128, 0, 1))
+        encoder.typeMap[univ.Real.typeId].binEncBase = binEncBase
+
+    def testBin5(self):
+        assert encoder.encode(
+            univ.Real((3, 2, -1020))  # case of 2 octs for exponent and
+            # negative exponenta and abs(exponent) is
+            # all 1's and fills the whole octet(s)
+        ) == ints2octs((9, 4, 129, 252, 4, 3))
+
+    def testBin6(self):
+        assert encoder.encode(
+            univ.Real((1, 2, 262140))  # case of 3 octs for exponent and
+            # check that first 9 bits for exponent
+            # are not all 1's
+        ) == ints2octs((9, 5, 130, 3, 255, 252, 1))
+
+    def testBin7(self):
+        assert encoder.encode(
+            univ.Real((-1, 2, 76354972))  # case of >3 octs for exponent and
+            # mantissa < 0
+        ) == ints2octs((9, 7, 195, 4, 4, 141, 21, 156, 1))
+
+    def testPlusInf(self):
+        assert encoder.encode(univ.Real('inf')) == ints2octs((9, 1, 64))
+
+    def testMinusInf(self):
+        assert encoder.encode(univ.Real('-inf')) == ints2octs((9, 1, 65))
+
+    def testZero(self):
+        assert encoder.encode(univ.Real(0)) == ints2octs((9, 0))
+
+
+class RealEncoderWithSchemaTestCase(BaseTestCase):
+    def testChar(self):
+        assert encoder.encode(
+            (123, 10, 11), asn1Spec=univ.Real()
+        ) == ints2octs((9, 7, 3, 49, 50, 51, 69, 49, 49))
+
+
+if sys.version_info[0:2] > (2, 5):
+    class UniversalStringEncoderTestCase(BaseTestCase):
+        def testEncoding(self):
+            assert encoder.encode(char.UniversalString(sys.version_info[0] == 3 and 'abc' or unicode('abc'))) == ints2octs(
+                (28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99)), 'Incorrect encoding'
+
+
+    class UniversalStringEncoderWithSchemaTestCase(BaseTestCase):
+        def testEncoding(self):
+            assert encoder.encode(
+                sys.version_info[0] == 3 and 'abc' or unicode('abc'), asn1Spec=char.UniversalString()
+            ) == ints2octs((28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99)), 'Incorrect encoding'
+
+
+class BMPStringEncoderTestCase(BaseTestCase):
+    def testEncoding(self):
+        assert encoder.encode(char.BMPString(sys.version_info[0] == 3 and 'abc' or unicode('abc'))) == ints2octs(
+            (30, 6, 0, 97, 0, 98, 0, 99)), 'Incorrect encoding'
+
+
+class BMPStringEncoderWithSchemaTestCase(BaseTestCase):
+    def testEncoding(self):
+        assert encoder.encode(
+            sys.version_info[0] == 3 and 'abc' or unicode('abc'), asn1Spec=char.BMPString()
+        ) == ints2octs((30, 6, 0, 97, 0, 98, 0, 99)), 'Incorrect encoding'
+
+
+class UTF8StringEncoderTestCase(BaseTestCase):
+    def testEncoding(self):
+        assert encoder.encode(char.UTF8String(sys.version_info[0] == 3 and 'abc' or unicode('abc'))) == ints2octs(
+            (12, 3, 97, 98, 99)), 'Incorrect encoding'
+
+
+class UTF8StringEncoderWithSchemaTestCase(BaseTestCase):
+    def testEncoding(self):
+        assert encoder.encode(
+            sys.version_info[0] == 3 and 'abc' or unicode('abc'), asn1Spec=char.UTF8String()
+        ) == ints2octs((12, 3, 97, 98, 99)), 'Incorrect encoding'
+
+
+class SequenceOfEncoderTestCase(BaseTestCase):
+    def testEmpty(self):
+        s = univ.SequenceOf()
+        assert encoder.encode(s) == ints2octs((48, 0))
+
+    def testDefMode(self):
+        s = univ.SequenceOf()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(s) == ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testIndefMode(self):
+        s = univ.SequenceOf()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(
+            s, defMode=False
+        ) == ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+    def testDefModeChunked(self):
+        s = univ.SequenceOf()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(
+            s, defMode=True, maxChunkSize=4
+        ) == ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+    def testIndefModeChunked(self):
+        s = univ.SequenceOf()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(
+            s, defMode=False, maxChunkSize=4
+        ) == ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+
+class SequenceOfEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SequenceOf(componentType=univ.OctetString())
+        self.v = ['quick brown']
+
+    def testEmpty(self):
+        assert encoder.encode([], asn1Spec=self.s) == ints2octs((48, 0))
+
+    def testDefMode(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s
+        ) == ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=False
+        ) == ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+
+class SequenceOfEncoderWithComponentsSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SequenceOf(componentType=univ.OctetString())
+
+    def __init(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, 'quick brown')
+
+    def testDefMode(self):
+        self.__init()
+        assert encoder.encode(self.s) == ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testIndefMode(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+    def testDefModeChunked(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+    def testIndefModeChunked(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+
+class SetOfEncoderTestCase(BaseTestCase):
+    def testEmpty(self):
+        s = univ.SetOf()
+        assert encoder.encode(s) == ints2octs((49, 0))
+
+    def testDefMode(self):
+        s = univ.SetOf()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(s) == ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testIndefMode(self):
+        s = univ.SetOf()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(
+            s, defMode=False
+        ) == ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+    def testDefModeChunked(self):
+        s = univ.SetOf()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(
+            s, defMode=True, maxChunkSize=4
+        ) == ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+    def testIndefModeChunked(self):
+        s = univ.SetOf()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(
+            s, defMode=False, maxChunkSize=4
+        ) == ints2octs((49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+
+class SetOfEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SetOf(componentType=univ.OctetString())
+        self.v = ['quick brown']
+
+    def testEmpty(self):
+        s = univ.SetOf()
+        assert encoder.encode([], asn1Spec=self.s) == ints2octs((49, 0))
+
+    def testDefMode(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s
+        ) == ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=False
+        ) == ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs(
+            (49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+
+class SetOfEncoderWithComponentsSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SetOf(componentType=univ.OctetString())
+
+    def __init(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, 'quick brown')
+
+    def testDefMode(self):
+        self.__init()
+        assert encoder.encode(self.s) == ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testIndefMode(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+    def testDefModeChunked(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+    def testIndefModeChunked(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+
+class SequenceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence()
+        self.s.setComponentByPosition(0, univ.Null(''))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testDefMode(self):
+        assert encoder.encode(self.s) == ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+
+
+class SequenceEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null()),
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+        self.v = {
+            'place-holder': None,
+            'first-name': 'quick brown',
+            'age': 1
+        }
+
+    def testEmpty(self):
+        try:
+            assert encoder.encode({}, asn1Spec=self.s)
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'empty bare sequence tolerated'
+
+    def testDefMode(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s
+        ) == ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=False
+        ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+
+
+class SequenceEncoderWithUntaggedOpenTypesTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        openType = opentype.OpenType(
+            'id',
+            {1: univ.Integer(),
+             2: univ.OctetString()}
+        )
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer()),
+                namedtype.NamedType('blob', univ.Any(), openType=openType)
+            )
+        )
+
+    def testEncodeOpenTypeChoiceOne(self):
+        self.s.clear()
+
+        self.s[0] = 1
+        self.s[1] = univ.Integer(12)
+
+        assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs(
+            (48, 6, 2, 1, 1, 2, 1, 12)
+        )
+
+    def testEncodeOpenTypeChoiceTwo(self):
+        self.s.clear()
+
+        self.s[0] = 2
+        self.s[1] = univ.OctetString('quick brown')
+
+        assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs(
+            (48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)
+        )
+
+    def testEncodeOpenTypeUnknownId(self):
+        self.s.clear()
+
+        self.s[0] = 2
+        self.s[1] = univ.ObjectIdentifier('1.3.6')
+
+        try:
+            encoder.encode(self.s, asn1Spec=self.s)
+
+        except PyAsn1Error:
+            assert False, 'incompatible open type tolerated'
+
+    def testEncodeOpenTypeIncompatibleType(self):
+        self.s.clear()
+
+        self.s[0] = 2
+        self.s[1] = univ.ObjectIdentifier('1.3.6')
+
+        try:
+            encoder.encode(self.s, asn1Spec=self.s)
+
+        except PyAsn1Error:
+            assert False, 'incompatible open type tolerated'
+
+
+class SequenceEncoderWithImplicitlyTaggedOpenTypesTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        openType = opentype.OpenType(
+            'id',
+            {1: univ.Integer(),
+             2: univ.OctetString()}
+        )
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer()),
+                namedtype.NamedType('blob', univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType)
+            )
+        )
+
+    def testEncodeOpenTypeChoiceOne(self):
+        self.s.clear()
+
+        self.s[0] = 1
+        self.s[1] = univ.Integer(12)
+
+        assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs(
+            (48, 8, 2, 1, 1, 131, 3, 2, 1, 12)
+        )
+
+
+class SequenceEncoderWithExplicitlyTaggedOpenTypesTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        openType = opentype.OpenType(
+            'id',
+            {1: univ.Integer(),
+             2: univ.OctetString()}
+        )
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer()),
+                namedtype.NamedType('blob', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType)
+            )
+        )
+
+    def testEncodeOpenTypeChoiceOne(self):
+        self.s.clear()
+
+        self.s[0] = 1
+        self.s[1] = univ.Integer(12)
+
+        assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs(
+            (48, 8, 2, 1, 1, 163, 3, 2, 1, 12)
+        )
+
+
+class SequenceEncoderWithComponentsSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null()),
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+
+    def __init(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, '')
+
+    def __initWithOptional(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, '')
+        self.s.setComponentByPosition(1, 'quick brown')
+
+    def __initWithDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, '')
+        self.s.setComponentByPosition(2, 1)
+
+    def __initWithOptionalAndDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(''))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testDefMode(self):
+        self.__init()
+        assert encoder.encode(self.s) == ints2octs((48, 2, 5, 0))
+
+    def testIndefMode(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((48, 128, 5, 0, 0, 0))
+
+    def testDefModeChunked(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((48, 2, 5, 0))
+
+    def testIndefModeChunked(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((48, 128, 5, 0, 0, 0))
+
+    def testWithOptionalDefMode(self):
+        self.__initWithOptional()
+        assert encoder.encode(self.s) == ints2octs(
+            (48, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testWithOptionalIndefMode(self):
+        self.__initWithOptional()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+    def testWithOptionalDefModeChunked(self):
+        self.__initWithOptional()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((48, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+    def testWithOptionalIndefModeChunked(self):
+        self.__initWithOptional()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs(
+            (48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+    def testWithDefaultedDefMode(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(self.s) == ints2octs((48, 5, 5, 0, 2, 1, 1))
+
+    def testWithDefaultedIndefMode(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0))
+
+    def testWithDefaultedDefModeChunked(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((48, 5, 5, 0, 2, 1, 1))
+
+    def testWithDefaultedIndefModeChunked(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0))
+
+    def testWithOptionalAndDefaultedDefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(self.s) == ints2octs(
+            (48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+    def testWithOptionalAndDefaultedDefModeChunked(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs(
+            (48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+
+    def testWithOptionalAndDefaultedIndefModeChunked(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0,
+                        0, 2, 1, 1, 0, 0))
+
+
+class ExpTaggedSequenceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('number', univ.Integer()),
+            )
+        )
+
+        s = s.subtype(
+            explicitTag=tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 5)
+        )
+
+        s[0] = 12
+
+        self.s = s
+
+    def testDefMode(self):
+        assert encoder.encode(self.s) == ints2octs((101, 5, 48, 3, 2, 1, 12))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((101, 128, 48, 128, 2, 1, 12, 0, 0, 0, 0))
+
+
+class ExpTaggedSequenceComponentEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('number', univ.Boolean().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+            )
+        )
+
+        self.s[0] = True
+
+    def testDefMode(self):
+        assert encoder.encode(self.s) == ints2octs((48, 5, 160, 3, 1, 1, 1))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((48, 128, 160, 3, 1, 1, 1, 0, 0, 0, 0))
+
+
+class SetEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Set()
+        self.s.setComponentByPosition(0, univ.Null(''))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testDefMode(self):
+        assert encoder.encode(self.s) == ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((49, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+
+
+class SetEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null()),
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+        self.v = {
+            'place-holder': None,
+            'first-name': 'quick brown',
+            'age': 1
+        }
+
+    def testEmpty(self):
+        try:
+            assert encoder.encode({}, asn1Spec=self.s)
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'empty bare SET tolerated'
+
+    def testDefMode(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s
+        ) == ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+
+    def testIndefMode(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=False
+        ) == ints2octs((49, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+    def testDefModeChunked(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+
+    def testIndefModeChunked(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+
+
+class SetEncoderWithComponentsSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null()),
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+
+    def __init(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, '')
+
+    def __initWithOptional(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, '')
+        self.s.setComponentByPosition(1, 'quick brown')
+
+    def __initWithDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, '')
+        self.s.setComponentByPosition(2, 1)
+
+    def __initWithOptionalAndDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(''))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testDefMode(self):
+        self.__init()
+        assert encoder.encode(self.s) == ints2octs((49, 2, 5, 0))
+
+    def testIndefMode(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((49, 128, 5, 0, 0, 0))
+
+    def testDefModeChunked(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((49, 2, 5, 0))
+
+    def testIndefModeChunked(self):
+        self.__init()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((49, 128, 5, 0, 0, 0))
+
+    def testWithOptionalDefMode(self):
+        self.__initWithOptional()
+        assert encoder.encode(self.s) == ints2octs(
+            (49, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testWithOptionalIndefMode(self):
+        self.__initWithOptional()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((49, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+    def testWithOptionalDefModeChunked(self):
+        self.__initWithOptional()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((49, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+    def testWithOptionalIndefModeChunked(self):
+        self.__initWithOptional()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs(
+            (49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+    def testWithDefaultedDefMode(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(self.s) == ints2octs((49, 5, 5, 0, 2, 1, 1))
+
+    def testWithDefaultedIndefMode(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((49, 128, 5, 0, 2, 1, 1, 0, 0))
+
+    def testWithDefaultedDefModeChunked(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs((49, 5, 5, 0, 2, 1, 1))
+
+    def testWithDefaultedIndefModeChunked(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((49, 128, 5, 0, 2, 1, 1, 0, 0))
+
+    def testWithOptionalAndDefaultedDefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(self.s) == ints2octs(
+            (49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(
+            self.s, defMode=False
+        ) == ints2octs((49, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+    def testWithOptionalAndDefaultedDefModeChunked(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(
+            self.s, defMode=True, maxChunkSize=4
+        ) == ints2octs(
+            (49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+
+    def testWithOptionalAndDefaultedIndefModeChunked(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(
+            self.s, defMode=False, maxChunkSize=4
+        ) == ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+
+
+class ChoiceEncoderTestCase(BaseTestCase):
+
+    def testEmpty(self):
+        s = univ.Choice()
+        try:
+            encoder.encode(s)
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'encoded unset choice'
+
+    def testDefModeOptionOne(self):
+        s = univ.Choice()
+        s.setComponentByPosition(0, univ.Null(''))
+        assert encoder.encode(s) == ints2octs((5, 0))
+
+    def testDefModeOptionTwo(self):
+        s = univ.Choice()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(s) == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testIndefMode(self):
+        s = univ.Choice()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(
+            s, defMode=False
+        ) == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+    def testDefModeChunked(self):
+        s = univ.Choice()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(
+            s, defMode=True, maxChunkSize=4
+        ) == ints2octs((36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+    def testIndefModeChunked(self):
+        s = univ.Choice()
+        s.setComponentByPosition(0, univ.OctetString('quick brown'))
+        assert encoder.encode(
+            s, defMode=False, maxChunkSize=4
+        ) == ints2octs((36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0))
+
+
+class ChoiceEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null('')),
+                namedtype.NamedType('number', univ.Integer(0)),
+                namedtype.NamedType('string', univ.OctetString())
+            )
+        )
+        self.v = {
+            'place-holder': None
+        }
+
+    def testFilled(self):
+        assert encoder.encode(
+            self.v, asn1Spec=self.s
+        ) == ints2octs((5, 0))
+
+
+class ChoiceEncoderWithComponentsSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null('')),
+                namedtype.NamedType('number', univ.Integer(0)),
+                namedtype.NamedType('string', univ.OctetString())
+            )
+        )
+
+    def testEmpty(self):
+        try:
+            encoder.encode(self.s)
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'encoded unset choice'
+
+    def testFilled(self):
+        self.s.setComponentByPosition(0, univ.Null(''))
+        assert encoder.encode(self.s) == ints2octs((5, 0))
+
+    def testTagged(self):
+        s = self.s.subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)
+        )
+        s.setComponentByPosition(0, univ.Null(''))
+        assert encoder.encode(s) == ints2octs((164, 2, 5, 0))
+
+    def testUndefLength(self):
+        self.s.setComponentByPosition(2, univ.OctetString('abcdefgh'))
+        assert encoder.encode(self.s, defMode=False, maxChunkSize=3) == ints2octs(
+            (36, 128, 4, 3, 97, 98, 99, 4, 3, 100, 101, 102, 4, 2, 103, 104, 0, 0))
+
+    def testTaggedUndefLength(self):
+        s = self.s.subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)
+        )
+        s.setComponentByPosition(2, univ.OctetString('abcdefgh'))
+        assert encoder.encode(s, defMode=False, maxChunkSize=3) == ints2octs(
+            (164, 128, 36, 128, 4, 3, 97, 98, 99, 4, 3, 100, 101, 102, 4, 2, 103, 104, 0, 0, 0, 0))
+
+
+class AnyEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Any(encoder.encode(univ.OctetString('fox')))
+
+    def testUntagged(self):
+        assert encoder.encode(self.s) == ints2octs((4, 3, 102, 111, 120))
+
+    def testTaggedEx(self):
+        s = self.s.subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)
+        )
+        assert encoder.encode(s) == ints2octs((164, 5, 4, 3, 102, 111, 120))
+
+    def testTaggedIm(self):
+        s = self.s.subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)
+        )
+        assert encoder.encode(s) == ints2octs((132, 5, 4, 3, 102, 111, 120))
+
+
+class AnyEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Any()
+        self.v = encoder.encode(univ.OctetString('fox'))
+
+    def testUntagged(self):
+        assert encoder.encode(self.v, asn1Spec=self.s) == ints2octs((4, 3, 102, 111, 120))
+
+    def testTaggedEx(self):
+        s = self.s.subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)
+        )
+        assert encoder.encode(self.v, asn1Spec=s) == ints2octs((164, 5, 4, 3, 102, 111, 120))
+
+    def testTaggedIm(self):
+        s = self.s.subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)
+        )
+        assert encoder.encode(self.v, asn1Spec=s) == ints2octs((132, 5, 4, 3, 102, 111, 120))
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/cer/__init__.py b/tests/codec/cer/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/tests/codec/cer/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/tests/codec/cer/__main__.py b/tests/codec/cer/__main__.py
new file mode 100644
index 0000000..043de87
--- /dev/null
+++ b/tests/codec/cer/__main__.py
@@ -0,0 +1,20 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+suite = unittest.TestLoader().loadTestsFromNames(
+    ['tests.codec.cer.test_encoder.suite',
+     'tests.codec.cer.test_decoder.suite']
+)
+
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/cer/test_decoder.py b/tests/codec/cer/test_decoder.py
new file mode 100644
index 0000000..c0aeb78
--- /dev/null
+++ b/tests/codec/cer/test_decoder.py
@@ -0,0 +1,71 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.codec.cer import decoder
+from pyasn1.compat.octets import ints2octs, str2octs, null
+from pyasn1.error import PyAsn1Error
+
+
+class BooleanDecoderTestCase(BaseTestCase):
+    def testTrue(self):
+        assert decoder.decode(ints2octs((1, 1, 255))) == (1, null)
+
+    def testFalse(self):
+        assert decoder.decode(ints2octs((1, 1, 0))) == (0, null)
+
+    def testEmpty(self):
+        try:
+            decoder.decode(ints2octs((1, 0)))
+        except PyAsn1Error:
+            pass
+
+    def testOverflow(self):
+        try:
+            decoder.decode(ints2octs((1, 2, 0, 0)))
+        except PyAsn1Error:
+            pass
+
+class BitStringDecoderTestCase(BaseTestCase):
+    def testShortMode(self):
+        assert decoder.decode(
+            ints2octs((3, 3, 6, 170, 128))
+        ) == (((1, 0) * 5), null)
+
+    def testLongMode(self):
+        assert decoder.decode(
+            ints2octs((3, 127, 6) + (170,) * 125 + (128,))
+        ) == (((1, 0) * 501), null)
+
+    # TODO: test failures on short chunked and long unchunked substrate samples
+
+
+class OctetStringDecoderTestCase(BaseTestCase):
+    def testShortMode(self):
+        assert decoder.decode(
+            ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)),
+        ) == (str2octs('Quick brown fox'), null)
+
+    def testLongMode(self):
+        assert decoder.decode(
+            ints2octs((36, 128, 4, 130, 3, 232) + (81,) * 1000 + (4, 1, 81, 0, 0))
+        ) == (str2octs('Q' * 1001), null)
+
+    # TODO: test failures on short chunked and long unchunked substrate samples
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/cer/test_encoder.py b/tests/codec/cer/test_encoder.py
new file mode 100644
index 0000000..fd50350
--- /dev/null
+++ b/tests/codec/cer/test_encoder.py
@@ -0,0 +1,723 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import tag
+from pyasn1.type import namedtype
+from pyasn1.type import univ
+from pyasn1.type import useful
+from pyasn1.codec.cer import encoder
+from pyasn1.compat.octets import ints2octs
+from pyasn1.error import PyAsn1Error
+
+
+class BooleanEncoderTestCase(BaseTestCase):
+    def testTrue(self):
+        assert encoder.encode(univ.Boolean(1)) == ints2octs((1, 1, 255))
+
+    def testFalse(self):
+        assert encoder.encode(univ.Boolean(0)) == ints2octs((1, 1, 0))
+
+
+class BitStringEncoderTestCase(BaseTestCase):
+    def testShortMode(self):
+        assert encoder.encode(
+            univ.BitString((1, 0) * 5)
+        ) == ints2octs((3, 3, 6, 170, 128))
+
+    def testLongMode(self):
+        assert encoder.encode(univ.BitString((1, 0) * 501)) == ints2octs((3, 127, 6) + (170,) * 125 + (128,))
+
+
+class OctetStringEncoderTestCase(BaseTestCase):
+    def testShortMode(self):
+        assert encoder.encode(
+            univ.OctetString('Quick brown fox')
+        ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+    def testLongMode(self):
+        assert encoder.encode(
+            univ.OctetString('Q' * 1001)
+        ) == ints2octs((36, 128, 4, 130, 3, 232) + (81,) * 1000 + (4, 1, 81, 0, 0))
+
+
+class GeneralizedTimeEncoderTestCase(BaseTestCase):
+    #    def testExtraZeroInSeconds(self):
+    #        try:
+    #            assert encoder.encode(
+    #                useful.GeneralizedTime('20150501120112.10Z')
+    #            )
+    #        except PyAsn1Error:
+    #            pass
+    #        else:
+    #            assert 0, 'Meaningless trailing zero in fraction part tolerated'
+
+    def testLocalTimezone(self):
+        try:
+            assert encoder.encode(
+                useful.GeneralizedTime('20150501120112.1+0200')
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Local timezone tolerated'
+
+    def testMissingTimezone(self):
+        try:
+            assert encoder.encode(
+                useful.GeneralizedTime('20150501120112.1')
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Missing timezone tolerated'
+
+
+    def testDecimalCommaPoint(self):
+        try:
+            assert encoder.encode(
+                    useful.GeneralizedTime('20150501120112,1Z')
+             )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Decimal comma tolerated'
+
+    def testWithSubseconds(self):
+        assert encoder.encode(
+                    useful.GeneralizedTime('20170801120112.59Z')
+             ) == ints2octs((24, 18, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 46, 53, 57, 90))
+
+    def testWithSeconds(self):
+        assert encoder.encode(
+                    useful.GeneralizedTime('20170801120112Z')
+             ) == ints2octs((24, 15, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 90))
+
+    def testWithMinutes(self):
+        assert encoder.encode(
+                    useful.GeneralizedTime('201708011201Z')
+             ) == ints2octs((24, 13, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 90))
+
+
+class UTCTimeEncoderTestCase(BaseTestCase):
+    def testFractionOfSecond(self):
+        try:
+            assert encoder.encode(
+                useful.UTCTime('150501120112.10Z')
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Decimal point tolerated'
+
+    def testMissingTimezone(self):
+        try:
+            assert encoder.encode(
+                useful.UTCTime('150501120112')
+            ) == ints2octs((23, 13, 49, 53, 48, 53, 48, 49, 49, 50, 48, 49, 49, 50, 90))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Missing timezone tolerated'
+
+    def testLocalTimezone(self):
+        try:
+            assert encoder.encode(
+                useful.UTCTime('150501120112+0200')
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Local timezone tolerated'
+
+    def testWithSeconds(self):
+        assert encoder.encode(
+                    useful.UTCTime('990801120112Z')
+             ) == ints2octs((23, 13, 57, 57, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 90))
+
+    def testWithMinutes(self):
+        assert encoder.encode(
+                    useful.UTCTime('9908011201Z')
+             ) == ints2octs((23, 11, 57, 57, 48, 56, 48, 49, 49, 50, 48, 49, 90))
+
+
+class SequenceOfEncoderTestCase(BaseTestCase):
+    def testEmpty(self):
+        s = univ.SequenceOf()
+        assert encoder.encode(s) == ints2octs((48, 128, 0, 0))
+
+    def testDefMode1(self):
+        s = univ.SequenceOf()
+        s.append(univ.OctetString('a'))
+        s.append(univ.OctetString('ab'))
+        assert encoder.encode(s) == ints2octs((48, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0))
+
+    def testDefMode2(self):
+        s = univ.SequenceOf()
+        s.append(univ.OctetString('ab'))
+        s.append(univ.OctetString('a'))
+        assert encoder.encode(s) == ints2octs((48, 128, 4, 2, 97, 98, 4, 1, 97, 0, 0))
+
+    def testDefMode3(self):
+        s = univ.SequenceOf()
+        s.append(univ.OctetString('b'))
+        s.append(univ.OctetString('a'))
+        assert encoder.encode(s) == ints2octs((48, 128, 4, 1, 98, 4, 1, 97, 0, 0))
+
+    def testDefMode4(self):
+        s = univ.SequenceOf()
+        s.append(univ.OctetString('a'))
+        s.append(univ.OctetString('b'))
+        assert encoder.encode(s) == ints2octs((48, 128, 4, 1, 97, 4, 1, 98, 0, 0))
+
+
+class SequenceOfEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SequenceOf(componentType=univ.OctetString())
+
+    def testEmpty(self):
+        self.s.clear()
+        assert encoder.encode(self.s) == ints2octs((48, 128, 0, 0))
+
+    def testIndefMode1(self):
+        self.s.clear()
+        self.s.append('a')
+        self.s.append('ab')
+        assert encoder.encode(self.s) == ints2octs((48, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0))
+
+    def testIndefMode2(self):
+        self.s.clear()
+        self.s.append('ab')
+        self.s.append('a')
+        assert encoder.encode(self.s) == ints2octs((48, 128, 4, 2, 97, 98, 4, 1, 97, 0, 0))
+
+    def testIndefMode3(self):
+        self.s.clear()
+        self.s.append('b')
+        self.s.append('a')
+        assert encoder.encode(self.s) == ints2octs((48, 128, 4, 1, 98, 4, 1, 97, 0, 0))
+
+    def testIndefMode4(self):
+        self.s.clear()
+        self.s.append('a')
+        self.s.append('b')
+        assert encoder.encode(self.s) == ints2octs((48, 128, 4, 1, 97, 4, 1, 98, 0, 0))
+
+
+class SetOfEncoderTestCase(BaseTestCase):
+    def testEmpty(self):
+        s = univ.SetOf()
+        assert encoder.encode(s) == ints2octs((49, 128, 0, 0))
+
+    def testDefMode1(self):
+        s = univ.SetOf()
+        s.append(univ.OctetString('a'))
+        s.append(univ.OctetString('ab'))
+        assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0))
+
+    def testDefMode2(self):
+        s = univ.SetOf()
+        s.append(univ.OctetString('ab'))
+        s.append(univ.OctetString('a'))
+        assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0))
+
+    def testDefMode3(self):
+        s = univ.SetOf()
+        s.append(univ.OctetString('b'))
+        s.append(univ.OctetString('a'))
+        assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0))
+
+    def testDefMode4(self):
+        s = univ.SetOf()
+        s.append(univ.OctetString('a'))
+        s.append(univ.OctetString('b'))
+        assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0))
+
+
+class SetOfEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.SetOf(componentType=univ.OctetString())
+
+    def testEmpty(self):
+        self.s.clear()
+        assert encoder.encode(self.s) == ints2octs((49, 128, 0, 0))
+
+    def testIndefMode1(self):
+        self.s.clear()
+        self.s.append('a')
+        self.s.append('ab')
+
+        assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0))
+
+    def testIndefMode2(self):
+        self.s.clear()
+        self.s.append('ab')
+        self.s.append('a')
+
+        assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0))
+
+    def testIndefMode3(self):
+        self.s.clear()
+        self.s.append('b')
+        self.s.append('a')
+
+        assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0))
+
+    def testIndefMode4(self):
+        self.s.clear()
+        self.s.append('a')
+        self.s.append('b')
+
+        assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0))
+
+
+class SetEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Set()
+        self.s.setComponentByPosition(0, univ.Null(''))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testIndefMode(self):
+        assert encoder.encode(self.s) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+    def testWithOptionalIndefMode(self):
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+    def testWithDefaultedIndefMode(self):
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+
+class SetEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Set(componentType=namedtype.NamedTypes(
+            namedtype.NamedType('place-holder', univ.Null('')),
+            namedtype.OptionalNamedType('first-name', univ.OctetString()),
+            namedtype.DefaultedNamedType('age', univ.Integer(33))
+        ))
+
+    def __init(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0)
+
+    def __initWithOptional(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0)
+        self.s.setComponentByPosition(1, 'quick brown')
+
+    def __initWithDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0)
+        self.s.setComponentByPosition(2, 1)
+
+    def __initWithOptionalAndDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(''))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testIndefMode(self):
+        self.__init()
+        assert encoder.encode(self.s) == ints2octs((49, 128, 5, 0, 0, 0))
+
+    def testWithOptionalIndefMode(self):
+        self.__initWithOptional()
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+    def testWithDefaultedIndefMode(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((49, 128, 2, 1, 1, 5, 0, 0, 0))
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+
+class SetWithChoiceWithSchemaEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        c = univ.Choice(componentType=namedtype.NamedTypes(
+            namedtype.NamedType('actual', univ.Boolean(0))
+        ))
+        self.s = univ.Set(componentType=namedtype.NamedTypes(
+            namedtype.NamedType('place-holder', univ.Null('')),
+            namedtype.NamedType('status', c)
+        ))
+
+    def testIndefMode(self):
+        self.s.setComponentByPosition(0)
+        self.s.setComponentByName('status')
+        self.s.getComponentByName('status').setComponentByPosition(0, 1)
+        assert encoder.encode(self.s) == ints2octs((49, 128, 1, 1, 255, 5, 0, 0, 0))
+
+
+class SetWithTaggedChoiceEncoderTestCase(BaseTestCase):
+
+    def testWithUntaggedChoice(self):
+
+        c = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('premium', univ.Boolean())
+            )
+        )
+
+        s = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('customer', c)
+            )
+        )
+
+        s.setComponentByName('name', 'A')
+        s.getComponentByName('customer').setComponentByName('premium', True)
+
+        assert encoder.encode(s) == ints2octs((49, 128, 1, 1, 255, 4, 1, 65, 0, 0))
+
+    def testWithTaggedChoice(self):
+
+        c = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('premium', univ.Boolean())
+            )
+        ).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 7))
+
+        s = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('customer', c)
+            )
+        )
+
+        s.setComponentByName('name', 'A')
+        s.getComponentByName('customer').setComponentByName('premium', True)
+
+        assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 65, 167, 128, 1, 1, 255, 0, 0, 0, 0))
+
+
+class SetEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Set()
+        self.s.setComponentByPosition(0, univ.Null(''))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testIndefMode(self):
+        assert encoder.encode(self.s) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+    def testWithOptionalIndefMode(self):
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+    def testWithDefaultedIndefMode(self):
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+
+class SequenceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence()
+        self.s.setComponentByPosition(0, univ.Null(''))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testIndefMode(self):
+        assert encoder.encode(self.s) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+    def testWithOptionalIndefMode(self):
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+    def testWithDefaultedIndefMode(self):
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+
+class SequenceEncoderWithSchemaTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null('')),
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33))
+            )
+        )
+
+    def __init(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0)
+
+    def __initWithOptional(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0)
+        self.s.setComponentByPosition(1, 'quick brown')
+
+    def __initWithDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0)
+        self.s.setComponentByPosition(2, 1)
+
+    def __initWithOptionalAndDefaulted(self):
+        self.s.clear()
+        self.s.setComponentByPosition(0, univ.Null(''))
+        self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+        self.s.setComponentByPosition(2, univ.Integer(1))
+
+    def testIndefMode(self):
+        self.__init()
+        assert encoder.encode(self.s) == ints2octs((48, 128, 5, 0, 0, 0))
+
+    def testWithOptionalIndefMode(self):
+        self.__initWithOptional()
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+    def testWithDefaultedIndefMode(self):
+        self.__initWithDefaulted()
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0))
+
+    def testWithOptionalAndDefaultedIndefMode(self):
+        self.__initWithOptionalAndDefaulted()
+        assert encoder.encode(
+            self.s
+        ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+
+class NestedOptionalSequenceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        inner = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+
+        outerWithOptional = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('inner', inner),
+            )
+        )
+
+        outerWithDefault = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.DefaultedNamedType('inner', inner),
+            )
+        )
+
+        self.s1 = outerWithOptional
+        self.s2 = outerWithDefault
+
+    def __initOptionalWithDefaultAndOptional(self):
+        self.s1.clear()
+        self.s1[0][0] = 'test'
+        self.s1[0][1] = 123
+        return self.s1
+
+    def __initOptionalWithDefault(self):
+        self.s1.clear()
+        self.s1[0][1] = 123
+        return self.s1
+
+    def __initOptionalWithOptional(self):
+        self.s1.clear()
+        self.s1[0][0] = 'test'
+        return self.s1
+
+    def __initOptional(self):
+        self.s1.clear()
+        return self.s1
+
+    def __initDefaultWithDefaultAndOptional(self):
+        self.s2.clear()
+        self.s2[0][0] = 'test'
+        self.s2[0][1] = 123
+        return self.s2
+
+    def __initDefaultWithDefault(self):
+        self.s2.clear()
+        self.s2[0][0] = 'test'
+        return self.s2
+
+    def __initDefaultWithOptional(self):
+        self.s2.clear()
+        self.s2[0][1] = 123
+        return self.s2
+
+    def testOptionalWithDefaultAndOptional(self):
+        s = self.__initOptionalWithDefaultAndOptional()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0))
+
+    def testOptionalWithDefault(self):
+        s = self.__initOptionalWithDefault()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0))
+
+    def testOptionalWithOptional(self):
+        s = self.__initOptionalWithOptional()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+    def testOptional(self):
+        s = self.__initOptional()
+        assert encoder.encode(s) == ints2octs((48, 128, 0, 0))
+
+    def testDefaultWithDefaultAndOptional(self):
+        s = self.__initDefaultWithDefaultAndOptional()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0))
+
+    def testDefaultWithDefault(self):
+        s = self.__initDefaultWithDefault()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+    def testDefaultWithOptional(self):
+        s = self.__initDefaultWithOptional()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0))
+
+
+class NestedOptionalChoiceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        layer3 = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+
+        layer2 = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('inner', layer3),
+                namedtype.NamedType('first-name', univ.OctetString())
+            )
+        )
+
+        layer1 = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('inner', layer2),
+            )
+        )
+
+        self.s = layer1
+
+    def __initOptionalWithDefaultAndOptional(self):
+        self.s.clear()
+        self.s[0][0][0] = 'test'
+        self.s[0][0][1] = 123
+        return self.s
+
+    def __initOptionalWithDefault(self):
+        self.s.clear()
+        self.s[0][0][1] = 123
+        return self.s
+
+    def __initOptionalWithOptional(self):
+        self.s.clear()
+        self.s[0][0][0] = 'test'
+        return self.s
+
+    def __initOptional(self):
+        self.s.clear()
+        return self.s
+
+    def testOptionalWithDefaultAndOptional(self):
+        s = self.__initOptionalWithDefaultAndOptional()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0))
+
+    def testOptionalWithDefault(self):
+        s = self.__initOptionalWithDefault()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0))
+
+    def testOptionalWithOptional(self):
+        s = self.__initOptionalWithOptional()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+    def testOptional(self):
+        s = self.__initOptional()
+        assert encoder.encode(s) == ints2octs((48, 128, 0, 0))
+
+
+class NestedOptionalSequenceOfEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        layer2 = univ.SequenceOf(
+            componentType=univ.OctetString()
+        )
+
+        layer1 = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('inner', layer2),
+            )
+        )
+
+        self.s = layer1
+
+    def __initOptionalWithValue(self):
+        self.s.clear()
+        self.s[0][0] = 'test'
+        return self.s
+
+    def __initOptional(self):
+        self.s.clear()
+        return self.s
+
+    def testOptionalWithValue(self):
+        s = self.__initOptionalWithValue()
+        assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+    def testOptional(self):
+        s = self.__initOptional()
+        assert encoder.encode(s) == ints2octs((48, 128, 0, 0))
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/der/__init__.py b/tests/codec/der/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/tests/codec/der/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/tests/codec/der/__main__.py b/tests/codec/der/__main__.py
new file mode 100644
index 0000000..b253abb
--- /dev/null
+++ b/tests/codec/der/__main__.py
@@ -0,0 +1,20 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+suite = unittest.TestLoader().loadTestsFromNames(
+    ['tests.codec.der.test_encoder.suite',
+     'tests.codec.der.test_decoder.suite']
+)
+
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/der/test_decoder.py b/tests/codec/der/test_decoder.py
new file mode 100644
index 0000000..554e2e6
--- /dev/null
+++ b/tests/codec/der/test_decoder.py
@@ -0,0 +1,79 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.codec.der import decoder
+from pyasn1.compat.octets import ints2octs, null
+from pyasn1.error import PyAsn1Error
+
+
+class BitStringDecoderTestCase(BaseTestCase):
+    def testShortMode(self):
+        assert decoder.decode(
+            ints2octs((3, 127, 6) + (170,) * 125 + (128,))
+        ) == (((1, 0) * 501), null)
+
+    def testIndefMode(self):
+        try:
+            decoder.decode(
+                ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'indefinite length encoding tolerated'
+
+    def testDefModeChunked(self):
+        try:
+            assert decoder.decode(
+                ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'chunked encoding tolerated'
+
+
+class OctetStringDecoderTestCase(BaseTestCase):
+    def testShortMode(self):
+        assert decoder.decode(
+            '\004\017Quick brown fox'.encode()
+        ) == ('Quick brown fox'.encode(), ''.encode())
+
+    def testIndefMode(self):
+        try:
+            decoder.decode(
+                ints2octs((36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'indefinite length encoding tolerated'
+
+    def testChunkedMode(self):
+        try:
+            decoder.decode(
+                ints2octs((36, 23, 4, 2, 81, 117, 4, 2, 105, 99, 4, 2, 107, 32, 4, 2, 98, 114, 4, 2, 111, 119, 4, 1, 110))
+            )
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'chunked encoding tolerated'
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/der/test_encoder.py b/tests/codec/der/test_encoder.py
new file mode 100644
index 0000000..4c58df2
--- /dev/null
+++ b/tests/codec/der/test_encoder.py
@@ -0,0 +1,409 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import tag
+from pyasn1.type import namedtype
+from pyasn1.type import univ
+from pyasn1.codec.der import encoder
+from pyasn1.compat.octets import ints2octs
+
+
+class OctetStringEncoderTestCase(BaseTestCase):
+    def testDefModeShort(self):
+        assert encoder.encode(
+            univ.OctetString('Quick brown fox')
+        ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+    def testDefModeLong(self):
+        assert encoder.encode(
+            univ.OctetString('Q' * 10000)
+        ) == ints2octs((4, 130, 39, 16) + (81,) * 10000)
+
+
+class BitStringEncoderTestCase(BaseTestCase):
+    def testDefModeShort(self):
+        assert encoder.encode(
+            univ.BitString((1,))
+        ) == ints2octs((3, 2, 7, 128))
+
+    def testDefModeLong(self):
+        assert encoder.encode(
+            univ.BitString((1,) * 80000)
+        ) == ints2octs((3, 130, 39, 17, 0) + (255,) * 10000)
+
+
+class SetOfEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.s = univ.SetOf(componentType=univ.OctetString())
+
+    def testDefMode1(self):
+        self.s.clear()
+        self.s.append('a')
+        self.s.append('ab')
+
+        assert encoder.encode(self.s) == ints2octs((49, 7, 4, 1, 97, 4, 2, 97, 98))
+
+    def testDefMode2(self):
+        self.s.clear()
+        self.s.append('ab')
+        self.s.append('a')
+
+        assert encoder.encode(self.s) == ints2octs((49, 7, 4, 1, 97, 4, 2, 97, 98))
+
+    def testDefMode3(self):
+        self.s.clear()
+        self.s.append('b')
+        self.s.append('a')
+
+        assert encoder.encode(self.s) == ints2octs((49, 6, 4, 1, 97, 4, 1, 98))
+
+    def testDefMode4(self):
+        self.s.clear()
+        self.s.append('a')
+        self.s.append('b')
+
+        assert encoder.encode(self.s) == ints2octs((49, 6, 4, 1, 97, 4, 1, 98))
+
+
+class SetWithAlternatingChoiceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        c = univ.Choice(componentType=namedtype.NamedTypes(
+            namedtype.NamedType('name', univ.OctetString()),
+            namedtype.NamedType('amount', univ.Boolean()))
+        )
+
+        self.s = univ.Set(componentType=namedtype.NamedTypes(
+            namedtype.NamedType('value', univ.Integer(5)),
+            namedtype.NamedType('status', c))
+        )
+
+    def testComponentsOrdering1(self):
+        self.s.setComponentByName('status')
+        self.s.getComponentByName('status').setComponentByPosition(0, 'A')
+        assert encoder.encode(self.s) == ints2octs((49, 6, 2, 1, 5, 4, 1, 65))
+
+    def testComponentsOrdering2(self):
+        self.s.setComponentByName('status')
+        self.s.getComponentByName('status').setComponentByPosition(1, True)
+        assert encoder.encode(self.s) == ints2octs((49, 6, 1, 1, 255, 2, 1, 5))
+
+
+class SetWithTaggedChoiceEncoderTestCase(BaseTestCase):
+
+    def testWithUntaggedChoice(self):
+
+        c = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('premium', univ.Boolean())
+            )
+        )
+
+        s = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('customer', c)
+            )
+        )
+
+        s.setComponentByName('name', 'A')
+        s.getComponentByName('customer').setComponentByName('premium', True)
+
+        assert encoder.encode(s) == ints2octs((49, 6, 1, 1, 255, 4, 1, 65))
+
+    def testWithTaggedChoice(self):
+
+        c = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('premium', univ.Boolean())
+            )
+        ).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 7))
+
+        s = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('customer', c)
+            )
+        )
+
+        s.setComponentByName('name', 'A')
+        s.getComponentByName('customer').setComponentByName('premium', True)
+
+        assert encoder.encode(s) == ints2octs((49, 8, 4, 1, 65, 167, 3, 1, 1, 255))
+
+
+class NestedOptionalSequenceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        inner = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+
+        outerWithOptional = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('inner', inner),
+            )
+        )
+
+        outerWithDefault = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.DefaultedNamedType('inner', inner),
+            )
+        )
+
+        self.s1 = outerWithOptional
+        self.s2 = outerWithDefault
+
+    def __initOptionalWithDefaultAndOptional(self):
+        self.s1.clear()
+        self.s1[0][0] = 'test'
+        self.s1[0][1] = 123
+        return self.s1
+
+    def __initOptionalWithDefault(self):
+        self.s1.clear()
+        self.s1[0][1] = 123
+        return self.s1
+
+    def __initOptionalWithOptional(self):
+        self.s1.clear()
+        self.s1[0][0] = 'test'
+        return self.s1
+
+    def __initOptional(self):
+        self.s1.clear()
+        return self.s1
+
+    def __initDefaultWithDefaultAndOptional(self):
+        self.s2.clear()
+        self.s2[0][0] = 'test'
+        self.s2[0][1] = 123
+        return self.s2
+
+    def __initDefaultWithDefault(self):
+        self.s2.clear()
+        self.s2[0][0] = 'test'
+        return self.s2
+
+    def __initDefaultWithOptional(self):
+        self.s2.clear()
+        self.s2[0][1] = 123
+        return self.s2
+
+    def testDefModeOptionalWithDefaultAndOptional(self):
+        s = self.__initOptionalWithDefaultAndOptional()
+        assert encoder.encode(s) == ints2octs((48, 11, 48, 9, 4, 4, 116, 101, 115, 116, 2, 1, 123))
+
+    def testDefModeOptionalWithDefault(self):
+        s = self.__initOptionalWithDefault()
+        assert encoder.encode(s) == ints2octs((48, 5, 48, 3, 2, 1, 123))
+
+    def testDefModeOptionalWithOptional(self):
+        s = self.__initOptionalWithOptional()
+        assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116))
+
+    def testDefModeOptional(self):
+        s = self.__initOptional()
+        assert encoder.encode(s) == ints2octs((48, 0))
+
+    def testDefModeDefaultWithDefaultAndOptional(self):
+        s = self.__initDefaultWithDefaultAndOptional()
+        assert encoder.encode(s) == ints2octs((48, 11, 48, 9, 4, 4, 116, 101, 115, 116, 2, 1, 123))
+
+    def testDefModeDefaultWithDefault(self):
+        s = self.__initDefaultWithDefault()
+        assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116))
+
+    def testDefModeDefaultWithOptional(self):
+        s = self.__initDefaultWithOptional()
+        assert encoder.encode(s) == ints2octs((48, 5, 48, 3, 2, 1, 123))
+
+
+class NestedOptionalChoiceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        layer3 = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('first-name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(33)),
+            )
+        )
+
+        layer2 = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('inner', layer3),
+                namedtype.NamedType('first-name', univ.OctetString())
+            )
+        )
+
+        layer1 = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('inner', layer2),
+            )
+        )
+
+        self.s = layer1
+
+    def __initOptionalWithDefaultAndOptional(self):
+        self.s.clear()
+        self.s[0][0][0] = 'test'
+        self.s[0][0][1] = 123
+        return self.s
+
+    def __initOptionalWithDefault(self):
+        self.s.clear()
+        self.s[0][0][1] = 123
+        return self.s
+
+    def __initOptionalWithOptional(self):
+        self.s.clear()
+        self.s[0][0][0] = 'test'
+        return self.s
+
+    def __initOptional(self):
+        self.s.clear()
+        return self.s
+
+    def testDefModeOptionalWithDefaultAndOptional(self):
+        s = self.__initOptionalWithDefaultAndOptional()
+        assert encoder.encode(s) == ints2octs((48, 11, 48, 9, 4, 4, 116, 101, 115, 116, 2, 1, 123))
+
+    def testDefModeOptionalWithDefault(self):
+        s = self.__initOptionalWithDefault()
+        assert encoder.encode(s) == ints2octs((48, 5, 48, 3, 2, 1, 123))
+
+    def testDefModeOptionalWithOptional(self):
+        s = self.__initOptionalWithOptional()
+        assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116))
+
+    def testDefModeOptional(self):
+        s = self.__initOptional()
+        assert encoder.encode(s) == ints2octs((48, 0))
+
+
+class NestedOptionalSequenceOfEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        layer2 = univ.SequenceOf(
+            componentType=univ.OctetString()
+        )
+
+        layer1 = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('inner', layer2),
+            )
+        )
+
+        self.s = layer1
+
+    def __initOptionalWithValue(self):
+        self.s.clear()
+        self.s[0][0] = 'test'
+        return self.s
+
+    def __initOptional(self):
+        self.s.clear()
+        return self.s
+
+    def testDefModeOptionalWithValue(self):
+        s = self.__initOptionalWithValue()
+        assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116))
+
+    def testDefModeOptional(self):
+        s = self.__initOptional()
+        assert encoder.encode(s) == ints2octs((48, 0))
+
+
+class EmptyInnerFieldOfSequenceEncoderTestCase(BaseTestCase):
+
+    def testInitializedOptionalNullIsEncoded(self):
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('null', univ.Null())
+            )
+        )
+
+        self.s.clear()
+        self.s[0] = ''
+        assert encoder.encode(self.s) == ints2octs((48, 2, 5, 0))
+
+    def testUninitializedOptionalNullIsNotEncoded(self):
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('null', univ.Null())
+            )
+        )
+
+        self.s.clear()
+        assert encoder.encode(self.s) == ints2octs((48, 0))
+
+    def testInitializedDefaultNullIsNotEncoded(self):
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.DefaultedNamedType('null', univ.Null(''))
+            )
+        )
+
+        self.s.clear()
+        self.s[0] = ''
+        assert encoder.encode(self.s) == ints2octs((48, 0))
+
+    def testInitializedOptionalOctetStringIsEncoded(self):
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('str', univ.OctetString())
+            )
+        )
+
+        self.s.clear()
+        self.s[0] = ''
+        assert encoder.encode(self.s) == ints2octs((48, 2, 4, 0))
+
+    def testUninitializedOptionalOctetStringIsNotEncoded(self):
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.OptionalNamedType('str', univ.OctetString())
+            )
+        )
+
+        self.s.clear()
+        assert encoder.encode(self.s) == ints2octs((48, 0))
+
+    def testInitializedDefaultOctetStringIsNotEncoded(self):
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.DefaultedNamedType('str', univ.OctetString(''))
+            )
+        )
+
+        self.s.clear()
+        self.s[0] = ''
+        assert encoder.encode(self.s) == ints2octs((48, 0))
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/native/__init__.py b/tests/codec/native/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/tests/codec/native/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/tests/codec/native/__main__.py b/tests/codec/native/__main__.py
new file mode 100644
index 0000000..efd4303
--- /dev/null
+++ b/tests/codec/native/__main__.py
@@ -0,0 +1,19 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+suite = unittest.TestLoader().loadTestsFromNames(
+    ['tests.codec.native.test_encoder.suite',
+     'tests.codec.native.test_decoder.suite']
+)
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/native/test_decoder.py b/tests/codec/native/test_decoder.py
new file mode 100644
index 0000000..7bdfff8
--- /dev/null
+++ b/tests/codec/native/test_decoder.py
@@ -0,0 +1,125 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import namedtype
+from pyasn1.type import univ
+from pyasn1.codec.native import decoder
+from pyasn1.error import PyAsn1Error
+
+
+class BadAsn1SpecTestCase(BaseTestCase):
+    def testBadSpec(self):
+        try:
+            decoder.decode('', asn1Spec='not an Asn1Item')
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Invalid asn1Spec accepted'
+
+
+class IntegerDecoderTestCase(BaseTestCase):
+    def testPosInt(self):
+        assert decoder.decode(12, asn1Spec=univ.Integer()) == univ.Integer(12)
+
+    def testNegInt(self):
+        assert decoder.decode(-12, asn1Spec=univ.Integer()) == univ.Integer(-12)
+
+
+class BooleanDecoderTestCase(BaseTestCase):
+    def testTrue(self):
+        assert decoder.decode(True, asn1Spec=univ.Boolean()) == univ.Boolean(True)
+
+    def testTrueNeg(self):
+        assert decoder.decode(False, asn1Spec=univ.Boolean()) == univ.Boolean(False)
+
+
+class BitStringDecoderTestCase(BaseTestCase):
+    def testSimple(self):
+        assert decoder.decode('11111111', asn1Spec=univ.BitString()) == univ.BitString(hexValue='ff')
+
+
+class OctetStringDecoderTestCase(BaseTestCase):
+    def testSimple(self):
+        assert decoder.decode('Quick brown fox', asn1Spec=univ.OctetString()) == univ.OctetString('Quick brown fox')
+
+
+class NullDecoderTestCase(BaseTestCase):
+    def testNull(self):
+        assert decoder.decode(None, asn1Spec=univ.Null()) == univ.Null('')
+
+
+class ObjectIdentifierDecoderTestCase(BaseTestCase):
+    def testOne(self):
+        assert decoder.decode('1.3.6.11', asn1Spec=univ.ObjectIdentifier()) == univ.ObjectIdentifier('1.3.6.11')
+
+
+class RealDecoderTestCase(BaseTestCase):
+    def testSimple(self):
+        assert decoder.decode(1.33, asn1Spec=univ.Real()) == univ.Real(1.33)
+
+
+class SequenceDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.s = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null()),
+                namedtype.NamedType('first-name', univ.OctetString()),
+                namedtype.NamedType('age', univ.Integer(33))
+            )
+        )
+
+    def testSimple(self):
+        s = self.s.clone()
+        s[0] = univ.Null('')
+        s[1] = univ.OctetString('xx')
+        s[2] = univ.Integer(33)
+        assert decoder.decode({'place-holder': None, 'first-name': 'xx', 'age': 33}, asn1Spec=self.s) == s
+
+
+class ChoiceDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.s = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null()),
+                namedtype.NamedType('first-name', univ.OctetString()),
+                namedtype.NamedType('age', univ.Integer(33))
+            )
+        )
+
+    def testSimple(self):
+        s = self.s.clone()
+        s[1] = univ.OctetString('xx')
+        assert decoder.decode({'first-name': 'xx'}, asn1Spec=self.s) == s
+
+
+class AnyDecoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.s = univ.Any()
+
+    def testSimple(self):
+        assert decoder.decode('fox', asn1Spec=univ.Any()) == univ.Any('fox')
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/codec/native/test_encoder.py b/tests/codec/native/test_encoder.py
new file mode 100644
index 0000000..8c35b55
--- /dev/null
+++ b/tests/codec/native/test_encoder.py
@@ -0,0 +1,146 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import namedtype
+from pyasn1.type import univ
+from pyasn1.codec.native import encoder
+from pyasn1.compat.octets import str2octs
+from pyasn1.error import PyAsn1Error
+
+
+class BadAsn1SpecTestCase(BaseTestCase):
+    def testBadValueType(self):
+        try:
+            encoder.encode('not an Asn1Item')
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert 0, 'Invalid value type accepted'
+
+
+class IntegerEncoderTestCase(BaseTestCase):
+    def testPosInt(self):
+        assert encoder.encode(univ.Integer(12)) == 12
+
+    def testNegInt(self):
+        assert encoder.encode(univ.Integer(-12)) == -12
+
+
+class BooleanEncoderTestCase(BaseTestCase):
+    def testTrue(self):
+        assert encoder.encode(univ.Boolean(1)) is True
+
+    def testFalse(self):
+        assert encoder.encode(univ.Boolean(0)) is False
+
+
+class BitStringEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.b = univ.BitString((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1))
+
+    def testValue(self):
+        assert encoder.encode(self.b) == '101010011000101'
+
+
+class OctetStringEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.o = univ.OctetString('Quick brown fox')
+
+    def testValue(self):
+        assert encoder.encode(self.o) == str2octs('Quick brown fox')
+
+
+class NullEncoderTestCase(BaseTestCase):
+    def testNull(self):
+        assert encoder.encode(univ.Null('')) is None
+
+
+class ObjectIdentifierEncoderTestCase(BaseTestCase):
+    def testOne(self):
+        assert encoder.encode(univ.ObjectIdentifier((1, 3, 6, 0, 12345))) == '1.3.6.0.12345'
+
+
+class RealEncoderTestCase(BaseTestCase):
+    def testChar(self):
+        assert encoder.encode(univ.Real((123, 10, 11))) == 1.23e+13
+
+    def testPlusInf(self):
+        assert encoder.encode(univ.Real('inf')) == float('inf')
+
+    def testMinusInf(self):
+        assert encoder.encode(univ.Real('-inf')) == float('-inf')
+
+
+class SequenceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.s = univ.Sequence(componentType=namedtype.NamedTypes(
+            namedtype.NamedType('place-holder', univ.Null('')),
+            namedtype.OptionalNamedType('first-name', univ.OctetString('')),
+            namedtype.DefaultedNamedType('age', univ.Integer(33)),
+        ))
+
+    def testSimple(self):
+        s = self.s.clone()
+        s[0] = univ.Null('')
+        s[1] = 'abc'
+        s[2] = 123
+        assert encoder.encode(s) == {'place-holder': None, 'first-name': str2octs('abc'), 'age': 123}
+
+
+class ChoiceEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.s = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('place-holder', univ.Null('')),
+                namedtype.NamedType('number', univ.Integer(0)),
+                namedtype.NamedType('string', univ.OctetString())
+           )
+        )
+
+    def testEmpty(self):
+        try:
+            encoder.encode(self.s)
+        except PyAsn1Error:
+            pass
+        else:
+            assert False, 'encoded unset choice'
+
+    def testFilled(self):
+        self.s.setComponentByPosition(0, univ.Null(''))
+        assert encoder.encode(self.s) == {'place-holder': None}
+
+
+class AnyEncoderTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s = univ.Any(encoder.encode(univ.OctetString('fox')))
+
+    def testSimple(self):
+        assert encoder.encode(self.s) == str2octs('fox')
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/compat/__init__.py b/tests/compat/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/tests/compat/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/tests/compat/__main__.py b/tests/compat/__main__.py
new file mode 100644
index 0000000..225ad90
--- /dev/null
+++ b/tests/compat/__main__.py
@@ -0,0 +1,21 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+suite = unittest.TestLoader().loadTestsFromNames(
+    ['tests.compat.test_binary.suite',
+     'tests.compat.test_integer.suite',
+     'tests.compat.test_octets.suite']
+)
+
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/compat/test_binary.py b/tests/compat/test_binary.py
new file mode 100644
index 0000000..4fa1d64
--- /dev/null
+++ b/tests/compat/test_binary.py
@@ -0,0 +1,56 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.compat import binary
+
+
+class BinaryTestCase(BaseTestCase):
+
+    def test_bin_zero(self):
+        assert '0b0' == binary.bin(0)
+
+
+    def test_bin_noarg(self):
+        try:
+            binary.bin()
+
+        except TypeError:
+            pass
+
+        except:
+            assert 0, 'bin() tolerates no arguments'
+
+
+    def test_bin_allones(self):
+        assert '0b1111111111111111111111111111111111111111111111111111111111111111' == binary.bin(0xffffffffffffffff)
+
+
+    def test_bin_allzeros(self):
+        assert '0b0' == binary.bin(0x0000000)
+
+
+
+    def test_bin_pos(self):
+        assert '0b1000000010000000100000001' == binary.bin(0x01010101)
+
+
+    def test_bin_neg(self):
+        assert '-0b1000000010000000100000001' == binary.bin(-0x01010101)
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/compat/test_integer.py b/tests/compat/test_integer.py
new file mode 100644
index 0000000..3f16f7a
--- /dev/null
+++ b/tests/compat/test_integer.py
@@ -0,0 +1,53 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.compat import integer
+
+
+class IntegerTestCase(BaseTestCase):
+
+    if sys.version_info[0] > 2:
+
+        def test_from_bytes_zero(self):
+            assert 0 == integer.from_bytes(bytes([0]), signed=False)
+
+        def test_from_bytes_unsigned(self):
+            assert -66051 == integer.from_bytes(bytes([254, 253, 253]), signed=True)
+
+        def test_from_bytes_signed(self):
+            assert 66051 == integer.from_bytes(bytes([0, 1, 2, 3]), signed=False)
+
+        def test_from_bytes_empty(self):
+            assert 0 == integer.from_bytes(bytes([]))
+
+    else:
+
+        def test_from_bytes_zero(self):
+            assert 0 == integer.from_bytes('\x00', signed=False)
+
+        def test_from_bytes_unsigned(self):
+            assert -66051 == integer.from_bytes('\xfe\xfd\xfd', signed=True)
+
+        def test_from_bytes_signed(self):
+            assert 66051 == integer.from_bytes('\x01\x02\x03', signed=False)
+
+        def test_from_bytes_empty(self):
+            assert 0 == integer.from_bytes('')
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/compat/test_octets.py b/tests/compat/test_octets.py
new file mode 100644
index 0000000..e552526
--- /dev/null
+++ b/tests/compat/test_octets.py
@@ -0,0 +1,117 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.compat import octets
+
+
+class OctetsTestCase(BaseTestCase):
+
+    if sys.version_info[0] > 2:
+
+        def test_ints2octs(self):
+            assert [1, 2, 3] == list(octets.ints2octs([1, 2, 3]))
+
+        def test_ints2octs_empty(self):
+            assert not octets.ints2octs([])
+
+        def test_int2oct(self):
+            assert [12] == list(octets.int2oct(12))
+
+        def test_octs2ints(self):
+            assert [1, 2, 3] == list(octets.octs2ints(bytes([1, 2, 3])))
+
+        def test_octs2ints_empty(self):
+            assert not octets.octs2ints(bytes([]))
+
+        def test_oct2int(self):
+            assert 12 == octets.oct2int(bytes([12]))[0]
+
+        def test_str2octs(self):
+            assert bytes([1, 2, 3]) == octets.str2octs('\x01\x02\x03')
+
+        def test_str2octs_empty(self):
+            assert not octets.str2octs('')
+
+        def test_octs2str(self):
+            assert '\x01\x02\x03' == octets.octs2str(bytes([1, 2, 3]))
+
+        def test_octs2str_empty(self):
+            assert not octets.octs2str(bytes([]))
+
+        def test_isOctetsType(self):
+            assert octets.isOctetsType('abc') == False
+            assert octets.isOctetsType(123) == False
+            assert octets.isOctetsType(bytes()) == True
+
+        def test_isStringType(self):
+            assert octets.isStringType('abc') == True
+            assert octets.isStringType(123) == False
+            assert octets.isStringType(bytes()) == False
+
+        def test_ensureString(self):
+            assert 'abc'.encode() == octets.ensureString('abc'.encode())
+            assert bytes([1, 2, 3]) == octets.ensureString([1, 2, 3])
+
+    else:
+
+        def test_ints2octs(self):
+            assert '\x01\x02\x03' == octets.ints2octs([1, 2, 3])
+
+        def test_ints2octs_empty(self):
+            assert not octets.ints2octs([])
+
+        def test_int2oct(self):
+            assert '\x0c' == octets.int2oct(12)
+
+        def test_octs2ints(self):
+            assert [1, 2, 3] == octets.octs2ints('\x01\x02\x03')
+
+        def test_octs2ints_empty(self):
+            assert not octets.octs2ints('')
+
+        def test_oct2int(self):
+            assert 12 == octets.oct2int('\x0c')
+
+        def test_str2octs(self):
+            assert '\x01\x02\x03' == octets.str2octs('\x01\x02\x03')
+
+        def test_str2octs_empty(self):
+            assert not octets.str2octs('')
+
+        def test_octs2str(self):
+            assert '\x01\x02\x03' == octets.octs2str('\x01\x02\x03')
+
+        def test_octs2str_empty(self):
+            assert not octets.octs2str('')
+
+        def test_isOctetsType(self):
+            assert octets.isOctetsType('abc') == True
+            assert octets.isOctetsType(123) == False
+            assert octets.isOctetsType(unicode('abc')) == False
+
+        def test_isStringType(self):
+            assert octets.isStringType('abc') == True
+            assert octets.isStringType(123) == False
+            assert octets.isStringType(unicode('abc')) == True
+
+        def test_ensureString(self):
+            assert 'abc' == octets.ensureString('abc')
+            assert '123' == octets.ensureString(123)
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/test_debug.py b/tests/test_debug.py
new file mode 100644
index 0000000..94a867e
--- /dev/null
+++ b/tests/test_debug.py
@@ -0,0 +1,42 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1 import debug
+from pyasn1 import error
+
+class DebugCaseBase(BaseTestCase):
+    def testKnownFlags(self):
+        debug.setLogger(0)
+        debug.setLogger(debug.Debug('all', 'encoder', 'decoder'))
+        debug.setLogger(0)
+
+    def testUnknownFlags(self):
+        try:
+            debug.setLogger(debug.Debug('all', 'unknown', loggerName='xxx'))
+
+        except error.PyAsn1Error:
+            debug.setLogger(0)
+            return
+
+        else:
+            debug.setLogger(0)
+            assert 0, 'unknown debug flag tolerated'
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/type/__init__.py b/tests/type/__init__.py
new file mode 100644
index 0000000..8c3066b
--- /dev/null
+++ b/tests/type/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/tests/type/__main__.py b/tests/type/__main__.py
new file mode 100644
index 0000000..b88bff9
--- /dev/null
+++ b/tests/type/__main__.py
@@ -0,0 +1,26 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+suite = unittest.TestLoader().loadTestsFromNames(
+    ['tests.type.test_constraint.suite',
+     'tests.type.test_opentype.suite',
+     'tests.type.test_namedtype.suite',
+     'tests.type.test_namedval.suite',
+     'tests.type.test_tag.suite',
+     'tests.type.test_univ.suite',
+     'tests.type.test_char.suite',
+     'tests.type.test_useful.suite']
+)
+
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/type/test_char.py b/tests/type/test_char.py
new file mode 100644
index 0000000..abae715
--- /dev/null
+++ b/tests/type/test_char.py
@@ -0,0 +1,175 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import pickle
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import char
+from pyasn1.type import univ
+from pyasn1.type import constraint
+from pyasn1.compat.octets import ints2octs
+from pyasn1.error import PyAsn1Error
+
+
+class AbstractStringTestCase(object):
+
+    initializer = ()
+    encoding = 'us-ascii'
+    asn1Type = None
+
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.asn1String = self.asn1Type(ints2octs(self.initializer), encoding=self.encoding)
+        self.pythonString = ints2octs(self.initializer).decode(self.encoding)
+
+    def testUnicode(self):
+        assert self.asn1String == self.pythonString, 'unicode init fails'
+
+    def testLength(self):
+        assert len(self.asn1String) == len(self.pythonString), 'unicode len() fails'
+
+    def testSizeConstraint(self):
+        asn1Spec = self.asn1Type(subtypeSpec=constraint.ValueSizeConstraint(1, 1))
+
+        try:
+            asn1Spec.clone(self.pythonString)
+        except PyAsn1Error:
+            pass
+        else:
+            assert False, 'Size constraint tolerated'
+
+        try:
+            asn1Spec.clone(self.pythonString[0])
+        except PyAsn1Error:
+            assert False, 'Size constraint failed'
+
+    def testSerialised(self):
+        if sys.version_info[0] < 3:
+            assert str(self.asn1String) == self.pythonString.encode(self.encoding), '__str__() fails'
+        else:
+            assert bytes(self.asn1String) == self.pythonString.encode(self.encoding), '__str__() fails'
+
+    def testPrintable(self):
+        if sys.version_info[0] < 3:
+            assert unicode(self.asn1String) == self.pythonString, '__str__() fails'
+        else:
+            assert str(self.asn1String) == self.pythonString, '__str__() fails'
+
+    def testInit(self):
+        assert self.asn1Type(self.pythonString) == self.pythonString
+        assert self.asn1Type(self.pythonString.encode(self.encoding)) == self.pythonString
+        assert self.asn1Type(univ.OctetString(self.pythonString.encode(self.encoding))) == self.pythonString
+        assert self.asn1Type(self.asn1Type(self.pythonString)) == self.pythonString
+        assert self.asn1Type(self.initializer, encoding=self.encoding) == self.pythonString
+
+    def testInitFromAsn1(self):
+        assert self.asn1Type(self.asn1Type(self.pythonString)) == self.pythonString
+        assert self.asn1Type(univ.OctetString(self.pythonString.encode(self.encoding), encoding=self.encoding)) == self.pythonString
+
+    def testAsOctets(self):
+        assert self.asn1String.asOctets() == self.pythonString.encode(self.encoding), 'testAsOctets() fails'
+
+    def testAsNumbers(self):
+        assert self.asn1String.asNumbers() == self.initializer, 'testAsNumbers() fails'
+
+    def testSeq(self):
+        assert self.asn1String[0] == self.pythonString[0], '__getitem__() fails'
+
+    def testEmpty(self):
+        try:
+            str(self.asn1Type())
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Value operation on ASN1 type tolerated'
+
+    def testAdd(self):
+        assert self.asn1String + self.pythonString.encode(self.encoding) == self.pythonString + self.pythonString, '__add__() fails'
+
+    def testRadd(self):
+        assert self.pythonString.encode(self.encoding) + self.asn1String == self.pythonString + self.pythonString, '__radd__() fails'
+
+    def testMul(self):
+        assert self.asn1String * 2 == self.pythonString * 2, '__mul__() fails'
+
+    def testRmul(self):
+        assert 2 * self.asn1String == 2 * self.pythonString, '__rmul__() fails'
+
+    def testContains(self):
+        assert self.pythonString in self.asn1String
+        assert self.pythonString + self.pythonString not in self.asn1String
+
+    if sys.version_info[:2] > (2, 4):
+        def testReverse(self):
+            assert list(reversed(self.asn1String)) == list(reversed(self.pythonString))
+
+    def testSchemaPickling(self):
+        old_asn1 = self.asn1Type()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == self.asn1Type
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = self.asn1String
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1 == self.asn1String
+
+
+class VisibleStringTestCase(AbstractStringTestCase, BaseTestCase):
+
+    initializer = (97, 102)
+    encoding = 'us-ascii'
+    asn1Type = char.VisibleString
+
+
+class GeneralStringTestCase(AbstractStringTestCase, BaseTestCase):
+
+    initializer = (169, 174)
+    encoding = 'iso-8859-1'
+    asn1Type = char.GeneralString
+
+
+class UTF8StringTestCase(AbstractStringTestCase, BaseTestCase):
+
+    initializer = (209, 132, 208, 176)
+    encoding = 'utf-8'
+    asn1Type = char.UTF8String
+
+
+class BMPStringTestCase(AbstractStringTestCase, BaseTestCase):
+
+    initializer = (4, 48, 4, 68)
+    encoding = 'utf-16-be'
+    asn1Type = char.BMPString
+
+
+if sys.version_info[0] > 2:
+
+    # Somehow comparison of UTF-32 encoded strings does not work in Py2
+
+    class UniversalStringTestCase(AbstractStringTestCase, BaseTestCase):
+        initializer = (0, 0, 4, 48, 0, 0, 4, 68)
+        encoding = 'utf-32-be'
+        asn1Type = char.UniversalString
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/type/test_constraint.py b/tests/type/test_constraint.py
new file mode 100644
index 0000000..2cdc66e
--- /dev/null
+++ b/tests/type/test_constraint.py
@@ -0,0 +1,335 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import constraint
+from pyasn1.type import error
+
+
+class SingleValueConstraintTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.c1 = constraint.SingleValueConstraint(1, 2)
+        self.c2 = constraint.SingleValueConstraint(3, 4)
+
+    def testCmp(self):
+        assert self.c1 == self.c1, 'comparation fails'
+
+    def testHash(self):
+        assert hash(self.c1) != hash(self.c2), 'hash() fails'
+
+    def testGoodVal(self):
+        try:
+            self.c1(1)
+
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        try:
+            self.c1(4)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+
+class ContainedSubtypeConstraintTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.c1 = constraint.ContainedSubtypeConstraint(
+            constraint.SingleValueConstraint(12)
+        )
+
+    def testGoodVal(self):
+        try:
+            self.c1(12)
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        try:
+            self.c1(4)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+
+class ValueRangeConstraintTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.c1 = constraint.ValueRangeConstraint(1, 4)
+
+    def testGoodVal(self):
+        try:
+            self.c1(1)
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        try:
+            self.c1(-5)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+
+class ValueSizeConstraintTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.c1 = constraint.ValueSizeConstraint(1, 2)
+
+    def testGoodVal(self):
+        try:
+            self.c1('a')
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        try:
+            self.c1('abc')
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+
+class PermittedAlphabetConstraintTestCase(SingleValueConstraintTestCase):
+    def setUp(self):
+        self.c1 = constraint.PermittedAlphabetConstraint('A', 'B', 'C')
+        self.c2 = constraint.PermittedAlphabetConstraint('DEF')
+
+    def testGoodVal(self):
+        try:
+            self.c1('A')
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        try:
+            self.c1('E')
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+
+class ConstraintsIntersectionTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.c1 = constraint.ConstraintsIntersection(
+            constraint.SingleValueConstraint(4),
+            constraint.ValueRangeConstraint(2, 4)
+        )
+
+    def testCmp1(self):
+        assert constraint.SingleValueConstraint(4) in self.c1, '__cmp__() fails'
+
+    def testCmp2(self):
+        assert constraint.SingleValueConstraint(5) not in self.c1, \
+            '__cmp__() fails'
+
+    def testCmp3(self):
+        c = constraint.ConstraintsUnion(constraint.ConstraintsIntersection(
+            constraint.SingleValueConstraint(4),
+            constraint.ValueRangeConstraint(2, 4))
+        )
+        assert self.c1 in c, '__cmp__() fails'
+
+    def testCmp4(self):
+        c = constraint.ConstraintsUnion(
+            constraint.ConstraintsIntersection(constraint.SingleValueConstraint(5))
+        )
+        assert self.c1 not in c, '__cmp__() fails'
+
+    def testGoodVal(self):
+        try:
+            self.c1(4)
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        try:
+            self.c1(-5)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+
+class InnerTypeConstraintTestCase(BaseTestCase):
+    def testConst1(self):
+        c = constraint.InnerTypeConstraint(
+            constraint.SingleValueConstraint(4)
+        )
+        try:
+            c(4, 32)
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+        try:
+            c(5, 32)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+    def testConst2(self):
+        c = constraint.InnerTypeConstraint(
+            (0, constraint.SingleValueConstraint(4), 'PRESENT'),
+            (1, constraint.SingleValueConstraint(4), 'ABSENT')
+        )
+        try:
+            c(4, 0)
+        except error.ValueConstraintError:
+            raise
+            assert 0, 'constraint check fails'
+        try:
+            c(4, 1)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+        try:
+            c(3, 0)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+        # Constraints compositions
+
+
+class ConstraintsIntersectionRangeTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.c1 = constraint.ConstraintsIntersection(
+            constraint.ValueRangeConstraint(1, 9),
+            constraint.ValueRangeConstraint(2, 5)
+        )
+
+    def testGoodVal(self):
+        try:
+            self.c1(3)
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        try:
+            self.c1(0)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+
+class ConstraintsUnionTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.c1 = constraint.ConstraintsUnion(
+            constraint.SingleValueConstraint(5),
+            constraint.ValueRangeConstraint(1, 3)
+        )
+
+    def testGoodVal(self):
+        try:
+            self.c1(2)
+            self.c1(5)
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        try:
+            self.c1(-5)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+
+class ConstraintsExclusionTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.c1 = constraint.ConstraintsExclusion(
+            constraint.ValueRangeConstraint(2, 4)
+        )
+
+    def testGoodVal(self):
+        try:
+            self.c1(6)
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        try:
+            self.c1(2)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint check fails'
+
+
+# Constraints derivations
+
+class DirectDerivationTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.c1 = constraint.SingleValueConstraint(5)
+
+        self.c2 = constraint.ConstraintsUnion(
+            self.c1, constraint.ValueRangeConstraint(1, 3)
+        )
+
+    def testGoodVal(self):
+        assert self.c1.isSuperTypeOf(self.c2), 'isSuperTypeOf failed'
+        assert not self.c1.isSubTypeOf(self.c2), 'isSubTypeOf failed'
+
+    def testBadVal(self):
+        assert not self.c2.isSuperTypeOf(self.c1), 'isSuperTypeOf failed'
+        assert self.c2.isSubTypeOf(self.c1), 'isSubTypeOf failed'
+
+
+class IndirectDerivationTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.c1 = constraint.ConstraintsIntersection(
+            constraint.ValueRangeConstraint(1, 30)
+        )
+
+        self.c2 = constraint.ConstraintsIntersection(
+            self.c1, constraint.ValueRangeConstraint(1, 20)
+        )
+
+        self.c2 = constraint.ConstraintsIntersection(
+            self.c2, constraint.ValueRangeConstraint(1, 10)
+        )
+
+    def testGoodVal(self):
+        assert self.c1.isSuperTypeOf(self.c2), 'isSuperTypeOf failed'
+        assert not self.c1.isSubTypeOf(self.c2), 'isSubTypeOf failed'
+
+    def testBadVal(self):
+        assert not self.c2.isSuperTypeOf(self.c1), 'isSuperTypeOf failed'
+        assert self.c2.isSubTypeOf(self.c1), 'isSubTypeOf failed'
+
+# TODO: how to apply size constraints to constructed types?
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/type/test_namedtype.py b/tests/type/test_namedtype.py
new file mode 100644
index 0000000..e8fda88
--- /dev/null
+++ b/tests/type/test_namedtype.py
@@ -0,0 +1,140 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import namedtype
+from pyasn1.type import univ
+from pyasn1.error import PyAsn1Error
+
+
+class NamedTypeCaseBase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.e = namedtype.NamedType('age', univ.Integer(0))
+
+    def testIter(self):
+        n, t = self.e
+        assert n == 'age' or t == univ.Integer(), 'unpack fails'
+
+    def testRepr(self):
+        assert 'age' in repr(self.e)
+
+
+class NamedTypesCaseBase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.e = namedtype.NamedTypes(
+            namedtype.NamedType('first-name', univ.OctetString('')),
+            namedtype.OptionalNamedType('age', univ.Integer(0)),
+            namedtype.NamedType('family-name', univ.OctetString(''))
+        )
+
+    def testRepr(self):
+        assert 'first-name' in repr(self.e)
+
+    def testContains(self):
+        assert 'first-name' in self.e
+        assert '<missing>' not in self.e
+
+    # noinspection PyUnusedLocal
+    def testGetItem(self):
+        assert self.e[0] == namedtype.NamedType('first-name', univ.OctetString(''))
+
+    def testIter(self):
+        assert list(self.e) == ['first-name', 'age', 'family-name']
+
+    def testGetTypeByPosition(self):
+        assert self.e.getTypeByPosition(0) == univ.OctetString(''), \
+            'getTypeByPosition() fails'
+
+    def testGetNameByPosition(self):
+        assert self.e.getNameByPosition(0) == 'first-name', \
+            'getNameByPosition() fails'
+
+    def testGetPositionByName(self):
+        assert self.e.getPositionByName('first-name') == 0, \
+            'getPositionByName() fails'
+
+    def testGetTypesNearPosition(self):
+        assert self.e.getTagMapNearPosition(0).presentTypes == {
+            univ.OctetString.tagSet: univ.OctetString('')
+        }
+        assert self.e.getTagMapNearPosition(1).presentTypes == {
+            univ.Integer.tagSet: univ.Integer(0),
+            univ.OctetString.tagSet: univ.OctetString('')
+        }
+        assert self.e.getTagMapNearPosition(2).presentTypes == {
+            univ.OctetString.tagSet: univ.OctetString('')
+        }
+
+    def testGetTagMap(self):
+        assert self.e.tagMap.presentTypes == {
+            univ.OctetString.tagSet: univ.OctetString(''),
+            univ.Integer.tagSet: univ.Integer(0)
+        }
+
+    def testStrTagMap(self):
+        assert 'TagMap' in str(self.e.tagMap)
+        assert 'OctetString' in str(self.e.tagMap)
+        assert 'Integer' in str(self.e.tagMap)
+
+    def testReprTagMap(self):
+        assert 'TagMap' in repr(self.e.tagMap)
+        assert 'OctetString' in repr(self.e.tagMap)
+        assert 'Integer' in repr(self.e.tagMap)
+
+    def testGetTagMapWithDups(self):
+        try:
+            self.e.tagMapUnique[0]
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'Duped types not noticed'
+
+    def testGetPositionNearType(self):
+        assert self.e.getPositionNearType(univ.OctetString.tagSet, 0) == 0
+        assert self.e.getPositionNearType(univ.Integer.tagSet, 1) == 1
+        assert self.e.getPositionNearType(univ.OctetString.tagSet, 2) == 2
+
+
+class OrderedNamedTypesCaseBase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.e = namedtype.NamedTypes(
+            namedtype.NamedType('first-name', univ.OctetString('')),
+            namedtype.NamedType('age', univ.Integer(0))
+        )
+
+    def testGetTypeByPosition(self):
+        assert self.e.getTypeByPosition(0) == univ.OctetString(''), \
+            'getTypeByPosition() fails'
+
+
+class DuplicateNamedTypesCaseBase(BaseTestCase):
+    def testDuplicateDefaultTags(self):
+        nt = namedtype.NamedTypes(
+            namedtype.NamedType('first-name', univ.Any()),
+            namedtype.NamedType('age', univ.Any())
+        )
+
+        assert isinstance(nt.tagMap, namedtype.NamedTypes.PostponedError)
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/type/test_namedval.py b/tests/type/test_namedval.py
new file mode 100644
index 0000000..0911d43
--- /dev/null
+++ b/tests/type/test_namedval.py
@@ -0,0 +1,58 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import namedval
+
+
+class NamedValuesCaseBase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.e = namedval.NamedValues(('off', 0), ('on', 1))
+
+    def testDict(self):
+        assert set(self.e.items()) == set([('off', 0), ('on', 1)])
+        assert set(self.e.keys()) == set(['off', 'on'])
+        assert set(self.e) == set(['off', 'on'])
+        assert set(self.e.values()) == set([0, 1])
+        assert 'on' in self.e and 'off' in self.e and 'xxx' not in self.e
+        assert 0 in self.e and 1 in self.e and 2 not in self.e
+
+    def testInit(self):
+        assert namedval.NamedValues(off=0, on=1) == {'off': 0, 'on': 1}
+        assert namedval.NamedValues('off', 'on') == {'off': 0, 'on': 1}
+        assert namedval.NamedValues(('c', 0)) == {'c': 0}
+        assert namedval.NamedValues('a', 'b', ('c', 0), d=1) == {'c': 0, 'd': 1, 'a': 2, 'b': 3}
+
+    def testLen(self):
+        assert len(self.e) == 2
+        assert len(namedval.NamedValues()) == 0
+
+    def testAdd(self):
+        assert namedval.NamedValues(off=0) + namedval.NamedValues(on=1) == {'off': 0, 'on': 1}
+
+    def testClone(self):
+        assert namedval.NamedValues(off=0).clone(('on', 1)) == {'off': 0, 'on': 1}
+        assert namedval.NamedValues(off=0).clone(on=1) == {'off': 0, 'on': 1}
+
+    def testStrRepr(self):
+        assert str(self.e)
+        assert repr(self.e)
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/type/test_opentype.py b/tests/type/test_opentype.py
new file mode 100644
index 0000000..46b5c10
--- /dev/null
+++ b/tests/type/test_opentype.py
@@ -0,0 +1,106 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import univ
+from pyasn1.type import tag
+from pyasn1.type import namedtype
+from pyasn1.type import opentype
+from pyasn1.compat.octets import str2octs
+from pyasn1.error import PyAsn1Error
+
+
+class UntaggedAnyTestCase(BaseTestCase):
+
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        class Sequence(univ.Sequence):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer()),
+                namedtype.NamedType('blob', univ.Any())
+            )
+
+        self.s = Sequence()
+
+    def testTypeCheckOnAssignment(self):
+
+        self.s.clear()
+
+        self.s['blob'] = univ.Any(str2octs('xxx'))
+
+        # this should succeed because Any is untagged and unconstrained
+        self.s['blob'] = univ.Integer(123)
+
+
+class TaggedAnyTestCase(BaseTestCase):
+
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.taggedAny = univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 20))
+
+        class Sequence(univ.Sequence):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer()),
+                namedtype.NamedType('blob', self.taggedAny)
+            )
+
+        self.s = Sequence()
+
+    def testTypeCheckOnAssignment(self):
+
+        self.s.clear()
+
+        self.s['blob'] = self.taggedAny.clone('xxx')
+
+        try:
+            self.s.setComponentByName('blob', univ.Integer(123))
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'non-open type assignment tolerated'
+
+
+class TaggedAnyOpenTypeTestCase(BaseTestCase):
+
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.taggedAny = univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 20))
+
+        class Sequence(univ.Sequence):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer()),
+                namedtype.NamedType('blob', self.taggedAny, openType=opentype.OpenType(name='id'))
+            )
+
+        self.s = Sequence()
+
+    def testTypeCheckOnAssignment(self):
+
+        self.s.clear()
+
+        self.s['blob'] = univ.Any(str2octs('xxx'))
+        self.s['blob'] = univ.Integer(123)
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/type/test_tag.py b/tests/type/test_tag.py
new file mode 100644
index 0000000..e0cf483
--- /dev/null
+++ b/tests/type/test_tag.py
@@ -0,0 +1,138 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import tag
+
+
+class TagTestCaseBase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.t1 = tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3)
+        self.t2 = tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3)
+
+
+class TagReprTestCase(TagTestCaseBase):
+    def testRepr(self):
+        assert 'Tag' in repr(self.t1)
+
+
+class TagCmpTestCase(TagTestCaseBase):
+    def testCmp(self):
+        assert self.t1 == self.t2, 'tag comparation fails'
+
+    def testHash(self):
+        assert hash(self.t1) == hash(self.t2), 'tag hash comparation fails'
+
+    def testSequence(self):
+        assert self.t1[0] == self.t2[0] and \
+               self.t1[1] == self.t2[1] and \
+               self.t1[2] == self.t2[2], 'tag sequence protocol fails'
+
+
+class TagSetTestCaseBase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.ts1 = tag.initTagSet(
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+        )
+
+        self.ts2 = tag.initTagSet(
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+        )
+
+
+class TagSetReprTestCase(TagSetTestCaseBase):
+    def testRepr(self):
+        assert 'TagSet' in repr(self.ts1)
+
+
+class TagSetCmpTestCase(TagSetTestCaseBase):
+    def testCmp(self):
+        assert self.ts1 == self.ts2, 'tag set comparation fails'
+
+    def testHash(self):
+        assert hash(self.ts1) == hash(self.ts2), 'tag set hash comp. fails'
+
+    def testLen(self):
+        assert len(self.ts1) == len(self.ts2), 'tag length comparation fails'
+
+
+class TaggingTestSuite(TagSetTestCaseBase):
+    def testImplicitTag(self):
+        t = self.ts1.tagImplicitly(
+            tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 14)
+        )
+        assert t == tag.TagSet(
+            tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 12),
+            tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 14)
+        ), 'implicit tagging went wrong'
+
+    def testExplicitTag(self):
+        t = self.ts1.tagExplicitly(
+            tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 32)
+        )
+        assert t == tag.TagSet(
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+            tag.Tag(tag.tagClassPrivate, tag.tagFormatConstructed, 32)
+        ), 'explicit tagging went wrong'
+
+
+class TagSetAddTestSuite(TagSetTestCaseBase):
+    def testAdd(self):
+        t = self.ts1 + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2)
+        assert t == tag.TagSet(
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+            tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2)
+        ), 'TagSet.__add__() fails'
+
+    def testRadd(self):
+        t = tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + self.ts1
+        assert t == tag.TagSet(
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+            tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+        ), 'TagSet.__radd__() fails'
+
+
+class SuperTagSetTestCase(TagSetTestCaseBase):
+    def testSuperTagCheck1(self):
+        assert self.ts1.isSuperTagSetOf(
+            tag.TagSet(
+                tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+                tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+            )), 'isSuperTagSetOf() fails'
+
+    def testSuperTagCheck2(self):
+        assert not self.ts1.isSuperTagSetOf(
+            tag.TagSet(
+                tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+                tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 13)
+            )), 'isSuperTagSetOf() fails'
+
+    def testSuperTagCheck3(self):
+        assert self.ts1.isSuperTagSetOf(
+            tag.TagSet((), tag.Tag(tag.tagClassUniversal,
+                                   tag.tagFormatSimple, 12))
+        ), 'isSuperTagSetOf() fails'
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py
new file mode 100644
index 0000000..ab44f3a
--- /dev/null
+++ b/tests/type/test_univ.py
@@ -0,0 +1,1913 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import math
+import pickle
+import sys
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import univ
+from pyasn1.type import tag
+from pyasn1.type import constraint
+from pyasn1.type import namedtype
+from pyasn1.type import namedval
+from pyasn1.type import error
+from pyasn1.compat.octets import str2octs, ints2octs, octs2ints
+from pyasn1.error import PyAsn1Error
+
+
+class NoValueTestCase(BaseTestCase):
+    def testSingleton(self):
+        assert univ.NoValue() is univ.NoValue(), 'NoValue is not a singleton'
+
+    def testRepr(self):
+        try:
+            repr(univ.noValue)
+
+        except PyAsn1Error:
+            assert False, 'repr() on NoValue object fails'
+
+    def testIsInstance(self):
+        try:
+            assert isinstance(univ.noValue, univ.NoValue), 'isinstance() on NoValue() object fails'
+
+        except PyAsn1Error:
+            assert False, 'isinstance() on NoValue object fails'
+
+    def testStr(self):
+        try:
+            str(univ.noValue)
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'str() works for NoValue object'
+
+    def testLen(self):
+        try:
+            len(univ.noValue)
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'len() works for NoValue object'
+
+    def testCmp(self):
+        try:
+            univ.noValue == 1
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'comparison works for NoValue object'
+
+    def testSubs(self):
+        try:
+            univ.noValue[0]
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, '__getitem__() works for NoValue object'
+
+    def testKey(self):
+        try:
+            univ.noValue['key']
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, '__getitem__() works for NoValue object'
+
+    def testKeyAssignment(self):
+        try:
+            univ.noValue['key'] = 123
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, '__setitem__() works for NoValue object'
+
+    def testInt(self):
+        try:
+            int(univ.noValue)
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'integer conversion works for NoValue object'
+
+    def testAdd(self):
+        try:
+            univ.noValue + univ.noValue
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'addition works for NoValue object'
+
+    def testBitShift(self):
+        try:
+            univ.noValue << 1
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'bitshift works for NoValue object'
+
+    def testBooleanEvaluation(self):
+        try:
+            if univ.noValue:
+                pass
+
+        except PyAsn1Error:
+            pass
+
+        else:
+            assert False, 'boolean evaluation works for NoValue object'
+    
+    def testSizeOf(self):
+        try:
+            if hasattr(sys, 'getsizeof'):
+                sys.getsizeof(univ.noValue)
+            else:
+                raise unittest.SkipTest("no sys.getsizeof() method")
+
+        except PyAsn1Error:
+            assert False, 'sizeof failed for NoValue object'
+        except TypeError:
+            raise unittest.SkipTest("sys.getsizeof() raises TypeError")
+
+
+class IntegerTestCase(BaseTestCase):
+    def testStr(self):
+        assert str(univ.Integer(1)) in ('1', '1L'), 'str() fails'
+
+    def testRepr(self):
+        assert '123' in repr(univ.Integer(123))
+
+    def testAnd(self):
+        assert univ.Integer(1) & 0 == 0, '__and__() fails'
+
+    def testOr(self):
+        assert univ.Integer(1) | 0 == 1, '__or__() fails'
+
+    def testXor(self):
+        assert univ.Integer(1) ^ 0 == 1, '__xor__() fails'
+
+    def testRand(self):
+        assert 0 & univ.Integer(1) == 0, '__rand__() fails'
+
+    def testRor(self):
+        assert 0 | univ.Integer(1) == 1, '__ror__() fails'
+
+    def testRxor(self):
+        assert 0 ^ univ.Integer(1) == 1, '__rxor__() fails'
+
+    def testAdd(self):
+        assert univ.Integer(-4) + 6 == 2, '__add__() fails'
+
+    def testRadd(self):
+        assert 4 + univ.Integer(5) == 9, '__radd__() fails'
+
+    def testSub(self):
+        assert univ.Integer(3) - 6 == -3, '__sub__() fails'
+
+    def testRsub(self):
+        assert 6 - univ.Integer(3) == 3, '__rsub__() fails'
+
+    def testMul(self):
+        assert univ.Integer(3) * -3 == -9, '__mul__() fails'
+
+    def testRmul(self):
+        assert 2 * univ.Integer(3) == 6, '__rmul__() fails'
+
+    def testDivInt(self):
+        assert univ.Integer(4) / 2 == 2, '__div__() fails'
+
+    if sys.version_info[0] > 2:
+        def testDivFloat(self):
+            assert univ.Integer(3) / 2 == 1.5, '__div__() fails'
+
+        def testRdivFloat(self):
+            assert 3 / univ.Integer(2) == 1.5, '__rdiv__() fails'
+    else:
+        def testDivFloat(self):
+            assert univ.Integer(3) / 2 == 1, '__div__() fails'
+
+        def testRdivFloat(self):
+            assert 3 / univ.Integer(2) == 1, '__rdiv__() fails'
+
+    def testRdivInt(self):
+        assert 6 / univ.Integer(3) == 2, '__rdiv__() fails'
+
+    if sys.version_info[0] > 2:
+        def testTrueDiv(self):
+            assert univ.Integer(3) / univ.Integer(2) == 1.5, '__truediv__() fails'
+
+    def testFloorDiv(self):
+        assert univ.Integer(3) // univ.Integer(2) == 1, '__floordiv__() fails'
+
+    def testMod(self):
+        assert univ.Integer(3) % 2 == 1, '__mod__() fails'
+
+    def testRmod(self):
+        assert 4 % univ.Integer(3) == 1, '__rmod__() fails'
+
+    def testPow(self):
+        assert univ.Integer(3) ** 2 == 9, '__pow__() fails'
+
+    def testRpow(self):
+        assert 2 ** univ.Integer(2) == 4, '__rpow__() fails'
+
+    def testLshift(self):
+        assert univ.Integer(1) << 1 == 2, '<< fails'
+
+    def testRshift(self):
+        assert univ.Integer(2) >> 1 == 1, '>> fails'
+
+    def testInt(self):
+        assert int(univ.Integer(3)) == 3, '__int__() fails'
+
+    def testLong(self):
+        assert int(univ.Integer(8)) == 8, '__long__() fails'
+
+    def testFloat(self):
+        assert float(univ.Integer(4)) == 4.0, '__float__() fails'
+
+    def testPos(self):
+        assert +univ.Integer(1) == 1, '__pos__() fails'
+
+    def testNeg(self):
+        assert -univ.Integer(1) == -1, '__neg__() fails'
+
+    def testInvert(self):
+        assert ~univ.Integer(1) == -2, '__invert__() fails'
+
+    def testRound(self):
+        assert round(univ.Integer(1), 3) == 1.0, '__round__() fails'
+
+    def testFloor(self):
+        assert math.floor(univ.Integer(1)) == 1, '__floor__() fails'
+
+    def testCeil(self):
+        assert math.ceil(univ.Integer(1)) == 1, '__ceil__() fails'
+
+    if sys.version_info[0:2] > (2, 5):
+        def testTrunc(self):
+            assert math.trunc(univ.Integer(1)) == 1, '__trunc__() fails'
+
+    def testPrettyIn(self):
+        assert univ.Integer('3') == 3, 'prettyIn() fails'
+
+    def testTag(self):
+        assert univ.Integer().tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02)
+        )
+
+    def testNamedVals(self):
+
+        class Integer(univ.Integer):
+            namedValues = univ.Integer.namedValues.clone(('asn1', 1))
+
+        assert Integer('asn1') == 1, 'named val fails'
+        assert int(Integer('asn1')) == 1, 'named val fails'
+        assert str(Integer('asn1')) == 'asn1', 'named val __str__() fails'
+
+    def testSubtype(self):
+        assert univ.Integer().subtype(
+            value=1,
+            implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 2),
+            subtypeSpec=constraint.SingleValueConstraint(1, 3)
+        ) == univ.Integer(
+            value=1,
+            tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate,
+                                      tag.tagFormatSimple, 2)),
+            subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1, 3))
+        )
+
+
+class IntegerPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.Integer()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.Integer
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.Integer(-123)
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1 == -123
+
+
+class BooleanTestCase(BaseTestCase):
+    def testTruth(self):
+        assert univ.Boolean(True) and univ.Boolean(1), 'Truth initializer fails'
+
+    def testFalse(self):
+        assert not univ.Boolean(False) and not univ.Boolean(0), 'False initializer fails'
+
+    def testStr(self):
+        assert str(univ.Boolean(1)) == 'True', 'str() fails'
+
+    def testInt(self):
+        assert int(univ.Boolean(1)) == 1, 'int() fails'
+
+    def testRepr(self):
+        assert 'Boolean' in repr(univ.Boolean(1))
+
+    def testTag(self):
+        assert univ.Boolean().tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01)
+        )
+
+    def testConstraints(self):
+
+        class Boolean(univ.Boolean):
+            pass
+
+        try:
+            Boolean(2)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint fail'
+
+
+class BooleanPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.Boolean()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.Boolean
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.Boolean(True)
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1 == True
+
+
+class BitStringTestCase(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.b = univ.BitString(
+            namedValues=namedval.NamedValues(('Active', 0), ('Urgent', 1))
+        )
+
+    def testBinDefault(self):
+
+        class BinDefault(univ.BitString):
+            defaultBinValue = '1010100110001010'
+
+        assert BinDefault() == univ.BitString(binValue='1010100110001010')
+
+    def testHexDefault(self):
+
+        class HexDefault(univ.BitString):
+            defaultHexValue = 'A98A'
+
+        assert HexDefault() == univ.BitString(hexValue='A98A')
+
+    def testSet(self):
+        assert self.b.clone('Active') == (1,)
+        assert self.b.clone('Urgent') == (0, 1)
+        assert self.b.clone('Urgent, Active') == (1, 1)
+        assert self.b.clone("'1010100110001010'B") == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0)
+        assert self.b.clone("'A98A'H") == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0)
+        assert self.b.clone(binValue='1010100110001010') == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0)
+        assert self.b.clone(hexValue='A98A') == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0)
+        assert self.b.clone('1010100110001010') == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0)
+        assert self.b.clone((1, 0, 1)) == (1, 0, 1)
+
+    def testStr(self):
+        assert str(self.b.clone('Urgent')) == '01'
+
+    def testRepr(self):
+        assert 'BitString' in repr(self.b.clone('Urgent,Active'))
+
+    def testTag(self):
+        assert univ.BitString().tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03)
+        )
+
+    def testLen(self):
+        assert len(self.b.clone("'A98A'H")) == 16
+
+    def testGetItem(self):
+        assert self.b.clone("'A98A'H")[0] == 1
+        assert self.b.clone("'A98A'H")[1] == 0
+        assert self.b.clone("'A98A'H")[2] == 1
+
+    if sys.version_info[:2] > (2, 4):
+        def testReverse(self):
+            assert list(reversed(univ.BitString([0, 0, 1]))) == list(univ.BitString([1, 0, 0]))
+
+    def testAsOctets(self):
+        assert self.b.clone(hexValue='A98A').asOctets() == ints2octs((0xa9, 0x8a)), 'testAsOctets() fails'
+
+    def testAsInts(self):
+        assert self.b.clone(hexValue='A98A').asNumbers() == (0xa9, 0x8a), 'testAsNumbers() fails'
+
+    def testMultipleOfEightPadding(self):
+        assert self.b.clone((1, 0, 1)).asNumbers() == (5,)
+
+    def testAsInteger(self):
+        assert self.b.clone('11000000011001').asInteger() == 12313
+        assert self.b.clone('1100110011011111').asInteger() == 52447
+
+    def testStaticDef(self):
+
+        class BitString(univ.BitString):
+            pass
+
+        assert BitString('11000000011001').asInteger() == 12313
+
+
+class BitStringPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.BitString()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.BitString
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.BitString((1, 0, 1, 0))
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1 == (1, 0, 1, 0)
+
+
+class OctetStringWithUnicodeMixIn(object):
+
+    initializer = ()
+    encoding = 'us-ascii'
+
+    def setUp(self):
+        self.pythonString = ints2octs(self.initializer).decode(self.encoding)
+        self.encodedPythonString = self.pythonString.encode(self.encoding)
+        self.numbersString = tuple(octs2ints(self.encodedPythonString))
+
+    def testInit(self):
+        assert univ.OctetString(self.encodedPythonString) == self.encodedPythonString, '__init__() fails'
+
+    def testInitFromAsn1(self):
+            assert univ.OctetString(univ.OctetString(self.encodedPythonString)) == self.encodedPythonString
+            assert univ.OctetString(univ.Integer(123)) == univ.OctetString('123')
+
+    def testSerialised(self):
+        if sys.version_info[0] < 3:
+            assert str(univ.OctetString(self.encodedPythonString, encoding=self.encoding)) == self.encodedPythonString, '__str__() fails'
+        else:
+            assert bytes(univ.OctetString(self.encodedPythonString, encoding=self.encoding)) == self.encodedPythonString, '__str__() fails'
+
+    def testPrintable(self):
+        if sys.version_info[0] < 3:
+            assert str(univ.OctetString(self.encodedPythonString, encoding=self.encoding)) == self.encodedPythonString, '__str__() fails'
+            assert unicode(univ.OctetString(self.pythonString, encoding=self.encoding)) == self.pythonString, 'unicode init fails'
+        else:
+            assert str(univ.OctetString(self.pythonString, encoding=self.encoding)) == self.pythonString, 'unicode init fails'
+
+    def testSeq(self):
+        assert univ.OctetString(self.encodedPythonString)[0] == self.encodedPythonString[0], '__getitem__() fails'
+
+    def testRepr(self):
+        assert 'abc' in repr(univ.OctetString('abc'))
+
+    def testAsOctets(self):
+        assert univ.OctetString(self.encodedPythonString).asOctets() == self.encodedPythonString, 'testAsOctets() fails'
+
+    def testAsInts(self):
+        assert univ.OctetString(self.encodedPythonString).asNumbers() == self.numbersString, 'testAsNumbers() fails'
+
+    def testAdd(self):
+        assert univ.OctetString(self.encodedPythonString) + self.encodedPythonString == self.encodedPythonString + self.encodedPythonString, '__add__() fails'
+
+    def testRadd(self):
+        assert self.encodedPythonString + univ.OctetString(self.encodedPythonString) == self.encodedPythonString + self.encodedPythonString, '__radd__() fails'
+
+    def testMul(self):
+        assert univ.OctetString(self.encodedPythonString) * 2 == self.encodedPythonString * 2, '__mul__() fails'
+
+    def testRmul(self):
+        assert 2 * univ.OctetString(self.encodedPythonString) == 2 * self.encodedPythonString, '__rmul__() fails'
+
+    def testContains(self):
+        s = univ.OctetString(self.encodedPythonString)
+        assert self.encodedPythonString in s
+        assert self.encodedPythonString * 2 not in s
+
+    if sys.version_info[:2] > (2, 4):
+       def testReverse(self):
+           assert list(reversed(univ.OctetString(self.encodedPythonString))) == list(reversed(self.encodedPythonString))
+
+
+class OctetStringWithAsciiTestCase(OctetStringWithUnicodeMixIn, BaseTestCase):
+    initializer = (97, 102)
+    encoding = 'us-ascii'
+
+
+class OctetStringWithUtf8TestCase(OctetStringWithUnicodeMixIn, BaseTestCase):
+    initializer = (208, 176, 208, 177, 208, 178)
+    encoding = 'utf-8'
+
+
+class OctetStringWithUtf16TestCase(OctetStringWithUnicodeMixIn, BaseTestCase):
+    initializer = (4, 48, 4, 49, 4, 50)
+    encoding = 'utf-16-be'
+
+
+if sys.version_info[0] > 2:
+
+    # Somehow comparison of UTF-32 encoded strings does not work in Py2
+
+    class OctetStringWithUtf32TestCase(OctetStringWithUnicodeMixIn, BaseTestCase):
+        initializer = (0, 0, 4, 48, 0, 0, 4, 49, 0, 0, 4, 50)
+        encoding = 'utf-32-be'
+
+
+class OctetStringTestCase(BaseTestCase):
+
+    def testBinDefault(self):
+
+        class BinDefault(univ.OctetString):
+            defaultBinValue = '1000010111101110101111000000111011'
+
+        assert BinDefault() == univ.OctetString(binValue='1000010111101110101111000000111011')
+
+    def testHexDefault(self):
+
+        class HexDefault(univ.OctetString):
+            defaultHexValue = 'FA9823C43E43510DE3422'
+
+        assert HexDefault() == univ.OctetString(hexValue='FA9823C43E43510DE3422')
+
+    def testBinStr(self):
+        assert univ.OctetString(binValue="1000010111101110101111000000111011") == ints2octs((133, 238, 188, 14, 192)), 'bin init fails'
+
+    def testHexStr(self):
+        assert univ.OctetString(hexValue="FA9823C43E43510DE3422") == ints2octs((250, 152, 35, 196, 62, 67, 81, 13, 227, 66, 32)), 'hex init fails'
+
+    def testTuple(self):
+        assert univ.OctetString((1, 2, 3, 4, 5)) == ints2octs((1, 2, 3, 4, 5)), 'tuple init failed'
+
+    def testRepr(self):
+        assert 'abc' in repr(univ.OctetString('abc'))
+
+    def testEmpty(self):
+        try:
+            str(univ.OctetString())
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'empty OctetString() not reported'
+
+    def testTag(self):
+        assert univ.OctetString().tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04)
+        )
+
+    def testStaticDef(self):
+
+        class OctetString(univ.OctetString):
+            pass
+
+        assert OctetString(hexValue="FA9823C43E43510DE3422") == ints2octs((250, 152, 35, 196, 62, 67, 81, 13, 227, 66, 32))
+
+
+class OctetStringPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.BitString()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.BitString
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.BitString((1, 0, 1, 0))
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1 == (1, 0, 1, 0)
+
+
+class Null(BaseTestCase):
+
+    def testInit(self):
+        assert not univ.Null().isValue
+        assert univ.Null(0) == str2octs('')
+        assert univ.Null(False) == str2octs('')
+        assert univ.Null('') == str2octs('')
+        assert univ.Null(None) == str2octs('')
+
+        try:
+            assert univ.Null(True)
+
+        except PyAsn1Error:
+            pass
+
+        try:
+            assert univ.Null('xxx')
+
+        except PyAsn1Error:
+            pass
+
+    def testStr(self):
+        assert str(univ.Null('')) == '', 'str() fails'
+
+    def testRepr(self):
+        assert 'Null' in repr(univ.Null(''))
+
+    def testTag(self):
+        assert univ.Null().tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05)
+        )
+
+    def testConstraints(self):
+        try:
+            univ.Null(2)
+        except error.ValueConstraintError:
+            pass
+        else:
+            assert 0, 'constraint fail'
+
+    def testStaticDef(self):
+
+        class Null(univ.Null):
+            pass
+
+        assert not Null('')
+
+
+class NullPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.Null()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.Null
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.Null('')
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert not new_asn1
+
+
+class RealTestCase(BaseTestCase):
+    def testFloat4BinEnc(self):
+        assert univ.Real((0.25, 2, 3)) == 2.0, 'float initializer for binary encoding fails'
+
+    def testStr(self):
+        assert str(univ.Real(1.0)) == '1.0', 'str() fails'
+
+    def testRepr(self):
+        assert 'Real' in repr(univ.Real(-4.1))
+        assert 'Real' in repr(univ.Real(-4.1))
+        assert 'inf' in repr(univ.Real('inf'))
+        assert '-inf' in repr(univ.Real('-inf'))
+
+    def testAdd(self):
+        assert univ.Real(-4.1) + 1.4 == -2.7, '__add__() fails'
+
+    def testRadd(self):
+        assert 4 + univ.Real(0.5) == 4.5, '__radd__() fails'
+
+    def testSub(self):
+        assert univ.Real(3.9) - 1.7 == 2.2, '__sub__() fails'
+
+    def testRsub(self):
+        assert 6.1 - univ.Real(0.1) == 6, '__rsub__() fails'
+
+    def testMul(self):
+        assert univ.Real(3.0) * -3 == -9, '__mul__() fails'
+
+    def testRmul(self):
+        assert 2 * univ.Real(3.0) == 6, '__rmul__() fails'
+
+    def testDiv(self):
+        assert univ.Real(3.0) / 2 == 1.5, '__div__() fails'
+
+    def testRdiv(self):
+        assert 6 / univ.Real(3.0) == 2, '__rdiv__() fails'
+
+    def testMod(self):
+        assert univ.Real(3.0) % 2 == 1, '__mod__() fails'
+
+    def testRmod(self):
+        assert 4 % univ.Real(3.0) == 1, '__rmod__() fails'
+
+    def testPow(self):
+        assert univ.Real(3.0) ** 2 == 9, '__pow__() fails'
+
+    def testRpow(self):
+        assert 2 ** univ.Real(2.0) == 4, '__rpow__() fails'
+
+    def testInt(self):
+        assert int(univ.Real(3.0)) == 3, '__int__() fails'
+
+    def testLong(self):
+        assert int(univ.Real(8.0)) == 8, '__long__() fails'
+
+    def testFloat(self):
+        assert float(univ.Real(4.0)) == 4.0, '__float__() fails'
+
+    def testPrettyIn(self):
+        assert univ.Real((3, 10, 0)) == 3, 'prettyIn() fails'
+
+    # infinite float values
+    def testStrInf(self):
+        assert str(univ.Real('inf')) == 'inf', 'str() fails'
+
+    def testAddInf(self):
+        assert univ.Real('inf') + 1 == float('inf'), '__add__() fails'
+
+    def testRaddInf(self):
+        assert 1 + univ.Real('inf') == float('inf'), '__radd__() fails'
+
+    def testIntInf(self):
+        try:
+            assert int(univ.Real('inf'))
+        except OverflowError:
+            pass
+        else:
+            assert 0, '__int__() fails'
+
+    def testLongInf(self):
+        try:
+            assert int(univ.Real('inf'))
+        except OverflowError:
+            pass
+        else:
+            assert 0, '__long__() fails'
+        assert int(univ.Real(8.0)) == 8, '__long__() fails'
+
+    def testFloatInf(self):
+        assert float(univ.Real('-inf')) == float('-inf'), '__float__() fails'
+
+    def testPrettyInInf(self):
+        assert univ.Real(float('inf')) == float('inf'), 'prettyIn() fails'
+
+    def testPlusInf(self):
+        assert univ.Real('inf').isPlusInf, 'isPlusInfinity failed'
+
+    def testMinusInf(self):
+        assert univ.Real('-inf').isMinusInf, 'isMinusInfinity failed'
+
+    def testPos(self):
+        assert +univ.Real(1.0) == 1.0, '__pos__() fails'
+
+    def testNeg(self):
+        assert -univ.Real(1.0) == -1.0, '__neg__() fails'
+
+    def testRound(self):
+        assert round(univ.Real(1.123), 2) == 1.12, '__round__() fails'
+
+    def testFloor(self):
+        assert math.floor(univ.Real(1.6)) == 1.0, '__floor__() fails'
+
+    def testCeil(self):
+        assert math.ceil(univ.Real(1.2)) == 2.0, '__ceil__() fails'
+
+    if sys.version_info[0:2] > (2, 5):
+        def testTrunc(self):
+            assert math.trunc(univ.Real(1.1)) == 1.0, '__trunc__() fails'
+
+    def testTag(self):
+        assert univ.Real().tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09)
+        )
+
+    def testStaticDef(self):
+
+        class Real(univ.Real):
+            pass
+
+        assert Real(1.0) == 1.0
+
+
+class RealPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.Real()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.Real
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.Real((1, 10, 3))
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1 == 1000
+
+
+class ObjectIdentifier(BaseTestCase):
+    def testStr(self):
+        assert str(univ.ObjectIdentifier((1, 3, 6))) == '1.3.6', 'str() fails'
+
+    def testRepr(self):
+        assert '1.3.6' in repr(univ.ObjectIdentifier('1.3.6'))
+
+    def testEq(self):
+        assert univ.ObjectIdentifier((1, 3, 6)) == (1, 3, 6), '__cmp__() fails'
+
+    def testAdd(self):
+        assert univ.ObjectIdentifier((1, 3)) + (6,) == (1, 3, 6), '__add__() fails'
+
+    def testRadd(self):
+        assert (1,) + univ.ObjectIdentifier((3, 6)) == (1, 3, 6), '__radd__() fails'
+
+    def testLen(self):
+        assert len(univ.ObjectIdentifier((1, 3))) == 2, '__len__() fails'
+
+    def testPrefix(self):
+        o = univ.ObjectIdentifier('1.3.6')
+        assert o.isPrefixOf((1, 3, 6)), 'isPrefixOf() fails'
+        assert o.isPrefixOf((1, 3, 6, 1)), 'isPrefixOf() fails'
+        assert not o.isPrefixOf((1, 3)), 'isPrefixOf() fails'
+
+    def testInput1(self):
+        assert univ.ObjectIdentifier('1.3.6') == (1, 3, 6), 'prettyIn() fails'
+
+    def testInput2(self):
+        assert univ.ObjectIdentifier((1, 3, 6)) == (1, 3, 6), 'prettyIn() fails'
+
+    def testInput3(self):
+        assert univ.ObjectIdentifier(univ.ObjectIdentifier('1.3') + (6,)) == (1, 3, 6), 'prettyIn() fails'
+
+    def testUnicode(self):
+        s = '1.3.6'
+        if sys.version_info[0] < 3:
+            s = s.decode()
+        assert univ.ObjectIdentifier(s) == (1, 3, 6), 'unicode init fails'
+
+    def testTag(self):
+        assert univ.ObjectIdentifier().tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06)
+        )
+
+    def testContains(self):
+        s = univ.ObjectIdentifier('1.3.6.1234.99999')
+        assert 1234 in s
+        assert 4321 not in s
+
+    def testStaticDef(self):
+
+        class ObjectIdentifier(univ.ObjectIdentifier):
+            pass
+
+        assert str(ObjectIdentifier((1, 3, 6))) == '1.3.6'
+
+
+class ObjectIdentifierPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.ObjectIdentifier()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.ObjectIdentifier
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.ObjectIdentifier('2.3.1.1.2')
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1 == (2, 3, 1, 1, 2)
+
+
+class SequenceOf(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s1 = univ.SequenceOf(
+            componentType=univ.OctetString('')
+        )
+        self.s2 = self.s1.clone()
+
+    def testRepr(self):
+        assert 'a' in repr(self.s1.clone().setComponents('a', 'b'))
+
+    def testTag(self):
+        assert self.s1.tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+        ), 'wrong tagSet'
+
+    def testSeq(self):
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        assert self.s1[0] == str2octs('abc'), 'set by idx fails'
+        self.s1[0] = 'cba'
+        assert self.s1[0] == str2octs('cba'), 'set by idx fails'
+
+    def testCmp(self):
+        self.s1.clear()
+        self.s1.setComponentByPosition(0, 'abc')
+        self.s2.clear()
+        self.s2.setComponentByPosition(0, univ.OctetString('abc'))
+        assert self.s1 == self.s2, '__cmp__() fails'
+
+    def testSubtypeSpec(self):
+        s = self.s1.clone(subtypeSpec=constraint.ConstraintsUnion(
+            constraint.SingleValueConstraint(str2octs('abc'))
+        ))
+        try:
+            s.setComponentByPosition(0, univ.OctetString('abc'))
+        except PyAsn1Error:
+            assert 0, 'constraint fails'
+        try:
+            s.setComponentByPosition(1, univ.OctetString('Abc'))
+        except PyAsn1Error:
+            try:
+                s.setComponentByPosition(1, univ.OctetString('Abc'),
+                                         verifyConstraints=False)
+            except PyAsn1Error:
+                assert 0, 'constraint failes with verifyConstraints=True'
+        else:
+            assert 0, 'constraint fails'
+
+    def testComponentTagsMatching(self):
+        s = self.s1.clone()
+        s.strictConstraints = True  # This requires types equality
+        o = univ.OctetString('abc').subtype(explicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 12))
+        try:
+            s.setComponentByPosition(0, o)
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'inner supertype tag allowed'
+
+    def testComponentConstraintsMatching(self):
+        s = self.s1.clone()
+        o = univ.OctetString().subtype(
+            subtypeSpec=constraint.ConstraintsUnion(constraint.SingleValueConstraint(str2octs('cba'))))
+        s.strictConstraints = True  # This requires types equality
+        try:
+            s.setComponentByPosition(0, o.clone('cba'))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'inner supertype constraint allowed'
+        s.strictConstraints = False  # This requires subtype relationships
+        try:
+            s.setComponentByPosition(0, o.clone('cba'))
+        except PyAsn1Error:
+            assert 0, 'inner supertype constraint disallowed'
+        else:
+            pass
+
+    def testSizeSpec(self):
+        s = self.s1.clone(sizeSpec=constraint.ConstraintsUnion(
+            constraint.ValueSizeConstraint(1, 1)
+        ))
+        s.setComponentByPosition(0, univ.OctetString('abc'))
+        try:
+            s.verifySizeSpec()
+        except PyAsn1Error:
+            assert 0, 'size spec fails'
+        s.setComponentByPosition(1, univ.OctetString('abc'))
+        try:
+            s.verifySizeSpec()
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'size spec fails'
+
+    def testGetComponentTagMap(self):
+        assert self.s1.componentType.tagMap.presentTypes == {
+            univ.OctetString.tagSet: univ.OctetString('')
+        }
+
+    def testSubtype(self):
+        self.s1.clear()
+        assert self.s1.subtype(
+            implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 2),
+            subtypeSpec=constraint.SingleValueConstraint(1, 3),
+            sizeSpec=constraint.ValueSizeConstraint(0, 1)
+        ) == self.s1.clone(
+            tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate,
+                                      tag.tagFormatSimple, 2)),
+            subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1, 3)),
+            sizeSpec=constraint.ValueSizeConstraint(0, 1)
+        )
+
+    def testClone(self):
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        s = self.s1.clone()
+        assert len(s) == 0
+        s = self.s1.clone(cloneValueFlag=1)
+        assert len(s) == 1
+        assert s.getComponentByPosition(0) == self.s1.getComponentByPosition(0)
+
+    def testSetComponents(self):
+        assert self.s1.clone().setComponents('abc', 'def') == \
+               self.s1.setComponentByPosition(0, 'abc').setComponentByPosition(1, 'def')
+
+    def testGetItem(self):
+        s = self.s1.clone()
+        s.append('xxx')
+        assert s[0]
+
+        try:
+            s[2]
+
+        except IndexError:
+            pass
+
+        else:
+            assert False, 'IndexError not raised'
+
+        # this is a deviation from standart sequence protocol
+        assert not s[1]
+
+    def testSetItem(self):
+        s = self.s1.clone()
+        s.append('xxx')
+
+        try:
+
+            s[2] = 'xxx'
+
+        except IndexError:
+            pass
+
+        else:
+            assert False, 'IndexError not raised'
+
+    def testAppend(self):
+        self.s1.clear()
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        assert len(self.s1) == 1
+        self.s1.append('def')
+        assert len(self.s1) == 2
+        assert list(self.s1) == [str2octs(x) for x in ['abc', 'def']]
+
+    def testExtend(self):
+        self.s1.clear()
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        assert len(self.s1) == 1
+        self.s1.extend(['def', 'ghi'])
+        assert len(self.s1) == 3
+        assert list(self.s1) == [str2octs(x) for x in ['abc', 'def', 'ghi']]
+
+    def testCount(self):
+        self.s1.clear()
+        for x in ['abc', 'def', 'abc']:
+            self.s1.append(x)
+        assert self.s1.count(str2octs('abc')) == 2
+        assert self.s1.count(str2octs('def')) == 1
+        assert self.s1.count(str2octs('ghi')) == 0
+
+    def testIndex(self):
+        self.s1.clear()
+        for x in ['abc', 'def', 'abc']:
+            self.s1.append(x)
+        assert self.s1.index(str2octs('abc')) == 0
+        assert self.s1.index(str2octs('def')) == 1
+        assert self.s1.index(str2octs('abc'), 1) == 2
+
+    def testSort(self):
+        self.s1.clear()
+        self.s1[0] = 'b'
+        self.s1[1] = 'a'
+        assert list(self.s1) == [str2octs('b'), str2octs('a')]
+        self.s1.sort()
+        assert list(self.s1) == [str2octs('a'), str2octs('b')]
+
+    def testStaticDef(self):
+
+        class SequenceOf(univ.SequenceOf):
+            componentType = univ.OctetString('')
+
+        s = SequenceOf()
+        s[0] = 'abc'
+        assert len(s) == 1
+        assert s == [str2octs('abc')]
+
+    def testLegacyInitializer(self):
+        n = univ.SequenceOf(
+            componentType=univ.OctetString()
+        )
+        o = univ.SequenceOf(
+            univ.OctetString()  # this is the old way
+        )
+
+        assert n.isSameTypeWith(o) and o.isSameTypeWith(n)
+
+        n[0] = 'fox'
+        o[0] = 'fox'
+
+        assert n == o
+
+    def testGetComponentWithDefault(self):
+
+        class SequenceOf(univ.SequenceOf):
+            componentType = univ.OctetString()
+
+        s = SequenceOf()
+        assert s.getComponentByPosition(0, default=None, instantiate=False) is None
+        assert s.getComponentByPosition(0, default=None) is None
+        s[0] = 'test'
+        assert s.getComponentByPosition(0, default=None) is not None
+        assert s.getComponentByPosition(0, default=None) == str2octs('test')
+        s.clear()
+        assert s.getComponentByPosition(0, default=None) is None
+
+    def testGetComponentNoInstantiation(self):
+
+        class SequenceOf(univ.SequenceOf):
+            componentType = univ.OctetString()
+
+        s = SequenceOf()
+        assert s.getComponentByPosition(0, instantiate=False) is univ.noValue
+        s[0] = 'test'
+        assert s.getComponentByPosition(0, instantiate=False) is not univ.noValue
+        assert s.getComponentByPosition(0, instantiate=False) == str2octs('test')
+        s.clear()
+        assert s.getComponentByPosition(0, instantiate=False) is univ.noValue
+
+
+class SequenceOfPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.SequenceOf(componentType=univ.OctetString())
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.SequenceOf
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.SequenceOf(componentType=univ.OctetString())
+        old_asn1[0] = 'test'
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1
+        assert new_asn1 == [str2octs('test')]
+
+
+class Sequence(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s1 = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString('')),
+                namedtype.OptionalNamedType('nick', univ.OctetString('')),
+                namedtype.DefaultedNamedType('age', univ.Integer(34))
+            )
+        )
+
+    def testRepr(self):
+        assert 'name' in repr(self.s1.clone().setComponents('a', 'b'))
+
+    def testTag(self):
+        assert self.s1.tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+        ), 'wrong tagSet'
+
+    def testById(self):
+        self.s1.setComponentByName('name', univ.OctetString('abc'))
+        assert self.s1.getComponentByName('name') == str2octs('abc'), 'set by name fails'
+
+    def testByKey(self):
+        self.s1['name'] = 'abc'
+        assert self.s1['name'] == str2octs('abc'), 'set by key fails'
+
+    def testContains(self):
+        assert 'name' in self.s1
+        assert '<missing>' not in self.s1
+
+    def testGetNearPosition(self):
+        assert self.s1.componentType.getTagMapNearPosition(1).presentTypes == {
+            univ.OctetString.tagSet: univ.OctetString(''),
+            univ.Integer.tagSet: univ.Integer(34)
+        }
+        assert self.s1.componentType.getPositionNearType(
+            univ.OctetString.tagSet, 1
+        ) == 1
+
+    def testSetDefaultComponents(self):
+        self.s1.clear()
+        self.s1.setComponentByPosition(0, univ.OctetString('Ping'))
+        self.s1.setComponentByPosition(1, univ.OctetString('Pong'))
+        assert self.s1.getComponentByPosition(2) == 34
+
+    def testClone(self):
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        self.s1.setComponentByPosition(1, univ.OctetString('def'))
+        self.s1.setComponentByPosition(2, univ.Integer(123))
+        s = self.s1.clone()
+        assert s.getComponentByPosition(0) != self.s1.getComponentByPosition(0)
+        assert s.getComponentByPosition(1) != self.s1.getComponentByPosition(1)
+        assert s.getComponentByPosition(2) != self.s1.getComponentByPosition(2)
+        s = self.s1.clone(cloneValueFlag=1)
+        assert s.getComponentByPosition(0) == self.s1.getComponentByPosition(0)
+        assert s.getComponentByPosition(1) == self.s1.getComponentByPosition(1)
+        assert s.getComponentByPosition(2) == self.s1.getComponentByPosition(2)
+
+    def testComponentTagsMatching(self):
+        s = self.s1.clone()
+        s.strictConstraints = True  # This requires types equality
+        o = univ.OctetString('abc').subtype(explicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 12))
+        try:
+            s.setComponentByName('name', o)
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'inner supertype tag allowed'
+
+    def testComponentConstraintsMatching(self):
+        s = self.s1.clone()
+        o = univ.OctetString().subtype(
+            subtypeSpec=constraint.ConstraintsUnion(constraint.SingleValueConstraint(str2octs('cba'))))
+        s.strictConstraints = True  # This requires types equality
+        try:
+            s.setComponentByName('name', o.clone('cba'))
+        except PyAsn1Error:
+            pass
+        else:
+            assert 0, 'inner supertype constraint allowed'
+        s.strictConstraints = False  # This requires subtype relationships
+        try:
+            s.setComponentByName('name', o.clone('cba'))
+        except PyAsn1Error:
+            assert 0, 'inner supertype constraint disallowed'
+        else:
+            pass
+
+    def testSetComponents(self):
+        assert self.s1.clone().setComponents(name='a', nick='b', age=1) == \
+               self.s1.setComponentByPosition(0, 'a').setComponentByPosition(1, 'b').setComponentByPosition(2, 1)
+
+    def testSetToDefault(self):
+        s = self.s1.clone()
+        s.setComponentByPosition(0, univ.noValue)
+        s[2] = univ.noValue
+        assert s[0] == univ.OctetString('')
+        assert s[2] == univ.Integer(34)
+
+    def testGetItem(self):
+        s = self.s1.clone()
+        s['name'] = 'xxx'
+        assert s['name']
+        assert s[0]
+
+        try:
+            s['xxx']
+
+        except KeyError:
+            pass
+
+        else:
+            assert False, 'KeyError not raised'
+
+        try:
+            s[100]
+
+        except IndexError:
+            pass
+
+        else:
+            assert False, 'IndexError not raised'
+
+    def testSetItem(self):
+        s = self.s1.clone()
+        s['name'] = 'xxx'
+
+        try:
+
+            s['xxx'] = 'xxx'
+
+        except KeyError:
+            pass
+
+        else:
+            assert False, 'KeyError not raised'
+
+        try:
+
+            s[100] = 'xxx'
+
+        except IndexError:
+            pass
+
+        else:
+            assert False, 'IndexError not raised'
+
+    def testIter(self):
+        assert list(self.s1) == ['name', 'nick', 'age']
+
+    def testKeys(self):
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        self.s1.setComponentByPosition(1, univ.OctetString('def'))
+        self.s1.setComponentByPosition(2, univ.Integer(123))
+        assert list(self.s1.keys()) == ['name', 'nick', 'age']
+
+    def testValues(self):
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        self.s1.setComponentByPosition(1, univ.OctetString('def'))
+        self.s1.setComponentByPosition(2, univ.Integer(123))
+        assert list(self.s1.values()) == [str2octs('abc'), str2octs('def'), 123]
+
+    def testItems(self):
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        self.s1.setComponentByPosition(1, univ.OctetString('def'))
+        self.s1.setComponentByPosition(2, univ.Integer(123))
+        assert list(self.s1.items()) == [(x[0], str2octs(x[1])) for x in [('name', 'abc'), ('nick', 'def')]] + [('age', 123)]
+
+    def testUpdate(self):
+        self.s1.clear()
+        assert list(self.s1.values()) == [str2octs(''), str2octs(''), 34]
+        self.s1.update(**{'name': 'abc', 'nick': 'def', 'age': 123})
+        assert list(self.s1.items()) == [(x[0], str2octs(x[1])) for x in [('name', 'abc'), ('nick', 'def')]] + [('age', 123)]
+        self.s1.update(('name', 'ABC'))
+        assert list(self.s1.items()) == [(x[0], str2octs(x[1])) for x in [('name', 'ABC'), ('nick', 'def')]] + [('age', 123)]
+        self.s1.update(name='CBA')
+        assert list(self.s1.items()) == [(x[0], str2octs(x[1])) for x in [('name', 'CBA'), ('nick', 'def')]] + [('age', 123)]
+
+    def testStaticDef(self):
+
+        class Sequence(univ.Sequence):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString('')),
+                namedtype.OptionalNamedType('nick', univ.OctetString('')),
+                namedtype.DefaultedNamedType('age', univ.Integer(34))
+            )
+
+        s = Sequence()
+        s['name'] = 'abc'
+        assert s['name'] == str2octs('abc')
+
+    def testGetComponentWithDefault(self):
+
+        class Sequence(univ.Sequence):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString('')),
+                namedtype.OptionalNamedType('nick', univ.OctetString()),
+            )
+
+        s = Sequence()
+
+        assert s[0] == str2octs('')
+        assert s.getComponentByPosition(1, default=None, instantiate=False) is None
+        assert s.getComponentByName('nick', default=None) is None
+        s[1] = 'test'
+        assert s.getComponentByPosition(1, default=None) is not None
+        assert s.getComponentByPosition(1, default=None) == str2octs('test')
+        s.clear()
+        assert s.getComponentByPosition(1, default=None) is None
+
+    def testGetComponentNoInstantiation(self):
+
+        class Sequence(univ.Sequence):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString('')),
+                namedtype.OptionalNamedType('nick', univ.OctetString()),
+            )
+
+        s = Sequence()
+        assert s[0] == str2octs('')
+        assert s.getComponentByPosition(1, instantiate=False) is univ.noValue
+        assert s.getComponentByName('nick', instantiate=False) is univ.noValue
+        s[1] = 'test'
+        assert s.getComponentByPosition(1, instantiate=False) is not univ.noValue
+        assert s.getComponentByPosition(1, instantiate=False) == str2octs('test')
+        s.clear()
+        assert s.getComponentByPosition(1, instantiate=False) is univ.noValue
+
+
+class SequenceWithoutSchema(BaseTestCase):
+
+    def testGetItem(self):
+        s = univ.Sequence()
+        s.setComponentByPosition(0, univ.OctetString('abc'))
+        s[0] = 'abc'
+        assert s['field-0']
+        assert s[0]
+
+        try:
+            s['field-1']
+
+        except KeyError:
+            pass
+
+        else:
+            assert False, 'KeyError not raised'
+
+    def testSetItem(self):
+        s = univ.Sequence()
+        s.setComponentByPosition(0, univ.OctetString('abc'))
+        s['field-0'] = 'xxx'
+
+        try:
+
+            s['field-1'] = 'xxx'
+
+        except KeyError:
+            pass
+
+        else:
+            assert False, 'KeyError not raised'
+
+    def testIter(self):
+        s = univ.Sequence()
+        s.setComponentByPosition(0, univ.OctetString('abc'))
+        s.setComponentByPosition(1, univ.Integer(123))
+        assert list(s) == ['field-0', 'field-1']
+
+    def testKeys(self):
+        s = univ.Sequence()
+        s.setComponentByPosition(0, univ.OctetString('abc'))
+        s.setComponentByPosition(1, univ.Integer(123))
+        assert list(s.keys()) == ['field-0', 'field-1']
+
+    def testValues(self):
+        s = univ.Sequence()
+        s.setComponentByPosition(0, univ.OctetString('abc'))
+        s.setComponentByPosition(1, univ.Integer(123))
+        assert list(s.values()) == [str2octs('abc'), 123]
+
+    def testItems(self):
+        s = univ.Sequence()
+        s.setComponentByPosition(0, univ.OctetString('abc'))
+        s.setComponentByPosition(1, univ.Integer(123))
+        assert list(s.items()) == [('field-0', str2octs('abc')), ('field-1', 123)]
+
+    def testUpdate(self):
+        s = univ.Sequence()
+        assert not s
+        s.setComponentByPosition(0, univ.OctetString('abc'))
+        s.setComponentByPosition(1, univ.Integer(123))
+        assert s
+        assert list(s.keys()) == ['field-0', 'field-1']
+        assert list(s.values()) == [str2octs('abc'), 123]
+        assert list(s.items()) == [('field-0', str2octs('abc')), ('field-1', 123)]
+        s['field-0'] = univ.OctetString('def')
+        assert list(s.values()) == [str2octs('def'), 123]
+        s['field-1'] = univ.OctetString('ghi')
+        assert list(s.values()) == [str2octs('def'), str2octs('ghi')]
+        try:
+            s['field-2'] = univ.OctetString('xxx')
+        except KeyError:
+            pass
+        else:
+            assert False, 'unknown field at schema-less object tolerated'
+        assert 'field-0' in s
+        s.clear()
+        assert 'field-0' not in s
+
+
+class SequencePicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString())
+            )
+        )
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.Sequence
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.Sequence(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString())
+            )
+        )
+        old_asn1['name'] = 'test'
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1
+        assert new_asn1['name'] == str2octs('test')
+
+
+class SetOf(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+        self.s1 = univ.SetOf(componentType=univ.OctetString(''))
+
+    def testTag(self):
+        assert self.s1.tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+        ), 'wrong tagSet'
+
+    def testSeq(self):
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        assert self.s1[0] == str2octs('abc'), 'set by idx fails'
+        self.s1.setComponentByPosition(0, self.s1[0].clone('cba'))
+        assert self.s1[0] == str2octs('cba'), 'set by idx fails'
+
+    def testStaticDef(self):
+
+        class SetOf(univ.SequenceOf):
+            componentType = univ.OctetString('')
+
+        s = SetOf()
+        s[0] = 'abc'
+        assert len(s) == 1
+        assert s == [str2octs('abc')]
+
+
+
+class SetOfPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.SetOf(componentType=univ.OctetString())
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.SetOf
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.SetOf(componentType=univ.OctetString())
+        old_asn1[0] = 'test'
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1
+        assert new_asn1 == [str2octs('test')]
+
+
+class Set(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        self.s1 = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString('')),
+                namedtype.OptionalNamedType('null', univ.Null('')),
+                namedtype.DefaultedNamedType('age', univ.Integer(34))
+            )
+        )
+        self.s2 = self.s1.clone()
+
+    def testTag(self):
+        assert self.s1.tagSet == tag.TagSet(
+            (),
+            tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+        ), 'wrong tagSet'
+
+    def testByTypeWithPythonValue(self):
+        self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+        assert self.s1.getComponentByType(
+            univ.OctetString.tagSet
+        ) == str2octs('abc'), 'set by name fails'
+
+    def testByTypeWithInstance(self):
+        self.s1.setComponentByType(univ.OctetString.tagSet, univ.OctetString('abc'))
+        assert self.s1.getComponentByType(
+            univ.OctetString.tagSet
+        ) == str2octs('abc'), 'set by name fails'
+
+    def testGetTagMap(self):
+        assert self.s1.tagMap.presentTypes == {
+            univ.Set.tagSet: univ.Set()
+        }
+
+    def testGetComponentTagMap(self):
+        assert self.s1.componentType.tagMapUnique.presentTypes == {
+            univ.OctetString.tagSet: univ.OctetString(''),
+            univ.Null.tagSet: univ.Null(''),
+            univ.Integer.tagSet: univ.Integer(34)
+        }
+
+    def testGetPositionByType(self):
+        assert self.s1.componentType.getPositionByType(univ.Null().tagSet) == 1
+
+    def testSetToDefault(self):
+        self.s1.setComponentByName('name', univ.noValue)
+        assert self.s1['name'] == univ.OctetString('')
+
+    def testIter(self):
+        assert list(self.s1) == ['name', 'null', 'age']
+
+    def testStaticDef(self):
+
+        class Set(univ.Set):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString('')),
+                namedtype.OptionalNamedType('nick', univ.OctetString('')),
+                namedtype.DefaultedNamedType('age', univ.Integer(34))
+            )
+
+        s = Set()
+        s['name'] = 'abc'
+        assert s['name'] == str2octs('abc')
+
+    def testGetComponentWithDefault(self):
+
+        class Set(univ.Set):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer(123)),
+                namedtype.OptionalNamedType('nick', univ.OctetString()),
+            )
+
+        s = Set()
+        assert s[0] == 123
+        assert s.getComponentByPosition(1, default=None, instantiate=False) is None
+        assert s.getComponentByName('nick', default=None) is None
+        s[1] = 'test'
+        assert s.getComponentByPosition(1, default=None) is not None
+        assert s.getComponentByPosition(1, default=None) == str2octs('test')
+        s.clear()
+        assert s.getComponentByPosition(1, default=None) is None
+
+    def testGetComponentNoInstantiation(self):
+
+        class Set(univ.Set):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('id', univ.Integer(123)),
+                namedtype.OptionalNamedType('nick', univ.OctetString()),
+            )
+
+        s = Set()
+        assert s[0] == 123
+        assert s.getComponentByPosition(1, instantiate=False) is univ.noValue
+        assert s.getComponentByName('nick', instantiate=False) is univ.noValue
+        assert s.getComponentByType(univ.OctetString.tagSet, instantiate=False) is univ.noValue
+        s[1] = 'test'
+        assert s.getComponentByPosition(1, instantiate=False) is not univ.noValue
+        assert s.getComponentByPosition(1, instantiate=False) == str2octs('test')
+        s.clear()
+        assert s.getComponentByPosition(1, instantiate=False) is univ.noValue
+
+
+class SetPicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString())
+            )
+        )
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.Set
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.Set(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString())
+            )
+        )
+        old_asn1['name'] = 'test'
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1
+        assert new_asn1['name'] == str2octs('test')
+
+
+class Choice(BaseTestCase):
+    def setUp(self):
+        BaseTestCase.setUp(self)
+
+        innerComp = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('count', univ.Integer()),
+                namedtype.NamedType('flag', univ.Boolean())
+            )
+        )
+        self.s1 = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('sex', innerComp)
+            )
+        )
+
+    def testTag(self):
+        assert self.s1.tagSet == tag.TagSet(), 'wrong tagSet'
+
+    def testRepr(self):
+        assert 'Choice' in repr(self.s1.clone().setComponents('a'))
+        s = self.s1.clone().setComponents(
+                sex=self.s1.setComponentByPosition(1).getComponentByPosition(1).clone().setComponents(count=univ.Integer(123))
+        )
+        assert 'Choice' in repr(s)
+
+    def testContains(self):
+        self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+        assert 'name' in self.s1
+        assert 'sex' not in self.s1
+
+        self.s1.setComponentByType(univ.Integer.tagSet, 123, innerFlag=True)
+        assert 'name' not in self.s1
+        assert 'sex' in self.s1
+
+    def testIter(self):
+        self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+        assert list(self.s1) == ['name']
+        self.s1.setComponentByType(univ.Integer.tagSet, 123, innerFlag=True)
+        assert list(self.s1) == ['sex']
+
+    def testOuterByTypeWithPythonValue(self):
+        self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+        assert self.s1.getComponentByType(
+            univ.OctetString.tagSet
+        ) == str2octs('abc')
+
+    def testOuterByTypeWithInstanceValue(self):
+        self.s1.setComponentByType(
+            univ.OctetString.tagSet, univ.OctetString('abc')
+        )
+        assert self.s1.getComponentByType(
+            univ.OctetString.tagSet
+        ) == str2octs('abc')
+
+    def testInnerByTypeWithPythonValue(self):
+        self.s1.setComponentByType(univ.Integer.tagSet, 123, innerFlag=True)
+        assert self.s1.getComponentByType(
+            univ.Integer.tagSet, 1
+        ) == 123
+
+    def testInnerByTypeWithInstanceValue(self):
+        self.s1.setComponentByType(
+            univ.Integer.tagSet, univ.Integer(123), innerFlag=True
+        )
+        assert self.s1.getComponentByType(
+            univ.Integer.tagSet, 1
+        ) == 123
+
+    def testCmp(self):
+        self.s1.setComponentByName('name', univ.OctetString('abc'))
+        assert self.s1 == str2octs('abc'), '__cmp__() fails'
+
+    def testGetComponent(self):
+        self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+        assert self.s1.getComponent() == str2octs('abc'), 'getComponent() fails'
+
+    def testGetName(self):
+        self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+        assert self.s1.getName() == 'name', 'getName() fails'
+
+    def testSetComponentByPosition(self):
+        self.s1.setComponentByPosition(0, univ.OctetString('Jim'))
+        assert self.s1 == str2octs('Jim')
+
+    def testClone(self):
+        self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+        s = self.s1.clone()
+        assert len(s) == 0
+        s = self.s1.clone(cloneValueFlag=1)
+        assert len(s) == 1
+        assert s.getComponentByPosition(0) == self.s1.getComponentByPosition(0)
+
+    def testSetToDefault(self):
+        s = self.s1.clone()
+        s.setComponentByName('sex', univ.noValue)
+        assert s['sex'] is not univ.noValue
+
+    def testStaticDef(self):
+
+        class InnerChoice(univ.Choice):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('count', univ.Integer()),
+                namedtype.NamedType('flag', univ.Boolean())
+            )
+
+        class OuterChoice(univ.Choice):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('sex', InnerChoice())
+            )
+
+        c = OuterChoice()
+
+        c.setComponentByType(univ.OctetString.tagSet, 'abc')
+        assert c.getName() == 'name'
+
+    def testGetComponentWithDefault(self):
+
+        s = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('id', univ.Integer())
+            )
+        )
+
+        assert s.getComponentByPosition(0, default=None, instantiate=False) is None
+        assert s.getComponentByPosition(1, default=None, instantiate=False) is None
+        assert s.getComponentByName('name', default=None, instantiate=False) is None
+        assert s.getComponentByName('id', default=None, instantiate=False) is None
+        assert s.getComponentByType(univ.OctetString.tagSet, default=None) is None
+        assert s.getComponentByType(univ.Integer.tagSet, default=None) is None
+        s[1] = 123
+        assert s.getComponentByPosition(1, default=None) is not None
+        assert s.getComponentByPosition(1, univ.noValue) == 123
+        s.clear()
+        assert s.getComponentByPosition(1, default=None, instantiate=False) is None
+
+    def testGetComponentNoInstantiation(self):
+
+        s = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('id', univ.Integer())
+            )
+        )
+
+        assert s.getComponentByPosition(0, instantiate=False) is univ.noValue
+        assert s.getComponentByPosition(1, instantiate=False) is univ.noValue
+        assert s.getComponentByName('name', instantiate=False) is univ.noValue
+        assert s.getComponentByName('id', instantiate=False) is univ.noValue
+        assert s.getComponentByType(univ.OctetString.tagSet, instantiate=False) is univ.noValue
+        assert s.getComponentByType(univ.Integer.tagSet, instantiate=False) is univ.noValue
+        s[1] = 123
+        assert s.getComponentByPosition(1, instantiate=False) is not univ.noValue
+        assert s.getComponentByPosition(1, instantiate=False) == 123
+        s.clear()
+        assert s.getComponentByPosition(1, instantiate=False) is univ.noValue
+
+
+class ChoicePicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('id', univ.Integer())
+            )
+        )
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == univ.Choice
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = univ.Choice(
+            componentType=namedtype.NamedTypes(
+                namedtype.NamedType('name', univ.OctetString()),
+                namedtype.NamedType('id', univ.Integer())
+            )
+        )
+        old_asn1['name'] = 'test'
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1
+        assert new_asn1['name'] == str2octs('test')
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/type/test_useful.py b/tests/type/test_useful.py
new file mode 100644
index 0000000..c1d7832
--- /dev/null
+++ b/tests/type/test_useful.py
@@ -0,0 +1,143 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import datetime
+import pickle
+import sys
+from copy import deepcopy
+
+try:
+    import unittest2 as unittest
+
+except ImportError:
+    import unittest
+
+from tests.base import BaseTestCase
+
+from pyasn1.type import useful
+
+
+class FixedOffset(datetime.tzinfo):
+    def __init__(self, offset, name):
+        self.__offset = datetime.timedelta(minutes=offset)
+        self.__name = name
+
+    def utcoffset(self, dt):
+        return self.__offset
+
+    def tzname(self, dt):
+        return self.__name
+
+    def dst(self, dt):
+        return datetime.timedelta(0)
+
+
+UTC = FixedOffset(0, 'UTC')
+UTC2 = FixedOffset(120, 'UTC')
+
+
+class ObjectDescriptorTestCase(BaseTestCase):
+    pass
+
+
+class GeneralizedTimeTestCase(BaseTestCase):
+
+    def testFromDateTime(self):
+        assert useful.GeneralizedTime.fromDateTime(datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC)) == '20170711000102.3Z'
+
+    def testToDateTime0(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2) == useful.GeneralizedTime('20170711000102').asDateTime
+
+    def testToDateTime1(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC) == useful.GeneralizedTime('20170711000102Z').asDateTime
+
+    def testToDateTime2(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC) == useful.GeneralizedTime('20170711000102.3Z').asDateTime
+
+    def testToDateTime3(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC) == useful.GeneralizedTime('20170711000102,3Z').asDateTime
+
+    def testToDateTime4(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC) == useful.GeneralizedTime('20170711000102.3+0000').asDateTime
+
+    def testToDateTime5(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC2) == useful.GeneralizedTime('20170711000102.3+0200').asDateTime
+
+    def testToDateTime6(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC2) == useful.GeneralizedTime('20170711000102.3+02').asDateTime
+
+    def testToDateTime7(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1) == useful.GeneralizedTime('201707110001').asDateTime
+
+    def testToDateTime8(self):
+        assert datetime.datetime(2017, 7, 11, 0) == useful.GeneralizedTime('2017071100').asDateTime
+
+    def testCopy(self):
+        dt = useful.GeneralizedTime("20170916234254+0130").asDateTime
+        assert dt == deepcopy(dt)
+
+
+class GeneralizedTimePicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = useful.GeneralizedTime()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == useful.GeneralizedTime
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = useful.GeneralizedTime("20170916234254+0130")
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1 == old_asn1
+
+
+class UTCTimeTestCase(BaseTestCase):
+
+    def testFromDateTime(self):
+        assert useful.UTCTime.fromDateTime(datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC)) == '170711000102Z'
+
+    def testToDateTime0(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2) == useful.UTCTime('170711000102').asDateTime
+
+    def testToDateTime1(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC) == useful.UTCTime('170711000102Z').asDateTime
+
+    def testToDateTime2(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC) == useful.UTCTime('170711000102+0000').asDateTime
+
+    def testToDateTime3(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC2) == useful.UTCTime('170711000102+0200').asDateTime
+
+    def testToDateTime4(self):
+        assert datetime.datetime(2017, 7, 11, 0, 1) == useful.UTCTime('1707110001').asDateTime
+
+
+class UTCTimePicklingTestCase(unittest.TestCase):
+
+    def testSchemaPickling(self):
+        old_asn1 = useful.UTCTime()
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert type(new_asn1) == useful.UTCTime
+        assert old_asn1.isSameTypeWith(new_asn1)
+
+    def testValuePickling(self):
+        old_asn1 = useful.UTCTime("170711000102")
+        serialised = pickle.dumps(old_asn1)
+        assert serialised
+        new_asn1 = pickle.loads(serialised)
+        assert new_asn1 == old_asn1
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)