constraint groking code

diff --git a/libasn1fix/Makefile.am b/libasn1fix/Makefile.am
index 0e52b4f..9373254 100644
--- a/libasn1fix/Makefile.am
+++ b/libasn1fix/Makefile.am
@@ -1,36 +1,42 @@
 
-
 AM_CFLAGS = @ADD_CFLAGS@
-AM_CPPFLAGS = -I${top_srcdir}/libasn1parser
+AM_CPPFLAGS =				\
+	-I$(top_srcdir)/libasn1parser	\
+	-I$(top_srcdir)/libasn1cnst
 
 noinst_LTLIBRARIES = libasn1fix.la
 
 libasn1fix_la_LDFLAGS = -all-static
-libasn1fix_la_SOURCES =				\
-	asn1fix.c asn1fix.h			\
-	asn1fix_internal.h			\
-	asn1fix_misc.c asn1fix_misc.h		\
-	asn1fix_value.c asn1fix_value.h		\
-	asn1fix_compat.c asn1fix_compat.h	\
-	asn1fix_constr.c asn1fix_constr.h	\
-	asn1fix_cstring.c asn1fix_cstring.h	\
-	asn1fix_retrieve.c asn1fix_retrieve.h	\
-	asn1fix_bitstring.c asn1fix_bitstring.h	\
-	asn1fix_integer.c asn1fix_integer.h	\
-	asn1fix_dereft.c asn1fix_dereft.h	\
-	asn1fix_derefv.c asn1fix_derefv.h	\
-	asn1fix_export.c asn1fix_export.h	\
-	asn1fix_param.c asn1fix_param.h		\
-	asn1fix_class.c asn1fix_class.h		\
-	asn1fix_tags.c asn1fix_tags.h		\
-	asn1fix_enum.c asn1fix_enum.h
-libasn1fix_la_LIBADD = ${top_builddir}/libasn1parser/libasn1parser.la
+libasn1fix_la_SOURCES =					\
+	asn1fix.c asn1fix.h				\
+	asn1fix_internal.h				\
+	asn1fix_misc.c asn1fix_misc.h			\
+	asn1fix_value.c asn1fix_value.h			\
+	asn1fix_compat.c asn1fix_compat.h		\
+	asn1fix_constr.c asn1fix_constr.h		\
+	asn1fix_cstring.c asn1fix_cstring.h		\
+	asn1fix_retrieve.c asn1fix_retrieve.h		\
+	asn1fix_bitstring.c asn1fix_bitstring.h		\
+	asn1fix_constraint.c asn1fix_constraint.h	\
+	asn1fix_integer.c asn1fix_integer.h		\
+	asn1fix_crange.c asn1fix_crange.h		\
+	asn1fix_dereft.c asn1fix_dereft.h		\
+	asn1fix_derefv.c asn1fix_derefv.h		\
+	asn1fix_export.c asn1fix_export.h		\
+	asn1fix_param.c asn1fix_param.h			\
+	asn1fix_class.c asn1fix_class.h			\
+	asn1fix_tags.c asn1fix_tags.h			\
+	asn1fix_enum.c asn1fix_enum.h			\
+	asn1fix_constraint_compat.c
 
 check_PROGRAMS = check_fixer
 
-LDADD = ${noinst_LTLIBRARIES} ${libasn1fix_la_LIBADD}
-DEPENDENCIES = ${LDADD} 
+check_fixer_LDADD = $(noinst_LTLIBRARIES)		\
+	$(top_builddir)/libasn1cnst/libasn1cnst.la	\
+	$(top_builddir)/libasn1parser/libasn1parser.la
+check_fixer_DEPENDENCIES = $(check_fixer_LDADD)
 
 TESTS_ENVIRONMENT= ./check_fixer
 TESTS = ${top_srcdir}/tests/*.asn1
 ## TESTS = ${check_PROGRAMS}	# This is an alternate form of testing
+
diff --git a/libasn1fix/Makefile.in b/libasn1fix/Makefile.in
index 91b9428..80a91c0 100644
--- a/libasn1fix/Makefile.in
+++ b/libasn1fix/Makefile.in
@@ -50,21 +50,18 @@
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 LTLIBRARIES = $(noinst_LTLIBRARIES)
-libasn1fix_la_DEPENDENCIES =  \
-	${top_builddir}/libasn1parser/libasn1parser.la
+libasn1fix_la_LIBADD =
 am_libasn1fix_la_OBJECTS = asn1fix.lo asn1fix_misc.lo asn1fix_value.lo \
 	asn1fix_compat.lo asn1fix_constr.lo asn1fix_cstring.lo \
-	asn1fix_retrieve.lo asn1fix_bitstring.lo asn1fix_integer.lo \
-	asn1fix_dereft.lo asn1fix_derefv.lo asn1fix_export.lo \
-	asn1fix_param.lo asn1fix_class.lo asn1fix_tags.lo \
-	asn1fix_enum.lo
+	asn1fix_retrieve.lo asn1fix_bitstring.lo asn1fix_constraint.lo \
+	asn1fix_integer.lo asn1fix_crange.lo asn1fix_dereft.lo \
+	asn1fix_derefv.lo asn1fix_export.lo asn1fix_param.lo \
+	asn1fix_class.lo asn1fix_tags.lo asn1fix_enum.lo \
+	asn1fix_constraint_compat.lo
 libasn1fix_la_OBJECTS = $(am_libasn1fix_la_OBJECTS)
 check_fixer_SOURCES = check_fixer.c
 check_fixer_OBJECTS = check_fixer.$(OBJEXT)
-check_fixer_LDADD = $(LDADD)
 am__DEPENDENCIES_1 = libasn1fix.la
-am__DEPENDENCIES_2 = ${top_builddir}/libasn1parser/libasn1parser.la
-check_fixer_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
@@ -73,6 +70,9 @@
 @AMDEP_TRUE@	./$(DEPDIR)/asn1fix_class.Plo \
 @AMDEP_TRUE@	./$(DEPDIR)/asn1fix_compat.Plo \
 @AMDEP_TRUE@	./$(DEPDIR)/asn1fix_constr.Plo \
+@AMDEP_TRUE@	./$(DEPDIR)/asn1fix_constraint.Plo \
+@AMDEP_TRUE@	./$(DEPDIR)/asn1fix_constraint_compat.Plo \
+@AMDEP_TRUE@	./$(DEPDIR)/asn1fix_crange.Plo \
 @AMDEP_TRUE@	./$(DEPDIR)/asn1fix_cstring.Plo \
 @AMDEP_TRUE@	./$(DEPDIR)/asn1fix_dereft.Plo \
 @AMDEP_TRUE@	./$(DEPDIR)/asn1fix_derefv.Plo \
@@ -209,31 +209,39 @@
 target_os = @target_os@
 target_vendor = @target_vendor@
 AM_CFLAGS = @ADD_CFLAGS@
-AM_CPPFLAGS = -I${top_srcdir}/libasn1parser
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/libasn1parser	\
+	-I$(top_srcdir)/libasn1cnst
+
 noinst_LTLIBRARIES = libasn1fix.la
 libasn1fix_la_LDFLAGS = -all-static
 libasn1fix_la_SOURCES = \
-	asn1fix.c asn1fix.h			\
-	asn1fix_internal.h			\
-	asn1fix_misc.c asn1fix_misc.h		\
-	asn1fix_value.c asn1fix_value.h		\
-	asn1fix_compat.c asn1fix_compat.h	\
-	asn1fix_constr.c asn1fix_constr.h	\
-	asn1fix_cstring.c asn1fix_cstring.h	\
-	asn1fix_retrieve.c asn1fix_retrieve.h	\
-	asn1fix_bitstring.c asn1fix_bitstring.h	\
-	asn1fix_integer.c asn1fix_integer.h	\
-	asn1fix_dereft.c asn1fix_dereft.h	\
-	asn1fix_derefv.c asn1fix_derefv.h	\
-	asn1fix_export.c asn1fix_export.h	\
-	asn1fix_param.c asn1fix_param.h		\
-	asn1fix_class.c asn1fix_class.h		\
-	asn1fix_tags.c asn1fix_tags.h		\
-	asn1fix_enum.c asn1fix_enum.h
+	asn1fix.c asn1fix.h				\
+	asn1fix_internal.h				\
+	asn1fix_misc.c asn1fix_misc.h			\
+	asn1fix_value.c asn1fix_value.h			\
+	asn1fix_compat.c asn1fix_compat.h		\
+	asn1fix_constr.c asn1fix_constr.h		\
+	asn1fix_cstring.c asn1fix_cstring.h		\
+	asn1fix_retrieve.c asn1fix_retrieve.h		\
+	asn1fix_bitstring.c asn1fix_bitstring.h		\
+	asn1fix_constraint.c asn1fix_constraint.h	\
+	asn1fix_integer.c asn1fix_integer.h		\
+	asn1fix_crange.c asn1fix_crange.h		\
+	asn1fix_dereft.c asn1fix_dereft.h		\
+	asn1fix_derefv.c asn1fix_derefv.h		\
+	asn1fix_export.c asn1fix_export.h		\
+	asn1fix_param.c asn1fix_param.h			\
+	asn1fix_class.c asn1fix_class.h			\
+	asn1fix_tags.c asn1fix_tags.h			\
+	asn1fix_enum.c asn1fix_enum.h			\
+	asn1fix_constraint_compat.c
 
-libasn1fix_la_LIBADD = ${top_builddir}/libasn1parser/libasn1parser.la
-LDADD = ${noinst_LTLIBRARIES} ${libasn1fix_la_LIBADD}
-DEPENDENCIES = ${LDADD} 
+check_fixer_LDADD = $(noinst_LTLIBRARIES)		\
+	$(top_builddir)/libasn1cnst/libasn1cnst.la	\
+	$(top_builddir)/libasn1parser/libasn1parser.la
+
+check_fixer_DEPENDENCIES = $(check_fixer_LDADD)
 TESTS_ENVIRONMENT = ./check_fixer
 TESTS = ${top_srcdir}/tests/*.asn1
 all: all-am
@@ -302,6 +310,9 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1fix_class.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1fix_compat.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1fix_constr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1fix_constraint.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1fix_constraint_compat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1fix_crange.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1fix_cstring.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1fix_dereft.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1fix_derefv.Plo@am__quote@
diff --git a/libasn1fix/asn1fix.c b/libasn1fix/asn1fix.c
index af11099..dd0d5e7 100644
--- a/libasn1fix/asn1fix.c
+++ b/libasn1fix/asn1fix.c
@@ -17,6 +17,7 @@
 static int asn1f_fix_constructed(arg_t *arg);	/* For SEQUENCE/SET/CHOICE */
 static int asn1f_fix_constraints(arg_t *arg);	/* For subtype constraints */
 
