aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE2
-rw-r--r--Makefile35
-rw-r--r--README2
-rw-r--r--common.h311
-rw-r--r--libnormalform.72
-rw-r--r--libnormalform.h836
-rw-r--r--libnormalform_all.c6
-rw-r--r--libnormalform_any.c6
-rw-r--r--libnormalform_apply_transformation__.c124
-rw-r--r--libnormalform_cdnf.c560
-rw-r--r--libnormalform_clause_cmp__.c24
-rw-r--r--libnormalform_clause_sort_cmp__.c33
-rw-r--r--libnormalform_clone.c8
-rw-r--r--libnormalform_cnf.c17
-rw-r--r--libnormalform_contains_clause__.c31
-rw-r--r--libnormalform_dnf.c17
-rw-r--r--libnormalform_express.c834
-rw-r--r--libnormalform_express__.c483
-rw-r--r--libnormalform_expression_to_term__.c82
-rw-r--r--libnormalform_fix_presentation__.c313
-rw-r--r--libnormalform_free.c6
-rw-r--r--libnormalform_free_expression__.c30
-rw-r--r--libnormalform_from_string.c4
-rw-r--r--libnormalform_get_relationship__.c23
-rw-r--r--libnormalform_literal_cmp__.c126
-rw-r--r--libnormalform_literal_sort_cmp__.c33
-rw-r--r--libnormalform_make_binary__.c31
-rw-r--r--libnormalform_one.c4
-rw-r--r--libnormalform_to_string.c4
-rw-r--r--libnormalform_transformation.c8
-rw-r--r--libnormalform_xor2.c128
l---------man3/enum_libnormalform_domain_relationship.31
l---------man3/enum_libnormalform_sentences_relationship.31
l---------man3/enum_libnormalform_ternary.31
-rw-r--r--man3/libnormalform_all.34
l---------man3/libnormalform_analysers.31
-rw-r--r--man3/libnormalform_and.34
-rw-r--r--man3/libnormalform_any.34
l---------man3/libnormalform_atom_comparison.31
-rw-r--r--man3/libnormalform_cdnf.3134
-rw-r--r--man3/libnormalform_clone.32
-rw-r--r--man3/libnormalform_cnf.3125
-rw-r--r--man3/libnormalform_dnf.3125
l---------man3/libnormalform_domain_comparison.31
l---------man3/libnormalform_domain_relationship.31
-rw-r--r--man3/libnormalform_express.31336
-rw-r--r--man3/libnormalform_false.32
-rw-r--r--man3/libnormalform_from_string.318
-rw-r--r--man3/libnormalform_function.32
-rw-r--r--man3/libnormalform_if.36
-rw-r--r--man3/libnormalform_imply.34
-rw-r--r--man3/libnormalform_nand.36
-rw-r--r--man3/libnormalform_nif.34
-rw-r--r--man3/libnormalform_nimply.36
-rw-r--r--man3/libnormalform_nor.36
-rw-r--r--man3/libnormalform_not.32
-rw-r--r--man3/libnormalform_one.34
-rw-r--r--man3/libnormalform_or.34
-rw-r--r--man3/libnormalform_ref.32
l---------man3/libnormalform_sentences_relationship.31
l---------man3/libnormalform_ternary.31
-rw-r--r--man3/libnormalform_to_string.34
-rw-r--r--man3/libnormalform_transformation.34
-rw-r--r--man3/libnormalform_true.32
-rw-r--r--man3/libnormalform_variable.32
-rw-r--r--man3/libnormalform_xnor.34
-rw-r--r--man3/libnormalform_xor.34
l---------man3/struct_libnormalform_analysers.31
l---------man3/struct_libnormalform_atom_comparison.31
l---------man3/struct_libnormalform_domain_comparison.31
-rw-r--r--memcheck.c26
71 files changed, 4939 insertions, 1042 deletions
diff --git a/LICENSE b/LICENSE
index fccd785..06a56b9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
ISC License
-© 2024 Mattias Andrée <maandree@kth.se>
+© 2024, 2025, 2026 Mattias Andrée <m@maandree.se>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/Makefile b/Makefile
index 30e8344..71df8ec 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,9 @@ OBJ_PUBLIC =\
libnormalform_clone.o\
libnormalform_evaluate.o\
libnormalform_express.o\
+ libnormalform_dnf.o\
+ libnormalform_cnf.o\
+ libnormalform_cdnf.o\
libnormalform_to_string.o\
libnormalform_from_string.o\
libnormalform_variable.o\
@@ -111,7 +114,7 @@ OBJ_PUBLIC =\
libnormalform_nor2.o\
libnormalform_xnor2.o\
libnormalform_nif2.o\
- libnormalform_nimply2.o\
+ libnormalform_nimply2.o
OBJ =\
$(OBJ_PUBLIC)\
@@ -119,7 +122,19 @@ OBJ =\
libnormalform_or2__.o\
libnormalform_xor2__.o\
libnormalform_set_indices_and_counts__.o\
- libnormalform_reset_indices_and_counts__.o
+ libnormalform_reset_indices_and_counts__.o\
+ libnormalform_express__.o\
+ libnormalform_free_expression__.o\
+ libnormalform_apply_transformation__.o\
+ libnormalform_make_binary__.o\
+ libnormalform_fix_presentation__.o\
+ libnormalform_literal_cmp__.o\
+ libnormalform_literal_sort_cmp__.o\
+ libnormalform_clause_cmp__.o\
+ libnormalform_clause_sort_cmp__.o\
+ libnormalform_contains_clause__.o\
+ libnormalform_expression_to_term__.o\
+ libnormalform_get_relationship__.o
TOBJ = $(OBJ:.o=.to)\
libnormalform_andl_macro_test.to\
@@ -164,7 +179,19 @@ MAN3 =\
libnormalform_value.3\
LIBNORMALFORM_SENTENCE.3\
struct_libnormalform_sentence.3\
- libnormalform_sentence.3
+ libnormalform_sentence.3\
+ enum_libnormalform_ternary.3\
+ libnormalform_ternary.3\
+ struct_libnormalform_atom_comparison.3\
+ libnormalform_atom_comparison.3\
+ struct_libnormalform_domain_comparison.3\
+ libnormalform_domain_comparison.3\
+ enum_libnormalform_domain_relationship.3\
+ libnormalform_domain_relationship.3\
+ enum_libnormalform_sentences_relationship.3\
+ libnormalform_sentences_relationship.3\
+ struct_libnormalform_analysers.3\
+ libnormalform_analysers.3
LOBJ = $(OBJ:.o=.lo)
TEST = $(TOBJ:.to=.t)
@@ -190,6 +217,8 @@ $L_nor2.to $L_norl.to $L_vnor.to $L_nor_$C.to $L_norl_$C.to $L_vn
$L_xnor2.to $L_xnorl.to $L_vxnor.to $L_xnor_$C.to $L_xnorl_$C.to $L_vxnor_$C.to $L_xnorl_macro_test.to : $L_xnor.c
$L_nif2.to $L_nifl.to $L_vnif.to $L_nif_$C.to $L_nifl_$C.to $L_vnif_$C.to $L_nifl_macro_test.to : $L_nif.c
$L_nimply2.to $L_nimplyl.to $L_vnimply.to $L_nimply_$C.to $L_nimplyl_$C.to $L_vnimply_$C.to $L_nimplyl_macro_test.to : $L_nimply.c
+libnormalform_literal_sort_cmp__.to: libnormalform_literal_cmp__.c
+libnormalform_clause_sort_cmp__.to: libnormalform_clause_cmp__.c
.c.o:
$(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
diff --git a/README b/README
index 37019cc..506463b 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
NAME
- libnormalform - First-order logic sentence canonicalisation library
+ libnormalform - First-order logic sentence normalisation library
SYNOPSIS
#include <libnormalform.h>
diff --git a/common.h b/common.h
index 68df99c..7b2eaa9 100644
--- a/common.h
+++ b/common.h
@@ -1,4 +1,7 @@
/* See LICENSE file for copyright and license details. */
+#ifndef COMMON_H
+#define COMMON_H
+
#ifndef LIBNORMALFORM_ALLOW_BAD_WARNINGS
# define LIBNORMALFORM_ALLOW_BAD_WARNINGS
#endif
@@ -13,6 +16,22 @@
#if defined(__GNUC__)
+# pragma GCC diagnostic ignored "-Wpadded"
+# if defined(__clang__)
+# pragma GCC diagnostic ignored "-Wcomma"
+# pragma GCC diagnostic ignored "-Wcovered-switch-default"
+# pragma GCC diagnostic ignored "-Wdisabled-macro-expansion" /* stupid, and exist in system header files */
+# pragma GCC diagnostic ignored "-Wunsafe-buffer-usage" /* clang is currently exceptionally stupid */
+# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" /* clang requires [[fallthrough]], we use GCC-compatible comments */
+# ifdef TEST
+# pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+# pragma GCC diagnostic ignored "-Wsentinel"
+# endif
+# endif
+#endif
+
+
+#if defined(__GNUC__)
# define PURE __attribute__((__pure__))
# define CONST __attribute__((__const__))
# define HIDDEN __attribute__((__visibility__("hidden")))
@@ -118,6 +137,16 @@
/**
+ * Normal forms
+ */
+enum normal_form {
+ NNF, /**< Negation normal form, optinally with XOR */
+ DNF, /**< Disjunctive normal form */
+ CNF /**< Conjunctive normal form */
+};
+
+
+/**
* Sentence classification
*/
enum type {
@@ -293,6 +322,86 @@ struct libnormalform_sentence {
/**
+ * Temporary data type for conversion between
+ * `LIBNORMALFORM_SENTENCE *` and `struct libnormalform_term *`
+ *
+ * For connective-clauses, `.nterms` can be any value
+ * (except exclusive disjunction is converted into
+ * inclusive disjunction if the clause becomes empty),
+ * and `.user_item` is `NULL`
+ *
+ * For literals, `.nterms` is 0, and the atomic
+ * sentence (which is application-provided) is
+ * stored in `.user_item`
+ *
+ * For transformations, `.nterms` is 1, and the
+ * input is stored in `.terms[0]`, whereas the
+ * function morphism (which may or may not be
+ * application-provided) is stored in `.user_item`
+ *
+ * For qualifications, the domain is stored in
+ * `.user_items` and `.nterms` is either 1 or 2;
+ * if 1 the antecedent and predicate are jointly
+ * expressed in `.terms[0]`, if 2 the antecedent
+ * is stored in `.terms[0]` and the predicate is
+ * stored in `.terms[1]`
+ */
+struct expression {
+ enum libnormalform_term_type type;
+
+ /**
+ * Whether the term is true more of often
+ * than the term it is used to represent
+ */
+ unsigned char reduced;
+
+ /**
+ * Whether the needs to be inverted,
+ * must be 1 or 0
+ */
+ unsigned char invert;
+
+ /**
+ * Number of references to this object
+ */
+ size_t refcount;
+
+ /**
+ * The number of subterms
+ */
+ size_t nterms;
+
+ /**
+ * List of subterms,
+ * see description of `struct expression`
+ */
+ struct expression **terms;
+
+ /**
+ * Non-sentence data for the term,
+ * see description of `struct expression`
+ */
+ void *user_item;
+};
+
+/**
+ * Intial values for `struct expression`
+ *
+ * @param TYPE:enum libnormalform_term_type The term's type
+ */
+#define EXPRESSION_INIT(TYPE)\
+ ((struct expression){\
+ .type = (TYPE),\
+ .reduced = 0,\
+ .invert = 0,\
+ .refcount = 1,\
+ .nterms = 0,\
+ .terms = NULL,\
+ .user_item = NULL\
+ })
+
+
+/**
* Helper macro to make macros type self, for macros that take sentences
*/
#define REQ_SENTENCE(OBJ, ASTERISKS, EXP) _Generic((OBJ), LIBNORMALFORM_SENTENCE ASTERISKS: (EXP))
@@ -440,6 +549,206 @@ size_t (libnormalform_set_indices_and_counts__)(LIBNORMALFORM_SENTENCE *this);
HIDDEN NONNULL_INPUT
void (libnormalform_reset_indices_and_counts__)(LIBNORMALFORM_SENTENCE *this);
+/**
+ * This function implements `libnormalform_express`,
+ * `libnormalform_dnf`, `libnormalform_cnf`, or
+ * `libnormalform_cdnf`, depending on `form` and
+ * `canonicalise`
+ *
+ * See above mentions functions for more information
+ *
+ * @param this The sentence to describe
+ * @param flags Refer to `libnormalform_express` (`LIBNORMALFORM_RELAX_XOR`
+ * is _not_ implied when `form` is `DNF` or `CNF`)
+ * @param analysers Refer to `libnormalform_express`
+ * @param form One of the following values:
+ * - NNF: Implement `libnormalform_express`
+ * - DNF: Implement `libnormalform_dnf`
+ * - CNF: Implement `libnormalform_cnf`
+ * @param canonicalise `NULL` or pointer to callback function to use
+ * to canonicalise the expression (used togather with
+ * `form == DNF` to implement `libnormalform_cdnf`);
+ * the first argument will be the final form of `this`
+ * before it is returned and should be modified in place,
+ * argument will be `flags`; the function shall return
+ * 0 on success and -1 on failure
+ * @return Refer to `libnormalform_express`
+ */
+HIDDEN USE_RESULT SOME_NONNULL_INPUT(1)
+struct libnormalform_term *(libnormalform_express__)(LIBNORMALFORM_SENTENCE *this, uint64_t flags,
+ const struct libnormalform_analysers *analysers, enum normal_form form,
+ int (*canonicalise)(struct expression *this, uint64_t flags));
+
+/**
+ * Deallocate `struct expression *`
+ *
+ * @param this The object to deallocate
+ */
+HIDDEN
+void (libnormalform_free_expression__)(struct expression *this);
+
+/**
+ * Apply a transformation on sentence, pushing it down
+ * to any already existing transformation (so that they
+ * stack up) or literal.
+ *
+ * It is distributed over clauses (applied to each term),
+ * and is dropped for any constant, boolean variable,
+ * or qualifier
+ *
+ * All objects must have a reference count of 1
+ *
+ * @param this The sentence to apply `transformer` on (in place)
+ * @param transformer The transformation function to apply over `this`
+ * @return 0 on success, -1 on failure
+ */
+HIDDEN USE_RESULT NONNULL_INPUT
+int (libnormalform_apply_transformation__)(struct expression *this, enum libnormalform_builtin_transformer transformer);
+
+/**
+ * Create a binary clause
+ *
+ * All objects must have a reference count of 1
+ *
+ * @param invert_left Whether `left` should be inverted (1, otherwise 0)
+ * @param left Desired left-hand proposition (inverted if `invert_left`)
+ * @param invert_right Whether `right` should be inverted (1, otherwise 0)
+ * @param right Desired right-hand proposition (inverted if `invert_right`)
+ * @param type The connective for the clause
+ * @return The described clause, `NULL` on failure
+ */
+HIDDEN USE_RESULT NONNULL_INPUT
+struct expression *(libnormalform_make_binary__)(int invert_left, struct expression *left,
+ int invert_right, struct expression *right,
+ enum libnormalform_term_type type);
+
+/**
+ * Apply any pending inversion and apply relaxation
+ * that is required for the application to be able
+ * to create a represention of the sentence
+ *
+ * All objects must have a reference count of 1
+ *
+ * @param this The sentence (modified in place)
+ * @param flags Flags from `libnormalform_express`
+ * @param require_inversion Pointer to the value 1 or 0, if it stores
+ * the value 1, there is in inversion required
+ * (possibility in addition to `this->invert`)
+ * which the function may choose to apply
+ * provided that this would not relax the
+ * sentence. If it stores the value 0, it
+ * is ignored. If the function applies the
+ * inversion marked by `require_inversion`,
+ * it will set the value to 0.
+ * @param reduced Should point to the value 0 (if called from,
+ * another function), and will be set to 1
+ * if the sentence was modified such that it
+ * is not logically equivalent to input sentence
+ * @return 0 on success, -1 on failure
+ */
+HIDDEN USE_RESULT NONNULL_INPUT
+int (libnormalform_fix_presentation__)(struct expression *this, uint64_t flags, unsigned char *require_inversion, int *reduced);
+
+/**
+ * Compare two `struct expression`'s, which are literals
+ * or qualifiers, for sorting, where both items have one
+ * level of indirection
+ *
+ * For any items x and y such that x ≺ y, x ≺ ¬x ≺ y ≺ ¬y
+ *
+ * @param a Left-hand item
+ * @param b Rigth-hand item
+ * @return Negative if the left-hand should be first,
+ * positive if the right-hand should be first
+ * 0 if the items are identical; the absolute
+ * value is 1 i the terms are each other's
+ * inverse, 2 they are otherwise different
+ */
+HIDDEN PURE USE_RESULT NONNULL_INPUT
+int (libnormalform_literal_cmp__)(struct expression *a, struct expression *b);
+
+/**
+ * Compare two `struct expression`'s, which are literals
+ * or qualifiers, for sorting, where both items have two
+ * levels of indirection
+ *
+ * For any items x and y such that x ≺ y, x ≺ ¬x ≺ y ≺ ¬y
+ *
+ * @param a Left-hand item
+ * @param b Rigth-hand item
+ * @return Negative if the left-hand should be first,
+ * positive if the right-hand should be first
+ * 0 if the items are identical; the absolute
+ * value is 1 i the terms are each other's
+ * inverse, 2 they are otherwise different
+ */
+HIDDEN PURE USE_RESULT NONNULL_INPUT
+int (libnormalform_literal_sort_cmp__)(const void *a, const void *b);
+
+/**
+ * Compare two `struct expression`'s, which are clauses,
+ * for sorting, where both items have one level of indirection
+ *
+ * @param a Left-hand item
+ * @param b Rigth-hand item
+ * @return -1 if the left-hand should be first,
+ * +1 if the right-hand should be first
+ * 0 if the items are identical
+ */
+HIDDEN PURE USE_RESULT NONNULL_INPUT
+int (libnormalform_clause_cmp__)(struct expression *a, struct expression *b);
+
+/**
+ * Compare two `struct expression`'s, which are clauses,
+ * for sorting, where both items have two levels of indirection
+ *
+ * @param a Left-hand item
+ * @param b Rigth-hand item
+ * @return -1 if the left-hand should be first,
+ * +1 if the right-hand should be first
+ * 0 if the items are identical
+ */
+HIDDEN PURE USE_RESULT NONNULL_INPUT
+int (libnormalform_clause_sort_cmp__)(const void *a, const void *b);
+
+/**
+ * Check whether one clause contains another clause
+ *
+ * @param this The proposed superclause, most be recursively sorted
+ * @param clause The proposed subclause, most be sorted
+ * @param ignore Index of term to ignore in `this`
+ * @return 1 if `this` contains `clause`, 0 otherwise
+ */
+HIDDEN PURE USE_RESULT NONNULL_INPUT
+int (libnormalform_contains_clause__)(struct expression *this, struct expression *clause, size_t ignore);
+
+/**
+ * Convert a sentence expressed in the temporary type
+ * `struct expression *` into the application-readable
+ * type `struct libnormalform_term *` without any
+ * transformations of the sentece
+ *
+ * @param out Output parameter for the application-readable representation
+ * @param this The sentence to convert
+ * @return 0 on success, -1 on failure
+ */
+HIDDEN USE_RESULT NONNULL_INPUT
+int (libnormalform_expression_to_term__)(struct libnormalform_term *out, struct expression *this);
+
+/**
+ * Get the dependency of the values between two sentences
+ *
+ * @param left The left-hand sentence
+ * @param right The right-hand sentence
+ * @param analysers Application-provided analysis functions, or `NULL`
+ * @param relationship_out Output parameter for the relationship
+ * @return 0 on success, -1 on failure
+ */
+HIDDEN PURE USE_RESULT SOME_NONNULL_INPUT(1, 2, 4)
+int (libnormalform_get_relationship__)(struct expression *left, struct expression *right,
+ const struct libnormalform_analysers *analysers,
+ enum libnormalform_sentence_relationship *relationship_out);
+
#ifdef DEBUG
/* libnormalform_to_string.c */
@@ -630,3 +939,5 @@ vatrampoline(LIBNORMALFORM_SENTENCE *(*f)(size_t, LIBNORMALFORM_SENTENCE *, va_l
DO_TEST /* no indent before, breaking the word TO|DO to hide from grep */ CONST int main(void) {return 0;}
#endif
+
+#endif
diff --git a/libnormalform.7 b/libnormalform.7
index 1607b34..88cfc91 100644
--- a/libnormalform.7
+++ b/libnormalform.7
@@ -1,6 +1,6 @@
.TH LIBNORMALFORM 7 LIBNORMALFORM
.SH NAME
-libnormalform \- First-order logic sentence canonicalisation library
+libnormalform \- First-order logic sentence normalisation library
.SH SYNOPSIS
.nf
diff --git a/libnormalform.h b/libnormalform.h
index ed3b256..c618485 100644
--- a/libnormalform.h
+++ b/libnormalform.h
@@ -10,6 +10,12 @@
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Winline"
+# pragma GCC diagnostic ignored "-Wpadded"
+# pragma GCC diagnostic ignored "-Wvla"
+# if defined(__clang__)
+# pragma GCC diagnostic ignored "-Wdocumentation"
+# pragma GCC diagnostic ignored "-Wunsafe-buffer-usage" /* clang is currently exceptionally stupid */
+# endif
#endif
@@ -17,7 +23,11 @@
#if defined(__GNUC__)
# define LIBNORMALFORM_USE_RESULT__ __attribute__((__warn_unused_result__))
-# define LIBNORMALFORM_FREE_WITH__(DESTRUCTOR) __attribute__((__malloc__(DESTRUCTOR)))
+# if defined(__clang__)
+# define LIBNORMALFORM_FREE_WITH__(DESTRUCTOR)
+# else
+# define LIBNORMALFORM_FREE_WITH__(DESTRUCTOR) __attribute__((__malloc__(DESTRUCTOR)))
+# endif
# define LIBNORMALFORM_USE_FREE__ __attribute__((__malloc__))
# define LIBNORMALFORM_NONNULL_INPUT__ __attribute__((__nonnull__))
# define LIBNORMALFORM_ONE_NONNULL_INPUT__(INDEX) __attribute__((__nonnull__(INDEX)))
@@ -148,8 +158,8 @@ struct libnormalform_term;
#define LIBNORMALFORM_AVOID_NEGATED_FOR_ALL (UINT64_C(1) << 1) /**< Prefer ∃x.¬φ over ¬∀x.φ */
#define LIBNORMALFORM_AVOID_FOR_ANY (UINT64_C(1) << 2) /**< Prefer ¬∀x.¬φ over ∃x.φ */
#define LIBNORMALFORM_AVOID_NEGATED_FOR_ANY (UINT64_C(1) << 3) /**< Prefer ∀x.¬φ over ¬∃x.φ */
-#define LIBNORMALFORM_RELAX_FOR_ONE (UINT64_C(1) << 4) /**< Relax any positive ∃!x.φ into ∃x.φ */
-#define LIBNORMALFORM_REDUCE_XOR (UINT64_C(1) << 5) /**< Rewrite XOR to negation normal form */
+#define LIBNORMALFORM_REDUCE_XOR (UINT64_C(1) << 4) /**< Rewrite XOR to negation normal form */
+#define LIBNORMALFORM_RELAX_XOR (UINT64_C(1) << 5) /**< Relax any non-reduce XOR to OR */
#define LIBNORMALFORM_JOIN_SIDES_IN_FOR_ALL (UINT64_C(1) << 6) /**< Express φ(x)∀x:θ(x) on the form ∀x.(θ(x) → φ(x)) */
#define LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ALL (UINT64_C(1) << 7) /**< Express ¬(φ(x)∀x:θ(x)) on the form ¬∀x.(θ(x) → φ(x)) */
#define LIBNORMALFORM_JOIN_SIDES_IN_FOR_ANY (UINT64_C(1) << 8) /**< Express φ(x)∃x:θ(x) on the form ∃x.(θ(x) ∧ φ(x)) */
@@ -166,7 +176,7 @@ struct libnormalform_term;
#define LIBNORMALFORM_ELIMINATE_NEGATED_VARIABLE (UINT64_C(1) << 19) /**< Relax any negative variable literal to ⊤ */
#define LIBNORMALFORM_ELIMINATE_FUNCTION (UINT64_C(1) << 20) /**< Relax any positive function literal to ⊤ */
#define LIBNORMALFORM_ELIMINATE_NEGATED_FUNCTION (UINT64_C(1) << 21) /**< Relax any negative function literal to ⊤ */
-#define LIBNORMALFORM_ELIMINATE_XOR (UINT64_C(1) << 22) /**< Relax any non-reduce XOR to ⊤ */
+#define LIBNORMALFORM_DISTRIBUTE_QUALIFIERS (UINT64_C(1) << 21) /**< Duplicate qualifers to move out clauses */
#define LIBNORMALFORM_ALL_EXPRESS_FLAGS__ ((UINT64_C(1) << 23) - 1U) /* For internal use */
@@ -183,81 +193,13 @@ typedef struct libnormalform_sentence LIBNORMALFORM_SENTENCE;
/**
- * Specification for how application provided strings
- * shall be interpreted
- */
-struct libnormalform_representation_spec {
- /**
- * Application-specific data that will be passed into
- * `.get_variable`, `.get_function`, `.get_map`, and
- * `.get_transformer` (making it reenterant so it can be
- * called by multiple threads, or to configure the function)
- *
- * May be `NULL`
- */
- void *user_data;
-
- /**
- * Deserialise a variable from a unterminated string
- *
- * The function shall be able to return the reference
- * to any `struct libnormalform_variable` given its
- * `.identifier` sans NUL-termination
- *
- * The function shall be pure in the sence that if
- * it is called twice (or more) with the identifer
- * at the beginning of `s` it shall return the same
- * pointer, not two different copies (`user_data` be
- * use used to store the variables if they are
- * constructed dynamically)
- *
- * May be `NULL`
- *
- * @param s The string beginning with the variable's
- * identifier
- * @param end_out Output parameter for the position in
- * `s` after the last byte in the identifer
- * @param user_data `.user_data`
- * @return A pointer to the referenced variable, `NULL`
- * on failure (`errno` may be set)
- *
- * It is recommended that, on failure, `errno` is set
- * according to the below specification, however it is
- * up to the application to choose what to do with `errno`
- *
- * @throws EINVAL `s` in not formatted to represent a valid variable
- * @throws ENOENT `s` does not represent any known variable
- */
- struct libnormalform_variable *(*get_variable)(char *s, char **end_out, void *user_data);
-
- /**
- * Same as `.get_variable` except that it returns
- * reference to a function (`struct libnormalform_function *`)
- */
- struct libnormalform_function *(*get_function)(char *s, char **end_out, void *user_data);
-
- /**
- * Same as `.get_variable` except that it returns
- * reference to a domain (`struct libnormalform_map *`)
- */
- struct libnormalform_map *(*get_map)(char *s, char **end_out, void *user_data);
-
- /**
- * Same as `.get_transformer` except that it returns
- * reference to a function (`struct libnormalform_transformer *`)
- */
- struct libnormalform_transformer *(*get_transformer)(char *s, char **end_out, void *user_data);
-};
-
-
-/**
* Type if a `struct libnormalform_term`, used to
* determine which member of `.term` to use and
* whether it is negated
*/
enum libnormalform_term_type {
- LIBNORMALFORM_CONJUNCTION, /**< AND clause; empty clause is used for contradiction */
- LIBNORMALFORM_DISJUNCTION, /**< OR clause; empty clause is used for tautology */
+ LIBNORMALFORM_CONJUNCTION, /**< AND clause; empty clause is used for tautology */
+ LIBNORMALFORM_DISJUNCTION, /**< OR clause; empty clause is used for contradiction */
LIBNORMALFORM_EXCLUSIVE_DISJUNCTION, /**< XOR clause; never used for empty clauses */
LIBNORMALFORM_TRANSFORMATION, /**< Transformater function with input **/
LIBNORMALFORM_VARIABLE, /**< Positive variable literal **/
@@ -293,9 +235,19 @@ enum libnormalform_builtin_transformer {
/**
+ * Boolean with possibly of being unknown
+ */
+enum libnormalform_ternary {
+ LIBNORMALFORM_NO, /**< No/False */
+ LIBNORMALFORM_MAYBE, /**< Maybe/Unknown */
+ LIBNORMALFORM_YES /**< Yes/True */
+};
+
+
+/**
* Relationship (dependency) between the value of two sentences
*/
-enum libnormalform_sentences_relationship {
+enum libnormalform_sentence_relationship {
/**
* Left-hand implies right-hand (⊨ P → Q)
*
@@ -718,7 +670,107 @@ enum libnormalform_domain_relationship {
* - ⋁A → ⋁B cannot be simplified
* - ⋁A ← ⋁B cannot be simplified
*/
- LIBNORMALFORM_CONJOINT_WITH
+ LIBNORMALFORM_CONJOINT_WITH,
+
+ /**
+ * Both sets have no formal relationship
+ *
+ * Venn diagram of possibly non-empty intersections:
+ * ┌────────────────────────────┐
+ * │ ┌─A─────────────┐ │
+ * │ │███████████████│ │
+ * │ │██████┌────────┼──────┐ │
+ * │ │██████│████████│██████│ │
+ * │ │██████│████████│██████│ │
+ * │ └──────┼────────┘██████│ │
+ * │ │███████████████│ │
+ * │ └─────────────B─┘ │
+ * └────────────────────────────┘
+ *
+ * Consequence:
+ * - A ∩ B cannot be simplified
+ * - A ∪ B cannot be simplified
+ * - A ∆ B cannot be simplified
+ * - ⋀A ∧ ⋀B = ⋀(A ∪ B)
+ * - ⋀A ∨ ⋀B cannot be simplified
+ * - ⋀A ⊕ ⋀B cannot be simplified
+ * - ⋀A → ⋀B cannot be simplified
+ * - ⋀A ← ⋀B cannot be simplified
+ * - ⋁A ∧ ⋁B cannot be simplified
+ * - ⋁A ∨ ⋁B = ⋁(A ∪ B)
+ * - ⋁A ⊕ ⋁B cannot be simplified
+ * - ⋁A → ⋁B cannot be simplified
+ * - ⋁A ← ⋁B cannot be simplified
+ */
+ LIBNORMALFORM_UNRELATED_TO
+};
+
+
+/**
+ * Specification for how application provided strings
+ * shall be interpreted
+ */
+struct libnormalform_representation_spec {
+ /**
+ * Application-specific data that will be passed into
+ * `.get_variable`, `.get_function`, `.get_map`, and
+ * `.get_transformer` (making it reenterant so it can be
+ * called by multiple threads, or to configure the function)
+ *
+ * May be `NULL`
+ */
+ void *user_data;
+
+ /**
+ * Deserialise a variable from a unterminated string
+ *
+ * The function shall be able to return the reference
+ * to any `struct libnormalform_variable` given its
+ * `.identifier` sans NUL-termination
+ *
+ * The function shall be pure in the sence that if
+ * it is called twice (or more) with the identifer
+ * at the beginning of `s` it shall return the same
+ * pointer, not two different copies (`user_data` be
+ * use used to store the variables if they are
+ * constructed dynamically)
+ *
+ * May be `NULL`
+ *
+ * @param s The string beginning with the variable's
+ * identifier
+ * @param end_out Output parameter for the position in
+ * `s` after the last byte in the identifer
+ * @param user_data `.user_data`
+ * @return A pointer to the referenced variable, `NULL`
+ * on failure (`errno` may be set)
+ *
+ * It is recommended that, on failure, `errno` is set
+ * according to the below specification, however it is
+ * up to the application to choose what to do with `errno`
+ *
+ * @throws EINVAL `s` in not formatted to represent a valid variable
+ * @throws ENOENT `s` does not represent any known variable
+ */
+ struct libnormalform_variable *(*get_variable)(char *s, char **end_out, void *user_data);
+
+ /**
+ * Same as `.get_variable` except that it returns
+ * reference to a function (`struct libnormalform_function *`)
+ */
+ struct libnormalform_function *(*get_function)(char *s, char **end_out, void *user_data);
+
+ /**
+ * Same as `.get_variable` except that it returns
+ * reference to a domain (`struct libnormalform_map *`)
+ */
+ struct libnormalform_map *(*get_map)(char *s, char **end_out, void *user_data);
+
+ /**
+ * Same as `.get_transformer` except that it returns
+ * reference to a function (`struct libnormalform_transformer *`)
+ */
+ struct libnormalform_transformer *(*get_transformer)(char *s, char **end_out, void *user_data);
};
@@ -1030,26 +1082,507 @@ struct libnormalform_map {
};
-struct libnormalform_atom_comparsion {
+/**
+ * Variable/function (atom sentence) comparison results
+ */
+struct libnormalform_atom_comparison {
+ /**
+ * The library will set this to the version of the `struct`
+ * that it uses, but before returning, if and only if the
+ * application uses an older version, it shall set the
+ * version number here
+ *
+ * Current version number is 1
+ *
+ * @since `.version == 1`
+ */
int version;
- enum libnormalform_sentences_relationship relationship;
+
+ /**
+ * Output field for the dependency relationship
+ * between the two atomic sentences
+ *
+ * @since `.version == 1`
+ */
+ enum libnormalform_sentence_relationship relationship;
+
+ /**
+ * The application can store any data here that will
+ * be useful for it's callback functions stored in
+ * the other fields
+ *
+ * May be `NULL`, and initialised so by the library
+ *
+ * @since `.version == 1`
+ */
+ void *user_data;
+
+ /**
+ * Called with `.user_data` (even if `.user_data` is `NULL`)
+ * before the library stops using the `struct`
+ *
+ * May be `NULL`, and initialised so by the library
+ *
+ * @param user_data `.user_data`
+ *
+ * @since `.version == 1`
+ */
+ void (*release_user_data)(void *user_data);
+
+ /**
+ * The library will call this function for every function,
+ * that the library will not use, that the application in
+ * one of the `struct`'s fields
+ *
+ * May be `NULL`, and initialised so by the library
+ *
+ * @param function The unused function
+ * @param user_data `.user_data`
+ *
+ * @since `.version == 1`
+ */
+ void (*dont_want_function)(struct libnormalform_function *function, void *user_data);
+
+ /**
+ * The library will call this function for every variable,
+ * that the library will not use, that the application in
+ * one of the `struct`'s fields
+ *
+ * May be `NULL`, and initialised so by the library
+ *
+ * @param variable The unused variable
+ * @param user_data `.user_data`
+ *
+ * @since `.version == 1`
+ */
+ void (*dont_want_variable)(struct libnormalform_variable *variable, void *user_data);
+
+ /**
+ * Set by the library to a hint to the application on
+ * whether it would like to create a conjunction of
+ * the two atomic sentences
+ *
+ * @since `.version == 1`
+ */
+ enum libnormalform_ternary conjunction_is_useful;
+
+ /**
+ * The library will set this field to `NULL`, but
+ * if the application has created a conjunction
+ * of the two atomic sentences, it should be stored
+ * here, if the conjunction is a function
+ *
+ * @since `.version == 1`
+ */
+ struct libnormalform_function *created_conjunction_as_function;
+
+ /**
+ * The library will set this field to `NULL`, but if
+ * the application is able to create a conjunction
+ * of the two atomic sentences, it shall store a pointer
+ * the a function for doing so in this field, if the
+ * result will be a function
+ *
+ * @param left The left-hand atomic sentence, will be the same
+ * (and thus have the same type) as the left-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param right The right-hand atomic sentence, will be the same
+ * (and thus have the same type) as the right-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param out Output parameter for the conjunction of the
+ * two atomic sentences
+ * @param user_data `.user_data`
+ * @return 1 on success if `*out` was created,
+ * 0 if it turned out that a conjunction could
+ * not be constructed, -1 on failure
+ *
+ * @since `.version == 1`
+ */
+ int (*create_conjunction_as_function)(void *left, void *right, struct libnormalform_function **out, void *user_data);
+
+ /**
+ * The library will set this field to `NULL`, but
+ * if the application has created a conjunction
+ * of the two atomic sentences, it should be stored
+ * here, if the conjunction is a variable
+ *
+ * @since `.version == 1`
+ */
+ struct libnormalform_variable *created_conjunction_as_variable;
+
+ /**
+ * The library will set this field to `NULL`, but if
+ * the application is able to create a conjunction
+ * of the two atomic sentences, it shall store a pointer
+ * the a function for doing so in this field, if the
+ * result will be a variable
+ *
+ * @param left The left-hand atomic sentence, will be the same
+ * (and thus have the same type) as the left-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param right The right-hand atomic sentence, will be the same
+ * (and thus have the same type) as the right-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param out Output parameter for the conjunction of the
+ * two atomic sentences
+ * @param user_data `.user_data`
+ * @return 1 on success if `*out` was created,
+ * 0 if it turned out that a conjunction could
+ * not be constructed, -1 on failure
+ *
+ * @since `.version == 1`
+ */
+ int (*create_conjunction_as_variable)(void *left, void *right, struct libnormalform_variable **out, void *user_data);
+
+ /**
+ * Set by the library to a hint to the application on
+ * whether it would like to create a disjunction of
+ * the two atomic sentences
+ *
+ * @since `.version == 1`
+ */
+ enum libnormalform_ternary disjunction_is_useful;
+
+ /**
+ * The library will set this field to `NULL`, but
+ * if the application has created a disjunction
+ * of the two atomic sentences, it should be stored
+ * here, if the disjunction is a function
+ *
+ * @since `.version == 1`
+ */
+ struct libnormalform_function *created_disjunction_as_function;
+
+ /**
+ * The library will set this field to `NULL`, but if
+ * the application is able to create a disjunction
+ * of the two atomic sentences, it shall store a pointer
+ * the a function for doing so in this field, if the
+ * result will be a function
+ *
+ * @param left The left-hand atomic sentence, will be the same
+ * (and thus have the same type) as the left-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param right The right-hand atomic sentence, will be the same
+ * (and thus have the same type) as the right-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param out Output parameter for the disjunction of the
+ * two atomic sentences
+ * @param user_data `.user_data`
+ * @return 1 on success if `*out` was created,
+ * 0 if it turned out that a disjunction could
+ * not be constructed, -1 on failure
+ *
+ * @since `.version == 1`
+ */
+ int (*create_disjunction_as_function)(void *left, void *right, struct libnormalform_function **out, void *user_data);
+
+ /**
+ * The library will set this field to `NULL`, but
+ * if the application has created a disjunction
+ * of the two atomic sentences, it should be stored
+ * here, if the disjunction is a variable
+ *
+ * @since `.version == 1`
+ */
+ struct libnormalform_variable *created_disjunction_as_variable;
+
+ /**
+ * The library will set this field to `NULL`, but if
+ * the application is able to create a disjunction
+ * of the two atomic sentences, it shall store a pointer
+ * the a function for doing so in this field, if the
+ * result will be a variable
+ *
+ * @param left The left-hand atomic sentence, will be the same
+ * (and thus have the same type) as the left-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param right The right-hand atomic sentence, will be the same
+ * (and thus have the same type) as the right-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param out Output parameter for the disjunction of the
+ * two atomic sentences
+ * @param user_data `.user_data`
+ * @return 1 on success if `*out` was created,
+ * 0 if it turned out that a disjunction could
+ * not be constructed, -1 on failure
+ *
+ * @since `.version == 1`
+ */
+ int (*create_disjunction_as_variable)(void *left, void *right, struct libnormalform_variable **out, void *user_data);
+
+ /**
+ * Set by the library to a hint to the application on
+ * whether it would like to create an exclusive disjunction
+ * of the two atomic sentences
+ *
+ * @since `.version == 1`
+ */
+ enum libnormalform_ternary exclusive_disjunction_is_useful;
+
+ /**
+ * The library will set this field to `NULL`, but
+ * if the application has created an exclusive disjunction
+ * of the two atomic sentences, it should be stored
+ * here, if the exclusive disjunction is a function
+ *
+ * @since `.version == 1`
+ */
+ struct libnormalform_function *created_exclusive_disjunction_as_function;
+
+ /**
+ * The library will set this field to `NULL`, but if
+ * the application is able to create an exclusive disjunction
+ * of the two atomic sentences, it shall store a pointer
+ * the a function for doing so in this field, if the
+ * result will be a function
+ *
+ * @param left The left-hand atomic sentence, will be the same
+ * (and thus have the same type) as the left-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param right The right-hand atomic sentence, will be the same
+ * (and thus have the same type) as the right-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param out Output parameter for the exclusive disjunction
+ * of the two atomic sentences
+ * @param user_data `.user_data`
+ * @return 1 on success if `*out` was created,
+ * 0 if it turned out that an exclusive disjunction
+ * could not be constructed, -1 on failure
+ *
+ * @since `.version == 1`
+ */
+ int (*create_exclusive_disjunction_as_function)(void *left, void *right, struct libnormalform_function **out, void *user_data);
+
+ /**
+ * The library will set this field to `NULL`, but
+ * if the application has created an exclusive disjunction
+ * of the two atomic sentences, it should be stored
+ * here, if the exclusive disjunction is a variable
+ *
+ * @since `.version == 1`
+ */
+ struct libnormalform_variable *created_exclusive_disjunction_as_variable;
+
+ /**
+ * The library will set this field to `NULL`, but if
+ * the application is able to create an exclusive disjunction
+ * of the two atomic sentences, it shall store a pointer
+ * the a function for doing so in this field, if the
+ * result will be a variable
+ *
+ * @param left The left-hand atomic sentence, will be the same
+ * (and thus have the same type) as the left-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param right The right-hand atomic sentence, will be the same
+ * (and thus have the same type) as the right-hand
+ * atomic sentence that was input the the application
+ * function together with this `struct`
+ * @param out Output parameter for the exclusive disjunction
+ * of the two atomic sentences
+ * @param user_data `.user_data`
+ * @return 1 on success if `*out` was created,
+ * 0 if it turned out that an exclusive disjunction
+ * could not be constructed, -1 on failure
+ *
+ * @since `.version == 1`
+ */
+ int (*create_exclusive_disjunction_as_variable)(void *left, void *right, struct libnormalform_variable **out, void *user_data);
};
-struct libnormalform_domain_comparsion {
+
+/**
+ * Domain comparison results
+ */
+struct libnormalform_domain_comparison {
+ /**
+ * The library will set this to the version of the `struct`
+ * that it uses, but before returning, if and only if the
+ * application uses an older version, it shall set the
+ * version number here
+ *
+ * Current version number is 1
+ *
+ * @since `.version == 1`
+ */
int version;
+
+ /**
+ * Output field for the dependency relationship
+ * between the two domains
+ *
+ * @since `.version == 1`
+ */
enum libnormalform_domain_relationship relationship;
+
+ /**
+ * The application can store any data here that will
+ * be useful for it's callback functions stored in
+ * the other fields
+ *
+ * May be `NULL`, and initialised so by the library
+ *
+ * @since `.version == 1`
+ */
+ void *user_data;
+
+ /**
+ * Called with `.user_data` (even if `.user_data` is `NULL`)
+ * before the library stops using the `struct`
+ *
+ * May be `NULL`, and initialised so by the library
+ *
+ * @param user_data `.user_data`
+ *
+ * @since `.version == 1`
+ */
+ void (*release_user_data)(void *user_data);
+
+ /**
+ * The library will call this function for every domain,
+ * that the library will not use, that the application in
+ * one of the `struct`'s fields
+ *
+ * May be `NULL`, and initialised so by the library
+ *
+ * @param domain The unused domain
+ * @param user_data `.user_data`
+ *
+ * @since `.version == 1`
+ */
+ void (*dont_want_domain)(struct libnormalform_map *domain, void *user_data);
+
+ /**
+ * Set by the library to a hint to the application on
+ * whether it would like to create a union of the two
+ * domains
+ *
+ * @since `.version == 1`
+ */
+ enum libnormalform_ternary union_is_useful;
+
+ /**
+ * The library will set this field to `NULL`, but
+ * if the application has created a union of the
+ * two domains, it should be stored here
+ *
+ * @since `.version == 1`
+ */
+ struct libnormalform_map *created_union;
+
+ /**
+ * The library will set this field to `NULL`, but if
+ * the application is able to create a union of the
+ * two domains, it shall store a pointer the a function
+ * for doing so in this field
+ *
+ * @param left The left-hand domain, will be the same as the
+ * left-hand domain that was input the the application
+ * function together with this `struct`
+ * @param right The right-hand domain, will be the same as the
+ * right-hand domain that was input the the application
+ * function together with this `struct`
+ * @param out Output parameter for the union of the two domains
+ * @param user_data `.user_data`
+ * @return 1 on success if `*out` was created,
+ * 0 if it turned out that a union could not be
+ * constructed, -1 on failure
+ *
+ * @since `.version == 1`
+ */
+ int (*create_union)(struct libnormalform_map *left, struct libnormalform_map *right,
+ struct libnormalform_map *out, void *user_data);
};
-struct libnormalform_analysers { /* TODO doc */
+
+/**
+ * Callback functions for comparing atomic sentences
+ * and for comparing domains
+ */
+struct libnormalform_analysers {
+ /**
+ * The application can store any data here that will
+ * be useful for it's callback functions stored in
+ * the other fields
+ *
+ * May be `NULL`
+ */
void *user_data;
- int (*compare_variable_to_variable)(void *user_data, struct libnormalform_atom_comparsion *result,
- struct libnormalform_variable *left, struct libnormalform_variable *right);
- int (*compare_function_to_function)(void *user_data, struct libnormalform_atom_comparsion *result,
- struct libnormalform_function *left, struct libnormalform_function *right);
- int (*compare_variable_to_function)(void *user_data, struct libnormalform_atom_comparsion *result,
- struct libnormalform_variable *left, struct libnormalform_function *right);
- int (*compare_domains)(void *user_data, struct libnormalform_domain_comparsion *result,
- struct libnormalform_map *left, struct libnormalform_map *right);
+
+ /**
+ * Callback function for comparing two variables
+ *
+ * @param left The left-hand atomic sentience (variable)
+ * @param right The right-hand atomic sentience (variable)
+ * @param result Structure where the application shall store results
+ * @param user_data `.user_data`
+ * @return 1 on success and a comparison was made,
+ * 0 on success but the sentences were incommensurable,
+ * -1 on failure
+ *
+ * May be `NULL`
+ */
+ int (*compare_variable_to_variable)(struct libnormalform_variable *left, struct libnormalform_variable *right,
+ struct libnormalform_atom_comparison *result, void *user_data);
+
+ /**
+ * Callback function for comparing two functions
+ *
+ * @param left The left-hand atomic sentience (function)
+ * @param right The right-hand atomic sentience (function)
+ * @param result Structure where the application shall store results
+ * @param user_data `.user_data`
+ * @return 1 on success and a comparison was made,
+ * 0 on success but the sentences were incommensurable,
+ * -1 on failure
+ *
+ * May be `NULL`
+ */
+ int (*compare_function_to_function)(struct libnormalform_function *left, struct libnormalform_function *right,
+ struct libnormalform_atom_comparison *result, void *user_data);
+
+ /**
+ * Callback function for comparing a variable and a function
+ *
+ * @param left The left-hand atomic sentience (variable)
+ * @param right The right-hand atomic sentience (function)
+ * @param result Structure where the application shall store results
+ * @param user_data `.user_data`
+ * @return 1 on success and a comparison was made,
+ * 0 on success but the sentences were incommensurable,
+ * -1 on failure
+ *
+ * May be `NULL`
+ */
+ int (*compare_variable_to_function)(struct libnormalform_variable *left, struct libnormalform_function *right,
+ struct libnormalform_atom_comparison *result, void *user_data);
+
+ /**
+ * Callback function for comparing two domains
+ *
+ * @param left The left-hand domain
+ * @param right The right-hand domain
+ * @param result Structure where the application shall store results
+ * @param user_data `.user_data`
+ * @return 1 on success and a comparison was made,
+ * 0 on success but the sentences were incommensurable,
+ * -1 on failure
+ *
+ * May be `NULL`
+ */
+ int (*compare_domains)(struct libnormalform_map *left, struct libnormalform_map *right,
+ struct libnormalform_domain_comparison *result, void *user_data);
};
@@ -1057,7 +1590,7 @@ struct libnormalform_analysers { /* TODO doc */
* Qualifier
*/
struct libnormalform_qualification {
- struct libnormalform_map *map; /**< The domain of interest */
+ struct libnormalform_map *domain; /**< The domain of interest */
struct libnormalform_term *antecedent; /**< The antecedent, `NULL` if joined with the predicate */
struct libnormalform_term *predicate; /**< The predicate */
};
@@ -1100,14 +1633,14 @@ struct libnormalform_term {
*/
union {
/**
- * Use if `.type` is `LIBNORMALFORM_DISJUNCTION`
+ * Use if `.type` is `LIBNORMALFORM_CONJUNCTION`
*/
- struct libnormalform_clause disjunction;
+ struct libnormalform_clause conjunction;
/**
- * Use if `.type` is `LIBNORMALFORM_CONJUNCTION`
+ * Use if `.type` is `LIBNORMALFORM_DISJUNCTION`
*/
- struct libnormalform_clause conjunction;
+ struct libnormalform_clause disjunction;
/**
* Use if `.type` is `LIBNORMALFORM_EXCLUSIVE_DISJUNCTION`
@@ -1302,22 +1835,22 @@ int (libnormalform_evaluate)(LIBNORMALFORM_SENTENCE *this);
/**
- * Return an application readable expression of a statement in negation
- * normal form, optionally with XOR, and reduce the statement to a
- * statement that is at least as true (but as false as possible), and
+ * Return an application-readable expression of a sentence in negation
+ * normal form, optionally with XOR, and reduce the sentence to a
+ * sentence that is at least as true (but as false as possible), and
* is minimised, according what the application specifies that it can
* work with
*
* Before calling this function, the application shall set `.relaxation`
- * `.requires_relaxation` in each `struct libnormalform_function` used
- * used by the sentence. `.requires_relaxation` may be set as 0 if the
+ * and `.requires_relaxation` in each `struct libnormalform_function` used
+ * used in the sentence. `.requires_relaxation` may be set to 0 if the
* function shall not be replaced (in this case `.relaxation` need not
* be set), but set to 1 if the function most be replaced with a more
* true function. In the latter case, `.relaxation` shall either be set
- * to the new function or to `NULL`; if set to null, the function is
- * reduced to a tautological sentence. For any `libnormalform_transformer`
+ * to the new function or to `NULL`; if set to `NULL`, the function is
+ * reduced to a tautological sentence. For any `struct libnormalform_transformer`
* it must also set `.requires_elimination` to 1 if the transformation
- * must be replaces with a tautology, and 0 otherwise.
+ * must be replaces with a tautology, and to 0 otherwise.
*
* @param this The sentence to describe
* @param flags The bitwise OR of any number of the following values:
@@ -1325,9 +1858,10 @@ int (libnormalform_evaluate)(LIBNORMALFORM_SENTENCE *this);
* - LIBNORMALFORM_AVOID_NEGATED_FOR_ALL: Prefer ∃x.¬φ over ¬∀x.φ
* - LIBNORMALFORM_AVOID_FOR_ANY: Prefer ¬∀x.¬φ over ∃x.φ
* - LIBNORMALFORM_AVOID_NEGATED_FOR_ANY: Prefer ∀x.¬φ over ¬∃x.φ
- * - LIBNORMALFORM_RELAX_FOR_ONE: Relax any positive ∃!x.φ into ∃x.φ
- * - LIBNORMALFORM_REDUCE_XOR: Do not use XOR in the returned statement,
+ * - LIBNORMALFORM_REDUCE_XOR: Do not use XOR in the returned sentence,
* but transform it to negation normal form
+ * - LIBNORMALFORM_RELAX_XOR Relax any XOR or OR
+ * (nullified by `LIBNORMALFORM_REDUCE_XOR`)
* - LIBNORMALFORM_JOIN_SIDES_IN_FOR_ALL Express φ(x)∀x:θ(x) on the form ∀x.(θ(x) → φ(x))
* - LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ALL Express ¬(φ(x)∀x:θ(x)) on the form ¬∀x.(θ(x) → φ(x))
* - LIBNORMALFORM_JOIN_SIDES_IN_FOR_ANY Express φ(x)∃x:θ(x) on the form ∃x.(θ(x) ∧ φ(x))
@@ -1337,72 +1871,123 @@ int (libnormalform_evaluate)(LIBNORMALFORM_SENTENCE *this);
* - LIBNORMALFORM_ELIMINATE_FOR_ALL Relax any non-translatable ∀x.φ to ⊤
* - LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL Relax any non-translatable ¬∀x.φ to ⊤
* - LIBNORMALFORM_ELIMINATE_FOR_ANY Relax any non-translatable ∃x.φ to ⊤
- * - LIBNORMALFORM_ELIMINATE_IN_NEGATED_FOR_ANY Relax any non-translatable ¬∃x.φ to ⊤
- * - LIBNORMALFORM_ELIMINATE_IN_FOR_ONE Relax any non-translatable ∃!x.φ to ⊤
- * - LIBNORMALFORM_ELIMINATE_IN_NEGATED_FOR_ONE Relax any ¬∃!x.φ to ⊤
+ * - LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ANY Relax any non-translatable ¬∃x.φ to ⊤
+ * - LIBNORMALFORM_ELIMINATE_FOR_ONE Relax any non-translatable ∃!x.φ to ⊤
+ * - LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ONE Relax any ¬∃!x.φ to ⊤
* - LIBNORMALFORM_ELIMINATE_VARIABLE Relax any positive variable literal to ⊤
* - LIBNORMALFORM_ELIMINATE_NEGATED_VARIABLE Relax any negative variable literal to ⊤
* - LIBNORMALFORM_ELIMINATE_FUNCTION Relax any positive function literal to ⊤
* - LIBNORMALFORM_ELIMINATE_NEGATED_FUNCTION Relax any negative function literal to ⊤
- * - LIBNORMALFORM_ELIMINATE_XOR Remove all XOR's (nullified by `LIBNORMALFORM_REDUCE_XOR`)
- * @param analysers Application provided functions to help reduce the statement, or `NULL`
+ * - LIBNORMALFORM_DISTRIBUTE_QUALIFIERS Express (⋀ᵩφ(y) ∀ ⟨x,y⟩ ∈ D : α(x)) as
+ * ⋀ᵩ(φ(y) ∀ ⟨x,y⟩ ∈ D : α(x)),
+ * express ¬(⋀ᵩφ(y) ∀ ⟨x,y⟩ ∈ D : α(x)) as
+ * ⋁ᵩ¬(φ(y) ∀ ⟨x,y⟩ ∈ D : α(x)),
+ * express (⋁ᵩφ(y) ∃ ⟨x,y⟩ ∈ D : α(x)) as
+ * ⋁ᵩ(φ(y) ∃ ⟨x,y⟩ ∈ D : α(x), and
+ * express ¬(⋁ᵩφ(y) ∃ ⟨x,y⟩ ∈ D : α(x)) as
+ * ⋀ᵩ¬(φ(y) ∃ ⟨x,y⟩ ∈ D : α(x));
+ * where α may be tautology (represented as `NULL`),
+ * and instead φ(y) would be φ({x,y}), due to
+ * `LIBNORMALFORM_JOIN_SIDES_IN_FOR_ALL`,
+ * `LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ALL`,
+ * `LIBNORMALFORM_JOIN_SIDES_IN_FOR_ANY`, or
+ * `LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ANY`
+ * @param analysers Application provided functions to help reduce the sentence, or `NULL`
* @return The description of the sentence, `NULL` on failure;
* the returned pointer shall be deallocated with
* `libnormalform_free` when it is no longer needed
*
+ * @throws ENOMEM Insufficient memory available
+ * @throws EINVAL `flags` contained unsupported options
+ *
* @seealso libnormalform_dnf
* @seealso libnormalform_cnf
+ * @seealso libnormalform_cdnf
*/
LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_FREE_WITH__(libnormalform_free) LIBNORMALFORM_ONE_NONNULL_INPUT__(1)
-struct libnormalform_term *(libnormalform_express)(LIBNORMALFORM_SENTENCE *this, uint64_t flags, /* TODO man */
+struct libnormalform_term *(libnormalform_express)(LIBNORMALFORM_SENTENCE *this, uint64_t flags,
const struct libnormalform_analysers *analysers);
/**
- * Variant of `libnormalform_express` that always canonicalises
- * the statement to disjunctive normal form
+ * Variant of `libnormalform_express` that always normalises
+ * the sentence to disjunctive normal form
*
* @param this The sentence to describe
- * @param flags See `libnormalform_express`; `LIBNORMALFORM_ELIMINATE_XOR`
+ * @param flags See `libnormalform_express`; `LIBNORMALFORM_RELAX_XOR`
* is always applied, however `LIBNORMALFORM_REDUCE_XOR` is not,
* but the user is adviced use `LIBNORMALFORM_REDUCE_XOR` unless
* there is a risk that it would be too costly (EXPSPACE)
- * @param analysers Application provided functions to help reduce the statement, or `NULL`
+ * @param analysers Application provided functions to help reduce the sentence, or `NULL`
* @return The description of the sentence, `NULL` on failure;
* the returned pointer shall be deallocated with
* `libnormalform_free` when it is no longer needed
*
+ * @throws ENOMEM Insufficient memory available
+ * @throws EINVAL `flags` contained unsupported options
+ *
* @seealso libnormalform_express
+ * @seealso libnormalform_cdnf
* @seealso libnormalform_cnf
*/
LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_FREE_WITH__(libnormalform_free) LIBNORMALFORM_ONE_NONNULL_INPUT__(1)
-struct libnormalform_term *(libnormalform_dnf)(LIBNORMALFORM_SENTENCE *this, uint64_t flags, /* TODO */ /* TODO man */
+struct libnormalform_term *(libnormalform_dnf)(LIBNORMALFORM_SENTENCE *this, uint64_t flags,
const struct libnormalform_analysers *analysers);
/**
- * Variant of `libnormalform_express` that always canonicalises
- * the statement to conjuctive normal form
+ * Variant of `libnormalform_express` that always normalises
+ * the sentence to conjuctive normal form
*
* @param this The sentence to describe
- * @param flags See `libnormalform_express`; `LIBNORMALFORM_ELIMINATE_XOR`
+ * @param flags See `libnormalform_express`; `LIBNORMALFORM_RELAX_XOR`
* is always applied, however `LIBNORMALFORM_REDUCE_XOR` is not,
* but the user is adviced use `LIBNORMALFORM_REDUCE_XOR` unless
* there is a risk that it would be too costly (EXPSPACE)
- * @param analysers Application provided functions to help reduce the statement, or `NULL`
+ * @param analysers Application provided functions to help reduce the sentence, or `NULL`
* @return The description of the sentence, `NULL` on failure;
* the returned pointer shall be deallocated with
* `libnormalform_free` when it is no longer needed
*
+ * @throws ENOMEM Insufficient memory available
+ * @throws EINVAL `flags` contained unsupported options
+ *
* @seealso libnormalform_express
* @seealso libnormalform_dnf
+ * @seealso libnormalform_cdnf
*/
LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_FREE_WITH__(libnormalform_free) LIBNORMALFORM_ONE_NONNULL_INPUT__(1)
-struct libnormalform_term *(libnormalform_cnf)(LIBNORMALFORM_SENTENCE *this, uint64_t flags, /* TODO */ /* TODO man */
+struct libnormalform_term *(libnormalform_cnf)(LIBNORMALFORM_SENTENCE *this, uint64_t flags,
const struct libnormalform_analysers *analysers);
/**
+ * Variant of `libnormalform_express` that always normalises
+ * the sentence to canonical disjunctive normal form
+ *
+ * @param this The sentence to describe
+ * @param flags See `libnormalform_express`; `LIBNORMALFORM_RELAX_XOR`
+ * is always applied, however `LIBNORMALFORM_REDUCE_XOR` is not,
+ * but the user is adviced use `LIBNORMALFORM_REDUCE_XOR` unless
+ * there is a risk that it would be too costly (EXPSPACE)
+ * @param analysers Application provided functions to help reduce the sentence, or `NULL`
+ * @return The description of the sentence, `NULL` on failure;
+ * the returned pointer shall be deallocated with
+ * `libnormalform_free` when it is no longer needed
+ *
+ * @throws ENOMEM Insufficient memory available
+ * @throws EINVAL `flags` contained unsupported options
+ *
+ * @seealso libnormalform_express
+ * @seealso libnormalform_dnf
+ * @seealso libnormalform_cnf
+ */
+LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_FREE_WITH__(libnormalform_free) LIBNORMALFORM_ONE_NONNULL_INPUT__(1)
+struct libnormalform_term *(libnormalform_cdnf)(LIBNORMALFORM_SENTENCE *this, uint64_t flags,
+ const struct libnormalform_analysers *analysers);
+
+
+/**
* Convert a sentence object to a string that can be parsed
* by `libnormalform_from_string` (and is somewhat human-readable)
*
@@ -1415,9 +2000,8 @@ struct libnormalform_term *(libnormalform_cnf)(LIBNORMALFORM_SENTENCE *this, uin
* (e.g., eithout there being a NUL-terminator)
*
* Note that when a sentence is created, it is optimised and
- * reduced into fewer types of connetives (it's reducted into
- * negation normal form, except with XOR allowed, but not
- * necessarily to any canonical form) during construction,
+ * reduced into fewer types of connetives (it's reduced into
+ * negation normal form, except with XOR allowed) during construction,
* so this function will not necessarily reproduce the sentence
* as it was specified when it was constructed.
*
@@ -1621,7 +2205,7 @@ LIBNORMALFORM_SENTENCE *(libnormalform_not)(LIBNORMALFORM_SENTENCE *x);
* - ∀x∈D.P(x) → ⊤ = ⊤
* - ∀x∈D.⊥ → Q(x) = ⊤
* - ∀x∈D.⊤ → ⊥ ⊨ D = ∅
- * - Conjunction
+ * - Conjunction: (⋀ᵩ(φ(y)) ∀ ⟨x,y⟩ ∈ D : α(x)) = ⋀ᵩ(φ(y) ∀ ⟨x,y⟩ ∈ D : α(x))
*
* Commonly written "∀" ("∀x∈d.k(x) → v(x)", "∀x∈d (k(x) → v(x))", or "v(x) ∀ x∈d : k(x)")
*
@@ -1664,7 +2248,7 @@ LIBNORMALFORM_SENTENCE *(libnormalform_all)(struct libnormalform_map *d, LIBNORM
* - ∃x∈D.P(x) ∧ ⊥ = ⊥
* - ∃x∈D.⊥ ∧ Q(x) = ⊥
* - ∃x∈D.⊤ ∧ ⊤ ⊨ D ⊃ ∅
- * - Disjunction
+ * - Disjunction: (⋁ᵩ(φ(y)) ∃ ⟨x,y⟩ ∈ D : α(x)) = ⋁ᵩ(φ(y) ∃ ⟨x,y⟩ ∈ D : α(x))
*
* Commonly written "∃" ("∃x∈d.k(x) ∧ v(x)", "∃x∈d (k(x) ∧ v(x))", or "v(x) ∃ x∈d : k(x)")
*
@@ -1868,7 +2452,7 @@ LIBNORMALFORM_SENTENCE *(libnormalform_or)(LIBNORMALFORM_SENTENCE **xs);
* Commonly written "⊕" ("⨁" if n-ary), "⊻", "↮", "⇎", "≢", or "^"
*
* Note that the name Exclusive disjunction is misleading
- * when there are more than two terms, as the statement
+ * when there are more than two terms, as the sentence
* will not check that exactly one term is true, but rather
* calculate the parity of the terms.
*
@@ -2240,7 +2824,7 @@ LIBNORMALFORM_SENTENCE *(libnormalform_nif)(LIBNORMALFORM_SENTENCE **xs);
/**
* NIMPLY: Material nonimplication
*
- * Also known as NIMPLIES, BUT NOT or Material abjunction
+ * Also known as NIMPLIES, NOT..WITH, BUT NOT or Material abjunction
*
* Creates a chain where any condition must be more
* satisfied than the condition left to it (xs[0] < xs[1])
diff --git a/libnormalform_all.c b/libnormalform_all.c
index 1b8d6fc..1d948fc 100644
--- a/libnormalform_all.c
+++ b/libnormalform_all.c
@@ -421,7 +421,7 @@ main(void)
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
- /* P = Q ⊭ ∀{x,y}∈X.(P(x) → Q(y)) = ∀{x,y}∈X.(P(x) → P(y)) = ∀{x,y}∈X.⊤ = ⊤ ∵ P → Q ⊭ P(x) → Q(y) */
+ /* P = Q ⊭ ∀⟨x,y⟩∈X.(P(x) → Q(y)) = ∀⟨x,y⟩∈X.(P(x) → P(y)) = ∀⟨x,y⟩∈X.⊤ = ⊤ ∵ P → Q ⊭ P(x) → Q(y) */
ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), libnormalform_true()));
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
@@ -429,8 +429,8 @@ main(void)
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
- /* P = ¬Q ⊭ ∀{x,y}∈X.(P(x) → Q(y)) = ∀{x,y}∈X.(¬Q(x) → Q(y)) = ∀{x,y}∈X.(⊤ → Q(y)) ∵ P → ¬Q ⊭ P(x) → ¬Q(y) */
- /* P = ¬Q ⊭ ∀{x,y}∈X.(P(x) → Q(y)) = ∀{x,y}∈X.(P(x) → ¬P(y)) = ∀{x,y}∈X.(⊤ → ¬P(y)) ∵ P → ¬Q ⊭ P(x) → ¬Q(y) */
+ /* P = ¬Q ⊭ ∀⟨x,y⟩∈X.(P(x) → Q(y)) = ∀⟨x,y⟩∈X.(¬Q(x) → Q(y)) = ∀⟨x,y⟩∈X.(⊤ → Q(y)) ∵ P → ¬Q ⊭ P(x) → ¬Q(y) */
+ /* P = ¬Q ⊭ ∀⟨x,y⟩∈X.(P(x) → Q(y)) = ∀⟨x,y⟩∈X.(P(x) → ¬P(y)) = ∀⟨x,y⟩∈X.(⊤ → ¬P(y)) ∵ P → ¬Q ⊭ P(x) → ¬Q(y) */
ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), REF(v2)));
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
diff --git a/libnormalform_any.c b/libnormalform_any.c
index 7e69210..1cd79fa 100644
--- a/libnormalform_any.c
+++ b/libnormalform_any.c
@@ -420,7 +420,7 @@ main(void)
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
- /* P = Q ⊭ ∃{x,y}∈X.(P(x) ∧ Q(y)) = ∃{x,y}∈X.(P(x) ∧ P(y)) = ∃{x,y}∈X.⊤ = ⊤ ∵ P = Q ⊭ P(x) = Q(y) */
+ /* P = Q ⊭ ∃⟨x,y⟩∈X.(P(x) ∧ Q(y)) = ∃⟨x,y⟩∈X.(P(x) ∧ P(y)) = ∃⟨x,y⟩∈X.⊤ = ⊤ ∵ P = Q ⊭ P(x) = Q(y) */
ASSUME(b = libnormalform_any(&dom1, libnormalform_true(), libnormalform_true()));
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
@@ -428,8 +428,8 @@ main(void)
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
- /* P = ¬Q ⊭ ∃{x,y}∈X.(P(x) ∧ Q(y)) = ∃{x,y}∈X.(¬Q(x) ∧ Q(y)) = ∃{x,y}∈X.⊥ = ⊥ ∵ P = ¬Q ⊭ P(x) = ¬Q(y) */
- /* P = ¬Q ⊭ ∃{x,y}∈X.(P(x) ∧ Q(y)) = ∃{x,y}∈X.(P(x) ∧ ¬P(y)) = ∃{x,y}∈X.⊥ = ⊥ ∵ P = ¬Q ⊭ P(x) = ¬Q(y) */
+ /* P = ¬Q ⊭ ∃⟨x,y⟩∈X.(P(x) ∧ Q(y)) = ∃⟨x,y⟩∈X.(¬Q(x) ∧ Q(y)) = ∃⟨x,y⟩∈X.⊥ = ⊥ ∵ P = ¬Q ⊭ P(x) = ¬Q(y) */
+ /* P = ¬Q ⊭ ∃⟨x,y⟩∈X.(P(x) ∧ Q(y)) = ∃⟨x,y⟩∈X.(P(x) ∧ ¬P(y)) = ∃⟨x,y⟩∈X.⊥ = ⊥ ∵ P = ¬Q ⊭ P(x) = ¬Q(y) */
ASSUME(b = libnormalform_any(&dom1, libnormalform_false(), libnormalform_false()));
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
diff --git a/libnormalform_apply_transformation__.c b/libnormalform_apply_transformation__.c
new file mode 100644
index 0000000..03af62c
--- /dev/null
+++ b/libnormalform_apply_transformation__.c
@@ -0,0 +1,124 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+/**
+ * Transformation function for `LIBNORMALFORM_DOMAIN_VIEW`,
+ * see `.transform` of `libnormalform_transformer` for
+ * more information
+ *
+ * Outputs the `.key` for `input`; `input` has the type
+ * `struct libnormalform_mapping *`
+ */
+static void *
+domain_view_transform(void *user_data, void *input)
+{
+ struct libnormalform_mapping *kvpair = input;
+ (void) user_data;
+ return kvpair->key;
+}
+
+
+/**
+ * Transformation function for `LIBNORMALFORM_IMAGE_VIEW`,
+ * see `.transform` of `libnormalform_transformer` for
+ * more information
+ *
+ * Outputs the `.value` for `input`; `input` has the type
+ * `struct libnormalform_mapping *`
+ */
+static void *
+image_view_transform(void *user_data, void *input)
+{
+ struct libnormalform_mapping *kvpair = input;
+ (void) user_data;
+ return kvpair->value;
+}
+
+
+int
+(libnormalform_apply_transformation__)(struct expression *this, enum libnormalform_builtin_transformer transformer)
+{
+ /* Only .builtin is actually needed, but the others
+ * could be useful for debugging by the user */
+ static struct libnormalform_transformer domain_view = {
+ .transform = &domain_view_transform,
+ .deallocate = NULL,
+ .user_data = NULL,
+ .identifier = "<builtin:domain-view>",
+ .builtin = LIBNORMALFORM_DOMAIN_VIEW
+ };
+ static struct libnormalform_transformer image_view = {
+ .transform = &image_view_transform,
+ .deallocate = NULL,
+ .user_data = NULL,
+ .identifier = "<builtin:image-view>",
+ .builtin = LIBNORMALFORM_IMAGE_VIEW
+ };
+
+ size_t i;
+ struct expression *new, **term_singleton;
+
+ switch (this->type) {
+ case LIBNORMALFORM_CONJUNCTION:
+ case LIBNORMALFORM_DISJUNCTION:
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ /* transformations are morphism */
+ for (i = 0; i < this->nterms; i++)
+ if (libnormalform_apply_transformation__(this->terms[i], transformer))
+ return -1;
+ break;
+
+ case LIBNORMALFORM_TRANSFORMATION:
+ case LIBNORMALFORM_FUNCTION:
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ new = malloc(sizeof(*new));
+ if (!new)
+ return -1;
+ term_singleton = malloc(sizeof(*term_singleton));
+ if (!term_singleton) {
+ free(new);
+ return -1;
+ }
+ *new = *this;
+ *this = EXPRESSION_INIT(LIBNORMALFORM_TRANSFORMATION);
+ this->nterms = 1;
+ this->terms = term_singleton;
+ this->terms[0] = new;
+ switch (transformer) {
+ case LIBNORMALFORM_DOMAIN_VIEW:
+ this->user_item = &domain_view;
+ break;
+ case LIBNORMALFORM_IMAGE_VIEW:
+ this->user_item = &image_view;
+ break;
+ default:
+ case LIBNORMALFORM_NOT_BUILT_IN:
+ abort();
+ }
+ break;
+
+ case LIBNORMALFORM_VARIABLE:
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ /* variables are input-independent leafs */
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ case LIBNORMALFORM_FOR_ONE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ /* qualifiers reset input */
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_cdnf.c b/libnormalform_cdnf.c
new file mode 100644
index 0000000..d846a45
--- /dev/null
+++ b/libnormalform_cdnf.c
@@ -0,0 +1,560 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+static int canonicalise_dnf(struct expression *this, uint64_t flags);
+
+
+/**
+ * Invert a sentence in place, but
+ * require the result to be unrelaxed
+ *
+ * The sentence may not contain XOR
+ *
+ * @param this The sentence to invert (in place)
+ * @param flags Flags from `libnormalform_express`
+ * @return 1 on success, 0 if the inversion could not
+ * be representated without being relaxed,
+ * -1 on failure
+ *
+ * In case the function returns 0 or -1, `this` may
+ * have been partially inverted
+ */
+static int
+perfectly_invert_expression(struct expression *this, uint64_t flags)
+{
+ struct expression **outer;
+ size_t i, j, k, ci, cn, nouter;
+ int r;
+
+beginning:
+ switch (this->type) {
+ case LIBNORMALFORM_TRANSFORMATION:
+ this = this->terms[0];
+ goto beginning;
+
+ case LIBNORMALFORM_DISJUNCTION:
+ /* ¬⋁{⋀{x | x ∈ X} | X ∈ Y} =
+ * ⋀{¬⋀{x | x ∈ X} | X ∈ Y} =
+ * ⋀{⋁{¬x | x ∈ X} | X ∈ Y} =
+ * ⋁{¬x₁ ∧ … ∧ ¬xₙ | x₁ ∈ X₁, …, xₙ ∈ Xₙ, {X₁, …, Xₙ} = Y} */
+ outer = NULL;
+ nouter = 1U;
+ for (i = 0; i < this->nterms; i++) {
+ if (this->terms[i]->type != LIBNORMALFORM_CONJUNCTION)
+ abort(); /* this function is only used for CDNF */
+ if (!this->terms[i]->nterms) {
+ nouter = 0;
+ break;
+ }
+ if (this->terms[i]->nterms > SIZE_MAX / nouter) {
+ errno = ENOMEM;
+ return -1;
+ }
+ nouter *= this->terms[i]->nterms;
+ }
+ if (!nouter) {
+ for (i = 0; i < this->nterms; i++)
+ libnormalform_free_expression__(this->terms[i]);
+ free(this->terms);
+ this->terms = NULL;
+ this->nterms = 0;
+ return 0;
+ }
+ for (i = 0; i < this->nterms; i++) {
+ this->reduced |= this->terms[i]->reduced;
+ for (j = 0; j < this->terms[i]->nterms; j++)
+ this->terms[i]->terms[j]->invert = 1;
+ }
+ for (i = 0; i < this->nterms; i++) {
+ for (j = 0; j < this->terms[i]->nterms; j++) {
+ if (!this->terms[i]->terms[j]->invert)
+ continue;
+ this->terms[i]->terms[j]->invert = 0;
+ r = perfectly_invert_expression(this->terms[i]->terms[j], flags);
+ if (r <= 0)
+ return r;
+ if (nouter / this->terms[i]->nterms > SIZE_MAX - this->terms[i]->terms[j]->refcount) {
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ }
+ for (i = 0; i < nouter; i++) {
+ outer[i] = malloc(sizeof(*outer[i]));
+ if (!outer[i])
+ return -1;
+ outer[i]->type = LIBNORMALFORM_CONJUNCTION;
+ outer[i]->reduced = 0;
+ outer[i]->invert = 0;
+ outer[i]->refcount = 1;
+ outer[i]->user_item = NULL;
+ outer[i]->nterms = 0;
+ outer[i]->terms = NULL;
+ if (!this->nterms)
+ continue;
+ outer[i]->terms = malloc(this->nterms * sizeof(*outer[i]->terms));
+ if (!outer[i]->terms) {
+ free(outer[i]);
+ while (i--) {
+ free(outer[i]->terms);
+ free(outer[i]);
+ }
+ return -1;
+ }
+ }
+ cn = 1U;
+ for (i = this->nterms; i--;) {
+ for (k = 0; k < nouter;) {
+ for (j = 0; j < this->terms[i]->nterms; j++) {
+ this->terms[i]->terms[j]->refcount += cn;
+ for (ci = 0; ci < cn; ci++)
+ outer[k++]->terms[i] = this->terms[i]->terms[j];
+ }
+ }
+ cn *= this->terms[i]->nterms;
+ libnormalform_free_expression__(this->terms[i]);
+ this->nterms--;
+ }
+ free(this->terms);
+ this->terms = outer;
+ this->nterms = nouter;
+ return canonicalise_dnf(this, flags) ? -1 : 1;
+
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ if (!(flags & (LIBNORMALFORM_ELIMINATE_FOR_ALL << (this->type - LIBNORMALFORM_FOR_ALL)))) {
+ this->type ^= LIBNORMALFORM_FOR_ALL ^ LIBNORMALFORM_NEGATED_FOR_ALL;
+ return 1;
+ }
+ /* ¬∀x.φ = ∃x.¬φ */
+ if (flags & (LIBNORMALFORM_ELIMINATE_FOR_ANY << (this->type - LIBNORMALFORM_FOR_ALL)))
+ return 0;
+ this->type = LIBNORMALFORM_FOR_ANY + (this->type - LIBNORMALFORM_FOR_ALL);
+ this = this->terms[this->nterms - 1];
+ goto beginning;
+
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ if (!(flags & (LIBNORMALFORM_ELIMINATE_FOR_ANY << (this->type - LIBNORMALFORM_FOR_ANY)))) {
+ this->type ^= LIBNORMALFORM_FOR_ANY ^ LIBNORMALFORM_NEGATED_FOR_ANY;
+ return 1;
+ }
+ /* ¬∃x.φ = ∀x.¬φ */
+ if (flags & (LIBNORMALFORM_ELIMINATE_FOR_ALL << (this->type - LIBNORMALFORM_FOR_ANY)))
+ return 0;
+ this->type = LIBNORMALFORM_FOR_ALL + (this->type - LIBNORMALFORM_FOR_ANY);
+ this = this->terms[this->nterms - 1];
+ goto beginning;
+
+ case LIBNORMALFORM_FUNCTION:
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ if (flags & (LIBNORMALFORM_ELIMINATE_FUNCTION << (this->type - LIBNORMALFORM_FUNCTION)))
+ return 0;
+ this->type ^= LIBNORMALFORM_FUNCTION ^ LIBNORMALFORM_NEGATED_FUNCTION;
+ return 1;
+
+ case LIBNORMALFORM_VARIABLE:
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ if (flags & (LIBNORMALFORM_ELIMINATE_VARIABLE << (this->type - LIBNORMALFORM_VARIABLE)))
+ return 0;
+ this->type ^= LIBNORMALFORM_VARIABLE ^ LIBNORMALFORM_NEGATED_VARIABLE;
+ return 1;
+
+ case LIBNORMALFORM_FOR_ONE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ if (flags & (LIBNORMALFORM_ELIMINATE_FOR_ONE << (this->type - LIBNORMALFORM_FOR_ONE)))
+ return 0;
+ this->type ^= LIBNORMALFORM_FOR_ONE ^LIBNORMALFORM_NEGATED_FOR_ONE;
+ return 1;
+
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION: /* disallowed since we only is this for CDNF */
+ case LIBNORMALFORM_CONJUNCTION: /* disallowed since we only is this for CDNF */
+ default:
+ abort();
+ }
+}
+
+
+/**
+ * Convert from DNF to CDNF
+ *
+ * @param this The DNF to convert to CDNF (in place)
+ * @param flags Flags from `libnormalform_express`
+ * @return 0 on success, -1 on failure
+ */
+static int
+canonicalise_dnf(struct expression *this, uint64_t flags)
+{
+ struct expression *clause, *new_clause, *term, *nested_term;
+ struct expression *positive = NULL, **req_positives = NULL;
+ struct expression *negative = NULL, **req_negatives = NULL;
+ struct expression *have, **want, **req_unsigneds = NULL;
+ size_t clause_i, term_i, req_i, req_n = 0, req_size, i, max;
+ int cmp, ret = -1, r;
+ void *new, *tmp;
+
+ if (this->type != LIBNORMALFORM_DISJUNCTION)
+ abort();
+
+ max = 0;
+ for (clause_i = 0; clause_i < this->nterms; clause_i++) {
+ clause = this->terms[clause_i];
+ if (clause->type != LIBNORMALFORM_CONJUNCTION)
+ abort();
+ if (clause->nterms > max)
+ max = clause->nterms;
+
+ for (i = 0; i < clause->nterms; i++) {
+ switch (clause->terms[i]->type) {
+ case LIBNORMALFORM_TRANSFORMATION:
+ case LIBNORMALFORM_VARIABLE:
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ case LIBNORMALFORM_FUNCTION:
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ break;
+
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ case LIBNORMALFORM_FOR_ONE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ if (canonicalise_dnf(clause->terms[i], flags))
+ return -1;
+ break;
+
+ default:
+ case LIBNORMALFORM_CONJUNCTION:
+ case LIBNORMALFORM_DISJUNCTION:
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ abort();
+ }
+ }
+
+ qsort(clause->terms, clause->nterms, sizeof(*clause->terms), &libnormalform_literal_sort_cmp__);
+ }
+ req_size = max;
+ req_positives = malloc(req_size * sizeof(*req_positives));
+ if (!req_positives)
+ goto fail;
+ req_negatives = malloc(req_size * sizeof(*req_negatives));
+ if (!req_negatives)
+ goto fail;
+ req_unsigneds = malloc(req_size * sizeof(*req_unsigneds));
+ if (!req_unsigneds)
+ goto fail;
+ for (clause_i = 0; clause_i < this->nterms; clause_i++) {
+ clause = this->terms[clause_i];
+ req_i = 0;
+ for (term_i = 0; term_i < clause->nterms; term_i++) {
+ term = clause->terms[term_i];
+ req_again:
+ if (req_i == req_n) {
+ req_new:
+ if (req_n == req_size) {
+ req_size += 64;
+ new = realloc(req_positives, req_size * sizeof(*req_positives));
+ if (!new)
+ goto fail;
+ req_positives = new;
+ req_negatives = realloc(req_negatives, req_size * sizeof(*req_negatives));
+ if (!new)
+ goto fail;
+ req_negatives = new;
+ req_unsigneds = realloc(req_unsigneds, req_size * sizeof(*req_unsigneds));
+ if (!new)
+ goto fail;
+ req_unsigneds = new;
+ }
+ memmove(&req_positives[req_i + 1U], &req_positives[req_i], (req_n - req_i) * sizeof(*req_positives));
+ memmove(&req_negatives[req_i + 1U], &req_negatives[req_i], (req_n - req_i) * sizeof(*req_negatives));
+ memmove(&req_unsigneds[req_i + 1U], &req_unsigneds[req_i], (req_n - req_i) * sizeof(*req_unsigneds));
+ req_positives[req_i] = NULL;
+ req_negatives[req_i] = NULL;
+ req_unsigneds[req_i] = NULL;
+ req_n += 1;
+ nested_term = term;
+ positivity_again:
+ switch (nested_term->type) {
+ case LIBNORMALFORM_TRANSFORMATION:
+ nested_term = nested_term->terms[0];
+ goto positivity_again;
+ case LIBNORMALFORM_FUNCTION:
+ case LIBNORMALFORM_VARIABLE:
+ case LIBNORMALFORM_FOR_ONE:
+ req_positives[req_i] = term;
+ req_positives[req_i]->refcount += 1;
+ break;
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ req_negatives[req_i] = term;
+ req_negatives[req_i]->refcount += 1;
+ break;
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ req_unsigneds[req_i] = term;
+ req_unsigneds[req_i]->refcount += 1;
+ break;
+ default:
+ case LIBNORMALFORM_CONJUNCTION:
+ case LIBNORMALFORM_DISJUNCTION:
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ abort();
+ }
+ } else if (req_positives[req_i]) {
+ cmp = libnormalform_literal_cmp__(term, req_positives[req_i]);
+ if (cmp < -1) {
+ goto req_new;
+ } else if (cmp == -1) {
+ abort();
+ } else if (cmp == +1) {
+ if (!req_negatives[req_i]) {
+ req_negatives[req_i] = term;
+ term->refcount += 1;
+ }
+ } else if (cmp > +1) {
+ req_i++;
+ goto req_again;
+ } else {
+ libnormalform_free_expression__(term);
+ clause->terms[term_i] = req_positives[req_i];
+ }
+ } else if (req_negatives[req_i]) {
+ cmp = libnormalform_literal_cmp__(term, req_negatives[req_i]);
+ if (cmp < -1) {
+ goto req_new;
+ } else if (cmp == -1) {
+ req_positives[req_i] = term;
+ term->refcount += 1;
+ } else if (cmp == +1) {
+ abort();
+ } else if (cmp > +1) {
+ req_i++;
+ goto req_again;
+ } else {
+ libnormalform_free_expression__(term);
+ clause->terms[term_i] = req_negatives[req_i];
+ }
+ } else if (req_unsigneds[req_i]) {
+ cmp = libnormalform_literal_cmp__(term, req_unsigneds[req_i]);
+ if (cmp < -1) {
+ goto req_new;
+ } else if (cmp == -1) {
+ req_positives[req_i] = term;
+ term->refcount += 1;
+ req_negatives[req_i] = req_unsigneds[req_i];
+ req_unsigneds[req_i] = NULL;
+ } else if (cmp == +1) {
+ req_negatives[req_i] = term;
+ term->refcount += 1;
+ req_positives[req_i] = req_unsigneds[req_i];
+ req_unsigneds[req_i] = NULL;
+ } else if (cmp > +1) {
+ req_i++;
+ goto req_again;
+ } else {
+ libnormalform_free_expression__(term);
+ clause->terms[term_i] = req_unsigneds[req_i];
+ }
+ } else {
+ abort();
+ }
+ }
+ }
+ qsort(this->terms, this->nterms, sizeof(*this->terms), &libnormalform_clause_sort_cmp__);
+
+ clause_i = 0;
+again:
+ for (; clause_i < this->nterms; clause_i++) {
+ clause = this->terms[clause_i];
+
+ if (clause->nterms < req_n) {
+ new = realloc(clause->terms, req_n * sizeof(*clause->terms));
+ if (!new)
+ goto fail;
+ clause->terms = new;
+ }
+
+ for (term_i = req_i = 0; req_i < req_n; term_i++, req_i++) {
+ positive = req_positives[req_i];
+ negative = req_negatives[req_i];
+ have = positive ? positive : negative;
+ have = have ? have : req_unsigneds[req_i];
+ if (!have) {
+ /* TODO (elimination) */
+ }
+
+ if (term_i < clause->nterms) {
+ cmp = libnormalform_literal_cmp__(have, clause->terms[term_i]);
+ if (cmp >= -1) {
+ if (cmp > +1)
+ abort();
+ continue;
+ }
+ }
+
+ if (!positive && !negative) {
+ positive = have;
+ negative = malloc(sizeof(*negative));
+ *negative = *positive;
+ negative->refcount = 1;
+ negative->terms = malloc(negative->nterms * sizeof(*negative->terms));
+ if (!negative->terms) {
+ free(negative);
+ goto fail;
+ }
+ for (i = 0; i < negative->nterms; i++) {
+ negative->terms[i] = positive->terms[i];
+ negative->terms[i]->refcount += 1;
+ }
+ req_positives[req_i] = positive;
+ req_negatives[req_i] = negative;
+ req_unsigneds[req_i] = NULL;
+ r = perfectly_invert_expression(negative, flags);
+ if (r < 0)
+ goto fail;
+ if (!r) {
+ eliminate_term:
+ libnormalform_free_expression__(req_positives[req_i]);
+ libnormalform_free_expression__(req_negatives[req_i]);
+ req_positives[req_i] = NULL;
+ req_negatives[req_i] = NULL;
+ /* TODO (elimination)
+ * the term as to be eliminated, and the clause may have
+ * to be moved down (and potentially eliminated)
+ */
+ }
+ cmp = libnormalform_literal_cmp__(positive, negative);
+ if (cmp == +1) {
+ tmp = positive, positive = negative, negative = tmp;
+ req_positives[req_i] = positive;
+ req_negatives[req_i] = negative;
+ } else if (cmp != -1) {
+ abort();
+ }
+ } else if (!positive || !negative) {
+ want = positive ? &positive : &negative;
+ *want = malloc(sizeof(**want));
+ **want = *have;
+ (*want)->refcount = 1;
+ (*want)->terms = malloc(have->nterms * sizeof(*have->terms));
+ if (!(*want)->terms) {
+ free(*want);
+ goto fail;
+ }
+ for (i = 0; i < have->nterms; i++) {
+ (*want)->terms[i] = have->terms[i];
+ (*want)->terms[i]->refcount += 1;
+ }
+ r = perfectly_invert_expression(*want, flags);
+ if (r < 0)
+ goto fail;
+ if (!r)
+ goto eliminate_term;
+ }
+
+ memmove(&clause->terms[term_i + 1U], &clause->terms[term_i],
+ (clause->nterms++ - term_i) * sizeof(*clause->terms));
+
+ clause->terms[term_i] = negative;
+ if (libnormalform_contains_clause__(this, clause, clause_i)) {
+ libnormalform_free_expression__(negative);
+ negative = NULL;
+ }
+
+ clause->terms[term_i] = positive;
+ if (libnormalform_contains_clause__(this, clause, clause_i)) {
+ libnormalform_free_expression__(positive);
+ positive = NULL;
+ }
+
+ if (positive) {
+ clause->terms[term_i] = positive;
+ if (!negative)
+ continue;
+ } else if (negative) {
+ clause->terms[term_i] = negative;
+ continue;
+ } else {
+ libnormalform_free_expression__(clause);
+ memmove(&this->terms[clause_i], &this->terms[clause_i + 1U],
+ (--clause->nterms - clause_i) * sizeof(*clause->terms));
+ goto again;
+ }
+
+ new_clause = malloc(sizeof(*new_clause));
+ if (!new_clause) {
+ libnormalform_free_expression__(negative);
+ goto fail;
+ }
+ *new_clause = *clause;
+ new_clause->refcount = 1;
+ new_clause->terms = malloc(new_clause->nterms * sizeof(*new_clause->terms));
+ if (!new_clause->terms) {
+ libnormalform_free_expression__(negative);
+ free(new_clause);
+ goto fail;
+ }
+ for (i = 0; i < new_clause->nterms; i++) {
+ new_clause->terms[i] = clause->terms[i];
+ new_clause->terms[i]->refcount += 1;
+ }
+
+ new_clause->terms[term_i]->refcount -= 1;
+ new_clause->terms[term_i] = negative;
+
+ new = realloc(this->terms, (this->nterms + 1U) * sizeof(*this->terms));
+ if (!new) {
+ libnormalform_free_expression__(new_clause);
+ goto fail;
+ }
+ this->terms = new;
+ memmove(&this->terms[clause_i + 1U], &this->terms[clause_i],
+ (this->nterms++ - clause_i) * sizeof(*this->terms));
+ this->terms[clause_i] = new_clause;
+ }
+ if (term_i != clause->nterms)
+ abort();
+ }
+
+ ret = 0;
+fail:
+ if (req_positives) {
+ for (i = 0; i < req_n; i++)
+ if (req_positives[i])
+ libnormalform_free_expression__(req_positives[i]);
+ free(req_positives);
+ }
+ if (req_negatives) {
+ for (i = 0; i < req_n; i++)
+ if (req_positives[i])
+ libnormalform_free_expression__(req_positives[i]);
+ free(req_positives);
+ }
+ if (req_unsigneds) {
+ for (i = 0; i < req_n; i++)
+ if (req_unsigneds[i])
+ libnormalform_free_expression__(req_unsigneds[i]);
+ free(req_unsigneds);
+ }
+ return ret;
+}
+
+
+struct libnormalform_term *
+(libnormalform_cdnf)(LIBNORMALFORM_SENTENCE *this, uint64_t flags, const struct libnormalform_analysers *analysers)
+{
+ return libnormalform_express__(this, flags | LIBNORMALFORM_RELAX_XOR, analysers, DNF, &canonicalise_dnf);
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_clause_cmp__.c b/libnormalform_clause_cmp__.c
new file mode 100644
index 0000000..407ea11
--- /dev/null
+++ b/libnormalform_clause_cmp__.c
@@ -0,0 +1,24 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+(libnormalform_clause_cmp__)(struct expression *a, struct expression *b)
+{
+ size_t i = 0, j = 0, n = a->nterms, m = b->nterms;
+ int cmp;
+ for (; i < n && j < m; i++, j++) {
+ cmp = libnormalform_literal_cmp__(a->terms[i], b->terms[j]);
+ if (cmp)
+ return cmp;
+ }
+ return (i < n) - (j < m);
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_clause_sort_cmp__.c b/libnormalform_clause_sort_cmp__.c
new file mode 100644
index 0000000..de9f754
--- /dev/null
+++ b/libnormalform_clause_sort_cmp__.c
@@ -0,0 +1,33 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+(libnormalform_clause_sort_cmp__)(const void *a, const void *b)
+{
+ struct expression *const *ap = a, *const *bp = b;
+ return libnormalform_clause_cmp__(*ap, *bp);
+}
+
+
+#else
+
+
+#if defined(__GNUC__) /* TODO remove when testing is implemented in libnormalform_clause_sort_cmp__.c */
+# pragma GCC diagnostic ignored "-Wunused-function"
+# pragma GCC diagnostic ignored "-Wunused-macros"
+#endif
+
+#define libnormalform_clause_cmp__(...) cmptrampoline(__VA_ARGS__)
+
+PURE static int
+cmptrampoline(struct expression *a, struct expression *b)
+{
+ return libnormalform_clause_sort_cmp__(&a, &b);
+}
+
+#include "libnormalform_clause_cmp__.c"
+
+
+#endif
diff --git a/libnormalform_clone.c b/libnormalform_clone.c
index 3f8d01f..311b807 100644
--- a/libnormalform_clone.c
+++ b/libnormalform_clone.c
@@ -15,12 +15,12 @@
* @param map Sufficiently large array of already constructed clones,
* will be updated with a the clone if `this` has a slot
* and `NULL` is stored in the slot
- * @param ap_out Output parameter for one of `this`'s substatements;
+ * @param ap_out Output parameter for one of `this`'s subsentences;
* `NULL` will be stored in `*ap_out` if `this` does not
- * have any substatements
- * @param bp_out Output parameter for `this`'s other substatement;
+ * have any subsentences
+ * @param bp_out Output parameter for `this`'s other subsentence;
* `NULL` will be stored in `*bp_out` if `this` does not
- * have any substatements
+ * have any subsentences
* @return The clone of `this`, `NULL` on failure
*/
USE_RESULT SOME_NONNULL_INPUT(1, 3, 4)
diff --git a/libnormalform_cnf.c b/libnormalform_cnf.c
new file mode 100644
index 0000000..d687fc6
--- /dev/null
+++ b/libnormalform_cnf.c
@@ -0,0 +1,17 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+struct libnormalform_term *
+(libnormalform_cnf)(LIBNORMALFORM_SENTENCE *this, uint64_t flags, const struct libnormalform_analysers *analysers)
+{
+ return libnormalform_express__(this, flags | LIBNORMALFORM_RELAX_XOR, analysers, CNF, NULL);
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_contains_clause__.c b/libnormalform_contains_clause__.c
new file mode 100644
index 0000000..6736e4a
--- /dev/null
+++ b/libnormalform_contains_clause__.c
@@ -0,0 +1,31 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+(libnormalform_contains_clause__)(struct expression *this, struct expression *clause, size_t ignore)
+{
+ size_t i;
+ int cmp;
+ if (ignore > this->nterms)
+ ignore = this->nterms;
+ for (i = 0; i < ignore; i++) {
+ cmp = libnormalform_clause_cmp__(clause, this->terms[i]);
+ if (cmp >= 0)
+ return !cmp;
+ }
+ for (i++; i < this->nterms; i++) {
+ cmp = libnormalform_clause_cmp__(clause, this->terms[i]);
+ if (cmp >= 0)
+ return !cmp;
+ }
+ return 0;
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_dnf.c b/libnormalform_dnf.c
new file mode 100644
index 0000000..79f1a3d
--- /dev/null
+++ b/libnormalform_dnf.c
@@ -0,0 +1,17 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+struct libnormalform_term *
+(libnormalform_dnf)(LIBNORMALFORM_SENTENCE *this, uint64_t flags, const struct libnormalform_analysers *analysers)
+{
+ return libnormalform_express__(this, flags | LIBNORMALFORM_RELAX_XOR, analysers, DNF, NULL);
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_express.c b/libnormalform_express.c
index 2532488..25c4d7c 100644
--- a/libnormalform_express.c
+++ b/libnormalform_express.c
@@ -3,842 +3,10 @@
#ifndef TEST
-struct expression {
- enum libnormalform_term_type type;
- unsigned char reduced;
- unsigned char invert;
- size_t nterms;
- struct expression **terms;
- void *user_item;
-};
-
-
-static enum libnormalform_sentences_relationship
-get_relationship(struct expression *l, struct expression *r, const struct libnormalform_analysers *analysers) /* TODO */
-{
- (void) l;
- (void) r;
- (void) analysers;
- return LIBNORMALFORM_MUTUALLY_INDEPENDENT;
-}
-
-
-static void
-free_expression(struct expression *this)
-{
- if (this->terms) {
- while (this->nterms)
- free_expression(this->terms[--this->nterms]);
- free(this->terms);
- }
- free(this);
-}
-
-
-static struct expression *
-sentence_to_expression(LIBNORMALFORM_SENTENCE *this, uint64_t flags)
-{
- LIBNORMALFORM_SENTENCE *left, *right, *free1 = NULL, *free2 = NULL;
- struct expression *ret, *sub;
-
- ret = malloc(sizeof(*ret));
- if (!ret)
- return NULL;
- ret->reduced = 0;
- ret->invert = 0;
- ret->nterms = 0;
- ret->terms = NULL;
- ret->user_item = NULL;
-
- switch (this->type) {
- case TYPE_TRUE:
- tautology:
- ret->type = LIBNORMALFORM_CONJUNCTION;
- goto out;
-
- case TYPE_FALSE:
- contradiction:
- ret->type = LIBNORMALFORM_DISJUNCTION;
- goto out;
-
- case TYPE_XOR:
- if (flags & LIBNORMALFORM_REDUCE_XOR) {
- /* x ⊕ y = (x ∨ y) ∧ ¬(x ∧ y) = (x ∨ y) ∧ (¬x ∨ ¬y) */
- free1 = left = libnormalform_or2(libnormalform_ref(LEFT(this)), libnormalform_ref(RIGHT(this)));
- free2 = right = libnormalform_or2(LEFT(this)->inverse(LEFT(this)), RIGHT(this)->inverse(RIGHT(this)));
- if (!left || !right)
- goto fail;
- ret->type = LIBNORMALFORM_CONJUNCTION;
- goto clause_prefetched_branches;
- }
- ret->type = LIBNORMALFORM_EXCLUSIVE_DISJUNCTION;
- goto clause;
- case TYPE_AND:
- ret->type = LIBNORMALFORM_CONJUNCTION;
- goto clause;
- case TYPE_OR:
- ret->type = LIBNORMALFORM_DISJUNCTION;
- clause:
- left = LEFT(this);
- right = RIGHT(this);
- clause_prefetched_branches:
- sub = sentence_to_expression(left, flags);
- if (sub)
- goto fail;
- if (sub->type == ret->type) {
- free(ret);
- ret = sub;
- } else if (!sub->nterms && ret->type == LIBNORMALFORM_CONJUNCTION && sub->type == LIBNORMALFORM_DISJUNCTION) {
- /* ⋀(X ∪ {⋁∅}) = ⋀(X ∪ {⊥}) = ⋀X ∧ ⊥ = ⊥ */
- if (!sub->reduced) {
- free_expression(sub);
- goto contradiction;
- }
- free_expression(sub);
- ret->reduced |= 1;
- } else if (!sub->nterms && ret->type == LIBNORMALFORM_DISJUNCTION && sub->type == LIBNORMALFORM_CONJUNCTION) {
- /* ⋁(X ∪ {⋀∅}) = ⋁(X ∪ {⊤}) = ⋁X ∨ ⊤ = ⊤ */
- free_expression(ret);
- ret = sub;
- goto tautology;
- } else {
- ret->terms = malloc(sizeof(*ret->terms));
- if (!ret->terms) {
- free_expression(sub);
- goto fail;
- }
- ret->nterms = 1;
- ret->terms[0] = sub;
- }
- sub = sentence_to_expression(right, flags);
- if (sub)
- goto fail;
- if (sub->type == ret->type) {
- void *new = realloc(ret->terms, (ret->nterms + sub->nterms) * sizeof(*ret->terms));
- if (!new) {
- free_expression(sub);
- goto fail;
- }
- ret->terms = new;
- memcpy(&ret->terms[ret->nterms], sub->terms, sub->nterms * sizeof(sub->terms));
- ret->nterms += sub->nterms;
- free(sub->terms);
- free(sub);
- } else if (!sub->nterms && ret->type == LIBNORMALFORM_CONJUNCTION && sub->type == LIBNORMALFORM_DISJUNCTION) {
- /* ⋀(X ∪ {⋁∅}) = ⋀(X ∪ {⊥}) = ⋀X ∧ ⊥ = ⊥ */
- if (!sub->reduced) {
- free_expression(sub);
- goto contradiction;
- }
- free_expression(sub);
- ret->reduced |= 1;
- } else if (!sub->nterms && ret->type == LIBNORMALFORM_DISJUNCTION && sub->type == LIBNORMALFORM_CONJUNCTION) {
- /* ⋁(X ∪ {⋀∅}) = ⋁(X ∪ {⊤}) = ⋁X ∨ ⊤ = ⊤ */
- free_expression(ret);
- ret = sub;
- goto tautology;
- } else {
- void *new = realloc(ret->terms, (ret->nterms + 1U) * sizeof(*ret->terms));
- if (!new) {
- free_expression(sub);
- goto fail;
- }
- ret->terms = new;
- ret->terms[ret->nterms++] = sub;
- }
- break;
-
- case TYPE_ALL:
- ret->type = LIBNORMALFORM_FOR_ALL;
- goto qualifier;
- case TYPE_ANY:
- ret->type = LIBNORMALFORM_FOR_ANY;
- goto qualifier;
- case TYPE_ONE:
- ret->type = LIBNORMALFORM_FOR_ONE;
- goto qualifier;
- case TYPE_NOT_ONE:
- ret->type = LIBNORMALFORM_NEGATED_FOR_ONE;
- qualifier:
- ret->user_item = this->data.qualifier.domain;
- ret->nterms = 2;
- ret->terms = malloc(2 * sizeof(*ret->terms));
- if (!ret->terms)
- goto fail;
- ret->terms[0] = sentence_to_expression(this->data.qualifier.antecedent, flags);
- if (!ret->terms[0])
- goto fail;
- ret->terms[1] = sentence_to_expression(this->data.qualifier.predicate, flags);
- if (!ret->terms[1])
- goto fail;
- break;
-
- case TYPE_VARIABLE:
- if ((flags & LIBNORMALFORM_ELIMINATE_VARIABLE) && (flags & LIBNORMALFORM_ELIMINATE_NEGATED_VARIABLE))
- goto eliminated;
- ret->user_item = this->data.literal.atom.variable;
- ret->type = LIBNORMALFORM_VARIABLE + this->data.literal.inverted;
- break;
-
- case TYPE_FUNCTION:
- if ((flags & LIBNORMALFORM_ELIMINATE_FUNCTION) && (flags & LIBNORMALFORM_ELIMINATE_NEGATED_FUNCTION))
- goto eliminated;
- ret->reduced = !!this->data.literal.atom.function->requires_relaxation;
- if (!ret->reduced)
- ret->user_item = this->data.literal.atom.function;
- else if (this->data.literal.atom.function->relaxation)
- ret->user_item = this->data.literal.atom.function->relaxation;
- else
- goto eliminated;
- ret->type = LIBNORMALFORM_FUNCTION + this->data.literal.inverted;
- break;
-
- case TYPE_TRANS:
- if (this->data.trans.function->requires_elimination)
- goto eliminated;
- ret->type = LIBNORMALFORM_TRANSFORMATION;
- ret->user_item = this->data.trans.function;
- ret->terms = malloc(sizeof(*ret->terms));
- if (ret->terms)
- goto fail;
- ret->terms[0] = sentence_to_expression(this->data.trans.input, flags);
- if (!ret->terms[0])
- goto fail;
- break;
-
- default:
- abort();
- }
-
- goto out;
-
-eliminated:
- ret->reduced = 1;
- ret->type = LIBNORMALFORM_CONJUNCTION;
-out:
- libnormalform_free(free1);
- libnormalform_free(free2);
- return ret;
-
-fail:
- libnormalform_free(free1);
- libnormalform_free(free2);
- free_expression(ret);
- return NULL;
-}
-
-
-static struct expression *
-make_binary(int invert_left, struct expression *left, int invert_right, struct expression *right, enum libnormalform_term_type type)
-{
- struct expression *ret;
- ret = malloc(sizeof(*ret));
- if (!ret)
- return NULL;
- ret->type = type;
- ret->reduced = 0;
- ret->invert = 0;
- ret->user_item = NULL;
- ret->nterms = 0;
- ret->terms = malloc(2 * sizeof(*ret->terms));
- if (!ret->terms) {
- free(ret);
- return NULL;
- }
- (ret->terms[0] = left)->invert ^= (unsigned char)invert_left;
- (ret->terms[1] = right)->invert ^= (unsigned char)invert_right;
- return ret;
-}
-
-
-static int
-reduce_expression(struct expression *this, const struct libnormalform_analysers *analysers)
-{
- enum libnormalform_sentences_relationship relationship;
- struct expression *new;
- size_t i, j;
-
- for (i = 0; i < this->nterms; i++)
- if (reduce_expression(this->terms[i], analysers))
- return -1;
-
- switch (this->type) {
- case LIBNORMALFORM_CONJUNCTION:
- for (i = 0; i < this->nterms; i++) {
- left_eliminated_conjunction:
- for (j = i + 1; j < this->nterms; j++) {
- relationship = get_relationship(this->terms[i], this->terms[j], analysers);
- switch (relationship) {
- case LIBNORMALFORM_MATERIAL_IMPLICATION:
- case LIBNORMALFORM_IDENTICAL:
- free_expression(this->terms[j]);
- this->terms[j--] = this->terms[--this->nterms];
- break;
- case LIBNORMALFORM_CONVERSE_IMPLICATION:
- free_expression(this->terms[i]);
- this->terms[i--] = this->terms[--this->nterms];
- goto left_eliminated_conjunction;
- case LIBNORMALFORM_MUTUALLY_INVERSE:
- case LIBNORMALFORM_MUTUALLY_EXCLUSIVE:
- goto contradiction;
- case LIBNORMALFORM_JOINTLY_UNDENIABLE:
- case LIBNORMALFORM_MUTUALLY_INDEPENDENT:
- default:
- break;
- }
- }
- }
- break;
- contradiction:
- for (i = 0; i < this->nterms; i++)
- free_expression(this->terms[i]);
- free(this->terms);
- this->terms = NULL;
- this->nterms = 0;
- this->type = LIBNORMALFORM_DISJUNCTION;
- break;
-
- case LIBNORMALFORM_DISJUNCTION:
- for (i = 0; i < this->nterms; i++) {
- left_eliminated_disjunction:
- for (j = i + 1; j < this->nterms; j++) {
- relationship = get_relationship(this->terms[i], this->terms[j], analysers);
- switch (relationship) {
- case LIBNORMALFORM_IDENTICAL:
- case LIBNORMALFORM_CONVERSE_IMPLICATION:
- free_expression(this->terms[j]);
- this->terms[j--] = this->terms[--this->nterms];
- break;
- case LIBNORMALFORM_MATERIAL_IMPLICATION:
- free_expression(this->terms[i]);
- this->terms[i--] = this->terms[--this->nterms];
- goto left_eliminated_disjunction;
- case LIBNORMALFORM_MUTUALLY_INVERSE:
- case LIBNORMALFORM_JOINTLY_UNDENIABLE:
- goto tautology;
- case LIBNORMALFORM_MUTUALLY_EXCLUSIVE:
- case LIBNORMALFORM_MUTUALLY_INDEPENDENT:
- default:
- break;
- }
- }
- }
- break;
- tautology:
- for (i = 0; i < this->nterms; i++)
- free_expression(this->terms[i]);
- free(this->terms);
- this->terms = NULL;
- this->nterms = 0;
- this->type = LIBNORMALFORM_CONJUNCTION;
- break;
-
- case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
- i = 0;
- both_eliminated_exclusive_disjunction:
- for (; i < this->nterms; i++) {
- for (j = i + 1; j < this->nterms; j++) {
- relationship = get_relationship(this->terms[i], this->terms[j], analysers);
- switch (relationship) {
- case LIBNORMALFORM_MATERIAL_IMPLICATION:
- new = make_binary(1, this->terms[i], 0, this->terms[j], LIBNORMALFORM_CONJUNCTION);
- if (!new)
- return -1;
- goto xor_reduced;
- case LIBNORMALFORM_CONVERSE_IMPLICATION:
- new = make_binary(0, this->terms[i], 1, this->terms[j], LIBNORMALFORM_CONJUNCTION);
- if (!new)
- return -1;
- goto xor_reduced;
- case LIBNORMALFORM_MUTUALLY_INVERSE:
- this->invert ^= 1;
- /* fall-through */
- case LIBNORMALFORM_IDENTICAL:
- free_expression(this->terms[i]);
- free_expression(this->terms[j]);
- this->terms[i--] = this->terms[--this->nterms];
- this->terms[j--] = this->terms[--this->nterms];
- goto both_eliminated_exclusive_disjunction;
- case LIBNORMALFORM_MUTUALLY_EXCLUSIVE:
- new = make_binary(0, this->terms[i], 0, this->terms[j], LIBNORMALFORM_DISJUNCTION);
- if (!new)
- return -1;
- goto xor_reduced;
- case LIBNORMALFORM_JOINTLY_UNDENIABLE:
- new = make_binary(1, this->terms[i], 1, this->terms[j], LIBNORMALFORM_DISJUNCTION);
- if (!new)
- return -1;
- goto xor_reduced;
- case LIBNORMALFORM_MUTUALLY_INDEPENDENT:
- default:
- break;
- xor_reduced:
- free_expression(this->terms[i]);
- free_expression(this->terms[j]);
- this->terms[j--] = this->terms[--this->nterms];
- this->terms[i] = new;
- goto both_eliminated_exclusive_disjunction;
- }
- }
- }
- if (!this->nterms) {
- if (this->invert) {
- this->invert = 0;
- this->type = LIBNORMALFORM_CONJUNCTION;
- } else {
- this->type = LIBNORMALFORM_DISJUNCTION;
- }
- }
- break;
-
- case LIBNORMALFORM_TRANSFORMATION:
- case LIBNORMALFORM_VARIABLE:
- case LIBNORMALFORM_NEGATED_VARIABLE:
- case LIBNORMALFORM_FUNCTION:
- case LIBNORMALFORM_NEGATED_FUNCTION:
- case LIBNORMALFORM_FOR_ALL:
- case LIBNORMALFORM_NEGATED_FOR_ALL:
- case LIBNORMALFORM_FOR_ANY:
- case LIBNORMALFORM_NEGATED_FOR_ANY:
- case LIBNORMALFORM_FOR_ONE:
- case LIBNORMALFORM_NEGATED_FOR_ONE:
- default:
- return 0;
- }
-
- if (this->nterms == 1) {
- new = this->terms[0];
- free(this->terms);
- new->invert ^= this->invert;
- *this = *new;
- free(new);
- }
-
- return 0;
-}
-
-
-static void *
-domain_view_transform(void *user_data, void *input)
-{
- struct libnormalform_mapping *kvpair = input;
- (void) user_data;
- return kvpair->key;
-}
-
-
-static void *
-image_view_transform(void *user_data, void *input)
-{
- struct libnormalform_mapping *kvpair = input;
- (void) user_data;
- return kvpair->value;
-}
-
-
-static int
-apply_transformation(struct expression *this, enum libnormalform_builtin_transformer transformer)
-{
- /* Only .builtin is actually needed, but the others
- * could be useful for debugging by the user */
- static struct libnormalform_transformer domain_view = {
- .transform = &domain_view_transform,
- .deallocate = NULL,
- .user_data = NULL,
- .identifier = "<builtin:domain-view>",
- .builtin = LIBNORMALFORM_DOMAIN_VIEW
- };
- static struct libnormalform_transformer image_view = {
- .transform = &image_view_transform,
- .deallocate = NULL,
- .user_data = NULL,
- .identifier = "<builtin:image-view>",
- .builtin = LIBNORMALFORM_IMAGE_VIEW
- };
-
- size_t i;
- struct expression *new, **term_singleton;
-
- switch (this->type) {
- case LIBNORMALFORM_CONJUNCTION:
- case LIBNORMALFORM_DISJUNCTION:
- case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
- /* transformations are morphism */
- for (i = 0; i < this->nterms; i++)
- if (apply_transformation(this->terms[i], transformer))
- return -1;
- break;
-
- case LIBNORMALFORM_TRANSFORMATION:
- case LIBNORMALFORM_FUNCTION:
- case LIBNORMALFORM_NEGATED_FUNCTION:
- new = malloc(sizeof(*new));
- if (!new)
- return -1;
- term_singleton = malloc(sizeof(*term_singleton));
- if (!term_singleton) {
- free(new);
- return -1;
- }
- *new = *this;
- this->type = LIBNORMALFORM_TRANSFORMATION;
- this->reduced = 0;
- this->invert = 0;
- this->nterms = 1;
- this->terms = term_singleton;
- this->terms[0] = new;
- switch (transformer) {
- case LIBNORMALFORM_DOMAIN_VIEW:
- this->user_item = &domain_view;
- break;
- case LIBNORMALFORM_IMAGE_VIEW:
- this->user_item = &image_view;
- break;
- default:
- case LIBNORMALFORM_NOT_BUILT_IN:
- abort();
- }
- break;
-
- case LIBNORMALFORM_VARIABLE:
- case LIBNORMALFORM_NEGATED_VARIABLE:
- /* variables are input-independent leafs */
- case LIBNORMALFORM_FOR_ALL:
- case LIBNORMALFORM_NEGATED_FOR_ALL:
- case LIBNORMALFORM_FOR_ANY:
- case LIBNORMALFORM_NEGATED_FOR_ANY:
- case LIBNORMALFORM_FOR_ONE:
- case LIBNORMALFORM_NEGATED_FOR_ONE:
- /* qualifiers reset input */
- default:
- break;
- }
-
- return 0;
-}
-
-
-static int
-fix_presentation(struct expression *this, uint64_t flags, unsigned char *require_inversion, int *reduced)
-{
-#define WILL_ELIMINATE(BASETYPE, INVERT)\
- (flags & (LIBNORMALFORM_ELIMINATE_##BASETYPE <<\
- ((this->type ^ ((LIBNORMALFORM_##BASETYPE ^ LIBNORMALFORM_NEGATED_##BASETYPE) *\
- (INVERT))) -\
- LIBNORMALFORM_##BASETYPE)))
-
- size_t i;
- unsigned char zero = 0;
- struct expression *new;
-
- switch (this->type) {
- case LIBNORMALFORM_CONJUNCTION:
- case LIBNORMALFORM_DISJUNCTION:
- this->type ^= LIBNORMALFORM_CONJUNCTION ^ LIBNORMALFORM_DISJUNCTION;
- if (this->invert) {
- if (*require_inversion) {
- this->invert ^= 1;
- *require_inversion = 0;
- }
- for (i = 0; i < this->nterms; i++)
- this->terms[i]->invert ^= 1;
- }
- require_inversion = &zero;
- break;
-
- case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
- if (!this->nterms)
- abort();
- if (*require_inversion) {
- this->invert ^= 1;
- *require_inversion = 0;
- }
- if (flags & LIBNORMALFORM_ELIMINATE_XOR)
- goto eliminate;
- for (i = 0; i < this->nterms; i++)
- if (fix_presentation(this->terms[i], flags, &this->invert, reduced))
- return -1;
- if (this->invert) {
- this->invert = 0;
- this->terms[0]->invert ^= 1;
- if (fix_presentation(this->terms[0], flags, &zero, reduced))
- return -1;
- }
- return 0;
-
- case LIBNORMALFORM_VARIABLE:
- case LIBNORMALFORM_NEGATED_VARIABLE:
- if (!WILL_ELIMINATE(VARIABLE, this->invert ^ *require_inversion)) {
- this->invert ^= *require_inversion;
- *require_inversion = 0;
- } else if (WILL_ELIMINATE(VARIABLE, this->invert)) {
- goto eliminate;
- }
- this->type ^= (LIBNORMALFORM_VARIABLE ^ LIBNORMALFORM_NEGATED_VARIABLE) * this->invert;
- break;
-
- case LIBNORMALFORM_FUNCTION:
- case LIBNORMALFORM_NEGATED_FUNCTION:
- if (!WILL_ELIMINATE(FUNCTION, this->invert ^ *require_inversion)) {
- this->invert ^= *require_inversion;
- *require_inversion = 0;
- } else if (WILL_ELIMINATE(FUNCTION, this->invert)) {
- goto eliminate;
- }
- this->type ^= (LIBNORMALFORM_FUNCTION ^ LIBNORMALFORM_NEGATED_FUNCTION) * this->invert;
- break;
-
- case LIBNORMALFORM_TRANSFORMATION:
- this->terms[0]->invert ^= this->invert;
- break;
-
- case LIBNORMALFORM_FOR_ALL:
- case LIBNORMALFORM_NEGATED_FOR_ALL:
- if (!WILL_ELIMINATE(FOR_ALL, this->invert ^ *require_inversion)) {
- this->invert ^= *require_inversion;
- *require_inversion = 0;
- } else if (WILL_ELIMINATE(FOR_ALL, this->invert)) {
- goto eliminate;
- }
- this->type ^= (LIBNORMALFORM_FOR_ALL ^ LIBNORMALFORM_NEGATED_FOR_ALL) * this->invert;
- this->invert = 0;
- if (flags & (LIBNORMALFORM_AVOID_FOR_ALL << (this->type - LIBNORMALFORM_FOR_ALL))) {
- this->terms[this->nterms - 1]->invert ^= 1;
- this->type = LIBNORMALFORM_AVOID_NEGATED_FOR_ANY - (this->type - LIBNORMALFORM_FOR_ALL);
- goto for_any_maybe_join_sides;
- }
- for_all_maybe_join_sides:
- if (this->nterms > 1)
- break;
- if (flags & (LIBNORMALFORM_JOIN_SIDES_IN_FOR_ALL << (this->type - LIBNORMALFORM_FOR_ALL)))
- goto for_all_join_sides;
- break;
-
- case LIBNORMALFORM_FOR_ANY:
- case LIBNORMALFORM_NEGATED_FOR_ANY:
- if (!WILL_ELIMINATE(FOR_ANY, this->invert ^ *require_inversion)) {
- this->invert ^= *require_inversion;
- *require_inversion = 0;
- } else if (WILL_ELIMINATE(FOR_ANY, this->invert)) {
- goto eliminate;
- }
- this->type ^= (LIBNORMALFORM_FOR_ANY ^ LIBNORMALFORM_NEGATED_FOR_ANY) * this->invert;
- for_any:
- this->invert = 0;
- if (flags & (LIBNORMALFORM_AVOID_FOR_ANY << (this->type - LIBNORMALFORM_FOR_ANY))) {
- this->terms[this->nterms - 1]->invert ^= 1;
- this->type = LIBNORMALFORM_AVOID_NEGATED_FOR_ALL - (this->type - LIBNORMALFORM_FOR_ANY);
- goto for_all_maybe_join_sides;
- }
- for_any_maybe_join_sides:
- if (this->nterms > 1)
- break;
- if (flags & (LIBNORMALFORM_JOIN_SIDES_IN_FOR_ANY << (this->type - LIBNORMALFORM_FOR_ANY)))
- goto for_any_join_sides;
- break;
-
- case LIBNORMALFORM_FOR_ONE:
- case LIBNORMALFORM_NEGATED_FOR_ONE:
- if (!WILL_ELIMINATE(FOR_ONE, this->invert ^ *require_inversion)) {
- this->invert ^= *require_inversion;
- *require_inversion = 0;
- } else if (WILL_ELIMINATE(FOR_ONE, this->invert)) {
- goto eliminate;
- }
- this->type ^= (LIBNORMALFORM_FOR_ONE ^ LIBNORMALFORM_NEGATED_FOR_ONE) * this->invert;
- if (this->type == LIBNORMALFORM_FOR_ONE) {
- if (flags & LIBNORMALFORM_RELAX_FOR_ONE) {
- this->type = LIBNORMALFORM_FOR_ANY;
- goto for_any;
- }
- }
- if (this->nterms > 1)
- break;
- if (flags & (LIBNORMALFORM_JOIN_SIDES_IN_FOR_ONE << (this->type - LIBNORMALFORM_FOR_ONE)))
- goto for_any_join_sides;
- break;
-
- default:
- abort();
- }
-
- this->invert = 0;
-
- for (i = 0; i < this->nterms; i++)
- if (fix_presentation(this->terms[i], flags, require_inversion, reduced))
- return -1;
-
- return 0;
-
-eliminate:
- *require_inversion = 0;
- this->reduced = 0;
- this->invert = 0;
- while (this->nterms)
- free_expression(this->terms[--this->nterms]);
- free(this->terms);
- this->terms = NULL;
- this->type = LIBNORMALFORM_CONJUNCTION;
- *reduced = 1;
- return 0;
-
-for_all_join_sides:
- new = make_binary(1, this->terms[0], 0, this->terms[1], LIBNORMALFORM_DISJUNCTION);
- goto join_sides;
-
-for_any_join_sides:
- new = make_binary(0, this->terms[0], 0, this->terms[1], LIBNORMALFORM_CONJUNCTION);
-join_sides:
- if (!new)
- return -1;
- this->terms[0] = new;
- this->nterms--;
- if (apply_transformation(this->terms[0]->terms[0], LIBNORMALFORM_DOMAIN_VIEW))
- return -1;
- if (apply_transformation(this->terms[0]->terms[1], LIBNORMALFORM_IMAGE_VIEW))
- return -1;
- *reduced = 1;
- return 0;
-
-#undef WILL_ELIMINATE
-}
-
-
-static int
-expression_to_term(struct libnormalform_term *out, struct expression *this)
-{
- size_t i;
-
- out->type = this->type;
- out->reduced = this->reduced;
-
- switch (this->type) {
- case LIBNORMALFORM_CONJUNCTION:
- case LIBNORMALFORM_DISJUNCTION:
- case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
- out->term.clause.nterms = 0;
- out->term.clause.terms = calloc(this->nterms, sizeof(*out->term.clause.terms));
- if (!out->term.clause.terms)
- return -1;
- for (i = 0; i < this->nterms; i++)
- if (expression_to_term(&out->term.clause.terms[out->term.clause.nterms++], this->terms[i]))
- return -1;
- break;
-
- case LIBNORMALFORM_TRANSFORMATION:
- out->term.transformation.transformer = this->user_item;
- out->term.transformation.sentence = calloc(1, sizeof(*out->term.transformation.sentence));
- if (!out->term.transformation.sentence ||
- expression_to_term(out->term.transformation.sentence, this->terms[0]))
- return -1;
- break;
-
- case LIBNORMALFORM_VARIABLE:
- case LIBNORMALFORM_NEGATED_VARIABLE:
- out->term.variable = this->user_item;
- break;
-
- case LIBNORMALFORM_FUNCTION:
- case LIBNORMALFORM_NEGATED_FUNCTION:
- out->term.function = this->user_item;
- break;
-
- case LIBNORMALFORM_FOR_ALL:
- case LIBNORMALFORM_NEGATED_FOR_ALL:
- case LIBNORMALFORM_FOR_ANY:
- case LIBNORMALFORM_NEGATED_FOR_ANY:
- case LIBNORMALFORM_FOR_ONE:
- case LIBNORMALFORM_NEGATED_FOR_ONE:
- out->term.qualification.map = this->user_item;
- if (this->nterms == 1) {
- out->term.qualification.antecedent = NULL;
- out->term.qualification.predicate = calloc(1, sizeof(*out->term.qualification.predicate));
- if (!out->term.qualification.predicate ||
- expression_to_term(out->term.qualification.predicate, this->terms[0]))
- return -1;
- } else {
- out->term.qualification.antecedent = calloc(1, sizeof(*out->term.qualification.antecedent));
- if (!out->term.qualification.antecedent)
- return -1;
- out->term.qualification.predicate = calloc(1, sizeof(*out->term.qualification.predicate));
- if (!out->term.qualification.predicate ||
- expression_to_term(out->term.qualification.antecedent, this->terms[0]) ||
- expression_to_term(out->term.qualification.predicate, this->terms[1]))
- return -1;
- }
- break;
-
- default:
- abort();
- }
-
- return 0;
-}
-
-
struct libnormalform_term *
(libnormalform_express)(LIBNORMALFORM_SENTENCE *this, uint64_t flags, const struct libnormalform_analysers *analysers)
{
- struct libnormalform_term *ret;
- struct expression *expression;
- int reduced;
-
- if (flags & ~LIBNORMALFORM_ALL_EXPRESS_FLAGS__) {
- errno = EINVAL;
- return NULL;
- }
-
- if (flags & LIBNORMALFORM_REDUCE_XOR)
- flags &= ~LIBNORMALFORM_ELIMINATE_XOR;
- if ((flags & LIBNORMALFORM_ELIMINATE_FOR_ANY) && (flags & LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL))
- flags &= ~LIBNORMALFORM_RELAX_FOR_ONE;
- if (flags & LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL)
- flags &= ~LIBNORMALFORM_AVOID_FOR_ANY;
- if (flags & LIBNORMALFORM_ELIMINATE_FOR_ALL)
- flags &= ~LIBNORMALFORM_AVOID_NEGATED_FOR_ANY;
- if (flags & LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ANY)
- flags &= ~LIBNORMALFORM_AVOID_FOR_ALL;
- if (flags & LIBNORMALFORM_ELIMINATE_FOR_ANY)
- flags &= ~LIBNORMALFORM_AVOID_NEGATED_FOR_ALL;
- if ((flags & LIBNORMALFORM_AVOID_FOR_ANY) && (flags & LIBNORMALFORM_AVOID_NEGATED_FOR_ALL))
- flags ^= LIBNORMALFORM_AVOID_FOR_ANY | LIBNORMALFORM_AVOID_NEGATED_FOR_ALL;
- if ((flags & LIBNORMALFORM_AVOID_FOR_ALL) && (flags & LIBNORMALFORM_AVOID_NEGATED_FOR_ANY))
- flags ^= LIBNORMALFORM_AVOID_FOR_ALL | LIBNORMALFORM_AVOID_NEGATED_FOR_ANY;
-
- expression = sentence_to_expression(this, flags);
- if (!expression)
- return NULL;
- do {
- if (reduce_expression(expression, analysers)) {
- free_expression(expression);
- return NULL;
- }
- reduced = 0;
- if (fix_presentation(expression, flags, &(unsigned char){0}, &reduced)) {
- free_expression(expression);
- return NULL;
- }
- } while (reduced);
-
- ret = malloc(sizeof(*ret));
- if (!ret) {
- free_expression(expression);
- return NULL;
- }
- if (expression_to_term(ret, expression)) {
-#if defined(__GNUC__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wmismatched-dealloc"
-#endif
- libnormalform_free(ret);
- ret = NULL;
-#if defined(__GNUC__)
-# pragma GCC diagnostic pop
-#endif
- }
- free_expression(expression);
-
- return ret;
+ return libnormalform_express__(this, flags, analysers, NNF, NULL);
}
diff --git a/libnormalform_express__.c b/libnormalform_express__.c
new file mode 100644
index 0000000..6c9373d
--- /dev/null
+++ b/libnormalform_express__.c
@@ -0,0 +1,483 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+/* TODO implement LIBNORMALFORM_DISTRIBUTE_QUALIFIERS */
+
+
+/**
+ * Convert a sentence of the type `LIBNORMALFORM_SENTENCE *`
+ * to the temporary type `struct expression *`
+ *
+ * This function will flatten the sentence so that no
+ * clause contains terms, that are direct children and,
+ * that are clauses using the same connective
+ *
+ * Literals and qualifiers cannot be represented will
+ * be removed, and any application-provided relaxation
+ * will be applied
+ *
+ * The flags `LIBNORMALFORM_RELAX_XOR` and
+ * `LIBNORMALFORM_REDUCE_XOR` will be applied
+ *
+ * @param this The sentence to convert
+ * @param flags Flags from `libnormalform_express`
+ * @return An sentence is at least as true as `this`,
+ * using the reductions and rewrites mentions,
+ * above expressed in the temporary type;
+ * `NULL` on failure
+ */
+static struct expression *
+sentence_to_expression(LIBNORMALFORM_SENTENCE *this, uint64_t flags)
+{
+ LIBNORMALFORM_SENTENCE *left, *right, *free1 = NULL, *free2 = NULL;
+ struct expression *ret, *sub;
+
+ ret = malloc(sizeof(*ret));
+ if (!ret)
+ return NULL;
+ *ret = EXPRESSION_INIT(0);
+
+ switch (this->type) {
+ case TYPE_TRUE:
+ tautology:
+ ret->type = LIBNORMALFORM_CONJUNCTION;
+ goto out;
+
+ case TYPE_FALSE:
+ contradiction:
+ ret->type = LIBNORMALFORM_DISJUNCTION;
+ goto out;
+
+ case TYPE_XOR:
+ if (flags & LIBNORMALFORM_RELAX_XOR)
+ goto inclusive_disjunction;
+ if (flags & LIBNORMALFORM_REDUCE_XOR) {
+ /* x ⊕ y = (x ∨ y) ∧ ¬(x ∧ y) = (x ∨ y) ∧ (¬x ∨ ¬y) */
+ free1 = left = libnormalform_or2(libnormalform_ref(LEFT(this)), libnormalform_ref(RIGHT(this)));
+ free2 = right = libnormalform_or2(LEFT(this)->inverse(LEFT(this)), RIGHT(this)->inverse(RIGHT(this)));
+ if (!left || !right)
+ goto fail;
+ ret->type = LIBNORMALFORM_CONJUNCTION;
+ goto clause_prefetched_branches;
+ }
+ ret->type = LIBNORMALFORM_EXCLUSIVE_DISJUNCTION;
+ goto clause;
+ case TYPE_AND:
+ ret->type = LIBNORMALFORM_CONJUNCTION;
+ goto clause;
+ case TYPE_OR:
+ inclusive_disjunction:
+ ret->type = LIBNORMALFORM_DISJUNCTION;
+ clause:
+ left = LEFT(this);
+ right = RIGHT(this);
+ clause_prefetched_branches:
+ sub = sentence_to_expression(left, flags);
+ if (sub)
+ goto fail;
+ if (sub->type == ret->type) {
+ free(ret);
+ ret = sub;
+ } else if (!sub->nterms && ret->type == LIBNORMALFORM_CONJUNCTION && sub->type == LIBNORMALFORM_DISJUNCTION) {
+ /* ⋀(X ∪ {⋁∅}) = ⋀(X ∪ {⊥}) = ⋀X ∧ ⊥ = ⊥ */
+ if (!sub->reduced) {
+ libnormalform_free_expression__(sub);
+ goto contradiction;
+ }
+ libnormalform_free_expression__(sub);
+ ret->reduced |= 1;
+ } else if (!sub->nterms && ret->type == LIBNORMALFORM_DISJUNCTION && sub->type == LIBNORMALFORM_CONJUNCTION) {
+ /* ⋁(X ∪ {⋀∅}) = ⋁(X ∪ {⊤}) = ⋁X ∨ ⊤ = ⊤ */
+ libnormalform_free_expression__(ret);
+ ret = sub;
+ goto tautology;
+ } else {
+ ret->terms = malloc(sizeof(*ret->terms));
+ if (!ret->terms) {
+ libnormalform_free_expression__(sub);
+ goto fail;
+ }
+ ret->nterms = 1;
+ ret->terms[0] = sub;
+ }
+ sub = sentence_to_expression(right, flags);
+ if (sub)
+ goto fail;
+ if (sub->type == ret->type) {
+ void *new = realloc(ret->terms, (ret->nterms + sub->nterms) * sizeof(*ret->terms));
+ if (!new) {
+ libnormalform_free_expression__(sub);
+ goto fail;
+ }
+ ret->terms = new;
+ memcpy(&ret->terms[ret->nterms], sub->terms, sub->nterms * sizeof(sub->terms));
+ ret->nterms += sub->nterms;
+ free(sub->terms);
+ free(sub);
+ } else if (!sub->nterms && ret->type == LIBNORMALFORM_CONJUNCTION && sub->type == LIBNORMALFORM_DISJUNCTION) {
+ /* ⋀(X ∪ {⋁∅}) = ⋀(X ∪ {⊥}) = ⋀X ∧ ⊥ = ⊥ */
+ if (!sub->reduced) {
+ libnormalform_free_expression__(sub);
+ goto contradiction;
+ }
+ libnormalform_free_expression__(sub);
+ ret->reduced |= 1;
+ } else if (!sub->nterms && ret->type == LIBNORMALFORM_DISJUNCTION && sub->type == LIBNORMALFORM_CONJUNCTION) {
+ /* ⋁(X ∪ {⋀∅}) = ⋁(X ∪ {⊤}) = ⋁X ∨ ⊤ = ⊤ */
+ libnormalform_free_expression__(ret);
+ ret = sub;
+ goto tautology;
+ } else {
+ void *new = realloc(ret->terms, (ret->nterms + 1U) * sizeof(*ret->terms));
+ if (!new) {
+ libnormalform_free_expression__(sub);
+ goto fail;
+ }
+ ret->terms = new;
+ ret->terms[ret->nterms++] = sub;
+ }
+ break;
+
+ case TYPE_ALL:
+ ret->type = LIBNORMALFORM_FOR_ALL;
+ goto qualifier;
+ case TYPE_ANY:
+ ret->type = LIBNORMALFORM_FOR_ANY;
+ goto qualifier;
+ case TYPE_ONE:
+ ret->type = LIBNORMALFORM_FOR_ONE;
+ goto qualifier;
+ case TYPE_NOT_ONE:
+ ret->type = LIBNORMALFORM_NEGATED_FOR_ONE;
+ qualifier:
+ ret->user_item = this->data.qualifier.domain;
+ ret->nterms = 2;
+ ret->terms = malloc(2 * sizeof(*ret->terms));
+ if (!ret->terms)
+ goto fail;
+ ret->terms[0] = sentence_to_expression(this->data.qualifier.antecedent, flags);
+ if (!ret->terms[0])
+ goto fail;
+ ret->terms[1] = sentence_to_expression(this->data.qualifier.predicate, flags);
+ if (!ret->terms[1])
+ goto fail;
+ break;
+
+ case TYPE_VARIABLE:
+ if ((flags & LIBNORMALFORM_ELIMINATE_VARIABLE) && (flags & LIBNORMALFORM_ELIMINATE_NEGATED_VARIABLE))
+ goto eliminated;
+ ret->user_item = this->data.literal.atom.variable;
+ ret->type = LIBNORMALFORM_VARIABLE + (enum libnormalform_term_type)this->data.literal.inverted;
+ break;
+
+ case TYPE_FUNCTION:
+ if ((flags & LIBNORMALFORM_ELIMINATE_FUNCTION) && (flags & LIBNORMALFORM_ELIMINATE_NEGATED_FUNCTION))
+ goto eliminated;
+ ret->reduced = !!this->data.literal.atom.function->requires_relaxation;
+ if (!ret->reduced)
+ ret->user_item = this->data.literal.atom.function;
+ else if (this->data.literal.atom.function->relaxation)
+ ret->user_item = this->data.literal.atom.function->relaxation;
+ else
+ goto eliminated;
+ ret->type = LIBNORMALFORM_FUNCTION + (enum libnormalform_term_type)this->data.literal.inverted;
+ break;
+
+ case TYPE_TRANS:
+ if (this->data.trans.function->requires_elimination)
+ goto eliminated;
+ ret->type = LIBNORMALFORM_TRANSFORMATION;
+ ret->user_item = this->data.trans.function;
+ ret->terms = malloc(sizeof(*ret->terms));
+ if (ret->terms)
+ goto fail;
+ ret->terms[0] = sentence_to_expression(this->data.trans.input, flags);
+ if (!ret->terms[0])
+ goto fail;
+ break;
+
+ default:
+ abort();
+ }
+
+ goto out;
+
+eliminated:
+ ret->reduced = 1;
+ ret->type = LIBNORMALFORM_CONJUNCTION;
+out:
+ libnormalform_free(free1);
+ libnormalform_free(free2);
+ return ret;
+
+fail:
+ libnormalform_free(free1);
+ libnormalform_free(free2);
+ libnormalform_free_expression__(ret);
+ return NULL;
+}
+
+
+
+/**
+ * Reduce a sentence as much as possible keeping it logically equivalent
+ *
+ * All objects must have a reference count of 1
+ *
+ * @param this The sentence to reduce (in place)
+ * @param analysers Application-provided analysis functions, or `NULL`
+ * @return 0 on success, -1 on failure
+ */
+static int
+reduce_expression(struct expression *this, const struct libnormalform_analysers *analysers)
+{
+ enum libnormalform_sentence_relationship relationship;
+ struct expression *new;
+ size_t i, j;
+
+ for (i = 0; i < this->nterms; i++)
+ if (reduce_expression(this->terms[i], analysers))
+ return -1;
+
+ switch (this->type) {
+ case LIBNORMALFORM_CONJUNCTION:
+ for (i = 0; i < this->nterms; i++) {
+ left_eliminated_conjunction:
+ for (j = i + 1; j < this->nterms; j++) {
+ if (libnormalform_get_relationship__(this->terms[i], this->terms[j], analysers, &relationship))
+ return -1;
+ switch (relationship) {
+ case LIBNORMALFORM_MATERIAL_IMPLICATION:
+ case LIBNORMALFORM_IDENTICAL:
+ libnormalform_free_expression__(this->terms[j]);
+ this->terms[j--] = this->terms[--this->nterms];
+ break;
+ case LIBNORMALFORM_CONVERSE_IMPLICATION:
+ libnormalform_free_expression__(this->terms[i]);
+ this->terms[i--] = this->terms[--this->nterms];
+ goto left_eliminated_conjunction;
+ case LIBNORMALFORM_MUTUALLY_INVERSE:
+ case LIBNORMALFORM_MUTUALLY_EXCLUSIVE:
+ goto contradiction;
+ case LIBNORMALFORM_JOINTLY_UNDENIABLE:
+ case LIBNORMALFORM_MUTUALLY_INDEPENDENT:
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ contradiction:
+ for (i = 0; i < this->nterms; i++)
+ libnormalform_free_expression__(this->terms[i]);
+ free(this->terms);
+ this->terms = NULL;
+ this->nterms = 0;
+ this->type = LIBNORMALFORM_DISJUNCTION;
+ break;
+
+ case LIBNORMALFORM_DISJUNCTION:
+ for (i = 0; i < this->nterms; i++) {
+ left_eliminated_disjunction:
+ for (j = i + 1; j < this->nterms; j++) {
+ if (libnormalform_get_relationship__(this->terms[i], this->terms[j], analysers, &relationship))
+ return -1;
+ switch (relationship) {
+ case LIBNORMALFORM_IDENTICAL:
+ case LIBNORMALFORM_CONVERSE_IMPLICATION:
+ libnormalform_free_expression__(this->terms[j]);
+ this->terms[j--] = this->terms[--this->nterms];
+ break;
+ case LIBNORMALFORM_MATERIAL_IMPLICATION:
+ libnormalform_free_expression__(this->terms[i]);
+ this->terms[i--] = this->terms[--this->nterms];
+ goto left_eliminated_disjunction;
+ case LIBNORMALFORM_MUTUALLY_INVERSE:
+ case LIBNORMALFORM_JOINTLY_UNDENIABLE:
+ goto tautology;
+ case LIBNORMALFORM_MUTUALLY_EXCLUSIVE:
+ case LIBNORMALFORM_MUTUALLY_INDEPENDENT:
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ tautology:
+ for (i = 0; i < this->nterms; i++)
+ libnormalform_free_expression__(this->terms[i]);
+ free(this->terms);
+ this->terms = NULL;
+ this->nterms = 0;
+ this->type = LIBNORMALFORM_CONJUNCTION;
+ break;
+
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ i = 0;
+ both_eliminated_exclusive_disjunction:
+ for (; i < this->nterms; i++) {
+ for (j = i + 1; j < this->nterms; j++) {
+ if (libnormalform_get_relationship__(this->terms[i], this->terms[j], analysers, &relationship))
+ return -1;
+ switch (relationship) {
+ case LIBNORMALFORM_MATERIAL_IMPLICATION:
+ new = libnormalform_make_binary__(1, this->terms[i], 0, this->terms[j],
+ LIBNORMALFORM_CONJUNCTION);
+ if (!new)
+ return -1;
+ goto xor_reduced;
+ case LIBNORMALFORM_CONVERSE_IMPLICATION:
+ new = libnormalform_make_binary__(0, this->terms[i], 1, this->terms[j],
+ LIBNORMALFORM_CONJUNCTION);
+ if (!new)
+ return -1;
+ goto xor_reduced;
+ case LIBNORMALFORM_MUTUALLY_INVERSE:
+ this->invert ^= 1;
+ /* fall through */
+ case LIBNORMALFORM_IDENTICAL:
+ libnormalform_free_expression__(this->terms[i]);
+ libnormalform_free_expression__(this->terms[j]);
+ this->terms[i--] = this->terms[--this->nterms];
+ this->terms[j--] = this->terms[--this->nterms];
+ goto both_eliminated_exclusive_disjunction;
+ case LIBNORMALFORM_MUTUALLY_EXCLUSIVE:
+ new = libnormalform_make_binary__(0, this->terms[i], 0, this->terms[j],
+ LIBNORMALFORM_DISJUNCTION);
+ if (!new)
+ return -1;
+ goto xor_reduced;
+ case LIBNORMALFORM_JOINTLY_UNDENIABLE:
+ new = libnormalform_make_binary__(1, this->terms[i], 1, this->terms[j],
+ LIBNORMALFORM_DISJUNCTION);
+ if (!new)
+ return -1;
+ goto xor_reduced;
+ case LIBNORMALFORM_MUTUALLY_INDEPENDENT:
+ default:
+ break;
+ xor_reduced:
+ libnormalform_free_expression__(this->terms[i]);
+ libnormalform_free_expression__(this->terms[j]);
+ this->terms[j--] = this->terms[--this->nterms];
+ this->terms[i] = new;
+ goto both_eliminated_exclusive_disjunction;
+ }
+ }
+ }
+ if (!this->nterms) {
+ if (this->invert) {
+ this->invert = 0;
+ this->type = LIBNORMALFORM_CONJUNCTION;
+ } else {
+ this->type = LIBNORMALFORM_DISJUNCTION;
+ }
+ }
+ break;
+
+ case LIBNORMALFORM_TRANSFORMATION:
+ case LIBNORMALFORM_VARIABLE:
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ case LIBNORMALFORM_FUNCTION:
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ case LIBNORMALFORM_FOR_ONE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ default:
+ return 0;
+ }
+
+ if (this->nterms == 1) {
+ new = this->terms[0];
+ free(this->terms);
+ new->invert ^= this->invert;
+ *this = *new;
+ free(new);
+ }
+
+ return 0;
+}
+
+
+struct libnormalform_term *
+(libnormalform_express__)(LIBNORMALFORM_SENTENCE *this, uint64_t flags,
+ const struct libnormalform_analysers *analysers, enum normal_form form,
+ int (*canonicalise)(struct expression *this, uint64_t flags))
+{
+ struct libnormalform_term *ret;
+ struct expression *expression;
+ int reduced;
+
+ if (flags & ~LIBNORMALFORM_ALL_EXPRESS_FLAGS__) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* just to be safe */
+ if (flags & LIBNORMALFORM_REDUCE_XOR)
+ flags |= LIBNORMALFORM_RELAX_XOR;
+ if ((flags & LIBNORMALFORM_AVOID_FOR_ANY) && (flags & LIBNORMALFORM_AVOID_NEGATED_FOR_ALL))
+ flags ^= LIBNORMALFORM_AVOID_FOR_ANY | LIBNORMALFORM_AVOID_NEGATED_FOR_ALL;
+ if ((flags & LIBNORMALFORM_AVOID_FOR_ALL) && (flags & LIBNORMALFORM_AVOID_NEGATED_FOR_ANY))
+ flags ^= LIBNORMALFORM_AVOID_FOR_ALL | LIBNORMALFORM_AVOID_NEGATED_FOR_ANY;
+
+ (void) form; /* TODO respect form=={CNF,DNF} */
+ expression = sentence_to_expression(this, flags);
+ if (!expression)
+ return NULL;
+ do {
+ if (reduce_expression(expression, analysers)) {
+ libnormalform_free_expression__(expression);
+ return NULL;
+ }
+ reduced = 0;
+ if (libnormalform_fix_presentation__(expression, flags, &(unsigned char){0}, &reduced)) {
+ libnormalform_free_expression__(expression);
+ return NULL;
+ }
+ } while (reduced);
+
+ if (canonicalise) {
+ if ((*canonicalise)(expression, flags)) {
+ libnormalform_free_expression__(expression);
+ return NULL;
+ }
+ }
+
+ ret = malloc(sizeof(*ret));
+ if (!ret) {
+ libnormalform_free_expression__(expression);
+ return NULL;
+ }
+ if (libnormalform_expression_to_term__(ret, expression)) {
+#if defined(__GNUC__) && !defined(__clang__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wmismatched-dealloc"
+#endif
+ libnormalform_free(ret);
+ ret = NULL;
+#if defined(__GNUC__) && !defined(__clang__)
+# pragma GCC diagnostic pop
+#endif
+ }
+ libnormalform_free_expression__(expression);
+
+ return ret;
+}
+
+
+#else
+
+
+CONST int
+main(void)
+{
+ return 0; /* indirectly tested via libnormalform_{express,dnf,cnf,cdnf} */
+}
+
+
+#endif
diff --git a/libnormalform_expression_to_term__.c b/libnormalform_expression_to_term__.c
new file mode 100644
index 0000000..af988ea
--- /dev/null
+++ b/libnormalform_expression_to_term__.c
@@ -0,0 +1,82 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+(libnormalform_expression_to_term__)(struct libnormalform_term *out, struct expression *this)
+{
+ size_t i;
+
+ out->type = this->type;
+ out->reduced = this->reduced;
+
+ switch (this->type) {
+ case LIBNORMALFORM_CONJUNCTION:
+ case LIBNORMALFORM_DISJUNCTION:
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ out->term.clause.nterms = 0;
+ out->term.clause.terms = calloc(this->nterms, sizeof(*out->term.clause.terms));
+ if (!out->term.clause.terms)
+ return -1;
+ for (i = 0; i < this->nterms; i++)
+ if (libnormalform_expression_to_term__(&out->term.clause.terms[out->term.clause.nterms++], this->terms[i]))
+ return -1;
+ break;
+
+ case LIBNORMALFORM_TRANSFORMATION:
+ out->term.transformation.transformer = this->user_item;
+ out->term.transformation.sentence = calloc(1, sizeof(*out->term.transformation.sentence));
+ if (!out->term.transformation.sentence ||
+ libnormalform_expression_to_term__(out->term.transformation.sentence, this->terms[0]))
+ return -1;
+ break;
+
+ case LIBNORMALFORM_VARIABLE:
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ out->term.variable = this->user_item;
+ break;
+
+ case LIBNORMALFORM_FUNCTION:
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ out->term.function = this->user_item;
+ break;
+
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ case LIBNORMALFORM_FOR_ONE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ out->term.qualification.domain = this->user_item;
+ if (this->nterms == 1) {
+ out->term.qualification.antecedent = NULL;
+ out->term.qualification.predicate = calloc(1, sizeof(*out->term.qualification.predicate));
+ if (!out->term.qualification.predicate ||
+ libnormalform_expression_to_term__(out->term.qualification.predicate, this->terms[0]))
+ return -1;
+ } else {
+ out->term.qualification.antecedent = calloc(1, sizeof(*out->term.qualification.antecedent));
+ if (!out->term.qualification.antecedent)
+ return -1;
+ out->term.qualification.predicate = calloc(1, sizeof(*out->term.qualification.predicate));
+ if (!out->term.qualification.predicate ||
+ libnormalform_expression_to_term__(out->term.qualification.antecedent, this->terms[0]) ||
+ libnormalform_expression_to_term__(out->term.qualification.predicate, this->terms[1]))
+ return -1;
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ return 0;
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_fix_presentation__.c b/libnormalform_fix_presentation__.c
new file mode 100644
index 0000000..8c0f4e0
--- /dev/null
+++ b/libnormalform_fix_presentation__.c
@@ -0,0 +1,313 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+/**
+ * Check whether inverting a sentence would require
+ * it to be relaxes before it can be represented
+ *
+ * @param this The sentence to check
+ * @param flags Flags from `libnormalform_express`
+ * @return 1 if the inversion can be exactly represented,
+ * 0 if the inversion requires relaxation be
+ */
+PURE static int
+inversion_would_relax(struct expression *this, uint64_t flags)
+{
+ size_t i;
+
+beginning:
+ switch (this->type) {
+ case LIBNORMALFORM_CONJUNCTION:
+ case LIBNORMALFORM_DISJUNCTION:
+ for (i = 0; i < this->nterms; i++)
+ if (inversion_would_relax(this->terms[i], flags))
+ return 1;
+ return 0;
+
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ for (i = 0; i < this->nterms; i++)
+ if (!inversion_would_relax(this->terms[i], flags))
+ return 0;
+ return 1;
+
+ case LIBNORMALFORM_VARIABLE:
+ return flags & LIBNORMALFORM_ELIMINATE_NEGATED_VARIABLE;
+
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ return flags & LIBNORMALFORM_ELIMINATE_VARIABLE;
+
+ case LIBNORMALFORM_FUNCTION:
+ return flags & LIBNORMALFORM_ELIMINATE_NEGATED_FUNCTION;
+
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ return flags & LIBNORMALFORM_ELIMINATE_FUNCTION;
+
+ case LIBNORMALFORM_TRANSFORMATION:
+ this = this->terms[0];
+ goto beginning;
+
+ case LIBNORMALFORM_FOR_ALL:
+ if (!(flags & LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL))
+ return 0;
+ if (flags & LIBNORMALFORM_ELIMINATE_FOR_ANY)
+ return 1;
+ this = this->terms[this->nterms - 1];
+ goto beginning;
+
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ if (!(flags & LIBNORMALFORM_ELIMINATE_FOR_ALL))
+ return 0;
+ if (flags & LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ANY)
+ return 1;
+ this = this->terms[this->nterms - 1];
+ goto beginning;
+
+ case LIBNORMALFORM_FOR_ANY:
+ if (!(flags & LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ANY))
+ return 0;
+ if (flags & LIBNORMALFORM_ELIMINATE_FOR_ALL)
+ return 1;
+ this = this->terms[this->nterms - 1];
+ goto beginning;
+
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ if (!(flags & LIBNORMALFORM_ELIMINATE_FOR_ANY))
+ return 0;
+ if (flags & LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL)
+ return 1;
+ this = this->terms[this->nterms - 1];
+ goto beginning;
+
+ case LIBNORMALFORM_FOR_ONE:
+ return flags & LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ONE;
+
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ return flags & LIBNORMALFORM_ELIMINATE_FOR_ONE;
+
+ default:
+ abort();
+ }
+}
+
+
+int
+(libnormalform_fix_presentation__)(struct expression *this, uint64_t flags, unsigned char *require_inversion, int *reduced)
+{
+#define WILL_ELIMINATE(BASETYPE, INVERT)\
+ (flags & (LIBNORMALFORM_ELIMINATE_##BASETYPE <<\
+ ((this->type ^ ((LIBNORMALFORM_##BASETYPE ^ LIBNORMALFORM_NEGATED_##BASETYPE) *\
+ (INVERT))) -\
+ LIBNORMALFORM_##BASETYPE)))
+
+ size_t i;
+ unsigned char zero = 0;
+ struct expression *new;
+ int may_switch = 1;
+
+ switch (this->type) {
+ case LIBNORMALFORM_CONJUNCTION:
+ case LIBNORMALFORM_DISJUNCTION:
+ this->type ^= LIBNORMALFORM_CONJUNCTION ^ LIBNORMALFORM_DISJUNCTION;
+ if (this->invert) {
+ if (*require_inversion) {
+ this->invert ^= 1;
+ *require_inversion = 0;
+ }
+ for (i = 0; i < this->nterms; i++)
+ this->terms[i]->invert ^= 1;
+ }
+ require_inversion = &zero;
+ break;
+
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ if (!this->nterms)
+ abort();
+ if (*require_inversion) { /* TODO always? */
+ this->invert ^= 1;
+ *require_inversion = 0;
+ }
+ for (i = 0; i < this->nterms; i++)
+ if (libnormalform_fix_presentation__(this->terms[i], flags, &this->invert, reduced))
+ return -1;
+ if (this->invert) {
+ /* TODO this is not necessarily the best element to invert,
+ * possibly the previous loop could be used to determine
+ * which is best (notably, clauses are not analysed) */
+ this->invert = 0;
+ this->terms[0]->invert ^= 1;
+ if (libnormalform_fix_presentation__(this->terms[0], flags, &zero, reduced))
+ return -1;
+ }
+ return 0;
+
+ case LIBNORMALFORM_VARIABLE:
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ if (!WILL_ELIMINATE(VARIABLE, this->invert ^ *require_inversion)) {
+ this->invert ^= *require_inversion;
+ *require_inversion = 0;
+ } else if (WILL_ELIMINATE(VARIABLE, this->invert)) {
+ goto eliminate;
+ }
+ this->type ^= (LIBNORMALFORM_VARIABLE ^ LIBNORMALFORM_NEGATED_VARIABLE) * this->invert;
+ break;
+
+ case LIBNORMALFORM_FUNCTION:
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ if (!WILL_ELIMINATE(FUNCTION, this->invert ^ *require_inversion)) {
+ this->invert ^= *require_inversion;
+ *require_inversion = 0;
+ } else if (WILL_ELIMINATE(FUNCTION, this->invert)) {
+ goto eliminate;
+ }
+ this->type ^= (LIBNORMALFORM_FUNCTION ^ LIBNORMALFORM_NEGATED_FUNCTION) * this->invert;
+ break;
+
+ case LIBNORMALFORM_TRANSFORMATION:
+ this->terms[0]->invert ^= this->invert;
+ break;
+
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ if (!WILL_ELIMINATE(FOR_ALL, this->invert ^ *require_inversion)) {
+ this->invert ^= *require_inversion;
+ *require_inversion = 0;
+ } else for_all: if (WILL_ELIMINATE(FOR_ALL, this->invert)) {
+ if (!may_switch)
+ goto eliminate;
+ may_switch = 0;
+ this->invert = 0;
+ this->terms[this->nterms - 1]->invert ^= 1;
+ this->type = LIBNORMALFORM_NEGATED_FOR_ANY - (this->type - LIBNORMALFORM_FOR_ALL);
+ goto for_any;
+ }
+ this->type ^= (LIBNORMALFORM_FOR_ALL ^ LIBNORMALFORM_NEGATED_FOR_ALL) * this->invert;
+ this->invert = 0;
+ if (flags & (LIBNORMALFORM_AVOID_FOR_ALL << (this->type - LIBNORMALFORM_FOR_ALL))) {
+ if (!inversion_would_relax(this, flags)) {
+ this->terms[this->nterms - 1]->invert ^= 1;
+ this->type = LIBNORMALFORM_NEGATED_FOR_ANY - (this->type - LIBNORMALFORM_FOR_ALL);
+ goto for_any_maybe_join_sides;
+ }
+ }
+ for_all_maybe_join_sides:
+ if (this->nterms > 1)
+ break;
+ if (flags & (LIBNORMALFORM_JOIN_SIDES_IN_FOR_ALL << (this->type - LIBNORMALFORM_FOR_ALL)))
+ goto for_all_join_sides;
+ break;
+
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ if (!WILL_ELIMINATE(FOR_ANY, this->invert ^ *require_inversion)) {
+ this->invert ^= *require_inversion;
+ *require_inversion = 0;
+ } else for_any: if (WILL_ELIMINATE(FOR_ANY, this->invert)) {
+ if (!may_switch)
+ goto eliminate;
+ may_switch = 0;
+ this->invert = 0;
+ this->terms[this->nterms - 1]->invert ^= 1;
+ this->type = LIBNORMALFORM_NEGATED_FOR_ALL - (this->type - LIBNORMALFORM_FOR_ANY);
+ goto for_all;
+ }
+ this->type ^= (LIBNORMALFORM_FOR_ANY ^ LIBNORMALFORM_NEGATED_FOR_ANY) * this->invert;
+ this->invert = 0;
+ if (flags & (LIBNORMALFORM_AVOID_FOR_ANY << (this->type - LIBNORMALFORM_FOR_ANY))) {
+ if (!inversion_would_relax(this, flags)) {
+ this->terms[this->nterms - 1]->invert ^= 1;
+ this->type = LIBNORMALFORM_NEGATED_FOR_ALL - (this->type - LIBNORMALFORM_FOR_ANY);
+ goto for_all_maybe_join_sides;
+ }
+ }
+ for_any_maybe_join_sides:
+ if (this->nterms > 1)
+ break;
+ if (flags & (LIBNORMALFORM_JOIN_SIDES_IN_FOR_ANY << (this->type - LIBNORMALFORM_FOR_ANY)))
+ goto for_any_join_sides;
+ break;
+
+ case LIBNORMALFORM_FOR_ONE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ if (!WILL_ELIMINATE(FOR_ONE, this->invert ^ *require_inversion)) {
+ this->invert ^= *require_inversion;
+ *require_inversion = 0;
+ } else if (WILL_ELIMINATE(FOR_ONE, this->invert)) {
+ this->type ^= (LIBNORMALFORM_FOR_ONE ^ LIBNORMALFORM_NEGATED_FOR_ONE) * this->invert;
+ if (this->type == LIBNORMALFORM_FOR_ONE) {
+ this->invert = 0;
+ this->reduced = 1;
+ *reduced = 1;
+ goto for_any;
+ }
+ goto eliminate;
+ }
+ this->type ^= (LIBNORMALFORM_FOR_ONE ^ LIBNORMALFORM_NEGATED_FOR_ONE) * this->invert;
+ if (this->type == LIBNORMALFORM_FOR_ONE) {
+ if (flags & LIBNORMALFORM_ELIMINATE_FOR_ONE) {
+ this->type = LIBNORMALFORM_FOR_ANY;
+ this->invert = 0;
+ this->reduced = 1;
+ *reduced = 1;
+ goto for_any;
+ }
+ }
+ if (this->nterms > 1)
+ break;
+ if (flags & (LIBNORMALFORM_JOIN_SIDES_IN_FOR_ONE << (this->type - LIBNORMALFORM_FOR_ONE)))
+ goto for_any_join_sides;
+ break;
+
+ default:
+ abort();
+ }
+
+ this->invert = 0;
+
+ for (i = 0; i < this->nterms; i++)
+ if (libnormalform_fix_presentation__(this->terms[i], flags, require_inversion, reduced))
+ return -1;
+
+ return 0;
+
+eliminate:
+ *require_inversion = 0;
+ this->refcount = 1;
+ this->reduced = 1;
+ this->invert = 0;
+ while (this->nterms)
+ libnormalform_free_expression__(this->terms[--this->nterms]);
+ free(this->terms);
+ this->terms = NULL;
+ this->type = LIBNORMALFORM_CONJUNCTION;
+ *reduced = 1;
+ return 0;
+
+for_all_join_sides:
+ new = libnormalform_make_binary__(1, this->terms[0], 0, this->terms[1], LIBNORMALFORM_DISJUNCTION);
+ goto join_sides;
+
+for_any_join_sides:
+ new = libnormalform_make_binary__(0, this->terms[0], 0, this->terms[1], LIBNORMALFORM_CONJUNCTION);
+join_sides:
+ if (!new)
+ return -1;
+ this->terms[0] = new;
+ this->nterms--;
+ if (libnormalform_apply_transformation__(this->terms[0]->terms[0], LIBNORMALFORM_DOMAIN_VIEW))
+ return -1;
+ if (libnormalform_apply_transformation__(this->terms[0]->terms[1], LIBNORMALFORM_IMAGE_VIEW))
+ return -1;
+ *reduced = 1;
+ return 0;
+
+#undef WILL_ELIMINATE
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_free.c b/libnormalform_free.c
index 3bce4df..26353c8 100644
--- a/libnormalform_free.c
+++ b/libnormalform_free.c
@@ -56,12 +56,11 @@ destroy_term(struct libnormalform_term *this)
while (this->term.clause.nterms)
destroy_term(&this->term.clause.terms[--this->term.clause.nterms]);
free(this->term.clause.terms);
- free(this);
break;
case LIBNORMALFORM_TRANSFORMATION:
+ destroy_term(this->term.transformation.sentence);
free(this->term.transformation.sentence);
- free(this);
return;
case LIBNORMALFORM_FOR_ALL:
@@ -74,13 +73,12 @@ destroy_term(struct libnormalform_term *this)
destroy_term(this->term.qualification.predicate);
free(this->term.qualification.antecedent);
free(this->term.qualification.predicate);
- /* fall through */
+ break;
case LIBNORMALFORM_VARIABLE:
case LIBNORMALFORM_NEGATED_VARIABLE:
case LIBNORMALFORM_FUNCTION:
case LIBNORMALFORM_NEGATED_FUNCTION:
- free(this);
break;
default:
diff --git a/libnormalform_free_expression__.c b/libnormalform_free_expression__.c
new file mode 100644
index 0000000..d9f1a47
--- /dev/null
+++ b/libnormalform_free_expression__.c
@@ -0,0 +1,30 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+void
+(libnormalform_free_expression__)(struct expression *this)
+{
+ if (!this || --this->refcount)
+ return;
+ if (this->terms) {
+ while (this->nterms)
+ libnormalform_free_expression__(this->terms[--this->nterms]);
+ free(this->terms);
+ }
+ free(this);
+}
+
+
+#else
+
+
+CONST int
+main(void)
+{
+ return 0; /* indirectly tested */
+}
+
+
+#endif
diff --git a/libnormalform_from_string.c b/libnormalform_from_string.c
index 8d64c41..7555f8e 100644
--- a/libnormalform_from_string.c
+++ b/libnormalform_from_string.c
@@ -7,7 +7,7 @@
do {\
while (isspace(*s))\
s++;\
- } while (0);
+ } while (0)
#define LEFT_BRACKET\
do {\
@@ -482,7 +482,7 @@ get_transformer(char *s, char **end_out, void *user_data)
}
-struct libnormalform_representation_spec spec = {
+static struct libnormalform_representation_spec spec = {
.get_variable = get_variable,
.get_function = get_function,
.get_map = get_map,
diff --git a/libnormalform_get_relationship__.c b/libnormalform_get_relationship__.c
new file mode 100644
index 0000000..baad786
--- /dev/null
+++ b/libnormalform_get_relationship__.c
@@ -0,0 +1,23 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+(libnormalform_get_relationship__)(struct expression *left, struct expression *right, /* TODO */
+ const struct libnormalform_analysers *analysers,
+ enum libnormalform_sentence_relationship *relationship_out)
+{
+ (void) left;
+ (void) right;
+ (void) analysers;
+ *relationship_out = LIBNORMALFORM_MUTUALLY_INDEPENDENT;
+ return 0;
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_literal_cmp__.c b/libnormalform_literal_cmp__.c
new file mode 100644
index 0000000..8d5e152
--- /dev/null
+++ b/libnormalform_literal_cmp__.c
@@ -0,0 +1,126 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+(libnormalform_literal_cmp__)(struct expression *a, struct expression *b)
+{
+ int a_class, b_class;
+ uintptr_t a_value, b_value;
+
+beginning:
+ switch (a->type) {
+ case LIBNORMALFORM_VARIABLE:
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ a_class = 0;
+ break;
+
+ case LIBNORMALFORM_TRANSFORMATION:
+ case LIBNORMALFORM_FUNCTION:
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ a_class = 1;
+ break;
+
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ a_class = 2;
+ break;
+
+ case LIBNORMALFORM_FOR_ONE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ a_class = 3;
+ break;
+
+ default:
+ case LIBNORMALFORM_CONJUNCTION:
+ case LIBNORMALFORM_DISJUNCTION:
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ abort();
+ }
+
+ switch (b->type) {
+ case LIBNORMALFORM_VARIABLE:
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ b_class = 0;
+ break;
+
+ case LIBNORMALFORM_TRANSFORMATION:
+ case LIBNORMALFORM_FUNCTION:
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ b_class = 1;
+ break;
+
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ b_class = 2;
+ break;
+
+ case LIBNORMALFORM_FOR_ONE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ b_class = 3;
+ break;
+
+ default:
+ case LIBNORMALFORM_CONJUNCTION:
+ case LIBNORMALFORM_DISJUNCTION:
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ abort();
+ }
+
+ if (a_class != b_class)
+ return a_class < b_class ? -2 : +2;
+
+ a_value = (uintptr_t)a->user_item;
+ b_value = (uintptr_t)b->user_item;
+ if (a_value != b_value)
+ return a_value < b_value ? -2 : +2;
+
+ switch (a->type) {
+ case LIBNORMALFORM_VARIABLE:
+ return -(b->type == LIBNORMALFORM_NEGATED_VARIABLE);
+
+ case LIBNORMALFORM_NEGATED_VARIABLE:
+ return +(b->type == LIBNORMALFORM_VARIABLE);
+
+ case LIBNORMALFORM_TRANSFORMATION:
+ a = a->terms[0];
+ b = b->terms[0];
+ goto beginning;
+
+ case LIBNORMALFORM_FUNCTION:
+ return -(b->type == LIBNORMALFORM_NEGATED_FUNCTION);
+
+ case LIBNORMALFORM_NEGATED_FUNCTION:
+ return +(b->type == LIBNORMALFORM_FUNCTION);
+
+ case LIBNORMALFORM_FOR_ALL:
+ case LIBNORMALFORM_NEGATED_FOR_ALL:
+ case LIBNORMALFORM_FOR_ANY:
+ case LIBNORMALFORM_NEGATED_FOR_ANY:
+ /* TODO (not) all/any */
+ return 0;
+
+ case LIBNORMALFORM_FOR_ONE:
+ case LIBNORMALFORM_NEGATED_FOR_ONE:
+ /* TODO (not) one */
+ return 0;
+
+ default:
+ case LIBNORMALFORM_CONJUNCTION:
+ case LIBNORMALFORM_DISJUNCTION:
+ case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION:
+ abort();
+ }
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_literal_sort_cmp__.c b/libnormalform_literal_sort_cmp__.c
new file mode 100644
index 0000000..c0b8937
--- /dev/null
+++ b/libnormalform_literal_sort_cmp__.c
@@ -0,0 +1,33 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+(libnormalform_literal_sort_cmp__)(const void *a, const void *b)
+{
+ struct expression *const *ap = a, *const *bp = b;
+ return libnormalform_literal_cmp__(*ap, *bp);
+}
+
+
+#else
+
+
+#if defined(__GNUC__) /* TODO remove when testing is implemented in libnormalform_literal_sort_cmp__.c */
+# pragma GCC diagnostic ignored "-Wunused-function"
+# pragma GCC diagnostic ignored "-Wunused-macros"
+#endif
+
+#define libnormalform_literal_cmp__(...) cmptrampoline(__VA_ARGS__)
+
+PURE static int
+cmptrampoline(struct expression *a, struct expression *b)
+{
+ return libnormalform_literal_sort_cmp__(&a, &b);
+}
+
+#include "libnormalform_literal_cmp__.c"
+
+
+#endif
diff --git a/libnormalform_make_binary__.c b/libnormalform_make_binary__.c
new file mode 100644
index 0000000..11bc0e1
--- /dev/null
+++ b/libnormalform_make_binary__.c
@@ -0,0 +1,31 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+struct expression *
+(libnormalform_make_binary__)(int invert_left, struct expression *left,
+ int invert_right, struct expression *right, enum libnormalform_term_type type)
+{
+ struct expression *ret;
+ ret = malloc(sizeof(*ret));
+ if (!ret)
+ return NULL;
+ *ret = EXPRESSION_INIT(type);
+ ret->nterms = 2;
+ ret->terms = malloc(2 * sizeof(*ret->terms));
+ if (!ret->terms) {
+ free(ret);
+ return NULL;
+ }
+ (ret->terms[0] = left)->invert ^= (unsigned char)invert_left;
+ (ret->terms[1] = right)->invert ^= (unsigned char)invert_right;
+ return ret;
+}
+
+
+#else
+
+TODO_TEST
+
+#endif
diff --git a/libnormalform_one.c b/libnormalform_one.c
index b5c17f1..3219097 100644
--- a/libnormalform_one.c
+++ b/libnormalform_one.c
@@ -426,8 +426,8 @@ main(void)
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
- /* P = ¬Q ⊭ ∃!{x,y}∈X.(P(x) ∧ Q(y)) = ∃!{x,y}∈X.(¬Q(x) ∧ Q(y)) = ∃!{x,y}∈X.⊥ = ⊥ ∵ P = ¬Q ⊭ P(x) = ¬Q(y) */
- /* P = ¬Q ⊭ ∃!{x,y}∈X.(P(x) ∧ Q(y)) = ∃!{x,y}∈X.(P(x) ∧ ¬P(y)) = ∃!{x,y}∈X.⊥ = ⊥ ∵ P = ¬Q ⊭ P(x) = ¬Q(y) */
+ /* P = ¬Q ⊭ ∃!⟨x,y⟩∈X.(P(x) ∧ Q(y)) = ∃!⟨x,y⟩∈X.(¬Q(x) ∧ Q(y)) = ∃!⟨x,y⟩∈X.⊥ = ⊥ ∵ P = ¬Q ⊭ P(x) = ¬Q(y) */
+ /* P = ¬Q ⊭ ∃!⟨x,y⟩∈X.(P(x) ∧ Q(y)) = ∃!⟨x,y⟩∈X.(P(x) ∧ ¬P(y)) = ∃!⟨x,y⟩∈X.⊥ = ⊥ ∵ P = ¬Q ⊭ P(x) = ¬Q(y) */
ASSUME(b = libnormalform_one(&dom1, libnormalform_false(), libnormalform_false()));
ASSERT_NOTEQUAL(a, b);
libnormalform_free(b);
diff --git a/libnormalform_to_string.c b/libnormalform_to_string.c
index 74aa839..fcf2981 100644
--- a/libnormalform_to_string.c
+++ b/libnormalform_to_string.c
@@ -65,10 +65,10 @@ add_string(struct string_construction *s, const char *str)
*/
USE_RESULT NONNULL_INPUT
static int
-add_number(struct string_construction *s, size_t n)
+add_number(struct string_construction *s, size_t num)
{
char buf[3 * sizeof(size_t)], *str;
- int len = sprintf(buf, "%zu", n);
+ int len = sprintf(buf, "%zu", num);
if (len < 0)
abort();
if (s->dyn_n == s->dyn_size) {
diff --git a/libnormalform_transformation.c b/libnormalform_transformation.c
index 3a1218b..82d794d 100644
--- a/libnormalform_transformation.c
+++ b/libnormalform_transformation.c
@@ -284,28 +284,28 @@ main(void)
ASSERT(LEFT(b)->data.trans.input != RIGHT(b)->data.trans.input);
libnormalform_free(b);
- /* F(Q(q)∀{p,q}:P(p)) illegal construct */
+ /* F(Q(q)∀⟨p,q⟩:P(p)) illegal construct */
ASSUME(a = libnormalform_function(&fun1));
ASSUME(b = libnormalform_function(&fun2));
ASSUME(a = libnormalform_all(&dom1, a, b));
errno = 0;
ASSERT(!libnormalform_transformation(&trans1, a) && errno == EDOM);
- /* F(Q(q)∃{p,q}:P(p)) illegal construct */
+ /* F(Q(q)∃⟨p,q⟩:P(p)) illegal construct */
ASSUME(a = libnormalform_function(&fun1));
ASSUME(b = libnormalform_function(&fun2));
ASSUME(a = libnormalform_any(&dom1, a, b));
errno = 0;
ASSERT(!libnormalform_transformation(&trans1, a) && errno == EDOM);
- /* F(Q(q)∃!{p,q}:P(p)) illegal construct */
+ /* F(Q(q)∃!⟨p,q⟩:P(p)) illegal construct */
ASSUME(a = libnormalform_function(&fun1));
ASSUME(b = libnormalform_function(&fun2));
ASSUME(a = libnormalform_one(&dom1, a, b));
errno = 0;
ASSERT(!libnormalform_transformation(&trans1, a) && errno == EDOM);
- /* F(¬(Q(q)∃!{p,q}:P(p))) illegal construct */
+ /* F(¬(Q(q)∃!⟨p,q⟩:P(p))) illegal construct */
ASSUME(a = libnormalform_function(&fun1));
ASSUME(b = libnormalform_function(&fun2));
ASSUME(a = libnormalform_one(&dom1, a, b));
diff --git a/libnormalform_xor2.c b/libnormalform_xor2.c
index a00c575..447d787 100644
--- a/libnormalform_xor2.c
+++ b/libnormalform_xor2.c
@@ -3,10 +3,66 @@
#include "common.h"
+/**
+ * Check if two clauses have a term in common or
+ * a term in common but mutually inverse
+ *
+ * Both clauses must be clauses of binary connectives
+ *
+ * @param a One of the clauses
+ * @param b The other clause
+ * @param common_inv_out Output parameter for whether the common term is mutually inverted
+ * @param a_common_out Output parameter for `a`'s copy of the common term
+ * @param a_unique_out Output parameter for `a`'s uncommon term
+ * @param b_common_out Output parameter for `b`'s copy of the common term
+ * @param b_unique_out Output parameter for `b`'s uncommon term
+ * @return Whether the the clauses have a common or inversely common term
+ */
+static int
+has_common(LIBNORMALFORM_SENTENCE *a, LIBNORMALFORM_SENTENCE *b, int *common_inv_out,
+ LIBNORMALFORM_SENTENCE **a_common_out, LIBNORMALFORM_SENTENCE **a_unique_out,
+ LIBNORMALFORM_SENTENCE **b_common_out, LIBNORMALFORM_SENTENCE **b_unique_out)
+{
+ LIBNORMALFORM_SENTENCE *al = LEFT(a);
+ LIBNORMALFORM_SENTENCE *ar = RIGHT(a);
+ LIBNORMALFORM_SENTENCE *bl = LEFT(b);
+ LIBNORMALFORM_SENTENCE *br = RIGHT(b);
+
+ if (al->equals(al, bl, common_inv_out)) {
+ *a_common_out = al;
+ *b_common_out = bl;
+ *a_unique_out = ar;
+ *b_unique_out = br;
+ return 1;
+ } else if (ar->equals(ar, br, common_inv_out)) {
+ *a_common_out = ar;
+ *b_common_out = br;
+ *a_unique_out = al;
+ *b_unique_out = bl;
+ return 1;
+ } else if (al->equals(al, br, common_inv_out)) {
+ *a_common_out = al;
+ *b_common_out = br;
+ *a_unique_out = ar;
+ *b_unique_out = bl;
+ return 1;
+ } else if (ar->equals(ar, bl, common_inv_out)) {
+ *a_common_out = ar;
+ *b_common_out = bl;
+ *a_unique_out = al;
+ *b_unique_out = br;
+ return 1;
+ }
+
+ return 0;
+}
+
+
LIBNORMALFORM_SENTENCE *
(libnormalform_xor2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r)
{
int inv;
+ LIBNORMALFORM_SENTENCE *t, *not_a, *a, *b, *c;
if (!l || !r) {
libnormalform_free(l);
@@ -49,7 +105,79 @@ LIBNORMALFORM_SENTENCE *
/* x ⊕ 0 = 0 ⊕ x = x */
goto return_l;
+ } else if (l->type == TYPE_OR && r->type == TYPE_OR) { /* TODO test */
+ if (!has_common(l, r, &inv, &a, &b, &not_a, &c) || !inv)
+ goto asis;
+ /* (a ∨ b) ⊕ (¬a ∨ c) =
+ * (a ∨ b ∨ ¬a ∨ c) ∧ ¬((a ∨ b) ∧ (¬a ∨ c)) =
+ * ¬(a ∨ b) ∨ ¬(¬a ∨ c) */
+ return libnormalform_or2(libnormalform_not(l), libnormalform_not(r));
+
+ } else if (l->type == TYPE_AND && r->type == TYPE_AND) { /* TODO test */
+ if (!has_common(l, r, &inv, &a, &b, &not_a, &c))
+ goto asis;
+ if (!inv) {
+ /* (a ∧ b) ⊕ (a ∧ c) =
+ * ((a ∧ b) ∨ (a ∧ c)) ∧ ¬((a ∧ b) ∧ (a ∧ c)) =
+ * a ∧ (b ∨ c) ∧ ¬(a ∧ b ∧ c) =
+ * a ∧ (b ∨ c) ∧ (¬a ∨ ¬b ∨ ¬c) =
+ * a ∧ (b ∨ c) ∧ (¬b ∨ ¬c) =
+ * a ∧ (b ⊕ c)
+ */
+ a = libnormalform_ref(a);
+ b = libnormalform_ref(b);
+ c = libnormalform_ref(c);
+ libnormalform_free(l);
+ libnormalform_free(r);
+ return libnormalform_and2(a, libnormalform_xor2(c, b));
+ } else {
+ /* (a ∧ b) ⊕ (¬a ∧ c) =
+ * ((a ∧ b) ∨ (¬a ∧ c)) ∧ ¬((a ∧ b) ∧ (¬a ∧ c)) =
+ * ((a ∧ b) ∨ (¬a ∧ c)) ∧ ¬(a ∧ b ∧ ¬a ∧ c) =
+ * ((a ∧ b) ∨ (¬a ∧ c)) ∧ ¬⊥ =
+ * (a ∧ b) ∨ (¬a ∧ c)
+ */
+ return libnormalform_or2__(l, r);
+ }
+
+ } else if (l->type == TYPE_OR && r->type == TYPE_AND) { /* TODO test */
+ t = l, l = r, r = t;
+ goto and_xor_or;
+
+ } else if (l->type == TYPE_AND && r->type == TYPE_OR) { /* TODO test */
+ and_xor_or:
+ if (!has_common(l, r, &inv, &a, &b, &not_a, &c))
+ goto asis;
+ if (!inv) {
+ /* (a ∧ b) ⊕ (a ∨ c) =
+ * ((a ∧ b) ∨ (a ∨ c)) ∧ ¬((a ∧ b) ∧ (a ∨ c)) =
+ * ((a ∧ b) ∨ a ∨ c) ∧ ¬(a ∧ b ∧ (a ∨ c)) =
+ * (a ∨ c) ∧ ¬(a ∧ b) =
+ * ¬(a ∧ b) ∧ (a ∨ c)
+ */
+ return libnormalform_and2__(libnormalform_not(l), r);
+ } else {
+ /* (a ∧ b) ⊕ (¬a ∨ c) =
+ * ((a ∧ b) ∨ (¬a ∨ c)) ∧ ¬((a ∧ b) ∧ (¬a ∨ c)) =
+ * ((a ∧ b) ∨ ¬a ∨ c) ∧ ¬(a ∧ b ∧ (¬a ∨ c)) =
+ * ((a ∧ b) ∨ ¬a ∨ c) ∧ ¬(a ∧ b ∧ (⊥ ∨ c)) =
+ * ((a ∧ b) ∨ ¬a ∨ c) ∧ ¬(a ∧ b ∧ c) =
+ * ((a ∧ b) ∨ ¬a ∨ c) ∧ (¬a ∨ ¬b ∨ ¬c) =
+ * (b ∨ ¬a ∨ c) ∧ (¬a ∨ ¬b ∨ ¬c) =
+ * (¬a ∨ b ∨ c) ∧ (¬a ∨ ¬b ∨ ¬c) =
+ * ¬a ∨ ((b ∨ c) ∧ (¬b ∨ ¬c)) =
+ * ¬a ∨ (b ⊕ c)
+ */
+ not_a = libnormalform_ref(not_a);
+ b = libnormalform_ref(b);
+ c = libnormalform_ref(c);
+ libnormalform_free(l);
+ libnormalform_free(r);
+ return libnormalform_or2(not_a, libnormalform_xor2(c, b));
+ }
+
} else {
+ asis:
return libnormalform_xor2__(l, r);
}
}
diff --git a/man3/enum_libnormalform_domain_relationship.3 b/man3/enum_libnormalform_domain_relationship.3
new file mode 120000
index 0000000..d76da20
--- /dev/null
+++ b/man3/enum_libnormalform_domain_relationship.3
@@ -0,0 +1 @@
+struct_libnormalform_domain_comparison.3 \ No newline at end of file
diff --git a/man3/enum_libnormalform_sentences_relationship.3 b/man3/enum_libnormalform_sentences_relationship.3
new file mode 120000
index 0000000..3b51671
--- /dev/null
+++ b/man3/enum_libnormalform_sentences_relationship.3
@@ -0,0 +1 @@
+struct_libnormalform_atom_comparison.3 \ No newline at end of file
diff --git a/man3/enum_libnormalform_ternary.3 b/man3/enum_libnormalform_ternary.3
new file mode 120000
index 0000000..0d9b1eb
--- /dev/null
+++ b/man3/enum_libnormalform_ternary.3
@@ -0,0 +1 @@
+struct_libnormalform_analysers.3 \ No newline at end of file
diff --git a/man3/libnormalform_all.3 b/man3/libnormalform_all.3
index 901bba2..207243f 100644
--- a/man3/libnormalform_all.3
+++ b/man3/libnormalform_all.3
@@ -154,9 +154,9 @@ The
.BR libnormalform_all ()
and
.BR libnormalform_universally ()
-functions fails if:
+functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_analysers.3 b/man3/libnormalform_analysers.3
new file mode 120000
index 0000000..0d9b1eb
--- /dev/null
+++ b/man3/libnormalform_analysers.3
@@ -0,0 +1 @@
+struct_libnormalform_analysers.3 \ No newline at end of file
diff --git a/man3/libnormalform_and.3 b/man3/libnormalform_and.3
index bb65a74..aeb4fa0 100644
--- a/man3/libnormalform_and.3
+++ b/man3/libnormalform_and.3
@@ -110,9 +110,9 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_any.3 b/man3/libnormalform_any.3
index ac44b0b..57ad55c 100644
--- a/man3/libnormalform_any.3
+++ b/man3/libnormalform_any.3
@@ -221,9 +221,9 @@ The
.BR libnormalform_empty (),
and
.BR libnormalform_nonempty (),
-functions fails if:
+functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_atom_comparison.3 b/man3/libnormalform_atom_comparison.3
new file mode 120000
index 0000000..3b51671
--- /dev/null
+++ b/man3/libnormalform_atom_comparison.3
@@ -0,0 +1 @@
+struct_libnormalform_atom_comparison.3 \ No newline at end of file
diff --git a/man3/libnormalform_cdnf.3 b/man3/libnormalform_cdnf.3
new file mode 100644
index 0000000..461e63e
--- /dev/null
+++ b/man3/libnormalform_cdnf.3
@@ -0,0 +1,134 @@
+.TH LIBNORMALFORM_CDNF 3 LIBNORMALFORM
+.SH NAME
+libnormalform_cdnf \- Reduce and express in canonical disjunctive normal form
+
+.SH SYNOPSIS
+.nf
+#include <libnormalform.h>
+
+/* See \fBlibnormalform_express\fP(3) for relevant types and constants */
+
+struct libnormalform_term *
+libnormalform_cdnf(LIBNORMALFORM_SENTENCE *\fIthis\fP, uint64_t \fIflags\fP,
+ const struct libnormalform_analysers *\fIanalysers\fP);
+.fi
+.PP
+Link with
+.IR -lnormalform .
+
+.SH DESCRIPTION
+The
+.BR libnormalform_cdnf ()
+function is similar to the
+.BR libnormalform_express (3)
+function, please refer to
+.BR libnormalform_express (3)
+for a detailed explanation. There are however
+the following differences:
+.PP
+The result will never contain a
+.I LIBNORMALFORM_EXCLUSIVE_DISJUNCTION
+clause, instead these are always relax
+to disjunctions
+.RI ( LIBNORMALFORM_RELAX_XOR ),
+or reduced to an equivalent form if the
+.I LIBNORMALFORM_REDUCE_XOR
+flag is used.
+.PP
+.I LIBNORMALFORM_CONJUNCTION
+and
+.I LIBNORMALFORM_DISJUNCTION
+clauses in the result can be singleton
+clauses.
+.PP
+The root term is always a
+.I LIBNORMALFORM_DISJUNCTION
+clause containing only
+.I LIBNORMALFORM_CONJUNCTION
+clauses, each containing only literals
+(boolean variables and boolean-output
+functions), input transformations on
+literals, and qualifications. Additionally
+each conjunctive clause contains a complete
+enumerate of all atomic sentences (boolean
+variables and boolean-output functions)
+and qualifications (universal and existential
+qualifiers either be negated or translated to
+the other depending on the specified
+.IR flags )
+used in any of the conjuctive clauses within
+the root disjuntive clause. That is
+the returned expression is always
+.I this
+(or some relaxation) expressed in
+canonical disjunctive normal form.
+.PP
+Each
+.RI non- NULL
+.I .term.qualifier.antecedent
+is expressed in canonical disjunctive normal form.
+.PP
+Each
+.I .term.qualifier.predicate
+is expressed in canonical disjunctive normal form.
+
+.SH RETURN VALUE
+Upon successful completion, the
+.BR libnormalform_cdnf ()
+function return an object representing
+the sentence, which is to be deallocated
+by the application using the
+.BR libnormalform_free (3)
+function; otherwise, the function returns
+.I NULL
+and set
+.I errno
+to indicate the error; if the function
+fails because an application-provided
+callback function fails,
+.I errno
+will remain as set by that function
+(or be unmodified if that function did
+not set
+.IR errno ).
+
+.SH ERRORS
+The
+.BR libnormalform_cdnf ()
+function fails if:
+.TP
+.B ENOMEM
+Insufficient memory was available to
+create the representation of the sentence.
+.TP
+.B EINVAL
+.I flags
+contain unsupported options.
+
+.SH ATTRIBUTES
+For an explanation of the terms used in this
+section, see
+.BR attributes (7)
+and
+.IR "info \(dq(libc)POSIX Safety Concepts\(dq" .
+.TS
+allbox;
+lb lb lb
+l l l.
+Interface Attribute Value
+T{
+.BR libnormalform_cdnf ()
+T} Thread safety MT-Safe race:\fIthis\fP
+T{
+.BR libnormalform_cdnf ()
+T} Async-signal safety AS-Unsafe heap
+T{
+.BR libnormalform_cdnf ()
+T} Async-cancel safety AC-Safe mem, AC-Unsafe heap
+.TE
+
+.SH SEE ALSO
+.BR libnormalform (7),
+.BR libnormalform_express (3),
+.BR libnormalform_dnf (3),
+.BR libnormalform_cnf (3)
diff --git a/man3/libnormalform_clone.3 b/man3/libnormalform_clone.3
index 0906a44..02cf5ca 100644
--- a/man3/libnormalform_clone.3
+++ b/man3/libnormalform_clone.3
@@ -63,7 +63,7 @@ The
.BR libnormalform_clone ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_cnf.3 b/man3/libnormalform_cnf.3
new file mode 100644
index 0000000..c0d7438
--- /dev/null
+++ b/man3/libnormalform_cnf.3
@@ -0,0 +1,125 @@
+.TH LIBNORMALFORM_CNF 3 LIBNORMALFORM
+.SH NAME
+libnormalform_cnf \- Reduce and express in conjunctive normal form
+
+.SH SYNOPSIS
+.nf
+#include <libnormalform.h>
+
+/* See \fBlibnormalform_express\fP(3) for relevant types and constants */
+
+struct libnormalform_term *
+libnormalform_cnf(LIBNORMALFORM_SENTENCE *\fIthis\fP, uint64_t \fIflags\fP,
+ const struct libnormalform_analysers *\fIanalysers\fP);
+.fi
+.PP
+Link with
+.IR -lnormalform .
+
+.SH DESCRIPTION
+The
+.BR libnormalform_cnf ()
+function is similar to the
+.BR libnormalform_express (3)
+function, please refer to
+.BR libnormalform_express (3)
+for a detailed explanation. There are however
+the following differences:
+.PP
+The result will never contain a
+.I LIBNORMALFORM_EXCLUSIVE_DISJUNCTION
+clause, instead these are always relax
+to disjunctions
+.RI ( LIBNORMALFORM_RELAX_XOR ),
+or reduced to an equivalent form if the
+.I LIBNORMALFORM_REDUCE_XOR
+flag is used.
+.PP
+.I LIBNORMALFORM_CONJUNCTION
+and
+.I LIBNORMALFORM_DISJUNCTION
+clauses in the result can be singleton
+clauses.
+.PP
+The root term is always a
+.I LIBNORMALFORM_CONJUNCTION
+clause containing only
+.I LIBNORMALFORM_DISJUNCTION
+clauses, each containing only literals
+(boolean variables and boolean-output
+functions), input transformations on
+literals, and qualifications. That is
+the returned expression is always
+.I this
+(or some relaxation) expressed in
+conjunctive normal form.
+.PP
+Each
+.RI non- NULL
+.I .term.qualifier.antecedent
+is expressed in conjunctive normal form.
+.PP
+Each
+.I .term.qualifier.predicate
+is expressed in conjunctive normal form.
+
+.SH RETURN VALUE
+Upon successful completion, the
+.BR libnormalform_cnf ()
+function return an object representing
+the sentence, which is to be deallocated
+by the application using the
+.BR libnormalform_free (3)
+function; otherwise, the function returns
+.I NULL
+and set
+.I errno
+to indicate the error; if the function
+fails because an application-provided
+callback function fails,
+.I errno
+will remain as set by that function
+(or be unmodified if that function did
+not set
+.IR errno ).
+
+.SH ERRORS
+The
+.BR libnormalform_cnf ()
+function fails if:
+.TP
+.B ENOMEM
+Insufficient memory was available to
+create the representation of the sentence.
+.TP
+.B EINVAL
+.I flags
+contain unsupported options.
+
+.SH ATTRIBUTES
+For an explanation of the terms used in this
+section, see
+.BR attributes (7)
+and
+.IR "info \(dq(libc)POSIX Safety Concepts\(dq" .
+.TS
+allbox;
+lb lb lb
+l l l.
+Interface Attribute Value
+T{
+.BR libnormalform_cnf ()
+T} Thread safety MT-Safe race:\fIthis\fP
+T{
+.BR libnormalform_cnf ()
+T} Async-signal safety AS-Unsafe heap
+T{
+.BR libnormalform_cnf ()
+T} Async-cancel safety AC-Safe mem, AC-Unsafe heap
+.TE
+
+.SH SEE ALSO
+.BR libnormalform (7),
+.BR libnormalform_express (3),
+.BR libnormalform_dnf (3),
+.BR libnormalform_cdnf (3)
diff --git a/man3/libnormalform_dnf.3 b/man3/libnormalform_dnf.3
new file mode 100644
index 0000000..825ff1b
--- /dev/null
+++ b/man3/libnormalform_dnf.3
@@ -0,0 +1,125 @@
+.TH LIBNORMALFORM_DNF 3 LIBNORMALFORM
+.SH NAME
+libnormalform_dnf \- Reduce and express in disjunctive normal form
+
+.SH SYNOPSIS
+.nf
+#include <libnormalform.h>
+
+/* See \fBlibnormalform_express\fP(3) for relevant types and constants */
+
+struct libnormalform_term *
+libnormalform_dnf(LIBNORMALFORM_SENTENCE *\fIthis\fP, uint64_t \fIflags\fP,
+ const struct libnormalform_analysers *\fIanalysers\fP);
+.fi
+.PP
+Link with
+.IR -lnormalform .
+
+.SH DESCRIPTION
+The
+.BR libnormalform_dnf ()
+function is similar to the
+.BR libnormalform_express (3)
+function, please refer to
+.BR libnormalform_express (3)
+for a detailed explanation. There are however
+the following differences:
+.PP
+The result will never contain a
+.I LIBNORMALFORM_EXCLUSIVE_DISJUNCTION
+clause, instead these are always relax
+to disjunctions
+.RI ( LIBNORMALFORM_RELAX_XOR ),
+or reduced to an equivalent form if the
+.I LIBNORMALFORM_REDUCE_XOR
+flag is used.
+.PP
+.I LIBNORMALFORM_CONJUNCTION
+and
+.I LIBNORMALFORM_DISJUNCTION
+clauses in the result can be singleton
+clauses.
+.PP
+The root term is always a
+.I LIBNORMALFORM_DISJUNCTION
+clause containing only
+.I LIBNORMALFORM_CONJUNCTION
+clauses, each containing only literals
+(boolean variables and boolean-output
+functions), input transformations on
+literals, and qualifications. That is
+the returned expression is always
+.I this
+(or some relaxation) expressed in
+disjunctive normal form.
+.PP
+Each
+.RI non- NULL
+.I .term.qualifier.antecedent
+is expressed in disjunctive normal form.
+.PP
+Each
+.I .term.qualifier.predicate
+is expressed in disjunctive normal form.
+
+.SH RETURN VALUE
+Upon successful completion, the
+.BR libnormalform_dnf ()
+function return an object representing
+the sentence, which is to be deallocated
+by the application using the
+.BR libnormalform_free (3)
+function; otherwise, the function returns
+.I NULL
+and set
+.I errno
+to indicate the error; if the function
+fails because an application-provided
+callback function fails,
+.I errno
+will remain as set by that function
+(or be unmodified if that function did
+not set
+.IR errno ).
+
+.SH ERRORS
+The
+.BR libnormalform_dnf ()
+function fails if:
+.TP
+.B ENOMEM
+Insufficient memory was available to
+create the representation of the sentence.
+.TP
+.B EINVAL
+.I flags
+contain unsupported options.
+
+.SH ATTRIBUTES
+For an explanation of the terms used in this
+section, see
+.BR attributes (7)
+and
+.IR "info \(dq(libc)POSIX Safety Concepts\(dq" .
+.TS
+allbox;
+lb lb lb
+l l l.
+Interface Attribute Value
+T{
+.BR libnormalform_dnf ()
+T} Thread safety MT-Safe race:\fIthis\fP
+T{
+.BR libnormalform_dnf ()
+T} Async-signal safety AS-Unsafe heap
+T{
+.BR libnormalform_dnf ()
+T} Async-cancel safety AC-Safe mem, AC-Unsafe heap
+.TE
+
+.SH SEE ALSO
+.BR libnormalform (7),
+.BR libnormalform_express (3),
+.BR libnormalform_cnf (3),
+.BR libnormalform_cdnf (3)
diff --git a/man3/libnormalform_domain_comparison.3 b/man3/libnormalform_domain_comparison.3
new file mode 120000
index 0000000..d76da20
--- /dev/null
+++ b/man3/libnormalform_domain_comparison.3
@@ -0,0 +1 @@
+struct_libnormalform_domain_comparison.3 \ No newline at end of file
diff --git a/man3/libnormalform_domain_relationship.3 b/man3/libnormalform_domain_relationship.3
new file mode 120000
index 0000000..a243086
--- /dev/null
+++ b/man3/libnormalform_domain_relationship.3
@@ -0,0 +1 @@
+enum_libnormalform_domain_relationship.3 \ No newline at end of file
diff --git a/man3/libnormalform_express.3 b/man3/libnormalform_express.3
new file mode 100644
index 0000000..b28750a
--- /dev/null
+++ b/man3/libnormalform_express.3
@@ -0,0 +1,1336 @@
+.TH LIBNORMALFORM_EXPRESS 3 LIBNORMALFORM
+.SH NAME
+libnormalform_express \- Sentence reduction and normalisation
+
+.SH SYNOPSIS
+.nf
+#include <libnormalform.h>
+
+#define \fILIBNORMALFORM_AVOID_FOR_ALL\fP /* ... */
+#define \fILIBNORMALFORM_AVOID_NEGATED_FOR_ALL\fP /* ... */
+#define \fILIBNORMALFORM_AVOID_FOR_ANY\fP /* ... */
+#define \fILIBNORMALFORM_AVOID_NEGATED_FOR_ANY\fP /* ... */
+#define \fILIBNORMALFORM_REDUCE_XOR\fP /* ... */
+#define \fILIBNORMALFORM_RELAX_XOR\fP /* ... */
+#define \fILIBNORMALFORM_JOIN_SIDES_IN_FOR_ALL\fP /* ... */
+#define \fILIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ALL\fP /* ... */
+#define \fILIBNORMALFORM_JOIN_SIDES_IN_FOR_ANY\fP /* ... */
+#define \fILIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ANY\fP /* ... */
+#define \fILIBNORMALFORM_JOIN_SIDES_IN_FOR_ONE\fP /* ... */
+#define \fILIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ONE\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_FOR_ALL\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_FOR_ANY\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_NEGATED_FOR_ANY\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_FOR_ONE\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_NEGATED_FOR_ONE\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_VARIABLE\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_NEGATED_VARIABLE\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_FUNCTION\fP /* ... */
+#define \fILIBNORMALFORM_ELIMINATE_NEGATED_FUNCTION\fP /* ... */
+#define \fILIBNORMALFORM_DISTRIBUTE_QUALIFIERS\fP /* ... */
+
+enum libnormalform_term_type {
+ \fILIBNORMALFORM_CONJUNCTION\fP,
+ \fILIBNORMALFORM_DISJUNCTION\fP,
+ \fILIBNORMALFORM_EXCLUSIVE_DISJUNCTION\fP,
+ \fILIBNORMALFORM_TRANSFORMATION\fP,
+ \fILIBNORMALFORM_VARIABLE\fP,
+ \fILIBNORMALFORM_NEGATED_VARIABLE\fP,
+ \fILIBNORMALFORM_FUNCTION\fP,
+ \fILIBNORMALFORM_NEGATED_FUNCTION\fP,
+ \fILIBNORMALFORM_FOR_ALL\fP,
+ \fILIBNORMALFORM_NEGATED_FOR_ALL\fP,
+ \fILIBNORMALFORM_FOR_ANY\fP,
+ \fILIBNORMALFORM_NEGATED_FOR_ANY\fP,
+ \fILIBNORMALFORM_FOR_ONE\fP,
+ \fILIBNORMALFORM_NEGATED_FOR_ONE\fP
+};
+
+enum libnormalform_ternary {
+ \fILIBNORMALFORM_NO\fP = 0,
+ \fILIBNORMALFORM_MAYBE\fP = 1,
+ \fILIBNORMALFORM_YES\fP = 2
+};
+
+enum libnormalform_sentence_relationship [
+ \fILIBNORMALFORM_MATERIAL_IMPLICATION\fP = -1,
+ \fILIBNORMALFORM_IDENTICAL\fP = 0,
+ \fILIBNORMALFORM_CONVERSE_IMPLICATION\fP = 1,
+ /* the following are >1 */
+ \fILIBNORMALFORM_MUTUALLY_INVERSE\fP,
+ \fILIBNORMALFORM_MUTUALLY_EXCLUSIVE\fP,
+ \fILIBNORMALFORM_JOINTLY_UNDENIABLE\fP,
+ \fILIBNORMALFORM_MUTUALLY_INDEPENDENT\fP
+};
+
+enum libnormalform_domain_relationship {
+ \fILIBNORMALFORM_SUPERSET_OF\fP = -1,
+ \fILIBNORMALFORM_SAME_AS\fP = 0,
+ \fILIBNORMALFORM_SUBSET_OF\fP = 1,
+ /* the following are >1 */
+ \fILIBNORMALFORM_DISJOINT_WITH\fP,
+ \fILIBNORMALFORM_CONJOINT_WITH\fP,
+ \fILIBNORMALFORM_UNRELATED_TO\fP
+};
+
+struct libnormalform_atom_comparison {
+ /* since .version == 1 { */
+ int \fIversion\fP;
+ enum libnormalform_sentence_relationship \fIrelationship\fP;
+ void *\fIuser_data\fP;
+
+ void (*\fIrelease_user_data\fP)(void *user_data);
+ void (*\fIdont_want_function\fP)(struct libnormalform_function *function, void *user_data);
+ void (*\fIdont_want_variable\fP)(struct libnormalform_variable *variable, void *user_data);
+
+ enum libnormalform_ternary \fIconjunction_is_useful\fP;
+ struct libnormalform_function *\fIcreated_conjunction_as_function\fP;
+ int (*\fIcreate_conjunction_as_function\fP)(void *left, void *right,
+ struct libnormalform_function **out,
+ void *user_data);
+ struct libnormalform_variable *\fIcreated_conjunction_as_variable\fP;
+ int (*\fIcreate_conjunction_as_variable\fP)(void *left, void *right,
+ struct libnormalform_variable **out,
+ void *user_data);
+
+ enum libnormalform_ternary \fIdisjunction_is_useful\fP;
+ struct libnormalform_function *\fIcreated_disjunction_as_function\fP;
+ int (*\fIcreate_disjunction_as_function\fP)(void *left, void *right,
+ struct libnormalform_function **out,
+ void *user_data);
+ struct libnormalform_variable *\fIcreated_disjunction_as_variable\fP;
+ int (*\fIcreate_disjunction_as_variable\fP)(void *left, void *right,
+ struct libnormalform_variable **out,
+ void *user_data);
+
+ enum libnormalform_ternary \fIexclusive_disjunction_is_useful\fP;
+ struct libnormalform_function *\fIcreated_exclusive_disjunction_as_function\fP;
+ int (*\fIcreate_exclusive_disjunction_as_function\fP)(void *left, void *right,
+ struct libnormalform_function **out,
+ void *user_data);
+ struct libnormalform_variable *\fIcreated_exclusive_disjunction_as_variable\fP;
+ int (*\fIcreate_exclusive_disjunction_as_variable\fP)(void *left, void *right,
+ struct libnormalform_variable **out,
+ void *user_data);
+ /* } */
+};
+
+struct libnormalform_domain_comparison {
+ /* since .version == 1 { */
+ int \fIversion\fP;
+ enum libnormalform_domain_relationship \fIrelationship\fP;
+
+ void *\fIuser_data\fP;
+ void (*\fIrelease_user_data\fP)(void *user_data);
+ void (*\fIdont_want_domain\fP)(struct libnormalform_map *domain, void *user_data);
+
+ enum libnormalform_ternary \fIunion_is_useful\fP;
+ struct libnormalform_map *\fIcreated_union\fP;
+ int (*\fIcreate_union\fP)(struct libnormalform_map *left,
+ struct libnormalform_map *right,
+ struct libnormalform_map *out, void *user_data);
+ /* } */
+};
+
+struct libnormalform_analysers {
+ void *\fIuser_data\fP;
+ int (*\fIcompare_variable_to_variable\fP)(struct libnormalform_variable *left,
+ struct libnormalform_variable *right,
+ struct libnormalform_atom_comparison *result,
+ void *user_data);
+ int (*\fIcompare_function_to_function\fP)(struct libnormalform_function *left,
+ struct libnormalform_function *right,
+ struct libnormalform_atom_comparison *result,
+ void *user_data);
+ int (*\fIcompare_variable_to_function\fP)(struct libnormalform_variable *left,
+ struct libnormalform_function *right,
+ struct libnormalform_atom_comparison *result,
+ void *user_data);
+ int (*\fIcompare_domains\fP)(struct libnormalform_map *left,
+ struct libnormalform_map *right,
+ struct libnormalform_domain_comparison *result,
+ void *user_data);
+};
+
+struct libnormalform_clause {
+ struct libnormalform_term *\fIterms\fP;
+ size_t \fInterms\fP;
+};
+
+struct libnormalform_qualification {
+ struct libnormalform_map *\fIdomain\fP;
+ struct libnormalform_term *\fIantecedent\fP;
+ struct libnormalform_term *\fIpredicate\fP;
+};
+
+struct libnormalform_transformation {
+ struct libnormalform_transformer *\fItransformer\fP;
+ struct libnormalform_term *\fIsentence\fP;
+};
+
+struct libnormalform_term {
+ enum libnormalform_term_type \fItype\fP;
+ int \fIreduced\fP;
+ union {
+ struct libnormalform_clause \fIconjunction\fP;
+ struct libnormalform_clause \fIdisjunction\fP;
+ struct libnormalform_clause \fIexclusive_disjunction\fP;
+ struct libnormalform_clause \fIclause\fP;
+ struct libnormalform_transformation \fItransformation\fP;
+ struct libnormalform_variable *\fIvariable\fP;
+ struct libnormalform_function *\fIfunction\fP;
+ struct libnormalform_qualification \fIfor_all\fP;
+ struct libnormalform_qualification \fIfor_any\fP;
+ struct libnormalform_qualification \fIfor_one\fP;
+ struct libnormalform_qualification \fIqualification\fP;
+ } \fIterm\fP;
+};
+
+struct libnormalform_term *
+libnormalform_express(LIBNORMALFORM_SENTENCE *\fIthis\fP, uint64_t \fIflags\fP,
+ const struct libnormalform_analysers *\fIanalysers\fP);
+.fi
+.PP
+Link with
+.IR -lnormalform .
+
+.SH DESCRIPTION
+The
+.BR libnormalform_express ()
+function creates an application-readable expression of
+the sentence
+.I this
+in negation normal form, optionally extended with XOR, and
+reduce the sentence to a sentence that is at least
+as true (but as false as possible), and is minimised,
+according what the application specifies that it can
+work with. For example, if the application shall serialise
+the sentence but has no way to express unique existential
+qualifications, it is reduced to existential qualification,
+but if existential qualifications cannot be represented,
+it is converted to a negated universal qualification,
+and if that cannot be represented it is eliminated.
+.PP
+Before calling this function, the application shall set
+.I .relaxation
+and
+.I .requires_relaxation
+in each
+.I struct
+.IR libnormalform_function (3)
+used in
+.IR this .
+.I .requires_relaxation
+mat be set to 0 if the function shall not be replaced
+in this case
+.I .relaxation
+need not be set), but set to 1 if the function most be
+replaced with a more true functon. In the latter case,
+.I .relaxation
+shall either beset to the new function or to
+.IR NULL ;
+if set to
+.Ir NULL ,
+the function is reduced to a tautological sentence.
+For any
+.I struct
+.IR libnormalform_transformer (3)
+it must also set
+.I .requires_elimination
+to 1 if the transformation must be replaced with a
+tautology, and to 0 otherwise (if it can be kept).
+.PP
+.I flags
+shall be the OR of zero or more of the following constants:
+.TP
+.B LIBNORMALFORM_AVOID_FOR_ALL
+The function shall, it it would not relax the sentence,
+express any non-negated universal qualification as a
+negated existential qualification.
+.TP
+.B LIBNORMALFORM_AVOID_NEGATED_FOR_ALL
+The function shall, it it would not relax the sentence,
+express any negated universal qualification as a
+non-negated existential qualification.
+.TP
+.B LIBNORMALFORM_AVOID_FOR_ANY
+The function shall, it it would not relax the sentence,
+express any non-negated existential qualification as a
+negated universal qualification.
+.TP
+.B LIBNORMALFORM_AVOID_NEGATED_FOR_ANY
+The function shall, it it would not relax the sentence,
+express any negated existential qualification as a
+non-negated universal qualification.
+.TP
+.B LIBNORMALFORM_REDUCE_XOR
+The function shall express every exclusive disjunction
+in negation normal form.
+.TP
+.B LIBNORMALFORM_RELAX_XOR
+The function shall relax every exclusive disjunction
+into a disjunction. This option nullifies the effects
+of the
+.I LIBNORMALFORM_REDUCE_XOR
+option.
+.TP
+.B LIBNORMALFORM_JOIN_SIDES_IN_FOR_ALL
+The function shall express any non-negated universal
+qualification with the antecedent and the predicate
+joined into once sentence, stored in
+.I .predicate
+for the returned
+.IR "struct libnormalform_qualification" ,
+leaving
+.I .antecedent
+set to
+.IR NULL .
+This will require the function to insert builtin
+transformations of the types
+.I LIBNORMALFORM_DOMAIN_VIEW
+(if the antecedent contains a boolean-output function)
+and
+.I LIBNORMALFORM_IMAGE_VIEW
+(if the predicate contains a boolean-output function);
+see
+.BR libnormalform_transformation (3)
+for more information.
+
+Without this option, the antecedent and the predicate
+will be stored separately, in
+.I .antecedent
+and
+.I .predicate
+respectively.
+.TP
+.B LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ALL
+The function shall express any negated universal
+qualification with the antecedent and the predicate
+joined into once sentence, stored in
+.I .predicate
+for the returned
+.IR "struct libnormalform_qualification" ,
+leaving
+.I .antecedent
+set to
+.IR NULL .
+This will require the function to insert builtin
+transformations of the types
+.I LIBNORMALFORM_DOMAIN_VIEW
+(if the antecedent contains a boolean-output function)
+and
+.I LIBNORMALFORM_IMAGE_VIEW
+(if the predicate contains a boolean-output function);
+see
+.BR libnormalform_transformation (3)
+for more information.
+
+Without this option, the antecedent and the predicate
+will be stored separately, in
+.I .antecedent
+and
+.I .predicate
+respectively.
+.TP
+.B LIBNORMALFORM_JOIN_SIDES_IN_FOR_ANY
+The function shall express any non-negated existential
+qualification with the antecedent and the predicate
+joined into once sentence, stored in
+.I .predicate
+for the returned
+.IR "struct libnormalform_qualification" ,
+leaving
+.I .antecedent
+set to
+.IR NULL .
+This will require the function to insert builtin
+transformations of the types
+.I LIBNORMALFORM_DOMAIN_VIEW
+(if the antecedent contains a boolean-output function)
+and
+.I LIBNORMALFORM_IMAGE_VIEW
+(if the predicate contains a boolean-output function);
+see
+.BR libnormalform_transformation (3)
+for more information.
+
+Without this option, the antecedent and the predicate
+will be stored separately, in
+.I .antecedent
+and
+.I .predicate
+respectively.
+.TP
+.B LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ANY
+The function shall express any negated existential
+qualification with the antecedent and the predicate
+joined into once sentence, stored in
+.I .predicate
+for the returned
+.IR "struct libnormalform_qualification" ,
+leaving
+.I .antecedent
+set to
+.IR NULL .
+This will require the function to insert builtin
+transformations of the types
+.I LIBNORMALFORM_DOMAIN_VIEW
+(if the antecedent contains a boolean-output function)
+and
+.I LIBNORMALFORM_IMAGE_VIEW
+(if the predicate contains a boolean-output function);
+see
+.BR libnormalform_transformation (3)
+for more information.
+
+Without this option, the antecedent and the predicate
+will be stored separately, in
+.I .antecedent
+and
+.I .predicate
+respectively.
+.TP
+.B LIBNORMALFORM_JOIN_SIDES_IN_FOR_ONE
+The function shall express any non-negated unique
+existential qualification with the antecedent and
+the predicate joined into once sentence, stored in
+.I .predicate
+for the returned
+.IR "struct libnormalform_qualification" ,
+leaving
+.I .antecedent
+set to
+.IR NULL .
+This will require the function to insert builtin
+transformations of the types
+.I LIBNORMALFORM_DOMAIN_VIEW
+(if the antecedent contains a boolean-output function)
+and
+.I LIBNORMALFORM_IMAGE_VIEW
+(if the predicate contains a boolean-output function);
+see
+.BR libnormalform_transformation (3)
+for more information.
+
+Without this option, the antecedent and the predicate
+will be stored separately, in
+.I .antecedent
+and
+.I .predicate
+respectively.
+.TP
+.B LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ONE
+The function shall express any negated unique
+existential qualification with the antecedent and
+the predicate joined into once sentence, stored in
+.I .predicate
+for the returned
+.IR "struct libnormalform_qualification" ,
+leaving
+.I .antecedent
+set to
+.IR NULL .
+This will require the function to insert builtin
+transformations of the types
+.I LIBNORMALFORM_DOMAIN_VIEW
+(if the antecedent contains a boolean-output function)
+and
+.I LIBNORMALFORM_IMAGE_VIEW
+(if the predicate contains a boolean-output function);
+see
+.BR libnormalform_transformation (3)
+for more information.
+
+Without this option, the antecedent and the predicate
+will be stored separately, in
+.I .antecedent
+and
+.I .predicate
+respectively.
+.TP
+.B LIBNORMALFORM_ELIMINATE_FOR_ALL
+The function shall relax any non-negated universal
+qualification into a tautology. However, unless
+.I LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ANY
+is also specified, the qualification will instead be
+converted into a negated existential qualification.
+.TP
+.B LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL
+The function shall relax any negated universal
+qualification into a tautology. However, unless
+.I LIBNORMALFORM_ELIMINATE_FOR_ANY
+is also specified, the qualification will instead be
+converted into a non-negated existential qualification.
+.TP
+.B LIBNORMALFORM_ELIMINATE_FOR_ANY
+The function shall relax any non-negated existential
+qualification into a tautology. However, unless
+.I LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL
+is also specified, the qualification will instead be
+converted into a negated universal qualification.
+.TP
+.B LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ANY
+The function shall relax any negated existential
+qualification into a tautology. However, unless
+.I LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL
+is also specified, the qualification will instead be
+converted into a non-negated universal qualification.
+.TP
+.B LIBNORMALFORM_ELIMINATE_FOR_ONE
+The function shall relax any non-negated unique
+existential qualification into a tautology. However,
+unless both
+.I LIBNORMALFORM_ELIMINATE_FOR_ANY
+and
+.I LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL
+is also specified, the qualification will instead be
+converted into a non-negated existential qualification
+or secondarily a negated universal qualification.
+.TP
+.B LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ONE
+The function shall relax any negated unique
+existential qualification into a tautology.
+.TP
+.B LIBNORMALFORM_ELIMINATE_VARIABLE
+The function shall relax any non-negated
+variable into a tautology.
+.TP
+.B LIBNORMALFORM_ELIMINATE_NEGATED_VARIABLE
+The function shall relax any negated
+variable into a tautology.
+.TP
+.B LIBNORMALFORM_ELIMINATE_FUNCTION
+The function shall relax any non-negated
+boolean-output function into a tautology.
+.TP
+.B LIBNORMALFORM_ELIMINATE_NEGATED_FUNCTION
+The function shall relax any negated
+boolean-output function into a tautology.
+.TP
+.B LIBNORMALFORM_DISTRIBUTE_QUALIFIERS
+Whenever possible, the function shall distribute
+a qualification over the clause of it's predicate.
+For example, if a for-all qualification has a
+predicate which is a conjuctive clause, it is
+rewritten as a conjuctive clause of for-all
+qualification, each having it's own term form
+the original qualification's clause as it's
+predicates. This is applied to both universal
+and existential qualifications, whether they are
+negated or not, but it does not apply to
+unique existential qualifications (neither
+negated nor non-negated) as these cannot be
+distributed).
+.PP
+Unless
+.I analysers
+is
+.I NULL
+it will be used by the function to query the
+application about relationships between different
+variables, boolean-output functions, and
+domains of interest for qualifiers. In this case,
+.I analysers->user_data
+may be set freely by the application and will
+be passed into the callback functions:
+.TP
+.I analysers->compare_variable_to_variable
+Will be used, unless it is
+.IR NULL ,
+by the application to query the relationship
+between two variables.
+.TP
+.I analysers->compare_function_to_function
+Will be used, unless it is
+.IR NULL ,
+by the application to query the relationship
+between two boolean-output functions.
+.TP
+.I analysers->compare_variable_to_function
+Will be used, unless it is
+.IR NULL ,
+by the application to query the relationship
+between a variable and a boolean-output function.
+.TP
+.I compare_domains
+Will be used, unless it is
+.IR NULL ,
+by the application to query the relationship
+between domains of interest for qualifiers.
+.PP
+For each of these functions
+.I left
+and
+.I right
+will be set to the two items being compare,
+.I result
+will be set structure where the application
+shall store it's results, and
+.I user_data
+will be set to
+.IR analysers->user_data .
+The functions shall return 1 if it could
+successfully compare the items, 0 if it was
+unable to compare the items, and -1 on failure
+(in which case it may also set
+.IR errno
+so the application knows what went wrong).
+The library will initialise all pointers in
+.I *result
+to
+.I NULL
+and set
+.I result->version
+to the version of the structed the library
+is compiled against, which currently is 1.
+It will also set following fields accordingly
+(refer to the
+.B SYNOPSIS
+section to see which fields, as hints, exists
+for the different callback functions):
+.TP
+.I result->conjunction_is_useful
+Set to
+.I LIBNORMALFORM_YES
+if the library will have use for a conjunction
+between the two atomic sentences,
+.I LIBNORMALFORM_NO
+otherwise, and
+.I LIBNORMALFORM_MAYBE
+if unknown.
+.TP
+.I result->disjunction_is_useful
+Set to
+.I LIBNORMALFORM_YES
+if the library will have use for a disjunction
+between the two atomic sentences,
+.I LIBNORMALFORM_NO
+otherwise, and
+.I LIBNORMALFORM_MAYBE
+if unknown.
+.TP
+.I result->exclusive_disjunction_is_useful
+Set to
+.I LIBNORMALFORM_YES
+if the library will have use for an
+exclusive disjunction between the two
+atomic sentences,
+.I LIBNORMALFORM_NO
+otherwise, and
+.I LIBNORMALFORM_MAYBE
+if unknown.
+.TP
+.I result->union_is_useful
+Set to
+.I LIBNORMALFORM_YES
+if the library will have use for an
+union between the two domains,
+.I LIBNORMALFORM_NO
+otherwise, and
+.I LIBNORMALFORM_MAYBE
+if unknown.
+.PP
+If the callback function returns 1,
+the application shall the set
+.I result->version
+the version number it is written for, but
+only if it is lower than the libraries version.
+It shall also set
+.I result->relationship
+to the relationship between the items. The
+application may also choose to set the other
+fields accordingly
+(refer to the
+.B SYNOPSIS
+section to see which fields exists for the
+different callback functions):
+.TP
+.I result->user_data
+The application may set this freely, and will
+be passed in as is to the functions in the
+structure.
+.TP
+.I result->release_user_data
+Will be called when the structure is no longer
+need, even if
+.I result->user_data
+is
+.IR NULL ;
+(of course it will not be called if this
+function pointer itself is
+.IR NULL ).
+.TP
+.I result->dont_want_function
+Will be called for any
+.I struct libnormalform_function *
+stored in
+.I *result
+that the library does not have use for.
+.TP
+.I result->dont_want_variable
+Will be called for any
+.I struct libnormalform_variable *
+stored in
+.I *result
+that the library does not have use for.
+.TP
+.I result->dont_want_domain
+Will be called for any
+.I struct libnormalform_map *
+stored in
+.I *result
+that the library does not have use for.
+.TP
+.I result->created_conjunction_as_function
+If the application created (or has) a
+boolean-output function that is the conjunction
+of the two atomic sentences, it should be stored
+here. (See
+.I result->create_conjunction_as_function
+for an alternative.)
+.TP
+.I result->created_conjunction_as_variable
+If the application created (or has) a variable
+that is the conjunction of the two atomic
+sentences, it should be stored here. (See
+.I result->create_conjunction_as_variable
+for an alternative.)
+.TP
+.I result->created_disjunction_as_function
+If the application created (or has) a
+boolean-output function that is the disjunction
+of the two atomic sentences, it should be stored
+here. (See
+.I result->create_disjunction_as_function
+for an alternative.)
+.TP
+.I result->created_disjunction_as_variable
+If the application created (or has) a variable
+that is the disjunction of the two atomic
+sentences, it should be stored here. (See
+.I result->create_disjunction_as_variable
+for an alternative.)
+.TP
+.I result->created_exclusive_disjunction_as_function
+If the application created (or has) a boolean-output
+function that is the exclusive disjunction
+of the two atomic sentences, it should be stored
+here. (See
+.I result->create_exclusive_disjunction_as_function
+for an alternative.)
+.TP
+.I result->created_exclusive_disjunction_as_variable
+If the application created (or has) a variable
+that is the exclusive disjunction of the two atomic
+sentences, it should be stored here. (See
+.I result->create_exclusive_disjunction_as_variable
+for an alternative.)
+.TP
+.I result->created_union
+If the application created (or has) a domain that
+is the union of the two domains, it should be stored
+here. (See
+.I result->create_union
+for an alternative.)
+.TP
+.I result->create_conjunction_as_function
+If the application thinks that it can create a
+boolean-output function that is the conjunction of
+.I left
+and
+.IR right ,
+a pointer to a function for doing so should be
+stored in this field.
+.I out
+is used as the output parameter for the result;
+the function shall return 1 on success, 0 if
+it couldn't create the conjunction or if the
+conjunction is a variable (the library will than
+call
+.I *result->create_conjunction_as_variable
+instead unless
+.I result->create_conjunction_as_variable
+is
+.IR NULL ),
+and -1 on failure (in which case it may set
+.I errno
+to let the application know what went wrong).
+.TP
+.I result->create_conjunction_as_variable
+If the application thinks that it can create
+a variable that is the conjunction of
+.I left
+and
+.IR right ,
+a pointer to a function for doing so should be
+stored in this field.
+.I out
+is used as the output parameter for the result;
+the function shall return 1 on success, 0 if
+it couldn't create the conjunction or if the
+conjunction is a boolean-output function (the
+library will than call
+.I *result->create_conjunction_as_function
+instead unless
+.I result->create_conjunction_as_function
+is
+.IR NULL ),
+and -1 on failure (in which case it may set
+.I errno
+to let the application know what went wrong).
+.TP
+.I result->create_disjunction_as_function
+If the application thinks that it can create a
+boolean-output function that is the disjunction of
+.I left
+and
+.IR right ,
+a pointer to a function for doing so should be
+stored in this field.
+.I out
+is used as the output parameter for the result;
+the function shall return 1 on success, 0 if
+it couldn't create the disjunction or if the
+disjunction is a variable (the library will than
+call
+.I *result->create_disjunction_as_variable
+instead unless
+.I result->create_disjunction_as_variable
+is
+.IR NULL ),
+and -1 on failure (in which case it may set
+.I errno
+to let the application know what went wrong).
+.TP
+.I result->create_disjunction_as_variable
+If the application thinks that it can create
+a variable that is the disjunction of
+.I left
+and
+.IR right ,
+a pointer to a function for doing so should be
+stored in this field.
+.I out
+is used as the output parameter for the result;
+the function shall return 1 on success, 0 if
+it couldn't create the disjunction or if the
+disjunction is a boolean-output function (the
+library will than call
+.I *result->create_disjunction_as_function
+instead unless
+.I result->create_disjunction_as_function
+is
+.IR NULL ),
+and -1 on failure (in which case it may set
+.I errno
+to let the application know what went wrong).
+.TP
+.I result->create_exclusive_disjunction_as_function
+If the application thinks that it can create a
+boolean-output function that is the exclusive
+disjunction of
+.I left
+and
+.IR right ,
+a pointer to a function for doing so should be
+stored in this field.
+.I out
+is used as the output parameter for the result;
+the function shall return 1 on success, 0 if
+it couldn't create the exclusive disjunction or if
+the exclusive_ disjunction is a variable (the
+library will than call
+.I *result->create_exclusive_disjunction_as_variable
+instead unless
+.I result->create_exclusive_disjunction_as_variable
+is
+.IR NULL ),
+and -1 on failure (in which case it may set
+.I errno
+to let the application know what went wrong).
+.TP
+.I result->create_exclusive_disjunction_as_variable
+If the application thinks that it can create
+a variable that is the exclusive disjunction of
+.I left
+and
+.IR right ,
+a pointer to a function for doing so should be
+stored in this field.
+.I out
+is used as the output parameter for the result;
+the function shall return 1 on success, 0 if
+it couldn't create the exclusive disjunction or
+if the exclusive disjunction is a boolean-output
+function (the library will than call
+.I *result->create_exclusive_disjunction_as_function
+instead unless
+.I result->create_exclusive_disjunction_as_function
+is
+.IR NULL ),
+and -1 on failure (in which case it may set
+.I errno
+to let the application know what went wrong).
+.TP
+.I result->create_union
+If the application thinks that it can create
+a domain that is the union of
+.I left
+and
+.IR right ,
+a pointer to a function for doing so should be
+stored in this field.
+.I out
+is used as the output parameter for the result;
+the function shall return 1 on success,
+0 if it couldn't create the union,
+and -1 on failure (in which case it may set
+.I errno
+to let the application know what went wrong).
+.PP
+For each of functions the parameter
+.I user_data
+will be
+.IR result->user_data ,
+and for the functions that have the parameters
+.I left
+and
+.IR right ,
+these will have the same value (and thus type)
+as the parameters with the same names that the
+callback function was called with
+.RI ( left
+will be
+.I left
+and
+.I right
+will be
+.IR right ).
+.PP
+The type of
+.I result->relationship
+depends on which callback function is called.
+For
+.IR analysers->compare_variable_to_variable ,
+.IR analysers->compare_function_to_function ,
+and
+.I analysers->compare_variable_to_function
+it is a
+.IR "enum libnormalform_sentence_relationship" .
+For
+.I analysers->compare_domains
+it is a
+.IR "enum libnormalform_domain_relationship" .
+.PP
+If
+.I result->relationship
+is a
+.IR "enum libnormalform_sentence_relationship"
+it shall be set to one the following constants
+if the callback function returns 1:
+.TP
+.B LIBNORMALFORM_MATERIAL_IMPLICATION
+.I right
+is always at least as true as
+.IR left .
+.TP
+.B LIBNORMALFORM_IDENTICAL
+.I left
+and
+.I right
+always have the same value.
+.TP
+.B LIBNORMALFORM_CONVERSE_IMPLICATION
+.I left
+is always at least as true as
+.IR right .
+.TP
+.B LIBNORMALFORM_MUTUALLY_INVERSE
+.I left
+and
+.I right
+always have the opposite value.
+.TP
+.B LIBNORMALFORM_MUTUALLY_EXCLUSIVE
+.I left
+and
+.I right
+can never both be true.
+.TP
+.B LIBNORMALFORM_JOINTLY_UNDENIABLE
+.I left
+and
+.I right
+can never both be false.
+.TP
+.B LIBNORMALFORM_MUTUALLY_INDEPENDENT
+The value of
+.I left
+and
+.I right
+are independent of each other.
+.PP
+If
+.I result->relationship
+is a
+.IR "enum libnormalform_domain_relationship"
+it shall be set to one the following constants
+if the callback function returns 1:
+.TP
+.B LIBNORMALFORM_SUPERSET_OF
+.I right
+can never contain any elements
+that are not also in
+.IR left .
+.TP
+.B LIBNORMALFORM_SAME_AS
+.I left
+and
+.I right
+always have the exact some elements.
+.TP
+.B LIBNORMALFORM_SUBSET_OF
+.I left
+can never contain any elements
+that are not also in
+.IR right .
+.TP
+.B LIBNORMALFORM_DISJOINT_WITH
+.I left
+and
+.I right
+can never have any elements in common.
+.TP
+.B LIBNORMALFORM_CONJOINT_WITH
+.I left
+and
+.I right
+can both have unique and common elements.
+.TP
+.B LIBNORMALFORM_UNRELATED_TO
+.I left
+and
+.I right
+are unrelated to each other.
+
+This is formally equivalent to
+.IR LIBNORMALFORM_CONJOINT_WITH ,
+but is semantically distinct.
+.PP
+Refer to the header file
+.B <libnormalform.h>
+for a more in-depth explanation of
+.IR "enum libnormalform_sentence_relationship"
+and
+.IR "enum libnormalform_domain_relationship" .
+.PP
+In the return object, and any subobject of
+the same type,
+.I .reduced
+will be 1 if the term recursively contains
+reductions such that it's can be true falsely,
+whereas
+.I .type
+is used to indicate the type's term, which
+determine which field in
+.I .term
+that is used:
+.TP
+.B LIBNORMALFORM_CONJUNCTION
+.I .term.conjunction
+.RI ( .term.clause
+may also be used) will contain a non-singleton
+set of terms. The clause is true exactly when
+all of these terms are true. The clause is
+empty, it is always true. The number terms is
+stored in
+.IR .term.conjunction.nterms ,
+and the terms are stored in
+.I .term.conjunction.terms
+as values (not as pointers).
+.TP
+.B LIBNORMALFORM_DISJUNCTION
+.I .term.disjunction
+.RI ( .term.clause
+may also be used) will contain a non-singleton
+set of terms. The clause is true exactly when
+at least one of these terms is true. The clause
+is empty, it is always false. The number terms
+is stored in
+.IR .term.disjunction.nterms ,
+and the terms are stored in
+.I .term.disjunction.terms
+as values (not as pointers).
+.TP
+.B LIBNORMALFORM_EXCLUSIVE_DISJUNCTION
+.I .term.exclusive_disjunction
+.RI ( .term.clause
+may also be used) will contain a non-singleton
+set of terms. The clause is true exactly when
+an odd number of these terms are true. The clause
+will never be empty (however if it were to be empty,
+it would always be false). The number terms
+is stored in
+.IR .term.exclusive_disjunction.nterms ,
+and the terms are stored in
+.I .term.exclusive_disjunction.terms
+as values (not as pointers).
+.TP
+.B LIBNORMALFORM_TRANSFORMATION
+.I .term.transformation.transformer
+will be either a built-in or application-provided
+input morphism
+.RI ( "struct libnormalform_transformer *" )
+that is applied over the input for the sentence
+.I .term.transformation.sentence .
+.TP
+.B LIBNORMALFORM_VARIABLE
+The term is
+.IR .term.variable ,
+which is an application-provided boolean variable.
+.TP
+.B LIBNORMALFORM_NEGATED_VARIABLE
+The term is negation of
+.IR .term.variable ,
+which is an application-provided boolean variable.
+.TP
+.B LIBNORMALFORM_FUNCTION
+The term is
+.IR .term.function ,
+which is an application-provided boolean-output
+function.
+.TP
+.B LIBNORMALFORM_NEGATED_FUNCTION
+The term is negation of
+.IR .term.function ,
+which is an application-provided boolean-output
+function.
+.TP
+.B LIBNORMALFORM_FOR_ALL
+The term is the universal qualification of
+the domain
+.IR .term.for_all.domain ,
+with predicate
+.IR .term.for_all.predicate .
+Additionally, depending on
+.I flags ,
+.I .term.for_all.antecedent
+may or may not be
+.IR NULL .
+.I .term.for_all.antecedent
+is
+.IR NULL ,
+.I .term.for_all.predicate
+operates both on the elements in the domain
+and image of it's value-mapping, with built-in
+transformations inserted to select between the
+domain-values and the image-values, but if
+.I .term.for_all.antecedent
+is not
+.IR NULL ,
+the domain-values are tested in
+.I .term.for_all.antecedent
+without any built-in transformation, and
+the image-values are tested in
+.I .term.for_all.predicate
+without any built-in transformation.
+The qualification is true exactly when the
+predicate is true for each element in the
+domain for which the antecedent is true.
+If there are not elements in the domain,
+or if the antecedent is false for each
+element in the domain, the qualification
+is true.
+.RI ( .terms.qualification
+may be used instead of
+.IR .terms.for_all .)
+.TP
+.B LIBNORMALFORM_NEGATED_FOR_ALL
+Identical to
+.BR LIBNORMALFORM_FOR_ALL ,
+except the qualification is negated, so
+resulting value is inverted. (The same
+field in
+.I .term
+is used.)
+.TP
+.B LIBNORMALFORM_FOR_ANY
+The term is the existential qualification of
+the domain
+.IR .term.for_any.domain ,
+with predicate
+.IR .term.for_any.predicate .
+Additionally, depending on
+.I flags ,
+.I .term.for_any.antecedent
+may or may not be
+.IR NULL .
+.I .term.for_any.antecedent
+is
+.IR NULL ,
+.I .term.for_any.predicate
+operates both on the elements in the domain
+and image of it's value-mapping, with built-in
+transformations inserted to select between the
+domain-values and the image-values, but if
+.I .term.for_any.antecedent
+is not
+.IR NULL ,
+the domain-values are tested in
+.I .term.for_any.antecedent
+without any built-in transformation, and
+the image-values are tested in
+.I .term.for_any.predicate
+without any built-in transformation.
+The qualification is true exactly when the
+predicate is true for at least one element
+in the domain for which the antecedent is
+true. If there are not elements in the
+domain, or if the antecedent is false for
+each element in the domain, the qualification
+is false.
+.RI ( .terms.qualification
+may be used instead of
+.IR .terms.for_any .)
+.TP
+.B LIBNORMALFORM_NEGATED_FOR_ALL
+Identical to
+.BR LIBNORMALFORM_FOR_ALL ,
+except the qualification is negated, so
+resulting value is inverted. (The same
+field in
+.I .term
+is used.)
+.TP
+.B LIBNORMALFORM_NEGATED_FOR_ANY
+Identical to
+.BR LIBNORMALFORM_FOR_ANY ,
+except the qualification is negated, so
+resulting value is inverted. (The same
+field in
+.I .term
+is used.)
+.TP
+.B LIBNORMALFORM_FOR_ONE
+The term is the unique existential qualification
+of the domain
+.IR .term.for_one.domain ,
+with predicate
+.IR .term.for_one.predicate .
+Additionally, depending on
+.I flags ,
+.I .term.for_one.antecedent
+may or may not be
+.IR NULL .
+.I .term.for_one.antecedent
+is
+.IR NULL ,
+.I .term.for_one.predicate
+operates both on the elements in the domain
+and image of it's value-mapping, with built-in
+transformations inserted to select between the
+domain-values and the image-values, but if
+.I .term.for_one.antecedent
+is not
+.IR NULL ,
+the domain-values are tested in
+.I .term.for_one.antecedent
+without any built-in transformation, and
+the image-values are tested in
+.I .term.for_one.predicate
+without any built-in transformation.
+The qualification is true exactly when the
+predicate is true for exactly one element
+in the domain for which the antecedent is
+true. If there are not elements in the
+domain, or if the antecedent is false for
+each element in the domain, the qualification
+is false.
+.RI ( .terms.qualification
+may be used instead of
+.IR .terms.for_one .)
+.TP
+.B LIBNORMALFORM_NEGATED_FOR_ONE
+Identical to
+.BR LIBNORMALFORM_FOR_ONE ,
+except the qualification is negated, so
+resulting value is inverted. (The same
+field in
+.I .term
+is used.)
+.PP
+.I this
+must not be
+.IR NULL .
+.PP
+The returned pointer shall either be
+deallocated with the
+.BR libnormalform_free (3)
+function.
+.PP
+This function
+.I does not
+consume
+.IR this .
+
+.SH RETURN VALUE
+Upon successful completion, the
+.BR libnormalform_express ()
+function return an object representing
+the sentence, which is to be deallocated
+by the application using the
+.BR libnormalform_free (3)
+function; otherwise, the function returns
+.I NULL
+and set
+.I errno
+to indicate the error; if the function
+fails because an application-provided
+callback function fails,
+.I errno
+will remain as set by that function
+(or be unmodified if that function did
+not set
+.IR errno ).
+
+.SH ERRORS
+The
+.BR libnormalform_express ()
+function fails if:
+.TP
+.B ENOMEM
+Insufficient memory was available to
+create the representation of the sentence.
+.TP
+.B EINVAL
+.I flags
+contain unsupported options.
+
+.SH ATTRIBUTES
+For an explanation of the terms used in this
+section, see
+.BR attributes (7)
+and
+.IR "info \(dq(libc)POSIX Safety Concepts\(dq" .
+.TS
+allbox;
+lb lb lb
+l l l.
+Interface Attribute Value
+T{
+.BR libnormalform_express ()
+T} Thread safety MT-Safe race:\fIthis\fP
+T{
+.BR libnormalform_express ()
+T} Async-signal safety AS-Unsafe heap
+T{
+.BR libnormalform_express ()
+T} Async-cancel safety AC-Safe mem, AC-Unsafe heap
+.TE
+
+.SH SEE ALSO
+.BR libnormalform (7),
+.BR libnormalform_dnf (3),
+.BR libnormalform_cnf (3),
+.BR libnormalform_cdnf (3)
diff --git a/man3/libnormalform_false.3 b/man3/libnormalform_false.3
index 83dd97a..54131c4 100644
--- a/man3/libnormalform_false.3
+++ b/man3/libnormalform_false.3
@@ -40,7 +40,7 @@ The
.BR libnormalform_false ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
diff --git a/man3/libnormalform_from_string.3 b/man3/libnormalform_from_string.3
index f0bfc98..108c5a7 100644
--- a/man3/libnormalform_from_string.3
+++ b/man3/libnormalform_from_string.3
@@ -111,14 +111,14 @@ The
.BR libnormalform_from_string ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.TP
-.I EINVAL
+.B EINVAL
The representation is invalid.
.TP
-.I EINVAL
+.B EINVAL
.I end_out
is
.I NULL
@@ -126,35 +126,35 @@ but there are non-whitespace characters in
.I s
after the detected end of the representation.
.TP
-.I EDOM
+.B EDOM
The described sentence contains an empty clause
for a connective that does not support empty
clauses.
.TP
-.I EDOM
+.B EDOM
The described sentence contains a transformation
somewhere over a qualifer.
.TP
-.I ENOENT
+.B ENOENT
The described sentence contains a boolean variable but
.I spec->get_variable
was
.IR NULL .
.TP
-.I ENOENT
+.B ENOENT
The described sentence contains a boolean function but
.I spec->get_function
was
.IR NULL .
.TP
-.I ENOENT
+.B ENOENT
The described sentence contains (a domain of interest
for a) qualifier but
.I spec->get_map
was
.IR NULL .
.TP
-.I ENOENT
+.B ENOENT
The described sentence contains a transformation
(a input transformation function) but
.I spec->get_transformer
diff --git a/man3/libnormalform_function.3 b/man3/libnormalform_function.3
index d89c0e9..87edd46 100644
--- a/man3/libnormalform_function.3
+++ b/man3/libnormalform_function.3
@@ -121,7 +121,7 @@ The
.BR libnormalform_function ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
diff --git a/man3/libnormalform_if.3 b/man3/libnormalform_if.3
index 86fde19..02c69dc 100644
--- a/man3/libnormalform_if.3
+++ b/man3/libnormalform_if.3
@@ -111,13 +111,13 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.TP
-.I EDOM
+.B EDOM
The function was not given any subsentences.
.PP
The
diff --git a/man3/libnormalform_imply.3 b/man3/libnormalform_imply.3
index b6e0284..852d965 100644
--- a/man3/libnormalform_imply.3
+++ b/man3/libnormalform_imply.3
@@ -111,9 +111,9 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_nand.3 b/man3/libnormalform_nand.3
index a3604ae..3973dca 100644
--- a/man3/libnormalform_nand.3
+++ b/man3/libnormalform_nand.3
@@ -113,13 +113,13 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.TP
-.I EDOM
+.B EDOM
The function was not given any subsentences.
.PP
The
diff --git a/man3/libnormalform_nif.3 b/man3/libnormalform_nif.3
index 174b9d4..39caec1 100644
--- a/man3/libnormalform_nif.3
+++ b/man3/libnormalform_nif.3
@@ -109,9 +109,9 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_nimply.3 b/man3/libnormalform_nimply.3
index ca7a0e9..c2d3d8e 100644
--- a/man3/libnormalform_nimply.3
+++ b/man3/libnormalform_nimply.3
@@ -109,13 +109,13 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.TP
-.I EDOM
+.B EDOM
The function was not given any subsentences.
.PP
The
diff --git a/man3/libnormalform_nor.3 b/man3/libnormalform_nor.3
index 164e8c5..23eaae2 100644
--- a/man3/libnormalform_nor.3
+++ b/man3/libnormalform_nor.3
@@ -113,13 +113,13 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.TP
-.I EDOM
+.B EDOM
The function was not given any subsentences.
.PP
The
diff --git a/man3/libnormalform_not.3 b/man3/libnormalform_not.3
index 0ebe548..ed6fdca 100644
--- a/man3/libnormalform_not.3
+++ b/man3/libnormalform_not.3
@@ -53,7 +53,7 @@ The
.BR libnormalform_not ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_one.3 b/man3/libnormalform_one.3
index bf79161..8aa88a5 100644
--- a/man3/libnormalform_one.3
+++ b/man3/libnormalform_one.3
@@ -192,9 +192,9 @@ The
.BR libnormalform_uniquely (),
and
.BR libnormalform_singleton ()
-functions fails if:
+functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_or.3 b/man3/libnormalform_or.3
index 9d57ee8..c0f58d8 100644
--- a/man3/libnormalform_or.3
+++ b/man3/libnormalform_or.3
@@ -110,9 +110,9 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_ref.3 b/man3/libnormalform_ref.3
index 5592e48..9295f12 100644
--- a/man3/libnormalform_ref.3
+++ b/man3/libnormalform_ref.3
@@ -48,7 +48,7 @@ The
.BR libnormalform_ref ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
The reference count of
.I x
was already maximised (at
diff --git a/man3/libnormalform_sentences_relationship.3 b/man3/libnormalform_sentences_relationship.3
new file mode 120000
index 0000000..03be66c
--- /dev/null
+++ b/man3/libnormalform_sentences_relationship.3
@@ -0,0 +1 @@
+enum_libnormalform_sentences_relationship.3 \ No newline at end of file
diff --git a/man3/libnormalform_ternary.3 b/man3/libnormalform_ternary.3
new file mode 120000
index 0000000..96ac9d0
--- /dev/null
+++ b/man3/libnormalform_ternary.3
@@ -0,0 +1 @@
+enum_libnormalform_ternary.3 \ No newline at end of file
diff --git a/man3/libnormalform_to_string.3 b/man3/libnormalform_to_string.3
index 7008990..2150a10 100644
--- a/man3/libnormalform_to_string.3
+++ b/man3/libnormalform_to_string.3
@@ -62,7 +62,7 @@ The
.BR libnormalform_to_string ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
serialise the sentence object.
@@ -93,7 +93,7 @@ When a
.I LIBNORMALFORM_SENTENCE
is created, it is optimised (this includes eliminiation
of redundant information and reordering) and reduced to
-into fewer types of connetives (it's reducted into negation
+into fewer types of connetives (it's reduced into negation
normal form, except with XOR allowed, but not necessarily
to any canonical form) during construction, so the
.BR libnormalform_to_string ()
diff --git a/man3/libnormalform_transformation.3 b/man3/libnormalform_transformation.3
index 7bb129e..d08ea47 100644
--- a/man3/libnormalform_transformation.3
+++ b/man3/libnormalform_transformation.3
@@ -202,11 +202,11 @@ The
.BR libnormalform_transformation ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.TP
-.I EDOM
+.B EDOM
.I sentence
contains a qualifier.
diff --git a/man3/libnormalform_true.3 b/man3/libnormalform_true.3
index a5da907..77bdf39 100644
--- a/man3/libnormalform_true.3
+++ b/man3/libnormalform_true.3
@@ -40,7 +40,7 @@ The
.BR libnormalform_true ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
diff --git a/man3/libnormalform_variable.3 b/man3/libnormalform_variable.3
index ad30a37..dca3d1e 100644
--- a/man3/libnormalform_variable.3
+++ b/man3/libnormalform_variable.3
@@ -96,7 +96,7 @@ The
.BR libnormalform_variable ()
function fails if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
diff --git a/man3/libnormalform_xnor.3 b/man3/libnormalform_xnor.3
index 56a3cab..322becc 100644
--- a/man3/libnormalform_xnor.3
+++ b/man3/libnormalform_xnor.3
@@ -111,9 +111,9 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/libnormalform_xor.3 b/man3/libnormalform_xor.3
index 70e9584..38571b4 100644
--- a/man3/libnormalform_xor.3
+++ b/man3/libnormalform_xor.3
@@ -110,9 +110,9 @@ and set
to indicate the error.
.SH ERRORS
-These functions fails if:
+These functions fail if:
.TP
-.I ENOMEM
+.B ENOMEM
Insufficient memory was available to
create the sentence object.
.PP
diff --git a/man3/struct_libnormalform_analysers.3 b/man3/struct_libnormalform_analysers.3
new file mode 120000
index 0000000..eae3a15
--- /dev/null
+++ b/man3/struct_libnormalform_analysers.3
@@ -0,0 +1 @@
+libnormalform_express.3 \ No newline at end of file
diff --git a/man3/struct_libnormalform_atom_comparison.3 b/man3/struct_libnormalform_atom_comparison.3
new file mode 120000
index 0000000..0d9b1eb
--- /dev/null
+++ b/man3/struct_libnormalform_atom_comparison.3
@@ -0,0 +1 @@
+struct_libnormalform_analysers.3 \ No newline at end of file
diff --git a/man3/struct_libnormalform_domain_comparison.3 b/man3/struct_libnormalform_domain_comparison.3
new file mode 120000
index 0000000..0d9b1eb
--- /dev/null
+++ b/man3/struct_libnormalform_domain_comparison.3
@@ -0,0 +1 @@
+struct_libnormalform_analysers.3 \ No newline at end of file
diff --git a/memcheck.c b/memcheck.c
index b119fad..7f788a3 100644
--- a/memcheck.c
+++ b/memcheck.c
@@ -17,6 +17,16 @@
#include <elfutils/libdwfl.h>
+#if defined(__GNUC__)
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
+# pragma clang diagnostic ignored "-Wcomma"
+# pragma clang diagnostic ignored "-Wunsafe-buffer-usage" /* clang is currently exceptionally stupid */
+#endif
+
+
enum alloctype {
ALLOCTYPE_REALLOC,
ALLOCTYPE_MMAP
@@ -150,7 +160,8 @@ fail:
static void
memcheck_print_backtrace(struct backtrace *backtrace, const char *indent)
{
- int saved_errno = errno, lineno;
+ int saved_errno = errno;
+ int lineno = 0; /* initialised to silence compiler */
char *debuginfo_path = NULL;
Dwarf_Addr ip;
Dwfl_Callbacks callbacks;
@@ -172,6 +183,8 @@ memcheck_print_backtrace(struct backtrace *backtrace, const char *indent)
callbacks.find_debuginfo = dwfl_standard_find_debuginfo;
callbacks.debuginfo_path = &debuginfo_path;
+ dwfl = dwfl_begin(&callbacks);
+
if (dwfl) {
if (dwfl_linux_proc_report(dwfl, getpid()) ||
dwfl_report_end(dwfl, NULL, NULL)) {
@@ -211,10 +224,12 @@ memcheck_print_backtrace(struct backtrace *backtrace, const char *indent)
#endif
-#if defined(__GNUC__)
+#if defined(__clang__)
+__attribute__((__format__(__printf__, 1, 2)))
+#elif defined(__GNUC__)
__attribute__((__format__(__gnu_printf__, 1, 2)))
#endif
-static void
+static _Noreturn void
memcheck_errorf(const char *fmt, ...)
{
va_list ap;
@@ -610,6 +625,7 @@ memcheck_check_memleaks(void)
{
struct allocinfo *leak;
size_t i, count = 0;
+ int first = 1;
memleak_exclusion = 1;
for (i = 0; i < nallocs; i++) {
leak = &allocs[i];
@@ -625,6 +641,10 @@ memcheck_check_memleaks(void)
leak = &allocs[i];
if (leak->dont_track)
continue;
+ if (first)
+ first = 0;
+ else
+ dprintf(STDERR_FILENO, "\n");
memcheck_print_backtrace(leak->backtrace, "\t");
}
#endif