+arg_t a1f_replace_me_with_proper_interface_arg;
 
 /*
  * Scan every module defined here in search for inconsistences.
@@ -53,6 +54,8 @@
 		flags &= ~A1F_DEBUG;
 	}
 
+	a1f_replace_me_with_proper_interface_arg = arg;
+
 	/*
 	 * Check that we haven't missed an unknown flag.
 	 */
@@ -74,6 +77,8 @@
 		if(ret == 1) warnings++;
 	}
 
+	memset(&a1f_replace_me_with_proper_interface_arg, 0, sizeof(arg_t));
+
 	/*
 	 * Compute a return value.
 	 */
@@ -88,8 +93,7 @@
 	asn1p_expr_t *expr;
 	int rvalue = 0;
 
-	switch((arg->mod->module_flags
-	& (MSF_EXPLICIT_TAGS | MSF_IMPLICIT_TAGS | MSF_AUTOMATIC_TAGS))) {
+	switch((arg->mod->module_flags & MSF_MASK_TAGS)) {
 	case MSF_NOFLAGS:
 	case MSF_EXPLICIT_TAGS:
 	case MSF_IMPLICIT_TAGS:
@@ -101,6 +105,24 @@
 		RET2RVAL(-1, rvalue);
 	}
 
+	switch((arg->mod->module_flags & MSF_MASK_INSTRUCTIONS)) {
+	case MSF_NOFLAGS:
+		//arg->mod->module_flags |= MSF_TAG_INSTRUCTIONS;
+		break;
+	case MSF_unk_INSTRUCTIONS:
+		WARNING("Module %s defined with unrecognized "
+			"encoding reference", arg->mod->Identifier);
+		RET2RVAL(1, rvalue);
+		/* Fall through */
+	case MSF_TAG_INSTRUCTIONS:
+	case MSF_XER_INSTRUCTIONS:
+		break;
+	default:
+		FATAL("Module %s defined with ambiguous encoding reference",
+			arg->mod->Identifier);
+		RET2RVAL(-1, rvalue);
+	}
+
 	/*
 	 * Do various non-recursive transformations.
 	 * Order is not important.
@@ -249,86 +271,34 @@
 }
 
 static int
-_constraint_value_resolve(arg_t *arg, asn1p_value_t **value) {
-	asn1p_expr_t expr;
-	asn1p_expr_t *tmp_expr;
-	asn1p_module_t *tmp_mod;
-	asn1p_module_t *mod_r = NULL;
-	int rvalue = 0;
-	int ret;
-
-	tmp_expr = asn1f_lookup_symbol(arg, (*value)->value.reference, &mod_r);
-	if(tmp_expr == NULL) {
-		FATAL("Cannot find symbol %s "
-			"used in %s subtype constraint at line %d",
-			asn1f_printable_reference((*value)->value.reference),
-			arg->expr->Identifier, arg->expr->_lineno);
-		assert((*value)->type == ATV_REFERENCED);
-		return -1;
-	}
-
-	memset(&expr, 0, sizeof(expr));
-	expr.meta_type = tmp_expr->meta_type;
-	expr.expr_type = tmp_expr->expr_type;
-	expr.Identifier = tmp_expr->Identifier;
-	expr.value = *value;
-	tmp_expr = arg->expr;
-	tmp_mod = arg->mod;
-	arg->expr = &expr;
-	arg->mod = mod_r;
-	ret = asn1f_fix_dereference_values(arg);
-	RET2RVAL(ret, rvalue);
-	arg->expr = tmp_expr;
-	arg->mod = tmp_mod;
-	assert(expr.value);
-	*value = expr.value;
-
-	return rvalue;
-}
-
-static int
-_resolve_constraints(arg_t *arg, asn1p_constraint_t *ct) {
-	int rvalue = 0;
-	int ret;
-	int el;
-
-	/* Don't touch information object classes */
-	if(ct->type == ACT_CT_WCOMP
-	|| ct->type == ACT_CT_WCOMPS
-	|| ct->type == ACT_CA_CRC)
-		return 0;
-
-	if(ct->value && ct->value->type == ATV_REFERENCED) {
-		ret = _constraint_value_resolve(arg, &ct->value);
-		RET2RVAL(ret, rvalue);
-	}
-	if(ct->range_start && ct->range_start->type == ATV_REFERENCED) {
-		ret = _constraint_value_resolve(arg, &ct->range_start);
-		RET2RVAL(ret, rvalue);
-	}
-	if(ct->range_stop && ct->range_stop->type == ATV_REFERENCED) {
-		ret = _constraint_value_resolve(arg, &ct->range_stop);
-		RET2RVAL(ret, rvalue);
-	}
-
-	for(el = 0; el < ct->el_count; el++) {
-		ret = _resolve_constraints(arg, ct->elements[el]);
-		RET2RVAL(ret, rvalue);
-	}
-
-	return rvalue;
-}
-
-static int
 asn1f_fix_constraints(arg_t *arg) {
+	asn1p_expr_t *top_parent;
 	int rvalue = 0;
 	int ret;
 
-	if(arg->expr->constraints) {
-		ret = _resolve_constraints(arg, arg->expr->constraints);
-		RET2RVAL(ret, rvalue);
-	}
+	ret = asn1constraint_resolve(arg, arg->expr->constraints);
+	RET2RVAL(ret, rvalue);
 
+	ret = asn1constraint_pullup(arg);
+	RET2RVAL(ret, rvalue);
+
+	top_parent = asn1f_find_terminal_type(arg, arg->expr, NULL);
+	if(top_parent) {
+		static enum asn1p_constraint_type_e test_types[] = {
+			ACT_EL_RANGE, ACT_CT_SIZE, ACT_CT_FROM };
+		unsigned int i;
+		for(i = 0; i < sizeof(test_types)/sizeof(test_types[0]); i++) {
+			asn1cnst_range_t *range;
+			range = asn1constraint_compute_PER_range(
+					top_parent->expr_type,
+					arg->expr->combined_constraints,
+					test_types[i], 0, 0);
+			if(!range && errno == EPERM)
+				return -1;
+			asn1constraint_range_free(range);
+		}
+	}
+	
 	return rvalue;
 }
 
diff --git a/libasn1fix/asn1fix.h b/libasn1fix/asn1fix.h
index 58ca537..1bed858 100644
--- a/libasn1fix/asn1fix.h
+++ b/libasn1fix/asn1fix.h
@@ -12,7 +12,7 @@
  */
 enum asn1f_flags {
 	A1F_NOFLAGS,
-	A1F_DEBUG,	/* Print debugging output using (_is_fatal = -1) */
+	A1F_DEBUG		= 0x01,	/* Print debugging output */
 };
 
 /*
diff --git a/libasn1fix/asn1fix_constraint.c b/libasn1fix/asn1fix_constraint.c
new file mode 100644
index 0000000..b7b66c4
--- /dev/null
+++ b/libasn1fix/asn1fix_constraint.c
@@ -0,0 +1,239 @@
+#include <asn1fix_internal.h>
+#include <asn1fix_constraint.h>
+#include <asn1fix_crange.h>
+
+static void _remove_exceptions(arg_t *arg, asn1p_constraint_t *ct);
+static int _constraint_value_resolve(arg_t *arg, asn1p_value_t **value);
+
+int
+asn1constraint_pullup(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_constraint_t *ct_parent;
+	asn1p_constraint_t *ct_expr;
+	int ret;
+
+	if(expr->combined_constraints)
+		return 0;	/* Operation already performed earlier */
+
+	switch(expr->meta_type) {
+	case AMT_TYPE:
+	case AMT_TYPEREF:
+		break;
+	default:
+		return 0;	/* Nothing to do */
+	}
+
+	if(expr->expr_type == A1TC_REFERENCE) {
+		asn1p_ref_t *ref = expr->reference;
+		asn1p_module_t *mod_rw = arg->mod;
+		asn1p_expr_t *parent_expr;
+
+		assert(ref);
+		parent_expr = asn1f_lookup_symbol(arg, ref, &mod_rw);
+		if(!parent_expr) {
+			if(errno != EEXIST) {
+				DEBUG("\tWhile fetching parent constraints: "
+					"type \"%s\" not found: %s",
+					asn1f_printable_reference(ref),
+					strerror(errno));
+				return -1;
+			} else {
+				/*
+				 * -fknown-extern-type is given.
+				 * Assume there are no constraints there.
+				 */
+				WARNING("External type \"%s\": "
+					"assuming no constraints",
+					asn1f_printable_reference(ref));
+				ct_parent = 0;
+			}
+		} else {
+			arg->expr = parent_expr;
+			ret = asn1constraint_pullup(arg);
+			arg->expr = expr;
+			if(ret) return ret;
+
+			ct_parent = parent_expr->combined_constraints;
+		}
+	} else {
+		ct_parent = 0;
+	}
+
+	ct_expr = expr->constraints;
+
+	if(!ct_parent && !ct_expr)
+		return 0;	/* No constraints to consider */
+
+	if(ct_parent) {
+		ct_parent = asn1p_constraint_clone(ct_parent);
+		assert(ct_parent);
+	}
+
+	/*
+	 * If the current type does not have constraints, it inherits
+	 * the constraints of a parent.
+	 */
+	if(ct_parent && !ct_expr) {
+		expr->combined_constraints = ct_parent;
+		return 0;
+	}
+
+	ct_expr = asn1p_constraint_clone(ct_expr);
+	assert(ct_expr);
+
+	/*
+	 * Now we have a set of current expression's constraints,
+	 * and an optional set of the parent expression's constraints.
+	 */
+
+	if(ct_parent) {
+		/*
+		 * If we have a parent, remove all the extensions (46.4).
+		 */
+		_remove_exceptions(arg, ct_parent);
+
+		expr->combined_constraints = ct_parent;
+		if(ct_expr->type == ACT_CA_SET) {
+			int i;
+			for(i = 0; i < ct_expr->el_count; i++) {
+				if(asn1p_constraint_insert(
+					expr->combined_constraints,
+						ct_expr->elements[i])) {
+					expr->combined_constraints = 0;
+					asn1p_constraint_free(ct_expr);
+					asn1p_constraint_free(ct_parent);
+					return -1;
+				} else {
+					ct_expr->elements[i] = 0;
+				}
+			}
+			asn1p_constraint_free(ct_expr);
+		} else {
+			asn1p_constraint_insert(expr->combined_constraints,
+				ct_expr);
+		}
+	} else {
+		expr->combined_constraints = ct_expr;
+	}
+
+	return 0;
+}
+
+int
+asn1constraint_resolve(arg_t *arg, asn1p_constraint_t *ct) {
+	asn1p_expr_t *top_parent;
+	int rvalue = 0;
+	int ret;
+	int el;
+
+	if(!ct) return 0;
+
+	/* Don't touch information object classes */
+	switch(ct->type) {
+	case ACT_CT_WCOMP:
+	case ACT_CT_WCOMPS:
+	case ACT_CA_CRC:
+		return 0;
+	default:
+		break;
+	}
+
+	top_parent = asn1f_find_terminal_type(arg, arg->expr, 0);
+	if(top_parent) {
+		ret = asn1constraint_compatible(top_parent->expr_type,
+			ct->type);
+		switch(ret) {
+		case -1:	/* If unknown, assume OK. */
+		case  1:
+			break;
+		case 0:
+		default:
+			FATAL("%s at line %d: "
+				"Constraint type %s is not applicable to %s",
+				arg->expr->Identifier, ct->_lineno,
+				asn1p_constraint_type2str(ct->type),
+				ASN_EXPR_TYPE2STR(top_parent->expr_type)
+			);
+			rvalue = -1;
+			break;
+		}
+	} else {
+		WARNING("%s at line %d: "
+			"Constraints ignored: Unresolved parent type",
+			arg->expr->Identifier, arg->expr->_lineno);
+	}
+
+	if(ct->value && ct->value->type == ATV_REFERENCED) {
+		ret = _constraint_value_resolve(arg, &ct->value);
+		RET2RVAL(ret, rvalue);
+	}
+	if(ct->range_start && ct->range_start->type == ATV_REFERENCED) {
+		ret = _constraint_value_resolve(arg, &ct->range_start);
+		RET2RVAL(ret, rvalue);
+	}
+	if(ct->range_stop && ct->range_stop->type == ATV_REFERENCED) {
+		ret = _constraint_value_resolve(arg, &ct->range_stop);
+		RET2RVAL(ret, rvalue);
+	}
+
+	for(el = 0; el < ct->el_count; el++) {
+		ret = asn1constraint_resolve(arg, ct->elements[el]);
+		RET2RVAL(ret, rvalue);
+	}
+
+	return rvalue;
+}
+
+static void
+_remove_exceptions(arg_t *arg, asn1p_constraint_t *ct) {
+	int i;
+
+	for(i = 0; i < ct->el_count; i++) {
+		if(ct->elements[i]->type == ACT_EL_EXT)
+			break;
+		_remove_exceptions(arg, ct->elements[i]);
+	}
+
+	/* Remove the elements at and after the extensibility mark */
+	for(; i < ct->el_count; ct->el_count--) {
+		asn1p_constraint_t *rm;
+		rm = ct->elements[ct->el_count-1];
+		asn1p_constraint_free(rm);
+	}
+
+	if(i < ct->el_size)
+		ct->elements[i] = 0;
+}
+
+
+static int
+_constraint_value_resolve(arg_t *arg, asn1p_value_t **value) {
+	asn1p_expr_t static_expr;
+	asn1p_expr_t *tmp_expr;
+	asn1p_module_t *mod_rw = arg->mod;
+	arg_t tmp_arg;
+	int rvalue = 0;
+	int ret;
+
+	tmp_expr = asn1f_lookup_symbol(arg, (*value)->value.reference, &mod_rw);
+	if(tmp_expr == NULL) {
+		FATAL("Cannot find symbol %s "
+			"used in %s subtype constraint at line %d",
+			asn1f_printable_reference((*value)->value.reference),
+			arg->expr->Identifier, arg->expr->_lineno);
+		assert((*value)->type == ATV_REFERENCED);
+		return -1;
+	}
+
+	static_expr = *tmp_expr;
+	static_expr.value = *value;
+	tmp_arg = *arg;
+	tmp_arg.mod = mod_rw;
+	tmp_arg.expr = &static_expr;
+	ret = asn1f_fix_dereference_values(&tmp_arg);
+	RET2RVAL(ret, rvalue);
+	assert(static_expr.value);
+	*value = static_expr.value;
+
+	return rvalue;
+}
diff --git a/libasn1fix/asn1fix_constraint.h b/libasn1fix/asn1fix_constraint.h
new file mode 100644
index 0000000..a04e6a5
--- /dev/null
+++ b/libasn1fix/asn1fix_constraint.h
@@ -0,0 +1,17 @@
+#ifndef	_ASN1FIX_CONSTRAINT_H_
+#define	_ASN1FIX_CONSTRAINT_H_
+
+/*
+ * Resolve referenced values inside constraints.
+ */
+int asn1constraint_resolve(arg_t *arg, asn1p_constraint_t *ct);
+
+/*
+ * Collect all subtype constraints from all parents of this type and
+ * the type itself, forming a full constraint structure.
+ * Honors the constraints extensibility rules (46.8)
+ * and does other useful transformations.
+ */
+int asn1constraint_pullup(arg_t *arg);
+
+#endif	/* _ASN1FIX_CONSTRAINT_H_ */
diff --git a/libasn1fix/asn1fix_constraint_compat.c b/libasn1fix/asn1fix_constraint_compat.c
new file mode 100644
index 0000000..28ebf92
--- /dev/null
+++ b/libasn1fix/asn1fix_constraint_compat.c
@@ -0,0 +1,181 @@
+#include <asn1fix_internal.h>
+#include <asn1fix_crange.h>
+
+/*
+ * Check that a specific constraint is compatible
+ * with the given expression type.
+ */
+int
+asn1constraint_compatible(asn1p_expr_type_e expr_type,
+	enum asn1p_constraint_type_e constr_type) {
+
+	/*
+	 * X.680-0207, Table 9.
+	 */
+
+	switch(constr_type) {
+	case ACT_INVALID:
+		return 0;
+	case ACT_EL_VALUE:
+		return 1;
+	case ACT_EL_RANGE:
+	case ACT_EL_LLRANGE:
+	case ACT_EL_RLRANGE:
+	case ACT_EL_ULRANGE:
+		switch(expr_type) {
+		case ASN_BASIC_ENUMERATED:
+		case ASN_BASIC_BOOLEAN:
+			/*
+			 * The ValueRange constraint is not formally
+			 * applicable to the above types. However, we
+			 * support it just fine.
+			 */
+			/* Fall through */
+		case ASN_BASIC_INTEGER:
+		case ASN_BASIC_REAL:
+			return 1;
+		default:
+			if(expr_type & ASN_STRING_MASK)
+				return 1;
+		}
+		return 0;
+	case ACT_EL_EXT:
+		return -1;
+	case ACT_CT_FROM:
+		if(expr_type & ASN_STRING_MASK)
+			return 1;
+		return 0;
+	case ACT_CT_SIZE:
+		switch(expr_type) {
+		case ASN_BASIC_BIT_STRING:
+		case ASN_BASIC_OCTET_STRING:
+		case ASN_BASIC_CHARACTER_STRING:
+		case ASN_CONSTR_SEQUENCE_OF:
+		case ASN_CONSTR_SET_OF:
+			return 1;
+		default:
+			if(expr_type & ASN_STRING_MASK)
+				return 1;
+		}
+		return 0;
+	case ACT_CT_WCOMP:
+	case ACT_CT_WCOMPS:
+		switch(expr_type) {
+		case A1TC_INSTANCE:
+		case ASN_BASIC_EXTERNAL:
+		case ASN_BASIC_EMBEDDED_PDV:
+		case ASN_BASIC_REAL:
+		case ASN_BASIC_CHARACTER_STRING:
+		case ASN_CONSTR_CHOICE:
+		case ASN_CONSTR_SEQUENCE:
+		case ASN_CONSTR_SEQUENCE_OF:
+		case ASN_CONSTR_SET:
+		case ASN_CONSTR_SET_OF:
+			return 1;
+		default: break;
+		}
+		return 0;
+	case ACT_CA_SET:
+	case ACT_CA_CRC:
+	case ACT_CA_CSV:
+	case ACT_CA_UNI:
+	case ACT_CA_INT:
+	case ACT_CA_EXC:
+		return 1;
+	}
+
+	return -1;
+}
+
+
+#define	DECL(foo, val1, val2)					\
+	static asn1cnst_range_t range_ ## foo = {		\
+			{ ARE_VALUE, 0, val1 },			\
+			{ ARE_VALUE, 0, val2 },			\
+			0, 0, 0, 0, 0, 0 };
+
+asn1cnst_range_t *
+asn1constraint_default_alphabet(asn1p_expr_type_e expr_type) {
+	DECL(uint7,	0x00, 0x7f);
+	DECL(uint8,	0x00, 0xff);
+	DECL(Space,	0x20, 0x20);
+	DECL(ApostropheAndParens, 0x27, 0x29);
+	DECL(PlusTillColon, 0x2b, 0x3a);
+	DECL(Equal,	0x3d, 0x3d);
+	DECL(QuestionMark, 0x3f, 0x3f);
+	DECL(Digits,	0x30, 0x39);
+	DECL(AlphaCap,	0x41, 0x5a);
+	DECL(AlphaLow,	0x61, 0x7a);
+	DECL(PlusCommaMinusDot, 0x2b, 0x2e);
+	DECL(Plus,	0x2b, 0x2b);
+	DECL(MinusDot,	0x2d, 0x2e);
+	DECL(Z,		0x5a, 0x5a);
+	static asn1cnst_range_t *range_NumericString_array[] = {
+			&range_Space, &range_Digits };
+	static asn1cnst_range_t *range_PrintableString_array[] = {
+			&range_Space,
+			&range_ApostropheAndParens,
+			&range_PlusTillColon,
+			&range_Equal,
+			&range_QuestionMark,
+			&range_AlphaCap,
+			&range_AlphaLow
+	};
+	static asn1cnst_range_t *range_UTCTime_array[] = {
+			&range_Plus, &range_MinusDot, &range_Digits, &range_Z };
+	static asn1cnst_range_t *range_GeneralizedTime_array[] = {
+			&range_PlusCommaMinusDot, &range_Digits, &range_Z };
+	static asn1cnst_range_t range_NumericString = {
+			{ ARE_VALUE, 0, 0x20 },
+			{ ARE_VALUE, 0, 0x39 },
+			range_NumericString_array,
+			sizeof(range_NumericString_array)
+				/sizeof(range_NumericString_array[0]),
+			0, 0, 0, 0 };
+	static asn1cnst_range_t range_PrintableString = {
+			{ ARE_VALUE, 0, 0x20 },
+			{ ARE_VALUE, 0, 0x7a },
+			range_PrintableString_array,
+			sizeof(range_PrintableString_array)
+				/sizeof(range_PrintableString_array[0]),
+			0, 0, 0, 0 };
+	static asn1cnst_range_t range_VisibleString = {
+			{ ARE_VALUE, 0, 0x20 },
+			{ ARE_VALUE, 0, 0x7e },
+			0, 0, 0, 0, 0, 0 };
+	static asn1cnst_range_t range_UTCTime = {
+			{ ARE_VALUE, 0, 0x2b },
+			{ ARE_VALUE, 0, 0x5a },
+			range_UTCTime_array,
+			sizeof(range_UTCTime_array)
+				/sizeof(range_UTCTime_array[0]),
+			0, 0, 0, 0 };
+	static asn1cnst_range_t range_GeneralizedTime = {
+			{ ARE_VALUE, 0, 0x2b },
+			{ ARE_VALUE, 0, 0x5a },
+			range_GeneralizedTime_array,
+			sizeof(range_GeneralizedTime_array)
+				/sizeof(range_GeneralizedTime_array[0]),
+			0, 0, 0, 0 };
+
+	switch(expr_type) {
+	case ASN_STRING_NumericString:
+		return &range_NumericString;
+	case ASN_STRING_PrintableString:
+		return &range_PrintableString;
+	case ASN_STRING_VisibleString:
+		return &range_VisibleString;
+	case ASN_STRING_IA5String:
+		return &range_uint7;
+	case ASN_BASIC_OCTET_STRING:
+		return &range_uint8;
+	case ASN_BASIC_UTCTime:
+		return &range_UTCTime;
+	case ASN_BASIC_GeneralizedTime:
+		return &range_GeneralizedTime;
+	default:
+		break;
+	}
+
+	return NULL;
+}
diff --git a/libasn1fix/asn1fix_crange.c b/libasn1fix/asn1fix_crange.c
new file mode 100644
index 0000000..17618ca
--- /dev/null
+++ b/libasn1fix/asn1fix_crange.c
@@ -0,0 +1,912 @@
+#include <asn1fix_internal.h>
+#include <asn1fix_constraint.h>
+#include <asn1fix_crange.h>
+
+#undef	FATAL
+#define	FATAL(fmt, args...)	do {			\
+		fprintf(stderr, "FATAL: ");		\
+		fprintf(stderr, fmt, ##args);		\
+		fprintf(stderr, "\n");			\
+	} while(0)
+
+void
+asn1constraint_range_free(asn1cnst_range_t *cr) {
+	if(cr) {
+		int i;
+		if(cr->elements) {
+			for(i = 0; i < cr->el_count; i++)
+				asn1constraint_range_free(cr->elements[i]);
+			free(cr->elements);
+		}
+		free(cr);
+	}
+}
+#define	_range_free(foo)	asn1constraint_range_free(foo)
+
+static asn1cnst_range_t *_range_new() {
+	asn1cnst_range_t *r;
+	r = calloc(1, sizeof(*r));
+	if(r) {
+		r->left.type = ARE_MIN;
+		r->right.type = ARE_MAX;
+	}
+	return r;
+}
+
+static void _range_remove_element(asn1cnst_range_t *range, int idx) {
+	assert(idx >= 0 && idx < range->el_count);
+
+	assert(!range->elements[idx]->elements);
+
+	_range_free(range->elements[idx]);
+
+	memmove(&range->elements[idx],
+		&range->elements[idx + 1],
+		(range->el_count - idx - 1)
+			* sizeof(range->elements[0])
+	);
+	range->el_count--;
+	range->elements[range->el_count] = 0;	/* JIC */
+
+	if(range->el_count == 0) {
+		range->el_size = 0;
+		free(range->elements);
+		range->elements = 0;
+	}
+}
+
+static int _range_insert(asn1cnst_range_t *into, asn1cnst_range_t *cr) {
+
+	assert(!cr->elements);
+
+	if(into->el_count == into->el_size) {
+		void *p;
+		int n = into->el_size?(into->el_size << 1):4;
+		p = realloc(into->elements, n * sizeof(into->elements[0]));
+		if(p) {
+			into->el_size = n;
+			into->elements = p;
+		} else {
+			assert(p);
+			return -1;
+		}
+	}
+
+	into->elements[into->el_count++] = cr;
+	return 0;
+}
+
+static asn1cnst_range_t *_range_clone(const asn1cnst_range_t *range) {
+	asn1cnst_range_t *clone;
+	int i;
+
+	clone = _range_new();
+	if(!clone) return NULL;
+
+	*clone = *range;
+	clone->elements = 0;
+	clone->el_count = 0;
+	clone->el_size = 0;
+
+	for(i = 0; i < range->el_count; i++) {
+		asn1cnst_range_t *r = _range_clone(range->elements[i]);
+		if(!r || _range_insert(clone, r)) {
+			_range_free(clone);
+			_range_free(r);
+			return NULL;
+		}
+	}
+
+	return clone;
+}
+
+static int
+_edge_compare(const asn1cnst_edge_t *el, const asn1cnst_edge_t *er) {
+
+	switch(el->type) {
+	case ARE_MIN:
+		switch(er->type) {
+		case ARE_MIN: return 0;
+		case ARE_MAX: return -1;
+		case ARE_VALUE: return -1;
+		}
+		break;
+	case ARE_MAX:
+		switch(er->type) {
+		case ARE_MIN: return 1;
+		case ARE_MAX: return 0;
+		case ARE_VALUE: return 1;
+		}
+		break;
+	case ARE_VALUE:
+		switch(er->type) {
+		case ARE_MIN: return 1;
+		case ARE_MAX: return -1;
+		case ARE_VALUE:
+			if(el->value < er->value)
+				return -1;
+			if(el->value > er->value)
+				return 1;
+			return 0;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int
+_range_compare(const void *a, const void *b) {
+	const asn1cnst_range_t *ra = *(const asn1cnst_range_t * const *)a;
+	const asn1cnst_range_t *rb = *(const asn1cnst_range_t * const *)b;
+	int ret;
+
+	ret = _edge_compare(&ra->left, &rb->left);
+	if(!ret) {
+		ret = _edge_compare(&ra->right, &rb->right);
+	}
+
+	return ret;
+}
+
+static char *
+_edge_value(const asn1cnst_edge_t *edge) {
+	static char buf[128];
+	*buf = '\0';
+	switch(edge->type) {
+	case ARE_MIN:	strcpy(buf, "MIN"); break;
+	case ARE_MAX:	strcpy(buf, "MAX"); break;
+	case ARE_VALUE:
+		snprintf(buf, sizeof(buf), "%lld", (long long)edge->value);
+	}
+	return buf;
+}
+
+static void
+_range_print(const asn1cnst_range_t *range) {
+
+	if(_edge_compare(&range->left, &range->right)) {
+		printf("(%s.", _edge_value(&range->left));
+		printf(".%s)", _edge_value(&range->right));
+	} else {
+		printf("(%s)", _edge_value(&range->left));
+	}
+
+	if(range->el_count) {
+		int i;
+		printf("-=>");
+		for(i = 0; i < range->el_count; i++)
+			_range_print(range->elements[i]);
+	}
+
+}
+
+static int
+_edge_is_within(const asn1cnst_range_t *range, const asn1cnst_edge_t *edge) {
+	int i;
+
+	for(i = -1; i < range->el_count; i++) {
+		const asn1cnst_range_t *r;
+		if(i == -1) {
+			if(range->el_count) continue;
+			r = range;
+		} else {
+			r = range->elements[i];
+		}
+		if(_edge_compare(&r->left, edge) <= 0
+		&& _edge_compare(&r->right, edge) >= 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+static int
+_check_edges_within(const asn1cnst_range_t *range, const asn1cnst_range_t *r) {
+
+	if(!_edge_is_within(range, &r->left)) {
+		FATAL("Constraint value %s at line %d "
+			"is not within "
+			"a parent constraint range",
+			_edge_value(&r->left),
+			r->left.lineno
+		);
+		return -1;
+	}
+
+	if(!_edge_is_within(range, &r->right)) {
+		FATAL("Constraint value %s at line %d "
+			"is not within "
+			"a parent constraint range",
+			_edge_value(&r->right),
+			r->right.lineno
+		);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int _range_merge_in(asn1cnst_range_t *into, asn1cnst_range_t *cr) {
+	asn1cnst_range_t *r;
+	int prev_count = into->el_count;
+	int i;
+
+	/*
+	 * Add the element OR all its children "into".
+	 */
+	for(i = -1; i < cr->el_count; i++) {
+
+		if(i == -1) {
+			if(cr->el_count) continue;
+			r = cr;
+		} else {
+			r = cr->elements[i];
+		}
+
+		if(_range_insert(into, r)) {
+			into->el_count = prev_count;	/* Undo */
+			return -1;
+		}
+	}
+
+	if(cr->el_count) {
+		cr->el_count = 0;
+		_range_free(cr);
+	} else {
+		/* This range is linked into "into". */
+	}
+
+	return 0;
+}
+
+static int _range_fill(asn1p_value_t *val, const asn1cnst_range_t *minmax, asn1cnst_edge_t *edge, asn1cnst_range_t *range, enum asn1p_constraint_type_e type, int lineno) {
+	unsigned char *p, *pend;
+
+	edge->lineno = lineno;
+
+	switch(val->type) {
+	case ATV_INTEGER:
+		if(type != ACT_EL_RANGE && type != ACT_CT_SIZE) {
+			FATAL("Integer %lld value invalid "
+				"for %s constraint at line %d",
+				(long long)val->value.v_integer,
+				asn1p_constraint_type2str(type), lineno);
+			return -1;
+		}
+		edge->type = ARE_VALUE;
+		edge->value = val->value.v_integer;
+		return 0;
+	case ATV_MIN:
+		if(type != ACT_EL_RANGE && type != ACT_CT_SIZE) {
+			FATAL("MIN invalid for %s constraint at line %d",
+				asn1p_constraint_type2str(type), lineno);
+			return -1;
+		}
+		edge->type = ARE_MIN;
+		if(minmax) *edge = minmax->left;
+		edge->lineno = lineno;	/* Restore lineno */
+		return 0;
+	case ATV_MAX:
+		if(type != ACT_EL_RANGE && type != ACT_CT_SIZE) {
+			FATAL("MAX invalid for %s constraint at line %d",
+				asn1p_constraint_type2str(type), lineno);
+			return -1;
+		}
+		edge->type = ARE_MAX;
+		if(minmax) *edge = minmax->right;
+		edge->lineno = lineno;	/* Restore lineno */
+		return 0;
+	case ATV_FALSE:
+	case ATV_TRUE:
+		if(type != ACT_EL_RANGE) {
+			FATAL("%s is invalid for %s constraint at line %d",
+				val->type==ATV_TRUE?"TRUE":"FALSE",
+				asn1p_constraint_type2str(type),
+				lineno);
+			return -1;
+		}
+		edge->type = ARE_VALUE;
+		edge->value = (val->type==ATV_TRUE);
+		return 0;
+	case ATV_STRING:
+		if(type != ACT_CT_FROM)
+			return 0;
+		break;
+	default:
+		FATAL("Unrecognized constraint element at line %d",
+			lineno);
+		return -1;
+	}
+
+	assert(val->type == ATV_STRING);
+
+	p = val->value.string.buf;
+	pend = p + val->value.string.size;
+	if(p == pend) return 0;
+
+	edge->type = ARE_VALUE;
+	if(val->value.string.size == 1) {
+		edge->value = *p;
+	} else {
+		/*
+		 * Else this is a set:
+		 * (FROM("abcdef"))
+		 * However, (FROM("abc".."def")) is forbidden.
+		 * See also 47.4.4.
+		 */
+		asn1_integer_t vmin, vmax;
+		vmin = vmax = *p;
+		for(; p < pend; p++) {
+			asn1cnst_range_t *nr = _range_new();
+			int ret;
+			assert(nr);
+
+			if(*p < vmin) vmin = *p;
+			if(*p > vmax) vmax = *p;
+
+			ret = _range_insert(range, nr);
+			assert(ret == 0);
+
+			nr->left.type = ARE_VALUE;
+			nr->left.value = *p;
+			nr->left.lineno = lineno;
+			nr->right = nr->left;
+		}
+		edge->value = (edge == &range->right) ? vmin : vmax;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if ranges contain common elements.
+ */
+static int
+_range_overlap(const asn1cnst_range_t *ra, const asn1cnst_range_t *rb) {
+	int lr, rl;
+	const asn1cnst_edge_t *ra_l = &ra->left;
+	const asn1cnst_edge_t *ra_r = &ra->right;
+	const asn1cnst_edge_t *rb_l = &rb->left;
+	const asn1cnst_edge_t *rb_r = &rb->right;
+
+	assert(_edge_compare(ra_l, ra_r) <= 0);
+	assert(_edge_compare(rb_l, rb_r) <= 0);
+
+	lr = _edge_compare(ra_l, rb_r);
+	rl = _edge_compare(ra_r, rb_l);
+
+	/*
+	 * L:       |---|
+	 * R: |---|
+	 */
+	if(lr > 0) return 0;
+
+	/*
+	 * L: |---|
+	 * R:       |---|
+	 */
+	if(rl < 0) return 0;
+
+	return 1;
+}
+
+/*
+ * (MIN..20) x (10..15) = (MIN..9,10..15,16..20)
+ */
+static asn1cnst_range_t *
+_range_split(asn1cnst_range_t *ra, const asn1cnst_range_t *rb) {
+	asn1cnst_range_t *range, *nr;
+	int ll, rr;
+
+	assert(ra);
+	assert(rb);
+	assert(!ra->el_count);
+	assert(!rb->el_count);
+
+	if(!_range_overlap(ra, rb)) {
+		errno = 0;
+		return 0;
+	}
+
+	ll = _edge_compare(&ra->left, &rb->left);
+	rr = _edge_compare(&ra->right, &rb->right);
+
+	/*
+	 * L:   |---|
+	 * R: |-------|
+	 */
+	if(ll >= 0 && rr <= 0) {
+		errno = 0;
+		return 0;
+	}
+
+	range = _range_new();
+	assert(range);
+
+	nr = _range_new();
+	assert(nr);
+
+	/*
+	 * L: |---...
+	 * R:   |--..
+	 */
+	if(ll < 0) {
+		nr->left = ra->left;
+		nr->right = rb->left;
+		if(nr->right.type == ARE_VALUE)
+			nr->right.value--;
+		_range_insert(range, nr);
+		nr = _range_new();
+		assert(nr);
+	}
+
+	/*
+	 * L: ...---|
+	 * R: ..--|
+	 */
+	if(rr > 0) {
+		nr->left = rb->right;
+		nr->right = ra->right;
+		if(nr->left.type == ARE_VALUE)
+			nr->left.value++;
+		_range_insert(range, nr);
+		nr = _range_new();
+		assert(nr);
+	}
+
+	/*
+	 * L:  |---|
+	 * R: |-----|
+	 */
+	nr->left = ra->left;
+	nr->right = ra->right;
+	if(_edge_compare(&ra->left, &rb->left) < 0)
+		nr->left = rb->left;
+	if(_edge_compare(&ra->right, &rb->right) > 0)
+		nr->right = rb->right;
+
+	_range_insert(range, nr);
+
+	return range;
+}
+
+static int
+_range_intersection(asn1cnst_range_t *range, const asn1cnst_range_t *with, int strict_edge_check) {
+	int ret;
+	int i, j;
+
+	if(with->empty_constraint || range->empty_constraint) {
+		range->empty_constraint = 1;	/* Propagate error */
+		return 0;
+	}
+
+	/*
+	 * This is an AND operation.
+	 */
+
+	/* If this is the only element, insert it into itself as a child */
+	if(range->el_count == 0) {
+		asn1cnst_range_t *r = _range_new();
+		r->left = range->left;
+		r->right = range->right;
+		_range_insert(range, r);
+		assert(range->el_count == 1);
+	}
+
+	/*
+	 * Make sure we're dealing with sane data.
+	 * G.4.2.3
+	 */
+	if(strict_edge_check) {
+		for(j = -1; j < with->el_count; j++) {
+
+			if(j == -1) {
+				if(with->el_count) continue;
+				if(_check_edges_within(range, with))
+					return -1;
+			} else {
+				if(_check_edges_within(range,
+						with->elements[j]))
+					return -1;
+			}
+		}
+	}
+
+	/*
+	 * Split range in pieces.
+	 */
+
+	for(i = 0; i < range->el_count; i++) {
+	  for(j = -1; j < with->el_count; j++) {
+		const asn1cnst_range_t *wel;
+		asn1cnst_range_t *r;
+
+		if(j == -1) {
+			if(with->el_count) continue;
+			wel = with;
+		} else {
+			wel = with->elements[j];
+		}
+
+		r = _range_split(range->elements[i], wel);
+		if(r) {
+			int ec;
+			/* Substitute the current element with a split */
+			_range_remove_element(range, i);
+			assert(r->el_count);
+			for(ec = 0; ec < r->el_count; ec++) {
+				ret = _range_insert(range, r->elements[ec]);
+				assert(ret == 0);
+			}
+			r->el_count = 0;
+			_range_free(r);
+			i--;
+			break;	/* Try again from this point */
+		}
+	  }
+	}
+
+	assert(range->el_count);
+
+	/*
+	 * Remove pieces which aren't AND-compatible "with" range.
+	 */
+
+	for(i = 0; i < range->el_count; i++) {
+		for(j = -1; j < with->el_count; j++) {
+			const asn1cnst_range_t *wel;
+	
+			if(j == -1) {
+				if(with->el_count) continue;
+				wel = with;
+			} else {
+				wel = with->elements[j];
+			}
+
+			if(_range_overlap(range->elements[i], wel))
+				break;
+		}
+		if(j == with->el_count) {
+			_range_remove_element(range, i);
+			i--;
+		}
+	}
+
+	if(range->el_count == 0)
+		range->empty_constraint = 1;
+
+	return 0;
+}
+
+static int
+_range_union(asn1cnst_range_t *range) {
+	int i;
+
+	qsort(range->elements, range->el_count, sizeof(range->elements[0]),
+		_range_compare);
+
+	/*
+	 * The range is sorted by the start values.
+	 */
+	for(i = 1; i < range->el_count; i++) {
+		asn1cnst_range_t *ra = range->elements[i - 1];
+		asn1cnst_range_t *rb = range->elements[i];
+
+		if(_range_overlap(ra, rb)) {
+			if(_edge_compare(&ra->left, &rb->left) < 0)
+				rb->left = ra->left;
+
+			if(_edge_compare(&ra->right, &rb->right) > 0)
+				rb->right = ra->right;
+		} else {
+			/*
+			 * Still, range may be joined: (1..4)(5..10).
+			 * This logic is valid only for whole numbers
+			 * (i.e., not REAL type, but REAL constraints
+			 * are not PER-visible (X.691, 9.3.12).
+			 */
+			if(ra->right.type == ARE_VALUE
+			&& rb->left.type == ARE_VALUE
+			&& (rb->left.value - ra->right.value) == 1) {
+				/* (1..10) */
+				rb->left = ra->left;
+			} else {
+				continue;
+			}
+		}
+
+		/*
+		 * Squeeze the array by removing the ra.
+		 */
+		_range_remove_element(range, i - 1);
+
+		i--;	/* Retry from the current point */
+	}
+
+	return 0;
+}
+
+static int
+_range_canonicalize(asn1cnst_range_t *range) {
+
+	if(range->el_count == 0) {
+		/*
+		 * Switch left and right edges, make them sorted.
+		 * It might be a mild warning though.
+		 */
+		if(_edge_compare(&range->left, &range->right) > 0) {
+			asn1cnst_edge_t tmp = range->left;
+			range->left = range->right;
+			range->right = tmp;
+		}
+
+		if(range->elements) {
+			free(range->elements);
+			range->elements = 0;
+		}
+		range->el_size = 0;
+		return 0;
+	}
+
+	/*
+	 * Remove duplicates and overlaps by merging them in.
+	 */
+	_range_union(range);
+
+	/* Refine the left edge of a parent */
+	range->left = range->elements[0]->left;
+
+	/* Refine the right edge of a parent */
+	range->right = range->elements[range->el_count - 1]->right;
+
+	/* Remove the child, if it's a single one */
+	if(range->el_count == 1) {
+		_range_remove_element(range, 0);
+	}
+
+	return 0;
+}
+
+asn1cnst_range_t *
+asn1constraint_compute_PER_range(asn1p_expr_type_e expr_type, const asn1p_constraint_t *ct, enum asn1p_constraint_type_e type, const asn1cnst_range_t *minmax, int *exmet) {
+	asn1cnst_range_t *range;
+	asn1cnst_range_t *tmp;
+	asn1p_value_t *vmin;
+	asn1p_value_t *vmax;
+	int expectation_met;
+	int ret;
+	int i;
+
+	if(!exmet) {
+		exmet = &expectation_met;
+		*exmet = 0;
+	}
+
+	/*
+	 * Check if the requested constraint is compatible with expression type.
+	 */
+	if(asn1constraint_compatible(expr_type, type) != 1) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	/* Check arguments' validity. */
+	switch(type) {
+	case ACT_EL_RANGE:
+		if(exmet == &expectation_met)
+			*exmet = 1;
+		break;
+	case ACT_CT_FROM:
+		if(!minmax) {
+			minmax = asn1constraint_default_alphabet(expr_type);
+			if(minmax) {
+				break;
+			}
+		}
+		/* Fall through */
+	case ACT_CT_SIZE:
+		if(!minmax) {
+			static asn1cnst_range_t mm;
+			mm.left.type = ARE_VALUE;
+			mm.left.value = 0;
+			mm.right.type = ARE_MAX;
+			minmax = &mm;
+		}
+		break;
+	default:
+		errno = EINVAL;
+		return 0;
+	}
+
+	if(minmax) {
+		range = _range_clone(minmax);
+	} else {
+		range = _range_new();
+	}
+
+	if(!ct || range->not_PER_visible)
+		return range;
+
+	switch(ct->type) {
+	case ACT_EL_VALUE:
+		vmin = vmax = ct->value;
+		break;
+	case ACT_EL_RANGE:
+	case ACT_EL_LLRANGE:
+	case ACT_EL_RLRANGE:
+	case ACT_EL_ULRANGE:
+		vmin = ct->range_start;
+		vmax = ct->range_stop;
+		break;
+	case ACT_EL_EXT:
+		if(!*exmet) {
+			range->not_PER_visible = 1;
+		} else {
+			range->extensible = 1;
+		}
+		return range;
+	case ACT_CT_SIZE:
+	case ACT_CT_FROM:
+		if(type == ct->type) {
+			*exmet = 1;
+		} else {
+			range->not_PER_visible = 1;
+			return range;
+		}
+		assert(ct->el_count == 1);
+		return asn1constraint_compute_PER_range(expr_type,
+			ct->elements[0], type, minmax, exmet);
+	case ACT_CA_SET:	/* (10..20)(15..17) */
+	case ACT_CA_INT:	/* SIZE(1..2) ^ FROM("ABCD") */
+
+		/* AND constraints, one after another. */
+		for(i = 0; i < ct->el_count; i++) {
+			tmp = asn1constraint_compute_PER_range(expr_type,
+				ct->elements[i], type,
+				ct->type==ACT_CA_SET?range:minmax, exmet);
+			if(!tmp) {
+				_range_free(range);
+				return NULL;
+			}
+
+			if(tmp->not_PER_visible) {
+				if(ct->type == ACT_CA_SET) {
+					/*
+					 * X.691, #9.3.18:
+					 * Ignore this separate component.
+					 */
+				} else {
+					/*
+					 * X.691, #9.3.19:
+					 * Ignore not PER-visible INTERSECTION
+					 */
+				}
+				_range_free(tmp);
+				continue;
+			}
+
+			range->extensible |= tmp->extensible;
+
+			if(tmp->extensible && type == ACT_CT_FROM) {
+				/*
+				 * X.691, #9.3.10:
+				 * Extensible permitted alphabet constraints
+				 * are not PER-visible.
+				 */
+				range->not_PER_visible = 1;
+			}
+
+			ret = _range_intersection(range, tmp,
+				ct->type == ACT_CA_SET);
+			_range_free(tmp);
+			if(ret) {
+				_range_free(range);
+				errno = EPERM;
+				return NULL;
+			}
+			_range_canonicalize(range);
+		}
+
+		return range;
+	case ACT_CA_CSV:	/* SIZE(1..2, 3..4) */
+	case ACT_CA_UNI:	/* SIZE(1..2) | FROM("ABCD") */
+
+		/* Merge everything. Canonicalizator will do union magic */
+		for(i = 0; i < ct->el_count; i++) {
+			tmp = asn1constraint_compute_PER_range(expr_type,
+				ct->elements[i], type, minmax, exmet);
+			if(!tmp) {
+				_range_free(range);
+				return NULL;
+			}
+
+			if(tmp->empty_constraint) {
+				/* Ignore empty constraints */
+				_range_free(tmp);
+				continue;
+			}
+
+			range->not_PER_visible |= tmp->not_PER_visible;
+			range->extensible |= tmp->extensible;
+
+			_range_merge_in(range, tmp);
+		}
+
+		_range_canonicalize(range);
+
+		if(range->not_PER_visible) {
+			/*
+			 * X.691, #9.3.19:
+			 * If not PER-visible constraint is part of UNION,
+			 * the resulting constraint is not PER-visible.
+			 */
+			_range_free(range);
+			if(minmax)
+				range = _range_clone(minmax);
+			else
+				range = _range_new();
+		}
+
+		return range;
+	case ACT_CA_EXC:	/* FROM("ABCD") EXCEPT FROM("AB") */
+		/*
+		 * X.691, #9.3.19:
+		 * EXCEPT and the following value set is completely ignored.
+		 */
+		assert(ct->el_count >= 1);
+		_range_free(range);
+		range = asn1constraint_compute_PER_range(expr_type,
+			ct->elements[0], type, minmax, exmet);
+		return range;
+	default:
+		range->not_PER_visible = 1;
+		return range;
+	}
+
+
+	if(!*exmet) {
+		/*
+		 * Expectation is not met. Return the default range.
+		 */
+		range->not_PER_visible = 1;
+		return range;
+	}
+
+	_range_free(range);
+	range = _range_new();
+
+	ret  = _range_fill(vmin, minmax, &range->left,
+				range, type, ct->_lineno);
+	ret |= _range_fill(vmax, minmax, &range->right,
+				range, type, ct->_lineno);
+	if(ret) {
+		_range_free(range);
+		errno = EPERM;
+		return NULL;
+	}
+
+	if(minmax) {
+		asn1cnst_range_t *clone;
+
+		clone = _range_clone(minmax);
+
+		/* Constrain parent type with given data. */
+		ret = _range_intersection(clone, range, 1);
+		_range_free(range);
+		if(ret) {
+			_range_free(clone);
+			errno = EPERM;
+			return NULL;
+		}
+		range = clone;
+	}
+
+	/*
+	 * Recompute elements's min/max, remove duplicates, etc.
+	 */
+	_range_canonicalize(range);
+
+	return range;
+}
+
diff --git a/libasn1fix/asn1fix_crange.h b/libasn1fix/asn1fix_crange.h
new file mode 100644
index 0000000..9873e3b
--- /dev/null
+++ b/libasn1fix/asn1fix_crange.h
@@ -0,0 +1,59 @@
+#ifndef	ASN1FIX_CRANGE_H
+#define	ASN1FIX_CRANGE_H
+
+typedef struct asn1cnst_edge_s {
+	enum asn1cnst_range_edge {
+		ARE_MIN,
+		ARE_MAX,
+		ARE_VALUE,
+	} type;
+	int lineno;	/* Line where the corresponding token was found */
+	asn1_integer_t value;	/* Value when type is ARE_VALUE */
+} asn1cnst_edge_t;
+
+typedef struct asn1cnst_range_s {
+	asn1cnst_edge_t left;	/* MIN from (MIN..10) */
+	asn1cnst_edge_t right;	/* 10 from (MIN..10) */
+
+	/* If range is split in parts, these are the parts */
+	struct asn1cnst_range_s **elements;
+	int el_count;
+	int el_size;
+
+	int empty_constraint;	/* If yes, too bad. */
+	int extensible;		/* Extension marker (...) present. */
+
+	int not_PER_visible;	/* Contains non PER-visible components */
+} asn1cnst_range_t;
+
+/*
+ * Compute the PER-visible constraint range.
+ * 
+ * (expr_type) must have the type of the top-level parent ASN.1 type.
+ * (required_type) must be one of ACT_EL_RANGE, ACT_CT_SIZE or ACT_CT_FROM.
+ * (minmax) and (expectation_met) should be 0.
+ * ERRORS:
+ * 	EINVAL:	Mandatory arguments missing.
+ * 	ENOMEM:	Memory allocation failure.
+ * 	EPERM:	Invalid constraint reference.
+ */
+asn1cnst_range_t *asn1constraint_compute_PER_range(asn1p_expr_type_e expr_type,
+	const asn1p_constraint_t *ct,
+	enum asn1p_constraint_type_e required_type,
+	const asn1cnst_range_t *minmax,
+	int *expectation_met);
+void asn1constraint_range_free(asn1cnst_range_t *);
+
+/*
+ * Check that a specific constraint is compatible
+ * with the given expression type.
+ */
+int asn1constraint_compatible(asn1p_expr_type_e expr_type,
+	enum asn1p_constraint_type_e constr_type);
+
+/*
+ * Fetch a default alphabet for this type.
+ */
+asn1cnst_range_t *asn1constraint_default_alphabet(asn1p_expr_type_e expr_type);
+
+#endif	/* ASN1FIX_CRANGE_H */
diff --git a/libasn1fix/asn1fix_export.c b/libasn1fix/asn1fix_export.c
index d6bb37b..cdae650 100644
--- a/libasn1fix/asn1fix_export.c
+++ b/libasn1fix/asn1fix_export.c
@@ -1,6 +1,9 @@
 #include "asn1fix_internal.h"
 #include "asn1fix_export.h"
 
+extern arg_t a1f_replace_me_with_proper_interface_arg;
+
+
 asn1p_expr_t *
 asn1f_lookup_symbol_ex(
 		asn1p_t *asn,
@@ -14,6 +17,9 @@
 	arg.asn = asn;
 	arg.mod = *module_rw;
 	arg.expr = expr;
+	arg.eh = a1f_replace_me_with_proper_interface_arg.eh;
+	arg.debug = a1f_replace_me_with_proper_interface_arg.debug;
+
 
 	return asn1f_lookup_symbol(&arg, ref, module_rw);
 }
@@ -24,11 +30,15 @@
 		asn1p_expr_t *expr,
 		asn1p_ref_t *ref,
 		asn1p_module_t **mod_r) {
-	static arg_t arg;
+	arg_t arg;
+
+	memset(&arg, 0, sizeof(arg));
 
 	arg.asn = asn;
 	arg.mod = mod;
 	arg.expr = expr;
+	arg.eh = a1f_replace_me_with_proper_interface_arg.eh;
+	arg.debug = a1f_replace_me_with_proper_interface_arg.debug;
 
 	return asn1f_class_access(&arg, ref, mod_r);
 }
@@ -38,11 +48,32 @@
 		asn1p_module_t *mod,
 		asn1p_expr_t *expr,
 		asn1p_module_t **mod_r) {
-	static arg_t arg;
+	arg_t arg;
+
+	memset(&arg, 0, sizeof(arg));
 
 	arg.asn = asn;
 	arg.mod = mod;
 	arg.expr = expr;
+	arg.eh = a1f_replace_me_with_proper_interface_arg.eh;
+	arg.debug = a1f_replace_me_with_proper_interface_arg.debug;
 
 	return asn1f_find_terminal_type(&arg, expr, mod_r);
 }
+
+int
+asn1f_fix_dereference_values_ex(asn1p_t *asn, asn1p_module_t *mod,
+        asn1p_expr_t *expr) {
+	arg_t arg;
+
+	memset(&arg, 0, sizeof(arg));
+
+	arg.asn = asn;
+	arg.mod = mod;
+	arg.expr = expr;
+	arg.eh = a1f_replace_me_with_proper_interface_arg.eh;
+	arg.debug = a1f_replace_me_with_proper_interface_arg.debug;
+
+	return asn1f_fix_dereference_values(&arg);
+}
+
diff --git a/libasn1fix/asn1fix_export.h b/libasn1fix/asn1fix_export.h
index 2ade0c9..7fd9ce1 100644
--- a/libasn1fix/asn1fix_export.h
+++ b/libasn1fix/asn1fix_export.h
@@ -8,6 +8,12 @@
 #include <asn1fix_tags.h>
 
 /*
+ * Create a human-readable representation of a reference and value.
+ */
+char const *asn1f_printable_reference(asn1p_ref_t *ref);
+char const *asn1f_printable_value(asn1p_value_t *value);
+
+/*
  *  Exportable version of an asn1f_lookup_symbol().
  */
 asn1p_expr_t *asn1f_lookup_symbol_ex(
@@ -28,5 +34,10 @@
 asn1p_expr_t *asn1f_find_terminal_type_ex(asn1p_t *asn, asn1p_module_t *mod,
 	asn1p_expr_t *tc, asn1p_module_t **opt_module_r);
 
-#endif	/* _ASN1FIX_EXPORT_H_ */
+/*
+ * Exportable version of asn1f_fix_dereference_values();
+ */
+int asn1f_fix_dereference_values_ex(asn1p_t *asn, asn1p_module_t *mod,
+	asn1p_expr_t *expr);
 
+#endif	/* _ASN1FIX_EXPORT_H_ */
diff --git a/libasn1fix/asn1fix_internal.h b/libasn1fix/asn1fix_internal.h
index ac543cb..c373963 100644
--- a/libasn1fix/asn1fix_internal.h
+++ b/libasn1fix/asn1fix_internal.h
@@ -16,6 +16,7 @@
 #include <assert.h>
 
 #include <asn1parser.h>		/* Our lovely ASN.1 parser module */
+#include <asn1fix.h>
 
 /*
  * A definition of a function that will log error messages.
@@ -32,6 +33,7 @@
 	error_logger_f   eh;
 	error_logger_f   debug;
 	void            *key;           /* The next level key */
+	enum asn1f_flags flags;
 } arg_t;
 
 /*
@@ -51,6 +53,9 @@
 #include "asn1fix_dereft.h"		/* Dereference types */
 #include "asn1fix_derefv.h"		/* Dereference values */
 #include "asn1fix_tags.h"		/* Tags-related stuff */
+#include "asn1fix_constraint.h"		/* Constraint manipulation */
+#include "asn1fix_crange.h"		/* Constraint groking, exportable */
+#include "asn1fix_export.h"		/* Exported functions */
 
 
 /*
diff --git a/libasn1fix/asn1fix_misc.h b/libasn1fix/asn1fix_misc.h
index a586faa..a90c5be 100644
--- a/libasn1fix/asn1fix_misc.h
+++ b/libasn1fix/asn1fix_misc.h
@@ -5,18 +5,6 @@
 #define	_ASN1FIX_MISC_H_
 
 /*
- * Return a pointer to the locally held string with human-readable
- * definition of the value.
- */
-char const *asn1f_printable_value(asn1p_value_t *);
-
-/*
- * Return a pointer to the locally held string with human-readable
- * definition of the reference.
- */
-char const *asn1f_printable_reference(asn1p_ref_t *);
-
-/*
  * Recursively invoke a given function over the given expr and all its
  * children.
  */
diff --git a/libasn1fix/asn1fix_value.c b/libasn1fix/asn1fix_value.c
index a22fd3d..a0560ed 100644
--- a/libasn1fix/asn1fix_value.c
+++ b/libasn1fix/asn1fix_value.c
@@ -21,6 +21,7 @@
 	 * 1. Find the terminal type for this assignment.
 	 */
 	type_expr = asn1f_find_terminal_type(arg, expr, 0);
+	DEBUG("%s(): terminal type %p", __func__, type_expr);
 	if(type_expr == 0) {
 		DEBUG("\tTerminal type for %s not found", expr->Identifier);
 		return -1;