diff options
244 files changed, 23224 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58ba5bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*\#* +*~ +*.o +*.a +*.lo +*.su +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda +*.t +*.to @@ -0,0 +1,15 @@ +ISC License + +© 2024 Mattias Andrée <maandree@kth.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 +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..30e8344 --- /dev/null +++ b/Makefile @@ -0,0 +1,251 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OS = linux +# Linux: linux +# Mac OS: macos +# Windows: windows +include mk/$(OS).mk + + +LIB_MAJOR = 1 +LIB_MINOR = 0 +LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) +LIB_NAME = normalform + + +OBJ_PUBLIC =\ + libnormalform_ref.o\ + libnormalform_free.o\ + libnormalform_clone.o\ + libnormalform_evaluate.o\ + libnormalform_express.o\ + libnormalform_to_string.o\ + libnormalform_from_string.o\ + libnormalform_variable.o\ + libnormalform_function.o\ + libnormalform_transformation.o\ + libnormalform_true.o\ + libnormalform_false.o\ + libnormalform_not.o\ + libnormalform_all.o\ + libnormalform_any.o\ + libnormalform_one.o\ + libnormalform_and.o\ + libnormalform_or.o\ + libnormalform_xor.o\ + libnormalform_if.o\ + libnormalform_imply.o\ + libnormalform_nand.o\ + libnormalform_nor.o\ + libnormalform_xnor.o\ + libnormalform_nif.o\ + libnormalform_nimply.o\ + libnormalform_exists.o\ + libnormalform_nexists.o\ + libnormalform_unique.o\ + libnormalform_existentially.o\ + libnormalform_universally.o\ + libnormalform_uniquely.o\ + libnormalform_empty.o\ + libnormalform_nonempty.o\ + libnormalform_singleton.o\ + libnormalform_and_checked.o\ + libnormalform_or_checked.o\ + libnormalform_xor_checked.o\ + libnormalform_if_checked.o\ + libnormalform_imply_checked.o\ + libnormalform_nand_checked.o\ + libnormalform_nor_checked.o\ + libnormalform_xnor_checked.o\ + libnormalform_nif_checked.o\ + libnormalform_nimply_checked.o\ + libnormalform_vand.o\ + libnormalform_vor.o\ + libnormalform_vxor.o\ + libnormalform_vif.o\ + libnormalform_vimply.o\ + libnormalform_vnand.o\ + libnormalform_vnor.o\ + libnormalform_vxnor.o\ + libnormalform_vnif.o\ + libnormalform_vnimply.o\ + libnormalform_andl.o\ + libnormalform_orl.o\ + libnormalform_xorl.o\ + libnormalform_ifl.o\ + libnormalform_implyl.o\ + libnormalform_nandl.o\ + libnormalform_norl.o\ + libnormalform_xnorl.o\ + libnormalform_nifl.o\ + libnormalform_nimplyl.o\ + libnormalform_vand_checked.o\ + libnormalform_vor_checked.o\ + libnormalform_vxor_checked.o\ + libnormalform_vif_checked.o\ + libnormalform_vimply_checked.o\ + libnormalform_vnand_checked.o\ + libnormalform_vnor_checked.o\ + libnormalform_vxnor_checked.o\ + libnormalform_vnif_checked.o\ + libnormalform_vnimply_checked.o\ + libnormalform_andl_checked.o\ + libnormalform_orl_checked.o\ + libnormalform_xorl_checked.o\ + libnormalform_ifl_checked.o\ + libnormalform_implyl_checked.o\ + libnormalform_nandl_checked.o\ + libnormalform_norl_checked.o\ + libnormalform_xnorl_checked.o\ + libnormalform_nifl_checked.o\ + libnormalform_nimplyl_checked.o\ + libnormalform_and2.o\ + libnormalform_or2.o\ + libnormalform_xor2.o\ + libnormalform_if2.o\ + libnormalform_imply2.o\ + libnormalform_nand2.o\ + libnormalform_nor2.o\ + libnormalform_xnor2.o\ + libnormalform_nif2.o\ + libnormalform_nimply2.o\ + +OBJ =\ + $(OBJ_PUBLIC)\ + libnormalform_and2__.o\ + libnormalform_or2__.o\ + libnormalform_xor2__.o\ + libnormalform_set_indices_and_counts__.o\ + libnormalform_reset_indices_and_counts__.o + +TOBJ = $(OBJ:.o=.to)\ + libnormalform_andl_macro_test.to\ + libnormalform_orl_macro_test.to\ + libnormalform_xorl_macro_test.to\ + libnormalform_ifl_macro_test.to\ + libnormalform_implyl_macro_test.to\ + libnormalform_nandl_macro_test.to\ + libnormalform_norl_macro_test.to\ + libnormalform_xnorl_macro_test.to\ + libnormalform_nifl_macro_test.to\ + libnormalform_nimplyl_macro_test.to + +HDR =\ + libnormalform.h + +MAN3 =\ + $(OBJ_PUBLIC:.o=.3)\ + LIBNORMALFORM_AND.3\ + LIBNORMALFORM_OR.3\ + LIBNORMALFORM_XOR.3\ + LIBNORMALFORM_IF.3\ + LIBNORMALFORM_IMPLY.3\ + LIBNORMALFORM_NAND.3\ + LIBNORMALFORM_NOR.3\ + LIBNORMALFORM_XNOR.3\ + LIBNORMALFORM_NIF.3\ + LIBNORMALFORM_NIMPLY.3\ + struct_libnormalform_map.3\ + libnormalform_map.3\ + struct_libnormalform_mapping.3\ + libnormalform_mapping.3\ + struct_libnormalform_representation_spec.3\ + libnormalform_representation_spec.3\ + struct_libnormalform_transformer.3\ + libnormalform_transformer.3\ + enum_libnormalform_builtin_transformer.3\ + libnormalform_builtin_transformer.3\ + struct_libnormalform_function.3\ + struct_libnormalform_variable.3\ + enum_libnormalform_value.3\ + libnormalform_value.3\ + LIBNORMALFORM_SENTENCE.3\ + struct_libnormalform_sentence.3\ + libnormalform_sentence.3 + +LOBJ = $(OBJ:.o=.lo) +TEST = $(TOBJ:.to=.t) + + +all: libnormalform.a libnormalform.$(LIBEXT) $(TEST) +$(OBJ): $(HDR) common.h +$(LOBJ): $(HDR) common.h +$(TOBJ): $(HDR) common.h +$(TEST): libnormalform.a $(MEMCHECK) +memcheck.to: memcheck.h + +L = libnormalform +C = checked + +$L_and2.to $L_andl.to $L_vand.to $L_and_$C.to $L_andl_$C.to $L_vand_$C.to $L_andl_macro_test.to : $L_and.c +$L_or2.to $L_orl.to $L_vor.to $L_or_$C.to $L_orl_$C.to $L_vor_$C.to $L_orl_macro_test.to : $L_or.c +$L_xor2.to $L_xorl.to $L_vxor.to $L_xor_$C.to $L_xorl_$C.to $L_vxor_$C.to $L_xorl_macro_test.to : $L_xor.c +$L_if2.to $L_ifl.to $L_vif.to $L_if_$C.to $L_ifl_$C.to $L_vif_$C.to $L_ifl_macro_test.to : $L_if.c +$L_imply2.to $L_implyl.to $L_vimply.to $L_imply_$C.to $L_implyl_$C.to $L_vimply_$C.to $L_implyl_macro_test.to : $L_imply.c +$L_nand2.to $L_nandl.to $L_vnand.to $L_nand_$C.to $L_nandl_$C.to $L_vnand_$C.to $L_nandl_macro_test.to : $L_nand.c +$L_nor2.to $L_norl.to $L_vnor.to $L_nor_$C.to $L_norl_$C.to $L_vnor_$C.to $L_norl_macro_test.to : $L_nor.c +$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 + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.lo: + $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.to: + $(CC) -DTEST -DTEST_TIMEOUT_SECONDS=$(TEST_TIMEOUT_SECONDS) -c -o $@ $< $(TEST_CFLAGS) $(TEST_CPPFLAGS) + +.to.t: + $(CC) -o $@ $< libnormalform.a $(MEMCHECK_OBJ) $(TEST_LDFLAGS) + +libnormalform.a: $(OBJ) + @rm -f -- $@ + $(AR) rc $@ $(OBJ) + $(AR) ts $@ > /dev/null + +libnormalform.$(LIBEXT): $(LOBJ) + $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS) + +check: $(TEST) + set -e;\ + for t in $(TEST:.t=); do\ + printf 'Testing %s\n' "$$t" >&2;\ + $(CHECK_PREFIX) ./$$t.t;\ + done + +install: libnormalform.a libnormalform.$(LIBEXT) + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + cp -- libnormalform.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- libnormalform.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libnormalform.$(LIBMINOREXT)" + $(FIX_INSTALL_NAME) "$(DESTDIR)$(PREFIX)/lib/libnormalform.$(LIBMINOREXT)" + ln -sf -- libnormalform.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/libnormalform.$(LIBMAJOREXT)" + ln -sf -- libnormalform.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/libnormalform.$(LIBEXT)" + cp -- libnormalform.h "$(DESTDIR)$(PREFIX)/include/" + mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man7" + cp -- libnormalform.7 "$(DESTDIR)$(MANPREFIX)/man7/" + mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man3" + cp -P -- $$(printf 'man3/%s\n' $(MAN3)) "$(DESTDIR)$(MANPREFIX)/man3/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libnormalform.a" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libnormalform.$(LIBMAJOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libnormalform.$(LIBMINOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libnormalform.$(LIBEXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/include/libnormalform.h" + -rm -f -- "$(DESTDIR)$(MANPREFIX)/man7/libnormalform.7" + -(cd -- "$(DESTDIR)$(MANPREFIX)/man3/" && rm -f -- $(MAN3)) + +clean: + -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.dll *.dylib + -rm -f -- *.gch *.gcov *.gcno *.gcda *.$(LIBEXT) *.t *.to + +.SUFFIXES: +.SUFFIXES: .lo .o .c .t .to + +.PHONY: all check install uninstall clean @@ -0,0 +1,38 @@ +NAME + libnormalform - First-order logic sentence canonicalisation library + +SYNOPSIS + #include <libnormalform.h> + + Link with -lnormalform. + +DESCRIPTION + The libnormalform library provides a mechanism for expressing + first-order logic sentence and normalises them on the fly to + negation normal form, and then lets the user choose disjunctive + normal form (DNF) or conjunctive normal form (CNF). + + libnormalform support all Boolean connectives: AND, OR, XOR, + IMPLY, IF, NAND, NOR, XNOR, NIMPLY, NIF, and NOT, as well as the + boolean constants TRUE and FALSE. Additionally, libnormalform + supports three standard qualifiers: the universal qualifier + (FOR ALL), the existential qualifier (THERE EXISTS), and the + unique existential (uniqueness) qualifier (THERE EXISTS ONE). + For all binary connectives, libnormalform support simply + chaining (e.g. AND(a, b, c) instead of AND(AND(a, b), c)). + For all qualifiers, libnormalform supports qualification over + an array or an associative array. + + libnormalform also provides application managed boolean + variables and functions. These as well as the domains for + qualifiers can be set dynamically and the value of the + sentence can be evaluate at any time. + + When libnormalform is asked to output the sentences in DNF + or CNF, it can relax parts of sentence in case the application + wants to express the sentence in a particular form but cannot + express all parts of, and therefore needs alternative that is + at least as true. While relaxing the sentence, it can eliminiate + parts of the sentence that become redundant; to be able to do + this, it queries the application for how the application + defined variables, functions and domains depend on each other. diff --git a/common.h b/common.h new file mode 100644 index 0000000..68df99c --- /dev/null +++ b/common.h @@ -0,0 +1,632 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBNORMALFORM_ALLOW_BAD_WARNINGS +# define LIBNORMALFORM_ALLOW_BAD_WARNINGS +#endif +#include "libnormalform.h" + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + + +#if defined(__GNUC__) +# define PURE __attribute__((__pure__)) +# define CONST __attribute__((__const__)) +# define HIDDEN __attribute__((__visibility__("hidden"))) +# define USE_RESULT __attribute__((__warn_unused_result__)) +# define NONNULL_INPUT __attribute__((__nonnull__)) +# define SOME_NONNULL_INPUT(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else +# define PURE +# define CONST +# define HIDDEN +# define USE_RESULT +# define NONNULL_INPUT +# define SOME_NONNULL_INPUT(...) +#endif + + +/** + * Hash used for tautologies and contradictions + */ +#define TRUE_FALSE_HASH 1U + +/** + * Returns the has for a user provided object + * + * @param A The user provided object + */ +#define USER_HASH__(P) ((uintmax_t)(uintptr_t)(P)) + +/** + * Hash generator for literals + * + * @param A The atomic sentence of the literal + */ +#define LITERAL_HASH(A)\ + _Generic((A),\ + struct libnormalform_variable *: USER_HASH__((A)),\ + struct libnormalform_function *: USER_HASH__((A))) + +/** + * Hash generator for transformations + * + * @param F:struct libnormalform_transformer * The transformation function + * @param S:LIBNORMALFORM_SENTENCE * The sentence + */ +#define TRANS_HASH(F, S)\ + _Generic((F), struct libnormalform_transformer *:\ + (USER_HASH__(F) ^ (S)->hash)) + +/** + * Hash generator for conjunctions and disjunctions + * + * @param L:LIBNORMALFORM_SENTENCE * The left-hand term + * @param R:LIBNORMALFORM_SENTENCE * The right-hand term + */ +#define AND_OR_HASH(L, R) ((L)->hash + (R)->hash) + +/** + * Hash generator for exclusive disjunctions + * + * The reason this is twice of conjunction or disjunction + * is because a ⊕ b = (a ∨ b) ∧ ¬(a ∧ b) and + * (a ∨ b) and ¬(a ∧ b) have the same hashes, and if + * if u = (a ∨ b) and v = ¬(a ∧ b), the hash of + * u ∧ v is the sum of the hashes of u and v which + * are both the sum of the hashes of a and b, thus + * twice the hash of a and b. + * + * @param L:LIBNORMALFORM_SENTENCE * The left-hand term + * @param R:LIBNORMALFORM_SENTENCE * The right-hand term + */ +#define XOR_HASH(L, R) (AND_OR_HASH(L, R) * 2) + +/** + * Hash generator for universial and existential qualifiers + * + * @param D:struct libnormalform_map * The domain + * @param K:LIBNORMALFORM_SENTENCE * The antecedent (left-hand term) + * @param V:LIBNORMALFORM_SENTENCE * The predicate (right-hand term) + */ +#define ANY_ALL_HASH(D, K, V)\ + _Generic((D), struct libnormalform_map *:\ + (USER_HASH__(D) * 5 + AND_OR_HASH(K, V) * 3)) + +/** + * Hash generator for uniqueness qualifiers + * + * @param D:struct libnormalform_map * The domain + * @param K:LIBNORMALFORM_SENTENCE * The antecedent (left-hand term) + * @param V:LIBNORMALFORM_SENTENCE * The predicate (right-hand term) + */ +#define ONE_HASH(D, K, V)\ + _Generic((D), struct libnormalform_map *:\ + (USER_HASH__(D) * 5 + AND_OR_HASH(K, V) * 7)) + + +/** + * Offset for values in `enum type`, + * used by `libnormalform_free` to distinguish + * `struct libnormalform_sentence *` for + * `struct libnormalform_term *` +*/ +#define SENTENCE_TYPE_OFFSET 1024 + + +/** + * Sentence classification + */ +enum type { + TYPE_TRUE = SENTENCE_TYPE_OFFSET, /**< Tautology */ + TYPE_FALSE, /**< Contradiction */ + TYPE_VARIABLE, /**< Variable literal */ + TYPE_FUNCTION, /**< Function literal */ + TYPE_TRANS, /**< Input transformation */ + /* Insert new non-branches (leafs and lines) above */ + TYPE_AND, /**< Conjuction */ + TYPE_OR, /**< Disjunction */ + TYPE_XOR, /**< Exclusive disjunction */ + TYPE_ALL, /**< Univerial qualification */ + TYPE_ANY, /**< Existential qualification */ + TYPE_ONE, /**< Uniqueness qualification */ + TYPE_NOT_ONE /**< Negated uniqueness qualification */ + /* Insert new binary branches here */ +}; + + +struct atom { /* TODO doc */ + /** + * The number of references to the object + */ + size_t refcount; +}; + + +/** + * See `LIBNORMALFORM_SENTENCE` + */ +struct libnormalform_sentence { + /** + * Sentence classification + */ + enum type type; + + /** + * The number of references to the object + */ + size_t refcount; + + /** + * Hash of the sentence + * + * For any two sentences it is preferably that + * they have the same hash if and only if they + * can be determined to be equivalent or each + * other's inverse + */ + uintmax_t hash; + + struct atom *atom; /* TODO doc */ + + /** + * Used by some function for sentence analysis, + * in particular to index duplicated subsentence + */ + size_t travel_index; + + /** + * Used by some function for sentence analysis, + * in particular to count duplications + */ + size_t travel_count; + + /** + * Used by some function recurse over the sentence + */ + LIBNORMALFORM_SENTENCE *travel_head; + + /** + * Create the inverse of the sentence + * + * @param this The sentence + * @return The inverse of the sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Unlike `libnormalform_not`, this function + * will not take ownership if `this`, so + * its reference count will not be modified + */ + LIBNORMALFORM_SENTENCE *(*inverse)(LIBNORMALFORM_SENTENCE *this); + + /** + * Check the equivalence between the sentence and another sentence + * + * @param this The sentence + * @param other The the other sentence + * @param inv_out Will be set to 0 if the sentences are equivalent, + * and set to 1 if the sentences are each other's inverse + * @return 1 if the functions are equal or each other's inverse, + * 0 otherwise + */ + int (*equals)(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out); + + /** + * Evaluate the truthness of the sentence + * + * @param this The sentence + * @param input See `libnormalform_evaluate` + * @return See `libnormalform_evaluate` + */ + int (*evaluate)(LIBNORMALFORM_SENTENCE *this, void *input); + + /** + * Type specification information + * + * Unused if `.type` is `TYPE_TRUE` or `TYPE_FALSE` + */ + union { + /** + * Used for literals (that is, + * if `.type` is `TYPE_VARIABLE` or `TYPE_FUNCTION`) + */ + struct { + /** + * The literal's atomic sentence + */ + union { + struct libnormalform_variable *variable; /**< Use if `.type == TYPE_VARIABLE` */ + struct libnormalform_function *function; /**< Use if `.type == TYPE_FUNCTION` */ + } atom; + + /** + * Whether the literal is the inverse of its atomic sentence + */ + int inverted; + } literal; + + /** + * Used for input transformations (that is, + * if `.type` is `TYPE_TRANS`) + */ + struct { + LIBNORMALFORM_SENTENCE *input; /**< Input */ + struct libnormalform_transformer *function; /**< Transformation function */ + } trans; + + /** + * Used for binary connectives (that is, + * if `.type` is `TYPE_AND`, `TYPE_OR`, or `TYPE_XOR`) + * + * Traversal code can also use this instead of `.data.qualifer` + */ + struct { + LIBNORMALFORM_SENTENCE *l; /**< Left-hand term */ + LIBNORMALFORM_SENTENCE *r; /**< Right-hand term */ + } binary; + + /** + * Used for qualifiers (that is, + * if `.type` is `TYPE_ALL`, `TYPE_ANY`, `TYPE_ONE`, `TYPE_NOT_ONE`) + */ + struct { + LIBNORMALFORM_SENTENCE *antecedent; /**< Antecedent */ + LIBNORMALFORM_SENTENCE *predicate; /**< Predicate */ + struct libnormalform_map *domain; /**< Domain of interest */ + } qualifier; + } data; +}; + +/** + * Intial values for `struct libnormalform_sentence` + * that should always be used + */ +#define PROTOTYPE_COMMON\ + .refcount = 1U,\ + .atom = NULL,\ + .travel_index = 0U,\ + .travel_count = 0U + + +/** + * Helper macro to make macros type self, for macros that take sentences + */ +#define REQ_SENTENCE(OBJ, ASTERISKS, EXP) _Generic((OBJ), LIBNORMALFORM_SENTENCE ASTERISKS: (EXP)) + +/** + * Transversal helper macro that return 1 + * if a sentence has two subsentences + * + * @param S:LIBNORMALFORM_SENTENCE * The sentence + * @return :int 1 if the sentence has two subsentences, 0 otherwise + */ +#define IS_BRANCH(S) REQ_SENTENCE((S), *, (S)->type >= TYPE_AND) + +/** + * Transversal helper macro that returns + * the left-hand subsentence (antecedent + * for qualifiers) + * + * @param S:LIBNORMALFORM_SENTENCE * The sentence + * @return :LIBNORMALFORM_SENTENCE * The left subsentence + */ +#define LEFT(S) REQ_SENTENCE((S), *, (S)->data.binary.l) + +/** + * Transversal helper macro that returns + * the right-hand subsentence (predicate + * for qualifiers) + * + * @param S:LIBNORMALFORM_SENTENCE * The sentence + * @return :LIBNORMALFORM_SENTENCE * The right subsentence + */ +#define RIGHT(S) REQ_SENTENCE((S), *, (S)->data.binary.r) + +/** + * Transversal helper macro that pushes + * a sentence onto a stack + * + * @param UNTO:LIBNORMALFORM_SENTENCE ** Reference to the head of the stack + * @param S:LIBNORMALFORM_SENTENCE * The sentence + */ +#define PUSH(UNTO, S)\ + REQ_SENTENCE((UNTO), **,\ + REQ_SENTENCE((S), *,\ + ((S)->travel_head = *(UNTO), *(UNTO) = (S), (void) 0)\ + )) + +/** + * Transversal helper macro that pops + * a sentence stack + * + * @param FROM:LIBNORMALFORM_SENTENCE ** Reference to the head of the stack + * @param INTO:LIBNORMALFORM_SENTENCE ** Output parameter for the sentence popped from the stack + * @return :int 1 if a sentences was popped, + * 0 if the stack was empty (*(INTO) will be set to `NULL`) + */ +#define POP(FROM, INTO)\ + REQ_SENTENCE((FROM), **,\ + REQ_SENTENCE((INTO), **,\ + ((*(INTO) = *(FROM)) ? (*(FROM) = (*(INTO))->travel_head, 1) : 0)\ + )) + + + + + +/** + * Create a non-reduced AND expression with two terms + * + * @param l The left-hand term; ownership is transfered to the function + * @param r The right-hand term; ownership is transfered to the function + * @return The expression (l ∧ r) (may commuted), `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +HIDDEN USE_RESULT NONNULL_INPUT +LIBNORMALFORM_SENTENCE *(libnormalform_and2__)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r); + +/** + * Create a non-reduced OR expression with two terms + * + * @param l The left-hand term; ownership is transfered to the function + * @param r The right-hand term; ownership is transfered to the function + * @return The expression (l ∨ r) (may commuted), `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +HIDDEN USE_RESULT NONNULL_INPUT +LIBNORMALFORM_SENTENCE *(libnormalform_or2__)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r); + +/** + * Create a non-reduced XOR expression with two terms + * + * @param l The left-hand term; ownership is transfered to the function + * @param r The right-hand term; ownership is transfered to the function + * @return The expression (l ⊕ r) (may commuted), `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +HIDDEN USE_RESULT NONNULL_INPUT +LIBNORMALFORM_SENTENCE *(libnormalform_xor2__)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r); + +/** + * Annotate every sentence that appear multiple times + * with a unique index (counted from 0 up) + * + * @param this The sentence that shall be inspected + * recursively for annotation + * @return The number of annotated sentences + */ +HIDDEN USE_RESULT NONNULL_INPUT +size_t (libnormalform_set_indices_and_counts__)(LIBNORMALFORM_SENTENCE *this); + +/** + * Undo `libnormalform_set_indices_and_counts__` + * + * This is needed because `libnormalform_set_indices_and_counts__` + * requires some otherwise unused memory to be properly initialised + * + * @param this The sentence that shall be recursively restored + */ +HIDDEN NONNULL_INPUT +void (libnormalform_reset_indices_and_counts__)(LIBNORMALFORM_SENTENCE *this); + + +#ifdef DEBUG +/* libnormalform_to_string.c */ +void libnormalform__debug_print__(LIBNORMALFORM_SENTENCE *this, size_t depth_limit); +#endif + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Winline" +#endif + + + + + +#ifdef TEST + +#include <sys/resource.h> +#include <unistd.h> + +#ifdef CHECK_MEMLEAK +# include "memcheck.h" +# define IF_MEMCHECK(...) __VA_ARGS__ +#else +# define IF_MEMCHECK(...) +#endif + +#define MEMLIMIT (512UL * 1024UL * 1024UL) /* valgrind requires quite a bit (but not this much) */ + +#define SET_LIMIT(WHAT, HOW_MUCH)\ + do {\ + struct rlimit lim;\ + if (getrlimit(WHAT, &lim)) {\ + perror("getrlimit "#WHAT);\ + break;\ + }\ + if ((HOW_MUCH) < lim.rlim_cur)\ + lim.rlim_cur = (HOW_MUCH);\ + lim.rlim_max = lim.rlim_cur;\ + if (setrlimit(WHAT, &lim)) {\ + perror("setrlimit "#WHAT);\ + break;\ + }\ + } while (0) + +#define TEST_BEGIN\ + SET_LIMIT(RLIMIT_CORE, 0);\ + SET_LIMIT(RLIMIT_AS, MEMLIMIT);\ + SET_LIMIT(RLIMIT_DATA, MEMLIMIT);\ + SET_LIMIT(RLIMIT_RSS, MEMLIMIT);\ + SET_LIMIT(RLIMIT_STACK, MEMLIMIT);\ + SET_LIMIT(RLIMIT_CPU, TEST_TIMEOUT_SECONDS);\ + alarm(TEST_TIMEOUT_SECONDS);\ + IF_MEMCHECK(memcheck_begin();)\ + do {\ + int exit_value_just_to_remove_warnings_about_this_following_variable_definitions__ = 0 + +#define TEST_END\ + IF_MEMCHECK(if (!memcheck_check_memleaks()) exit(1);)\ + exit(exit_value_just_to_remove_warnings_about_this_following_variable_definitions__); \ + } while (0) + + +#define ASSUME(X)\ + do {\ + int assumption_saved_errno__ = errno;\ + errno = 0;\ + if (!(X)) {\ + fprintf(stderr, "Assumption `%s` failed at %s:%i, errno: %s\n", #X, __FILE__, __LINE__, strerror(errno));\ + exit(1);\ + }\ + errno = assumption_saved_errno__;\ + } while (0) + +#define ASSERT(X)\ + do {\ + if (!(X)) {\ + fprintf(stderr, "Assertion `%s` failed at %s:%i\n", #X, __FILE__, __LINE__);\ + exit(1);\ + }\ + } while (0) + +#define ASSERT_EQUAL(A, B)\ + do {\ + int inv__;\ + LIBNORMALFORM_SENTENCE *a__ = (A);\ + LIBNORMALFORM_SENTENCE *b__ = (B);\ + ASSERT(a__->equals(a__, b__, &inv__) == 1);\ + ASSERT(inv__ == 0);\ + } while (0) + +#define ASSERT_INVEQUAL(A, B)\ + do {\ + int inv__;\ + LIBNORMALFORM_SENTENCE *a__ = (A);\ + LIBNORMALFORM_SENTENCE *b__ = (B);\ + ASSERT(a__->equals(a__, b__, &inv__) == 1);\ + ASSERT(inv__ == 1);\ + } while (0) + +#define ASSERT_NOTEQUAL(A, B)\ + do {\ + int inv__;\ + LIBNORMALFORM_SENTENCE *a__ = (A);\ + LIBNORMALFORM_SENTENCE *b__ = (B);\ + ASSERT(a__->equals(a__, b__, &inv__) == 0);\ + } while (0) + +#define REF libnormalform_ref + +#define VALIST(...) __VA_OPT__(__VA_ARGS__,) NULL +#define LIST(...) ((LIBNORMALFORM_SENTENCE *[]){VALIST(__VA_ARGS__)}) +#define COUNT(...) (sizeof(LIST(__VA_ARGS__)) / sizeof(LIBNORMALFORM_SENTENCE *) - 1U) + +#if defined(USE_TWO) +# define USE_CHECKED_VERSION +# undef LIBNORMALFORM_MACRO__ +# define LIBNORMALFORM_MACRO__(CONNECTIVE, A, B)\ + libnormalform_##CONNECTIVE##2(A, B) + +#elif defined(USE_VARARGS) || defined(USE_VARARGS_MACRO) +# undef LIBNORMALFORM_MACRO__ +# define LIBNORMALFORM_MACRO__(CONNECTIVE, ...)\ + (libnormalform_##CONNECTIVE##l(VALIST(__VA_ARGS__))) + +#elif defined(USE_CHECKED_VARARGS) +# define USE_CHECKED_VERSION +# undef LIBNORMALFORM_MACRO__ +# define LIBNORMALFORM_MACRO__(CONNECTIVE, ...)\ + (libnormalform_##CONNECTIVE##l_checked(COUNT(__VA_ARGS__), VALIST(__VA_ARGS__))) + +#elif defined(USE_VALIST) +# undef LIBNORMALFORM_MACRO__ +# define LIBNORMALFORM_MACRO__(CONNECTIVE, ...)\ + (vatrampoline(&libnormalform_v##CONNECTIVE, VALIST(__VA_ARGS__))) +USE_RESULT static LIBNORMALFORM_SENTENCE * +vatrampoline(LIBNORMALFORM_SENTENCE *(*f)(LIBNORMALFORM_SENTENCE *, va_list), + LIBNORMALFORM_SENTENCE *a, ...) +{ + LIBNORMALFORM_SENTENCE *ret; + va_list args; + va_start(args, a); + ret = (*f)(a, args); + va_end(args); + return ret; +} + +#elif defined(USE_CHECKED_VALIST) +# define USE_CHECKED_VERSION +# undef LIBNORMALFORM_MACRO__ +# define LIBNORMALFORM_MACRO__(CONNECTIVE, ...)\ + (vatrampoline(&libnormalform_v##CONNECTIVE##_checked, COUNT(__VA_ARGS__), VALIST(__VA_ARGS__))) +USE_RESULT static LIBNORMALFORM_SENTENCE * +vatrampoline(LIBNORMALFORM_SENTENCE *(*f)(size_t, LIBNORMALFORM_SENTENCE *, va_list), + size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ + LIBNORMALFORM_SENTENCE *ret; + va_list args; + va_start(args, a); + ret = (*f)(n, a, args); + va_end(args); + return ret; +} + +#elif defined(USE_CHECKED) +# define USE_CHECKED_VERSION + +#else +# undef LIBNORMALFORM_MACRO__ +# define LIBNORMALFORM_MACRO__(CONNECTIVE, ...)\ + (libnormalform_##CONNECTIVE(LIST(__VA_ARGS__))) +#endif + +#if !defined(USE_VARARGS_MACRO) +# undef libnormalform_andl +# undef libnormalform_orl +# undef libnormalform_xorl +# undef libnormalform_ifl +# undef libnormalform_implyl +# undef libnormalform_nandl +# undef libnormalform_norl +# undef libnormalform_xnorl +# undef libnormalform_nifl +# undef libnormalform_nimplyl +#endif + +#define TO\ +DO_TEST /* no indent before, breaking the word TO|DO to hide from grep */ CONST int main(void) {return 0;} + +#endif diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..a9ae3eb --- /dev/null +++ b/config.mk @@ -0,0 +1,19 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = cc -std=c23 + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = +LDFLAGS = + +#MEMCHECK_OBJ = memcheck.to +#MEMCHECK = $(MEMCHECK_OBJ) memcheck.h +#MEMCHECK_CPPFLAGS = -DCHECK_MEMLEAK -DPRINT_BACKTRACES +#MEMCHECK_LDFLAGS = -lunwind -ldw + +TEST_CPPFLAGS = $(CPPFLAGS) $(MEMCHECK_CPPFLAGS) +TEST_CFLAGS = $(CFLAGS) +TEST_LDFLAGS = $(LDFLAGS) $(MEMCHECK_LDFLAGS) + +TEST_TIMEOUT_SECONDS = 5 diff --git a/libnormalform.7 b/libnormalform.7 new file mode 100644 index 0000000..1607b34 --- /dev/null +++ b/libnormalform.7 @@ -0,0 +1,94 @@ +.TH LIBNORMALFORM 7 LIBNORMALFORM +.SH NAME +libnormalform \- First-order logic sentence canonicalisation library + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_true(void); +LIBNORMALFORM_SENTENCE *libnormalform_false(void); +LIBNORMALFORM_SENTENCE *libnormalform_variable(struct libnormalform_variable *); +LIBNORMALFORM_SENTENCE *libnormalform_function(struct libnormalform_function *); +LIBNORMALFORM_SENTENCE *libnormalform_transformation(struct libnormalform_transformer *, LIBNORMALFORM_SENTENCE *); + +LIBNORMALFORM_SENTENCE *libnormalform_not(LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_and(LIBNORMALFORM_SENTENCE **); +LIBNORMALFORM_SENTENCE *libnormalform_or(LIBNORMALFORM_SENTENCE **); +LIBNORMALFORM_SENTENCE *libnormalform_xor(LIBNORMALFORM_SENTENCE **); +LIBNORMALFORM_SENTENCE *libnormalform_imply(LIBNORMALFORM_SENTENCE **); +LIBNORMALFORM_SENTENCE *libnormalform_if(LIBNORMALFORM_SENTENCE **); +LIBNORMALFORM_SENTENCE *libnormalform_nand(LIBNORMALFORM_SENTENCE **); +LIBNORMALFORM_SENTENCE *libnormalform_nor(LIBNORMALFORM_SENTENCE **); +LIBNORMALFORM_SENTENCE *libnormalform_xnor(LIBNORMALFORM_SENTENCE **); +LIBNORMALFORM_SENTENCE *libnormalform_nimply(LIBNORMALFORM_SENTENCE **); +LIBNORMALFORM_SENTENCE *libnormalform_nif(LIBNORMALFORM_SENTENCE **); + +LIBNORMALFORM_SENTENCE *libnormalform_all(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_any(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_one(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_exists(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_nexists(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_unique(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_existentially(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_universally(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_uniquely(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_empty(struct libnormalform_map *); +LIBNORMALFORM_SENTENCE *libnormalform_nonempty(struct libnormalform_map *); +LIBNORMALFORM_SENTENCE *libnormalform_singleton(struct libnormalform_map *); + +LIBNORMALFORM_SENTENCE *libnormalform_ref(LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_clone(LIBNORMALFORM_SENTENCE *); +void libnormalform_free(LIBNORMALFORM_SENTENCE *); + +char *libnormalform_to_string(LIBNORMALFORM_SENTENCE *); +LIBNORMALFORM_SENTENCE *libnormalform_from_string(char *, char **, const struct libnormalform_representation_spec *); + +int libnormalform_evaluate(LIBNORMALFORM_SENTENCE *); +struct libnormalform_term *libnormalform_dnf(LIBNORMALFORM_SENTENCE *, uint64_t); +struct libnormalform_term *libnormalform_cnf(LIBNORMALFORM_SENTENCE *, uint64_t); +struct libnormalform_term *libnormalform_express(LIBNORMALFORM_SENTENCE *, uint64_t); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.B libnormalform +library provides a mechanism for expressing +first-order logic sentence and normalises them on the fly to +negation normal form, and then lets the user choose disjunctive +normal form (DNF) or conjunctive normal form (CNF). +.PP +.B libnormalform +support all Boolean connectives: AND, OR, XOR, +IMPLY, IF, NAND, NOR, XNOR, NIMPLY, NIF, and NOT, as well as the +boolean constants TRUE and FALSE. Additionally, +.B libnormalform +supports three standard qualifiers: the universal qualifier +(FOR ALL), the existential qualifier (THERE EXISTS), and the +unique existential (uniqueness) qualifier (THERE EXISTS ONE). +For all binary connectives, libnormalform support simply +chaining (e.g. AND(a, b, c) instead of AND(AND(a, b), c)). +For all qualifiers, libnormalform supports qualification over +an array or an associative array. +.P +.B libnormalform +also provides application managed boolean +variables and functions. These as well as the domains for +qualifiers can be set dynamically and the value of the +sentence can be evaluate at any time. +.PP +When +.B libnormalform +is asked to output the sentences in DNF +or CNF, it can relax parts of sentence in case the application +wants to express the sentence in a particular form but cannot +express all parts of, and therefore needs alternative that is +at least as true. While relaxing the sentence, it can eliminiate +parts of the sentence that become redundant; to be able to do +this, it queries the application for how the application +defined variables, functions and domains depend on each other. +.SH SEE ALSO +None. diff --git a/libnormalform.h b/libnormalform.h new file mode 100644 index 0000000..ed3b256 --- /dev/null +++ b/libnormalform.h @@ -0,0 +1,4213 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBNORMALFORM_H +#define LIBNORMALFORM_H + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> + + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Winline" +#endif + + +/* For internal use { */ + +#if defined(__GNUC__) +# define LIBNORMALFORM_USE_RESULT__ __attribute__((__warn_unused_result__)) +# define LIBNORMALFORM_FREE_WITH__(DESTRUCTOR) __attribute__((__malloc__(DESTRUCTOR))) +# define LIBNORMALFORM_USE_FREE__ __attribute__((__malloc__)) +# define LIBNORMALFORM_NONNULL_INPUT__ __attribute__((__nonnull__)) +# define LIBNORMALFORM_ONE_NONNULL_INPUT__(INDEX) __attribute__((__nonnull__(INDEX))) +# if defined(LIBNORMALFORM_ALLOW_BAD_WARNINGS) +# define LIBNORMALFORM_NULL_LAST__ __attribute__((__sentinel__)) +# else +# define LIBNORMALFORM_NULL_LAST__ +# endif +/* + * Unfortunately __attribute__((__sentinel__)) requires (in contrary to the + * documentation) the `NULL` to be inside `...`, and not at the argument before + * the `...`, so a "-Wformat=" warning will be generated if and empty list is + * used when constructing a sentence. Therefore, by default this attribute is + * not used, as the user may have configured the compiler to fail if the warning + * is generated. + */ +#else +# define LIBNORMALFORM_USE_RESULT__ +# define LIBNORMALFORM_FREE_WITH__(DESTRUCTOR) +# define LIBNORMALFORM_USE_FREE__ +# define LIBNORMALFORM_NONNULL_INPUT__ +# define LIBNORMALFORM_ONE_NONNULL_INPUT__(INDEX) +# define LIBNORMALFORM_NULL_LAST__ +#endif + + +#define LIBNORMALFORM_CHECKED__(CONNECTIVE)\ + size_t i__ = 0;\ + while (xs[i__])\ + i__ += 1;\ + if (i__ < n) {\ + while (n--)\ + libnormalform_free(xs[n]);\ + return NULL;\ + }\ + return libnormalform_##CONNECTIVE(xs); + +#define LIBNORMALFORM_VALIST__(CONNECTIVE)\ + size_t n__ = 0, i__ = 0;\ + va_list args2__;\ + va_copy(args2__, args);\ + if (a) {\ + do {\ + n__ += 1;\ + } while (va_arg(args2__, LIBNORMALFORM_SENTENCE *));\ + }\ + va_end(args2__);\ + {\ + LIBNORMALFORM_SENTENCE *xs[n__ + 1U];\ + if (n__) {\ + xs[i__++] = a;\ + while (--n__)\ + xs[i__++] = va_arg(args, LIBNORMALFORM_SENTENCE *);\ + }\ + xs[i__] = NULL;\ + return libnormalform_##CONNECTIVE(xs);\ + } + +#define LIBNORMALFORM_ELLIPSIS__(CONNECTIVE)\ + LIBNORMALFORM_SENTENCE *ret__;\ + va_list args__;\ + va_start(args__, a);\ + ret__ = libnormalform_v##CONNECTIVE(a, args__);\ + va_end(args__);\ + return ret__; + +#define LIBNORMALFORM_VALIST_CHECKED__(CONNECTIVE)\ + size_t n__ = 0, i__ = 0;\ + va_list args2__;\ + va_copy(args2__, args);\ + if (a) {\ + do {\ + n__ += 1;\ + } while (va_arg(args2__, LIBNORMALFORM_SENTENCE *));\ + }\ + va_end(args2__);\ + if (n__ < n) {\ + if (n) {\ + libnormalform_free(a);\ + while (--n)\ + libnormalform_free(va_arg(args2__, LIBNORMALFORM_SENTENCE *));\ + }\ + return NULL;\ + }\ + {\ + LIBNORMALFORM_SENTENCE *xs__[n__ + 1U];\ + if (n__) {\ + xs__[i__++] = a;\ + while (--n__)\ + xs__[i__++] = va_arg(args, LIBNORMALFORM_SENTENCE *);\ + }\ + xs__[i__] = NULL;\ + return libnormalform_##CONNECTIVE(xs__);\ + } + +#define LIBNORMALFORM_ELLIPSIS_CHECKED__(CONNECTIVE)\ + LIBNORMALFORM_SENTENCE *ret__;\ + va_list args__;\ + va_start(args__, a);\ + ret__ = libnormalform_v##CONNECTIVE##_checked(n, a, args__);\ + va_end(args__);\ + return ret__; + +#define LIBNORMALFORM_TWO__(CONNECTIVE)\ + LIBNORMALFORM_SENTENCE *xs__[3] = {l, r, NULL};\ + if (!l || !r) {\ + libnormalform_free(l);\ + libnormalform_free(r);\ + return NULL;\ + }\ + return libnormalform_##CONNECTIVE(xs__); + +#define LIBNORMALFORM_MACRO__(CONNECTIVE, ...)\ + (libnormalform_##CONNECTIVE##_checked(\ + sizeof((LIBNORMALFORM_SENTENCE *[]){__VA_OPT__(__VA_ARGS__,) NULL}) / sizeof(LIBNORMALFORM_SENTENCE *) - 1U,\ + (LIBNORMALFORM_SENTENCE *[]){__VA_OPT__(__VA_ARGS__,) NULL})) + +/* } */ + + +struct libnormalform_term; + + + + + +#define LIBNORMALFORM_AVOID_FOR_ALL (UINT64_C(1) << 0) /**< Prefer ¬∃x.¬φ over ∀x.φ */ +#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_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)) */ +#define LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ANY (UINT64_C(1) << 9) /**< Express ¬(φ(x)∃x:θ(x)) on the form ¬∃x.(θ(x) ∧ φ(x)) */ +#define LIBNORMALFORM_JOIN_SIDES_IN_FOR_ONE (UINT64_C(1) << 10) /**< Express φ(x)∃!x:θ(x) on the form ∃!x.(θ(x) ∧ φ(x)) */ +#define LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ONE (UINT64_C(1) << 11) /**< Express ¬(φ(x)∃!x:θ(x)) on the form ¬∃!x.(θ(x) ∧ φ(x)) */ +#define LIBNORMALFORM_ELIMINATE_FOR_ALL (UINT64_C(1) << 12) /**< Relax any non-translatable ∀x.φ to ⊤ */ +#define LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ALL (UINT64_C(1) << 13) /**< Relax any non-translatable ¬∀x.φ to ⊤ */ +#define LIBNORMALFORM_ELIMINATE_FOR_ANY (UINT64_C(1) << 14) /**< Relax any non-translatable ∃x.φ to ⊤ */ +#define LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ANY (UINT64_C(1) << 15) /**< Relax any non-translatable ¬∃x.φ to ⊤ */ +#define LIBNORMALFORM_ELIMINATE_FOR_ONE (UINT64_C(1) << 16) /**< Relax any non-translatable ∃!x.φ to ⊤ */ +#define LIBNORMALFORM_ELIMINATE_NEGATED_FOR_ONE (UINT64_C(1) << 17) /**< Relax any ¬∃!x.φ to ⊤ */ +#define LIBNORMALFORM_ELIMINATE_VARIABLE (UINT64_C(1) << 18) /**< Relax any positive variable literal to ⊤ */ +#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_ALL_EXPRESS_FLAGS__ ((UINT64_C(1) << 23) - 1U) /* For internal use */ + + +/** + * Opaque data type use to express and sentences + * + * This object incluses caches and preallocated memory + * makes object unsafe to be used from two different + * threads at the same time + * + * @seealso libnormalform_clone + */ +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_EXCLUSIVE_DISJUNCTION, /**< XOR clause; never used for empty clauses */ + LIBNORMALFORM_TRANSFORMATION, /**< Transformater function with input **/ + LIBNORMALFORM_VARIABLE, /**< Positive variable literal **/ + LIBNORMALFORM_NEGATED_VARIABLE, /**< Negative variable literal **/ + LIBNORMALFORM_FUNCTION, /**< Positive boolean function literal **/ + LIBNORMALFORM_NEGATED_FUNCTION, /**< Negative boolean function literal **/ + LIBNORMALFORM_FOR_ALL, /**< ∀x∈D.(P(x) → Q(x)) expression */ + LIBNORMALFORM_NEGATED_FOR_ALL, /**< ¬∀x∈D.(P(x) → Q(x)) expression */ + LIBNORMALFORM_FOR_ANY, /**< ∃x∈D.(P(x) ∧ Q(x)) expression */ + LIBNORMALFORM_NEGATED_FOR_ANY, /**< ¬∃x∈D.(P(x) ∧ Q(x)) expression */ + LIBNORMALFORM_FOR_ONE, /**< ∃!x∈D.(P(x) ∧ Q(x)) expression */ + LIBNORMALFORM_NEGATED_FOR_ONE /**< ¬∃!x∈D.(P(x) ∧ Q(x)) expression */ +}; + + +/** + * Value of an atomic sentence + */ +enum libnormalform_value { + LIBNORMALFORM_FALSE = 0, /**< Variable has the value False */ + LIBNORMALFORM_TRUE = 1 /**< Variable has the value True */ +}; + + +/** + * Built in transformer functions + */ +enum libnormalform_builtin_transformer { + LIBNORMALFORM_NOT_BUILT_IN = 0, /**< Application provided transformer */ + LIBNORMALFORM_DOMAIN_VIEW, /**< Selects the `.key` from a `struct libnormalform_mapping *` */ + LIBNORMALFORM_IMAGE_VIEW /**< Selects the `.value` from a `struct libnormalform_mapping *` */ +}; + + +/** + * Relationship (dependency) between the value of two sentences + */ +enum libnormalform_sentences_relationship { + /** + * Left-hand implies right-hand (⊨ P → Q) + * + * Example: + * - P = ⋀{a, b}, Q = ⋀{a} + * + * Truthtable: + * P: 001 + * Q: 011 + * ∧: 001 + * ∨: 011 + * ⊕: 010 + * →: 111 + * ←: 101 + * + * Consequence: + * - P ∧ Q = P + * - P ∨ Q = Q + * - P ⊕ Q = ¬P ∧ Q + * - P → Q = ⊤ + * - P ← Q cannot be simplified + */ + LIBNORMALFORM_MATERIAL_IMPLICATION = -1, + + /** + * The terms are identical (⊨ P ↔ Q) + * + * Example: + * - P = ⋀{a, b}, Q = ⋀{a, b} + * + * Truthtable: + * P: 01 + * Q: 01 + * ∧: 01 + * ∨: 01 + * ⊕: 00 + * →: 11 + * ←: 11 + * + * Consequence: + * - P ∧ Q = P = Q + * - P ∨ Q = P = Q + * - P ⊕ Q = ⊥ + * - P → Q = ⊤ + * - P ← Q = ⊤ + */ + LIBNORMALFORM_IDENTICAL, + + /** + * Right-hand implies left-hand (⊨ P ← Q) + * + * Example: + * - P = ⋀{a}, Q = ⋀{a, b} + * + * Truthtable: + * P: 011 + * Q: 001 + * ∧: 001 + * ∨: 011 + * ⊕: 010 + * →: 101 + * ←: 111 + * + * Consequence: + * - P ∧ Q = Q + * - P ∨ Q = P + * - P ⊕ Q = P ∧ ¬Q + * - P → Q cannot be simplified + * - P ← Q = ⊤ + */ + LIBNORMALFORM_CONVERSE_IMPLICATION, + + /** + * The terms are the inverse of each other (⊨ P ↮ Q) + * + * Example: + * - P = a, Q = ¬a + * + * Truthtable: + * P: 01 + * Q: 10 + * ∧: 00 + * ∨: 11 + * ⊕: 11 + * →: 10 + * ←: 01 + * + * Consequence: + * - P ∧ Q = ⊥ + * - P ∨ Q = ⊤ + * - P ⊕ Q = ⊤ + * - P → Q = Q + * - P ← Q = P + */ + LIBNORMALFORM_MUTUALLY_INVERSE, + + /** + * Both terms cannot be satisfied at the same time, + * but are not each other's inverse (⊨ P ⊼ Q) + * + * Example: + * - P = ⋀{a, b}, Q = ⋀{¬b} + * + * Truthtable: + * P: 001 + * Q: 010 + * ∧: 000 + * ∨: 011 + * ⊕: 011 + * →: 110 + * ←: 101 + * + * Consequence: + * - P ∧ Q = ⊥ + * - P ∨ Q cannot be simplified + * - P ⊕ Q = P ∨ Q + * - P → Q = ¬P + * - P ← Q = ¬Q + */ + LIBNORMALFORM_MUTUALLY_EXCLUSIVE, + + /** + * The terms cannot be unsatisfied at the same time, + * but they are not identical (⊨ P ∨ Q) + * + * Example: + * - P = ⋁{a, b}, Q = ⋁{¬b} + * + * Truthtable: + * P: 011 + * Q: 101 + * ∧: 001 + * ∨: 111 + * ⊕: 110 + * →: 101 + * ←: 011 + * + * Consequence: + * - P ∧ Q cannot be simplified + * - P ∨ Q = ⊤ + * - P ⊕ Q = ¬P ∨ ¬Q + * - P → Q = Q + * - P ← Q = P + */ + LIBNORMALFORM_JOINTLY_UNDENIABLE, + + /** + * The terms are unrelated to each other + * + * Example: + * - P = a, Q = b + * + * Truthtable: + * P: 0011 + * Q: 0101 + * ∧: 0001 + * ∨: 0111 + * ⊕: 0110 + * →: 1101 + * ←: 1011 + * + * Consequence: + * - P ∧ Q cannot be simplified + * - P ∨ Q cannot be simplified + * - P ⊕ Q cannot be simplified + * - P → Q cannot be simplified + * - P ← Q cannot be simplified + */ + LIBNORMALFORM_MUTUALLY_INDEPENDENT +}; + + + +/** + * Relationship (commonality) between the value of two domains + * + * (The documentation uses the term "set", however, + * properly they are bags (multisets), however this + * is only important for uniqueness qualification; + * for the mathematics, you can thing about it as + * sets) + */ +enum libnormalform_domain_relationship { + /** + * The left-hand set is a superset of the right-hand set (A ⊇ B) + * + * Example: + * - A = {a, b}, B = {a} + * + * Euler diagram: + * ┌────────────────────────────┐ + * │ ┌─A────────────────┐ │ + * │ │╱╱╱╱╱╱╱┌─B──────┐╱│ │ + * │ │╱╱╱╱╱╱╱│╳╳╳╳╳╳╳╳│╱│ │ + * │ │╱╱╱╱╱╱╱│╳╳╳╳╳╳╳╳│╱│ │ + * │ │╱╱╱╱╱╱╱│╳╳╳╳╳╳╳╳│╱│ │ + * │ │╱╱╱╱╱╱╱└────────┘╱│ │ + * │ │╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱│ │ + * │ └──────────────────┘ │ + * └────────────────────────────┘ + * + * Venn diagram of possibly non-empty intersections: + * ┌────────────────────────────┐ + * │ ┌─A─────────────┐ │ + * │ │███████████████│ │ + * │ │██████┌────────┼──────┐ │ + * │ │██████│████████│ │ │ + * │ │██████│████████│ │ │ + * │ └──────┼────────┘ │ │ + * │ │ │ │ + * │ └─────────────B─┘ │ + * └────────────────────────────┘ + * + * Consequence: + * - A ∩ B = B + * - A ∪ B = A + * - A ∆ B = A ∖ B + * - ⋀A ⊨ ⋀B + * - ⋁B ⊨ ⋁A + * - ⋀A ∧ ⋀B = ⋀A + * - ⋀A ∨ ⋀B = ⋀B + * - ⋀A ⊕ ⋀B = ¬⋀A ∧ ⋀B + * - ⋀A → ⋀B = ⊤ + * - ⋀A ← ⋀B cannot be simplified + * - ⋁A ∧ ⋁B = ⋁B + * - ⋁A ∨ ⋁B = ⋁A + * - ⋁A ⊕ ⋁B = ⋁A ∧ ¬⋁B + * - ⋁A → ⋁B cannot be simplified + * - ⋁A ← ⋁B = ⊤ + */ + LIBNORMALFORM_SUPERSET_OF = -1, + + /** + * The two terms are identical (A = B) + * + * Example: + * - A = {a, b}, B = {a, b} + * + * Euler diagram: + * ┌────────────────────────────┐ + * │ ┌─A,B──────────────┐ │ + * │ │╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳│ │ + * │ │╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳│ │ + * │ │╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳│ │ + * │ │╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳│ │ + * │ │╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳│ │ + * │ │╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳│ │ + * │ └──────────────────┘ │ + * └────────────────────────────┘ + * + * Venn diagram of possibly non-empty intersections: + * ┌────────────────────────────┐ + * │ ┌─A─────────────┐ │ + * │ │ │ │ + * │ │ ┌────────┼──────┐ │ + * │ │ │████████│ │ │ + * │ │ │████████│ │ │ + * │ └──────┼────────┘ │ │ + * │ │ │ │ + * │ └─────────────B─┘ │ + * └────────────────────────────┘ + * + * Consequence: + * - A ∩ B = A = B + * - A ∪ B = A = B + * - A ∆ B = ∅ + * - ⋀A = ⋀B + * - ⋁A = ⋁B + * - ⋀A ∧ ⋀B = ⋀A = ⋀B + * - ⋀A ∨ ⋀B = ⋀A = ⋀B + * - ⋀A ⊕ ⋀B = ⊥ + * - ⋀A → ⋀B = ⊤ + * - ⋀A ← ⋀B = ⊤ + * - ⋁A ∧ ⋁B = ⋁A = ⋁B + * - ⋁A ∨ ⋁B = ⋁A = ⋁B + * - ⋁A ⊕ ⋁B = ⊥ + * - ⋁A → ⋁B = ⊤ + * - ⋁A ← ⋁B = ⊤ + */ + LIBNORMALFORM_SAME_AS, + + /** + * The left-hand set is a subset of the right-hand set (A ⊆ B) + * + * Example: + * - A = {a}, B = {a, b} + * + * Euler diagram: + * ┌────────────────────────────┐ + * │ ┌─B────────────────┐ │ + * │ │╲┌─A──────┐╲╲╲╲╲╲╲│ │ + * │ │╲│╳╳╳╳╳╳╳╳│╲╲╲╲╲╲╲│ │ + * │ │╲│╳╳╳╳╳╳╳╳│╲╲╲╲╲╲╲│ │ + * │ │╲│╳╳╳╳╳╳╳╳│╲╲╲╲╲╲╲│ │ + * │ │╲└────────┘╲╲╲╲╲╲╲│ │ + * │ │╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲│ │ + * │ └──────────────────┘ │ + * └────────────────────────────┘ + * + * Venn diagram of possibly non-empty intersections: + * ┌────────────────────────────┐ + * │ ┌─A─────────────┐ │ + * │ │ │ │ + * │ │ ┌────────┼──────┐ │ + * │ │ │████████│██████│ │ + * │ │ │████████│██████│ │ + * │ └──────┼────────┘██████│ │ + * │ │███████████████│ │ + * │ └─────────────B─┘ │ + * └────────────────────────────┘ + * + * Consequence: + * - A ∩ B = A + * - A ∪ B = B + * - A ∆ B = B ∖ A + * - ⋀B ⊨ ⋀A + * - ⋁A ⊨ ⋁B + * - ⋀A ∧ ⋀B = ⋀B + * - ⋀A ∨ ⋀B = ⋀A + * - ⋀A ⊕ ⋀B = ⋀A ∧ ¬⋀B + * - ⋀A → ⋀B cannot be simplified + * - ⋀A ← ⋀B = ⊤ + * - ⋁A ∧ ⋁B = ⋁A + * - ⋁A ∨ ⋁B = ⋁B + * - ⋁A ⊕ ⋁B = ¬⋁A ∧ ⋁B + * - ⋁A → ⋁B = ⊤ + * - ⋁A ← ⋁B cannot be simplified + */ + LIBNORMALFORM_SUBSET_OF, + + /** + * The sets have no common elements (A ∩ B = ∅) + * + * Example: + * - A = {a}, B = {b} + * + * Euler diagram: + * ┌────────────────────────────┐ + * │ ┌─A───────┐ ┌─B───────┐ │ + * │ │╱╱╱╱╱╱╱╱╱│ │╲╲╲╲╲╲╲╲╲│ │ + * │ │╱╱╱╱╱╱╱╱╱│ │╲╲╲╲╲╲╲╲╲│ │ + * │ │╱╱╱╱╱╱╱╱╱│ │╲╲╲╲╲╲╲╲╲│ │ + * │ │╱╱╱╱╱╱╱╱╱│ │╲╲╲╲╲╲╲╲╲│ │ + * │ │╱╱╱╱╱╱╱╱╱│ │╲╲╲╲╲╲╲╲╲│ │ + * │ │╱╱╱╱╱╱╱╱╱│ │╲╲╲╲╲╲╲╲╲│ │ + * │ └─────────┘ └─────────┘ │ + * └────────────────────────────┘ + * + * Venn diagram of possibly non-empty intersections: + * ┌────────────────────────────┐ + * │ ┌─A─────────────┐ │ + * │ │███████████████│ │ + * │ │██████┌────────┼──────┐ │ + * │ │██████│ │██████│ │ + * │ │██████│ │██████│ │ + * │ └──────┼────────┘██████│ │ + * │ │███████████████│ │ + * │ └─────────────B─┘ │ + * └────────────────────────────┘ + * + * Consequence: + * - A ∩ B = ∅ + * - A ∪ B cannot be simplified + * - A ∆ B = A ∪ B + * - ⋀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_DISJOINT_WITH, + + /** + * Both sets have unique elements (A ⊆ A ∪ B ⊇ B, A ∩ B ⊇ ∅) + * + * Example: + * - A = {a, b}, B = {b, c} + * + * Euler diagram: + * ┌────────────────────────────┐ + * │ ┌─A─────────────┐ │ + * │ │╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱│ │ + * │ │╱╱╱╱╱╱┌────────┼────B─┐ │ + * │ │╱╱╱╱╱╱│╳╳╳╳╳╳╳╳│╲╲╲╲╲╲│ │ + * │ │╱╱╱╱╱╱│╳╳╳╳╳╳╳╳│╲╲╲╲╲╲│ │ + * │ └──────┼────────┘╲╲╲╲╲╲│ │ + * │ │╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲│ │ + * │ └───────────────┘ │ + * └────────────────────────────┘ + * + * 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_CONJOINT_WITH +}; + + +/** + * Variable that can be referenced in a sentence + */ +struct libnormalform_variable { + /** + * The value of the sentence + * + * This need not be sent when the sentence is constructed, + * but not be set before it is evaluated; it is only used + * by `libnormalform_evaluate` + */ + enum libnormalform_value value; + + /** + * Application-specific data that the application + * could use to identify the variable + * + * May be `NULL` + */ + void *user_data; + + /** + * Identifier for variable + * + * This need only be set when `libnormalform_to_string` + * is called. The value must be parsable by the application + * without any explicit termination + */ + const char *identifier; + + /** + * `NULL` or copy of of the object for `libnormalform_clone` + * + * This field should normally be unset, but before calling + * `libnormalform_clone`, the application must set it + * either to `NULL` (if the clone can use the same reference) + * or to a clone of the variable. + */ + struct libnormalform_variable *copy_for_clone; +}; + + +/** + * Boolean-codomain function that can be referenced in a sentence + */ +struct libnormalform_function { + /** + * Function that is used to evaluate the trueness of something + * + * @param user_data `.user_data` + * @param input Input to the function, normally it will + * be `NULL`, but it is inside the antecedent + * for a qualifier, it will be the `.key` + * for some `struct libnormalform_mapping`, + * and if it is inside the predicate of a + * qualifier, it will be the `.value` + * for some `struct libnormalform_mapping` + * @return 1 if the function is true, + * 0 if the function is false, + * -1 on failure (the function may set `errno`) + * + * For `libnormalform_exists`, `libnormalform_nexists`, + * and `libnormalform_unique`, it will only be called + * for the `.key` for `struct libnormalform_mapping`'s + * + * For `libnormalform_universally`, `libnormalform_existentially`, + * and `libnormalform_uniquely`, it will only be called + * for the `.value` for `struct libnormalform_mapping`'s + */ + int (*evaluate)(void *user_data, void *input); + + /** + * Application-specific data that will be passed into + * `.evaluate` (making it reenterant so it can be called + * by multiple threads, or to configure the function) + * + * May be `NULL` + */ + void *user_data; + + /** + * Identifier for function + * + * This need only be set when `libnormalform_to_string` + * is called. The value must be parsable by the application + * without any explicit termination + */ + const char *identifier; + + /** + * `NULL` or copy of of the object for `libnormalform_clone` + * + * This field should normally be unset, but before calling + * `libnormalform_clone`, the application must set it + * either to `NULL` (if the clone can use the same reference) + * or to a clone of the variable. + */ + struct libnormalform_function *copy_for_clone; + + /** + * See `.requires_relaxation` + */ + struct libnormalform_function *relaxation; + + /** + * Before calling `libnormalform_express`, `libnormalform_dnf`, + * or `libnormalform_cnf` this shall be set to 0 if the function + * may keep the function as is, or to a non-0 value if it shall + * be replaced `.relaxation` or by TRUE if `.relaxation` is `NULL` + */ + int requires_relaxation; +}; + + +/** + * Generic function that can be referenced in a sentence + * + * The function must be pure and distributive over any boolean operator + * (it must be an endomorphism). That is, the output is always the + * same for the same input without any side effects, and for any sentence + * A * B, where * is any boolean operator and F is the function, + * F(A * B) = F(A) * F(B). + */ +struct libnormalform_transformer { + /** + * Function that is used to transform input + * + * @param user_data `.user_data` + * @param input Input to the function, normally it will + * be `NULL`, but it is inside the antecedent + * for a qualifier, it will be the `.key` + * for some `struct libnormalform_mapping`, + * and if it is inside the predicate of a + * qualifier, it will be the `.value` + * for some `struct libnormalform_mapping`, + * except that during the create of a + * `struct libnormalform_term`, the library + * may insert builtin transformers that will + * have the `struct libnormalform_mapping *` + * as input and will output the `.key` or + * `.value` + * @return The transformation of `input` (not `NULL`), + * `NULL` on failure (the function may set `errno`) + * + * For `libnormalform_exists`, `libnormalform_nexists`, + * and `libnormalform_unique`, it will only be called + * for the `.key` for `struct libnormalform_mapping`'s + * + * For `libnormalform_universally`, `libnormalform_existentially`, + * and `libnormalform_uniquely`, it will only be called + * for the `.value` for `struct libnormalform_mapping`'s + */ + void *(*transform)(void *user_data, void *input); + + /** + * The function is called when the output of `.transform` + * is not longer needed by the library + * + * May be `NULL` + * + * This function is not called if `.transform` output `NULL` + * + * @param user_data `.user_data` + * @param output The pointer that was returned by `.transform` + */ + void (*deallocate)(void *user_data, void *output); + + /** + * Application-specific data that will be passed into + * `.transform` and `.deallocate` (making them reenterant + * so it can be called by multiple threads, or to + * configure the function) + * + * May be `NULL` + */ + void *user_data; + + /** + * Identifier for function + * + * This need only be set when `libnormalform_to_string` + * is called. The value must be parsable by the application + * without any explicit termination + */ + const char *identifier; + + /** + * `NULL` or copy of of the object for `libnormalform_clone` + * + * This field should normally be unset, but before calling + * `libnormalform_clone`, the application must set it + * either to `NULL` (if the clone can use the same reference) + * or to a clone of the variable. + */ + struct libnormalform_transformer *copy_for_clone; + + /** + * Before calling `libnormalform_express`, `libnormalform_dnf`, + * or `libnormalform_cnf` this shall be set to 0 if the function + * may keep the function, or to a non-0 value if it shall + * be replaced with a tautology + */ + int requires_elimination; + + /** + * This field will automatically be set to `LIBNORMALFORM_NOT_BUILT_IN` + * to indicated that the object originates in the application, + * however a `struct libnormalform_term *` created by the library + * (via `libnormalform_express`, `libnormalform_dnf`, or + * `libnormalform_cnf`) can contain objects with other values in this + * field, to describe what it does. In particular, depending on the + * flags used when creating the `struct libnormalform_term *`, + * qualification sentences may contain `LIBNORMALFORM_DOMAIN_VIEW` + * and `LIBNORMALFORM_IMAGE_VIEW` and transformers. + */ + enum libnormalform_builtin_transformer builtin; +}; + + +/** + * Key–value pair + */ +struct libnormalform_mapping { + /** + * Key + * + * In mapped qualifiers (`libnormalform_all`, + * `libnormalform_any`, `libnormalform_one`), this + * is used as the input for the antecedent + * + * In unmapped qualifiers (`libnormalform_exists`, + * `libnormalform_nexists`, `libnormalform_unique`), + * is this used as the input for the predicate + * + * In premapped qualifiers (`libnormalform_universally`, + * `libnormalform_existentially`, `libnormalform_uniquely`), + * is this unused + * + * Unused for domain size checks (`libnormalform_empty`, + * `libnormalform_nonempty`, `libnormalform_singleton`) + */ + void *key; + + /** + * Value + * + * In mapped qualifiers (`libnormalform_all`, + * `libnormalform_any`, `libnormalform_one`), this + * is used as the input for the predicate + * + * In unmapped qualifiers (`libnormalform_exists`, + * `libnormalform_nexists`, `libnormalform_unique`), + * is this unused + * + * In premapped qualifiers (`libnormalform_universally`, + * `libnormalform_existentially`, `libnormalform_uniquely`), + * is this used as the input for the predicate + * + * Unused for domain size checks (`libnormalform_empty`, + * `libnormalform_nonempty`, `libnormalform_singleton`) + */ + void *value; +}; + + +/** + * Domain of interest for qualifiers + */ +struct libnormalform_map { + /** + * Associative array over values in the domain + */ + struct libnormalform_mapping *mappings; + + /** + * The number of elements in `.mappings` + */ + size_t nmappings; + + /** + * Application-specific data that the application + * could use to identify the domain + * + * May be `NULL` + */ + void *user_data; + + /** + * Identifier for domain + * + * This need only be set when `libnormalform_to_string` + * is called. The value must be parsable by the application + * without any explicit termination + */ + const char *identifier; + + /** + * `NULL` or copy of of the object for `libnormalform_clone` + * + * This field should normally be unset, but before calling + * `libnormalform_clone`, the application must set it + * either to `NULL` (if the clone can use the same reference) + * or to a clone of the variable. + */ + struct libnormalform_map *copy_for_clone; +}; + + +struct libnormalform_atom_comparsion { + int version; + enum libnormalform_sentences_relationship relationship; +}; + +struct libnormalform_domain_comparsion { + int version; + enum libnormalform_domain_relationship relationship; +}; + +struct libnormalform_analysers { /* TODO doc */ + 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); +}; + + +/** + * Qualifier + */ +struct libnormalform_qualification { + struct libnormalform_map *map; /**< The domain of interest */ + struct libnormalform_term *antecedent; /**< The antecedent, `NULL` if joined with the predicate */ + struct libnormalform_term *predicate; /**< The predicate */ +}; + + +/** + * Clause of terms + */ +struct libnormalform_clause { + struct libnormalform_term *terms; /**< The terms in the clause */ + size_t nterms; /**< The number of terms in the clause */ +}; + + +/** + * Transformater function with input + */ +struct libnormalform_transformation { + struct libnormalform_transformer *transformer; /**< The transformer function */ + struct libnormalform_term *sentence; /**< The sentence the transformation is over */ +}; + + +/** + * Application-readable sentence + */ +struct libnormalform_term { + /** + * The type of the clause, qualifier, literal, or transformation + */ + enum libnormalform_term_type type; + + /** + * Whether the term or own of its subterms have been reduced; + */ + int reduced; + + /** + * The description of the sentence + */ + union { + /** + * Use if `.type` is `LIBNORMALFORM_DISJUNCTION` + */ + struct libnormalform_clause disjunction; + + /** + * Use if `.type` is `LIBNORMALFORM_CONJUNCTION` + */ + struct libnormalform_clause conjunction; + + /** + * Use if `.type` is `LIBNORMALFORM_EXCLUSIVE_DISJUNCTION` + */ + struct libnormalform_clause exclusive_disjunction; + + /** + * Use if `.type` is `LIBNORMALFORM_DISJUNCTION`, + * `LIBNORMALFORM_CONJUNCTION`, or + * `LIBNORMALFORM_EXCLUSIVE_DISJUNCTION` + */ + struct libnormalform_clause clause; + + /** + * Use if `.type` is `LIBNORMALFORM_TRANSFORMATION` + */ + struct libnormalform_transformation transformation; + + /** + * Use if `.type` is `LIBNORMALFORM_VARIABLE` or + * `LIBNORMALFORM_NEGATED_VARIABLE` + */ + struct libnormalform_variable *variable; + + /** + * Use if `.type` is `LIBNORMALFORM_FUNCTION` or + * `LIBNORMALFORM_NEGATED_FUNCTION` + */ + struct libnormalform_function *function; + + /** + * Use if '.type` is `LIBNORMALFORM_FOR_ALL` or + * `LIBNORMALFORM_NEGATED_FOR_ALL` + */ + struct libnormalform_qualification for_all; + + /** + * Use if '.type` is `LIBNORMALFORM_FOR_ANY` or + * `LIBNORMALFORM_NEGATED_FOR_ANY` + */ + struct libnormalform_qualification for_any; + + /** + * Use if '.type` is `LIBNORMALFORM_FOR_ONE` or + * `LIBNORMALFORM_NEGATED_FOR_ONE` + */ + struct libnormalform_qualification for_one; + + /** + * Use if '.type` is `LIBNORMALFORM_FOR_ALL`, + * `LIBNORMALFORM_NEGATED_FOR_ALL`, `LIBNORMALFORM_FOR_ANY`, + * `LIBNORMALFORM_NEGATED_FOR_ANY`, `LIBNORMALFORM_FOR_ONT`, + * or `LIBNORMALFORM_NEGATED_FOR_ONE` + */ + struct libnormalform_qualification qualification; + } term; +}; + + + + + +/** + * Increase the reference count of a sentence object by 1 + * + * @param this The sentence object + * @return `this` on success, `NULL` on failure + * + * @throws ENOMEM The reference count is already maximised (at SIZE_MAX) + * + * Reasonable application can safely assume that `this` is always returned + * + * Note, `NULL` is returned without `errno` modified if `this` is `NULL` + * + * Be aware that this function is not thread-safe + * + * @seealso libnormalform_clone + * @seealso libnormalform_free + */ +LIBNORMALFORM_SENTENCE *(libnormalform_ref)(LIBNORMALFORM_SENTENCE *this); + + +/** + * Create a copy of a sentence object + * + * This may be necessary as usage of sentence objects are not thread-safe, + * creating a copy of a sentence object will allow one thread to safely + * use one copy and another thread the other copy + * + * Before calling this function, the application shall set `.copy_for_clone` + * in each `struct libnormalform_variable`, `struct libnormalform_function`, + * `struct libnormalform_map` and `struct libnormalform_transformer`, used + * by the sentence. `.copy_for_clone` shall either be set to `NULL` or a copy + * of object. If `NULL` is used, the copy of `this` will use the same + * instance of the object as `this`. + * + * @param this The sentence object + * @return A unique pointer on success or `NULL` if + * `this` is `NULL`; `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create a copy of `this` + * + * Be aware that this function is not thread-safe + * + * @seealso libnormalform_ref + * @seealso libnormalform_free + */ +LIBNORMALFORM_USE_RESULT__ +LIBNORMALFORM_SENTENCE *(libnormalform_clone)(LIBNORMALFORM_SENTENCE *this); + + +/** + * When `this` is a `LIBNORMALFORM_SENTENCE *`: + * + * Decrease the reference count of a sentence object by 1, + * if the object reference count reaches 0, deallocate the + * object. + * + * Because all functions that return a new sentence object + * (excluding `libnormalform_clone`), adopt the ownership + * of the reference to each input sentence (and may create + * it's own subsentences), this function is called recursively, + * and the application shall not attempt to call this function + * for sentences used to create other sentences + * + * Be aware the this function will not deallocate any + * `struct libnormalform_variable *`, + * `struct libnormalform_function *`, or + * `struct libnormalform_map *` used in the sentence as + * these are owned any managed by the application + * + * When `this` is a `struct libnormalform_term *`: + * + * Recursively deallocate `this`. This will only deallocate + * `this` and subobjects with the same type + * + * If `.type` is invalid for `this` or any subobject, the + * function's behaviour is undefined + * + * This function is a no-operation function when `this` is `NULL` + * + * @param this The object + * + * This function's behaviour is undefined if `this` is not `NULL` + * but not of the one of the above listed types + * + * Be aware that this function is not thread-safe + */ +void (libnormalform_free)(void *this); + + +/** + * Evaluate a sentence to determine it's trueness + * + * Before calling this function, the application code shall + * configure any `struct libnormalform_variable`, + * `struct libnormalform_function`, and `struct libnormalform_map` + * used by the sentence. For each `struct libnormalform_variable`, + * `.value` shall either be set to `LIBNORMALFORM_TRUE` or + * `LIBNORMALFORM_FALSE` to indicate the value of the atom. + * For each `struct libnormalform_function`, `.evaluate` shall + * be set to a pointer to function that evaluates the input and + * `.user_data` shall be set to any application data the function + * needs to operate; `.evaluate` shall return 1, 0, or -1 as + * specified for this function; and may set `errno` to indicate + * an error (library itself does not look at `errno`, but may + * be used to signal the error to the application, of course + * the error may also be specified to the application in an + * application-specific way). For each `struct libnormalform_map`, + * `.mappings` shall be to the associated array of values + * that shall be tested by the qualifier-sentence it is + * used by, and `.nmappings` shall be set to the number of + * pairs in this array. + * + * @param this The sentence to evaluate + * @return 1 if the sentence is true, + * 0 if the sentence is false, + * -1 on failure + * + * Only application code may cause this function to + * fail, therefore no error codes are predefined for + * this function, instead `errno` is only set by + * the failing application code + * + * Be aware that this function is not thread-safe + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +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 + * 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 + * 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` + * it must also set `.requires_elimination` to 1 if the transformation + * must be replaces with a tautology, and 0 otherwise. + * + * @param this The sentence to describe + * @param flags The bitwise OR of any number of the following values: + * - LIBNORMALFORM_AVOID_FOR_ALL: Prefer ¬∃x.¬φ over ∀x.φ + * - 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, + * but transform it to negation normal form + * - 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)) + * - LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ANY Express ¬(φ(x)∃x:θ(x)) on the form ¬∃x.(θ(x) ∧ φ(x)) + * - LIBNORMALFORM_JOIN_SIDES_IN_FOR_ONE Express φ(x)∃!x:θ(x) on the form ∃!x.(θ(x) ∧ φ(x)) + * - LIBNORMALFORM_JOIN_SIDES_IN_NEGATED_FOR_ONE Express ¬(φ(x)∃!x:θ(x)) on the form ¬∃!x.(θ(x) ∧ φ(x)) + * - 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_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` + * @return The description of the sentence, `NULL` on failure; + * the returned pointer shall be deallocated with + * `libnormalform_free` when it is no longer needed + * + * @seealso libnormalform_dnf + * @seealso libnormalform_cnf + */ +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 */ + const struct libnormalform_analysers *analysers); + + +/** + * Variant of `libnormalform_express` that always canonicalises + * the statement to disjunctive normal form + * + * @param this The sentence to describe + * @param flags See `libnormalform_express`; `LIBNORMALFORM_ELIMINATE_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` + * @return The description of the sentence, `NULL` on failure; + * the returned pointer shall be deallocated with + * `libnormalform_free` when it is no longer needed + * + * @seealso libnormalform_express + * @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 */ + const struct libnormalform_analysers *analysers); + + +/** + * Variant of `libnormalform_express` that always canonicalises + * the statement to conjuctive normal form + * + * @param this The sentence to describe + * @param flags See `libnormalform_express`; `LIBNORMALFORM_ELIMINATE_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` + * @return The description of the sentence, `NULL` on failure; + * the returned pointer shall be deallocated with + * `libnormalform_free` when it is no longer needed + * + * @seealso libnormalform_express + * @seealso libnormalform_dnf + */ +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 */ + 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) + * + * Before calling this function, the application shall set `.identifier` + * in each `struct libnormalform_variable`, `struct libnormalform_function`, + * `struct libnormalform_map`, and `struct libnormalform_transformer` used + * by the sentence to a non-`NULL`, unique string that identifies (but + * does not necessarily describe) the object. The application must be able + * to find the end of any such string without any explicit termination + * (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, + * so this function will not necessarily reproduce the sentence + * as it was specified when it was constructed. + * + * @param this The sentence object to serialise + * @return String-representation of `this`, `NULL` failure; + * the caller shall deallocate the returned pointer + * with free(3) when it is no longer needed + * + * @throws ENOMEM Insufficient memory available to serialise `this` + * + * @seealso libnormalform_from_string + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_USE_FREE__ LIBNORMALFORM_NONNULL_INPUT__ +char *(libnormalform_to_string)(LIBNORMALFORM_SENTENCE *this); + + +/** + * Convert a string, created with `libnormalform_to_string` + * (or is on similar form), to sentence object + * + * The string shall be on the whitespace-insensitive form: + * + * constant ::= "TRUE" | "FALSE"; + * connective1 ::= "NOT"; + * connective2 ::= "AND" | "OR" | "XOR" | "IF" | "IMPLY" | + * "NAND" | "NOR" | "XNOR" | "NIF" | "NIMPLY"; + * qualifier0 ::= "EMPTY" | "NONEMPTY" | "SINGLETON"; + * qualifier1 ::= "EXISTS" | "NEXISTS" | "UNIQUE" | + * "EXISTENTIALLY" | "UNIVERSALLY" | "UNIQUELY"; + * qualifier2 ::= "ALL" | "ANY" | "ONE"; + * unary ::= connective1 [reference-point] "(" sentence ")"; + * variadic ::= connective2 [reference-point] "(" [sentence-list] ")"; + * sentence-list ::= sentence ["," sentence-list]; + * qualified0 ::= qualifier0 [reference-point] "(" map ")"; + * qualified1 ::= qualifier1 [reference-point] "(" map "," sentence ")"; + * qualified2 ::= qualifier2 [reference-point] "(" map "," sentence "," sentence ")"; + * atom ::= atom-var | atom-fun; + * atom-var ::= "VARIABLE" [reference-point] "(" variable ")"; + * atom-fun ::= "FUNCTION" [reference-point] "(" function ")"; + * transformation ::= "TRANSFORMATION" [reference-point] "(" transformer "," sentence ")"; + * reference-point ::= "@" decimal-number; + * reference ::= "REF(" decimal-number ")"; + * one-to-nine ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; + * zero-to-nine ::= "0" | one-to-nine; + * decimal-number ::= "0" | positive-decimal; + * positive-decimal ::= one-to-nine [digits]; + * digits ::= zero-to-nine [digits]; + * sentence ::= constant | unary | variadic | qualified0 | qualified1 | + * qualified2 | atom | transformation | reference; + * + * where "sentence" is the root, and where "map" is parsed by `*spec->get_map`, + * "variable" is parsed by `*spec->get_variable`, "function" is parsed by + * `*spec->get_function`, and "transformer" is parsed by `*spec->get_transformer`. + * References must start at 0 and increment by one, in left-to-right order, + * every time reference point is specified, and forward-references are not allowed + * (this includes reference to containing sentence (whose reference ID is lower + * because it is written earlier)). + * + * @param s The representation of the sentence to deserialise + * @param end_out Output parameter for the end of `s`; + * if `NULL` the function will fail if `s` contains + * excess information + * @param spec Specification for how application managed objects + * are to be deserialise + * @return A unique pointer, that is a deserialisation of `s`, + * or `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to deserialise `s` + * @throws EINVAL `s` was not properly formatted + * @throws EINVAL `end_out` was `NULL` and `s` containd excess information + * @throws EDOM `s` contained a empty clause for a connective + * that does not support empty clauses + * @throws EDOM `s` contained a transformation over a qualifer + * @throws ENOENT `s` contained a variable but `spec->get_variable` was `NULL` + * @throws ENOENT `s` contained a boolean function but `spec->get_function` was `NULL` + * @throws ENOENT `s` contained a domain but `spec->get_map` was `NULL` + * @throws ENOENT `s` contained a transformation but `spec->get_transformer` was `NULL` + * + * If function in `spec` fails, this function will fail and keep `errno` + * as set by the function that failed + * + * The function itself does not modify `s` and can operate on read-only + * memory, however an offset of the string may be passed into application + * code. The application is free to modify `s` if it knows the memory + * is indeed writable. + * + * @seealso libnormalform_to_string + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_ONE_NONNULL_INPUT__(1) LIBNORMALFORM_ONE_NONNULL_INPUT__(3) +LIBNORMALFORM_SENTENCE *(libnormalform_from_string)(char *s, char **end_out, const struct libnormalform_representation_spec *spec); + + + + + +/** + * Create an atomic sentence whose value is externally set + * + * @param variable The variable the sentence shall express + * @return A sentence object referencing `variable`, `NULL` on failure + * @throws ENOMEM Insufficient memory available to create the sentence + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_variable)(struct libnormalform_variable *variable); + + +/** + * Create an atomic sentence whose value is externally evaluated + * + * @param function The function the sentence shall express + * @return A sentence object referencing `function`, `NULL` on failure + * @throws ENOMEM Insufficient memory available to create the sentence + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_function)(struct libnormalform_function *function); + + +/** + * Create an endomorphic transformation of the input for a sentence + * + * `function->builtin` will automatically be initialised to `LIBNORMALFORM_NOT_BUILT_IN` + * + * @param function The transformation function + * @param sentence The sentence whose input shall be transformed + * @return A sentence whose input is transformed by `function`, `NULL` on failure + * @throws ENOMEM Insufficient memory available to create the sentence + * @throws EDOM `sentence` is a qualifier + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_ONE_NONNULL_INPUT__(1) +LIBNORMALFORM_SENTENCE *(libnormalform_transformation)(struct libnormalform_transformer *function, LIBNORMALFORM_SENTENCE *sentence); + + +/** + * TRUE: Tautology + * + * Always true + * + * Commonly written "⊤", "1", or "T" + * + * @return An atomic sentence with a constant value of True, `NULL` on failure + * @throws ENOMEM Insufficient memory available to create the sentence + */ +LIBNORMALFORM_USE_RESULT__ +LIBNORMALFORM_SENTENCE *(libnormalform_true)(void); + + +/** + * FALSE: Contradiction + * + * Always false + * + * Commonly written "⊥", "1", or "F" + * + * @return An atomic sentence with a constant value of False, `NULL` on failure + * @throws ENOMEM Insufficient memory available to create the sentence + */ +LIBNORMALFORM_USE_RESULT__ +LIBNORMALFORM_SENTENCE *(libnormalform_false)(void); + + +/** + * NOT: Negation + * + * The condition must not be met: + * ¬0 = 1 + * ¬1 = 0 + * + * Properties: + * - Involution + * - Distribution over AND changes connective to OR (De Morgan's law) + * - Distribution over OR changes connective to AND (De Morgan's law) + * + * Commonly written "¬" ("¬x"), "~", or "!", or with overstroke + * + * @param x The sentence that should be negated; + * ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `x` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `x` to + * be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +LIBNORMALFORM_SENTENCE *(libnormalform_not)(LIBNORMALFORM_SENTENCE *x); + + +/** + * FOR ALL: Universal quantification (mapped) + * + * The predicate must be met for every element that the antecedent + * is met for; if no element exists, or the antecedent is false for + * every element, the sentence is true + * + * Properties: + * - Vacuous truth + * - ∀x∈D.P(x) → ⊤ = ⊤ + * - ∀x∈D.⊥ → Q(x) = ⊤ + * - ∀x∈D.⊤ → ⊥ ⊨ D = ∅ + * - Conjunction + * + * Commonly written "∀" ("∀x∈d.k(x) → v(x)", "∀x∈d (k(x) → v(x))", or "v(x) ∀ x∈d : k(x)") + * + * The antecedent is tested for every `.key` in the domain, + * and the predicate is tested for every associated `.value` + * where the antecedent is met; but the testing is cut short + * if the antecedent is met and not the predicate for an element + * + * @param d The domain of interest; ownership is retained by the caller + * @param k The antecedent; ownership is transfered to this function + * @param v The predicate; ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `k` or `v` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `k` or + * `v` to be `NULL`) + * + * @seealso libnormalform_nexists + * @seealso libnormalform_universally + * @seealso libnormalform_any + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_ONE_NONNULL_INPUT__(1) +LIBNORMALFORM_SENTENCE *(libnormalform_all)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *k, LIBNORMALFORM_SENTENCE *v); + + +/** + * THERE EXISTS: Existential quantification (mapped) + * + * The predicate must be met for at least one element that the + * antecedent is met for; if no element exists, or the antecedent is + * false for every element, the sentence is false + * + * Properties: + * - Vacuous falsity + * - ∃x∈D.P(x) ∧ ⊥ = ⊥ + * - ∃x∈D.⊥ ∧ Q(x) = ⊥ + * - ∃x∈D.⊤ ∧ ⊤ ⊨ D ⊃ ∅ + * - Disjunction + * + * Commonly written "∃" ("∃x∈d.k(x) ∧ v(x)", "∃x∈d (k(x) ∧ v(x))", or "v(x) ∃ x∈d : k(x)") + * + * The antecedent is tested for every `.key` in the domain, + * and the predicate is tested for every associated `.value` + * where the antecedent is met; but the testing is cut short + * if both the antecedent and predicate is met for an element + * + * @param d The domain of interest; ownership is retained by the caller + * @param k The antecedent; ownership is transfered to this function + * @param v The predicate; ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `k` or `v` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `k` or + * `v` to be `NULL`) + * + * @seealso libnormalform_exists + * @seealso libnormalform_existentially + * @seealso libnormalform_one + * @seealso libnormalform_all + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_ONE_NONNULL_INPUT__(1) +LIBNORMALFORM_SENTENCE *(libnormalform_any)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *k, LIBNORMALFORM_SENTENCE *v); + + +/** + * THERE EXISTS ONE: Unique existential quantification (mapped) + * + * The predicate must be met for exactly one element that the + * antecedent is met for; if no element exists, or the antecedent is + * false for every element, the sentence is false + * + * Properties: + * - Vacuous falsity + * - ∃!x∈D.P(x) ∧ ⊥ = ⊥ + * - ∃!x∈D.⊥ ∧ Q(x) = ⊥ + * - ∃!x∈D.⊤ ∧ ⊤ ⊨ |D| = 1 + * + * Commonly written "∃!" ("∃!x∈d.k(x) ∧ v(x)", "∃!x∈d (k(x) ∧ v(x))", or "v(x) ∃! x∈d : k(x)") + * + * The antecedent is tested for every `.key` in the domain, + * and the predicate is tested for every associated `.value` + * where the antecedent is met; but the testing is cut short + * if both the antecedent and predicate is met for two elements + * + * @param d The domain of interest; ownership is retained by the caller + * @param k The antecedent; ownership is transfered to this function + * @param v The predicate; ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `k` or `v` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `k` or + * `v` to be `NULL`) + * + * @seealso libnormalform_unique + * @seealso libnormalform_uniquely + * @seealso libnormalform_any + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_ONE_NONNULL_INPUT__(1) +LIBNORMALFORM_SENTENCE *(libnormalform_one)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *k, LIBNORMALFORM_SENTENCE *v); + + + + + +/** + * AND: Conjunction + * + * Also known as BUT + * + * All conditions must be met + * + * 0 ∧ 0 = 0 + * 0 ∧ 1 = 0 + * 1 ∧ 0 = 0 + * 1 ∧ 1 = 1 + * + * Properties: + * - Vacuous truth + * - Identity singleton + * - Commutative + * - Associative + * - Distributive over OR + * - Distributive over XOR + * - ⊤ as identity + * - Dominated by ⊥ + * - Idempotent + * - Connecting complement results in contradiction + * - More lax term is absorbed (A ∧ (A ∨ B) simplifies to A) + * + * Commonly written "∧" ("⋀" if n-ary), "&", or with juxtaposition + * + * @param xs `NULL`-terminated list of sentences that should be + * joined by the connective AND; this can be any number + * of arguments, even none; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_AND + * @seealso libnormalform_and_checked + * @seealso libnormalform_andl_checked + * @seealso libnormalform_vand_checked + * @seealso libnormalform_andl + * @seealso libnormalform_vand + * @seealso libnormalform_and2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_and)(LIBNORMALFORM_SENTENCE **xs); + + +/** + * OR: Disjunction + * + * Also known as Inclusive disjunction + * + * At least one condition must be met + * + * 0 ∨ 0 = 0 + * 0 ∨ 1 = 1 + * 1 ∨ 0 = 1 + * 1 ∨ 1 = 1 + * + * Properties: + * - Vacuous falsity + * - Identity singleton + * - Commutative + * - Associative + * - Distributive over AND + * - ⊥ as identity + * - Dominated by ⊤ + * - Idempotent + * - Connecting complement results in tautology + * - More strict term is absorbed (A ∨ (A ∧ B) simplifies to A) + * + * Commonly written "∨" ("⋁" if n-ary), "|", or "+" + * + * @param xs `NULL`-terminated list of sentences that should be + * joined by the connective OR; this can be any number + * of arguments, even none; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_OR + * @seealso libnormalform_or_checked + * @seealso libnormalform_orl_checked + * @seealso libnormalform_vor_checked + * @seealso libnormalform_orl + * @seealso libnormalform_vor + * @seealso libnormalform_or2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_or)(LIBNORMALFORM_SENTENCE **xs); + + +/** + * XOR: Exclusive disjunction + * + * Also known as EOR, EXOR, NEQ, NIFF, Parity (especially when n-ary), + * Exclusive alternation, Exclusive or, Logical non-equivalence, + * Logical non-equivalence, Logical inequality, Not if and only if, + * or Unless and only unless + * + * An odd number of conditions must be met + * + * 0 ⊕ 0 = 0 + * 0 ⊕ 1 = 1 + * 1 ⊕ 0 = 1 + * 1 ⊕ 1 = 0 + * + * Properties: + * - Vacuous falsity + * - Identity singleton + * - Commutative + * - Associative + * - ⊥ as identity + * - Complemented by ⊤ + * - Annihiliation by reflextion + * - Connecting complement results in tautology + * - A ⊕ B is reduced to (A ∨ B) ∧ (¬A ∨ ¬B) or + * (A ∧ ¬B) ∨ (¬A ∧ B) in negation normal form + * + * Commonly written "⊕" ("⨁" if n-ary), "⊻", "↮", "⇎", "≢", or "^" + * + * Note that the name Exclusive disjunction is misleading + * when there are more than two terms, as the statement + * will not check that exactly one term is true, but rather + * calculate the parity of the terms. + * + * @param xs `NULL`-terminated list of sentences that should be + * joined by the connective XOR; this can be any number + * of arguments, even none; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_XOR + * @seealso libnormalform_xor_checked + * @seealso libnormalform_xorl_checked + * @seealso libnormalform_vxor_checked + * @seealso libnormalform_xorl + * @seealso libnormalform_vxor + * @seealso libnormalform_xor2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_xor)(LIBNORMALFORM_SENTENCE **xs); + + +/** + * IF: Converse implication + * + * Also known as Converse conditional + * + * Creates a chain where any condition must be at least + * as satisfied as the condition left to it (xs[0] ≤ xs[1]) + * (Not xs[1] without xs[0]); it is false only when only + * the left-most term is false + * + * 0 ← 0 = 1 + * 0 ← 1 = 0 + * 1 ← 0 = 1 + * 1 ← 1 = 1 + * + * Properties: + * - Identity singleton + * - Non-commutative + * - Left-associative: A ← B ← C = (A ← B) ← C ≠ A ← (B ← C) + * - Complemented by left ⊥ + * - Dominated by left ⊤ + * - Right falsity results in tautology + * - ⊤ as right identity + * - Contrapositivity: A ← B = ¬B ← ¬A + * - Transitive: (A ← B) ∧ (B ← C) ⊨ A ← C + * - Reflexive: A ← A = ⊤ + * - Complete: (A ← B) ∨ (B ← A) = ⊤ + * - Excluded middle: (A ← B) ∨ (¬A ← B) = ⊤ + * - Import-export: (A ← B) ← C = A ← (B ∧ C) + * - Commutativity of antecedents: (A ← B) ← C = (A ← C) ← B + * - Vacuous conditional: ¬A ⊨ B ← A + * - Antecedent strengthening: A ← B ⊨ A ← (B ∧ C) + * - Right-distributive: (A ← B) ← C = (A ← C) ← (B ← C) + * - Distributive over left AND + * - Distributive over left OR + * - Distributing over right AND changes AND to OR + * - Distributing over right OR changes OR to AND + * - A ← B is reduced to A ∨ ¬B in negation normal form + * + * Commonly written "←", "⇐", "<-", "<=", "≤", "⩽", or "⊂" + * + * @param xs `NULL`-terminated list of sentences that should be + * joined by the connective IF; this can be any positive + * number of arguments; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * @throws EDOM `xs` contains no term (but only `NULL`) + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_IF + * @seealso libnormalform_if_checked + * @seealso libnormalform_ifl_checked + * @seealso libnormalform_vif_checked + * @seealso libnormalform_ifl + * @seealso libnormalform_vif + * @seealso libnormalform_if2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_if)(LIBNORMALFORM_SENTENCE **xs); + + +/** + * IMPLY: Material implication + * + * Also known as IMPLIES, IF..THEN, NOT..WITHOUT or material conditional + * + * Creates a chain where any condition must be at most + * as satisfied as the condition left to it (xs[0] ≥ xs[1]) + * (Not xs[0] without xs[1]); it is false only when only + * the right-most term is false + * + * 0 → 0 = 1 + * 0 → 1 = 1 + * 1 → 0 = 0 + * 1 → 1 = 1 + * + * Properties: + * - Vacuous truth + * - Identity singleton + * - Non-commutative + * - Right-associative: A → B → C = A → (B → C) ≠ (A → B) → C + * - Complemented by right ⊥ + * - Dominated by right ⊤ + * - Left falsity results in tautology + * - ⊤ as left identity + * - Contrapositivity: A → B = ¬B → ¬A + * - Transitive: (A → B) ∧ (B → C) ⊨ A → C + * - Reflexive: A → A = ⊤ + * - Complete: (A → B) ∨ (B → A) = ⊤ + * - Excluded middle: (A → B) ∨ (A → ¬B) = ⊤ + * - Import-export: A → (B → C) = (A ∧ B) → C + * - Commutativity of antecedents: A → (B → C) = B → (A → C) + * - Vacuous conditional: ¬A ⊨ A → B + * - Antecedent strengthening: A → B ⊨ (A ∧ C) → B + * - Left-distributive: A → (B → C) = (A → B) → (A → C) + * - Distributive over right AND + * - Distributive over right OR + * - Distributing over left AND changes AND to OR + * - Distributing over left OR changes OR to AND + * - A → B is reduced to ¬A ∨ B in negation normal form + * + * Commonly written "→", "⇒", "->", "=>", ">=", "≥", "⩾", or "⊃" + * + * @param xs `NULL`-terminated list of sentences that should + * be joined by the connective IMPLY; this can be any + * number of arguments; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_IMPLY + * @seealso libnormalform_imply_checked + * @seealso libnormalform_implyl_checked + * @seealso libnormalform_vimply_checked + * @seealso libnormalform_implyl + * @seealso libnormalform_vimply + * @seealso libnormalform_imply2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_imply)(LIBNORMALFORM_SENTENCE **xs); + + +/** + * NAND: Alternative denial + * + * Also known as Non-conjunction, Negated conjunction, + * Mutual exclusion, or Sheffer stroke + * + * Creates a left-associated NAND-chain; in NAND, the + * two conditions must not be met at the same time + * + * 0 ⊼ 0 = 1 + * 0 ⊼ 1 = 1 + * 1 ⊼ 0 = 1 + * 1 ⊼ 1 = 0 + * + * There is no good interpretation for a NAND-chain + * + * Commonly written "⊼", "↑", or as AND with the expression over-struck + * + * Properties: + * - Identity singleton + * - Commutative (however A ⊼ B ⊼ C ≠ A ⊼ C ⊼ B due to non-associativity) + * - Left-associative: A ⊼ B ⊼ C = (A ⊼ B) ⊼ C ≠ A ⊼ (B ⊼ C) + * - Antireflexive: A ⊼ A = ⊥ + * - Annihilated by ⊥: A ⊼ ⊥ = ⊤ + * - Complemented by ⊤ + * - Distributative over AND + * - A ⊼ B is reduced to ¬A ∨ ¬B in negation normal form + * + * @param xs `NULL`-terminated list of sentences that should be + * joined by the connective NAND; this can be any positive + * number of arguments; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * @throws EDOM `xs` contains no term (but only `NULL`) + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_NAND + * @seealso libnormalform_nand_checked + * @seealso libnormalform_nandl_checked + * @seealso libnormalform_vnand_checked + * @seealso libnormalform_nandl + * @seealso libnormalform_vnand + * @seealso libnormalform_nand2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_nand)(LIBNORMALFORM_SENTENCE **xs); + + +/** + * NOR: Joint denial + * + * Also known as Non-disjunction, Negated disjunction, Peirce arrow, + * Quine dagger, or Webb operator + * + * Creates a left-associated NOR-chain; in NOR, the + * two conditions must be unmet at same time + * + * 0 ⊽ 0 = 1 + * 0 ⊽ 1 = 0 + * 1 ⊽ 0 = 0 + * 1 ⊽ 1 = 0 + * + * There is no good interpretation for a NOR-chain + * + * Commonly written "⊽", "↓", or as OR with the expression over-struck + * + * Properties: + * - Identity singleton + * - Commutative (however A ⊽ B ⊽ C ≠ A ⊽ C ⊽ B due to non-associativity) + * - Left-associative: A ⊽ B ⊽ C = (A ⊽ B) ⊽ C ≠ A ⊽ (B ⊽ C) + * - Anti-idempotent: A ⊽ A = ¬A + * - Annihilated by ⊤: A ⊽ ⊤ = ⊥ + * - Complemented by ⊤ + * - Distributing over AND changes AND to OR + * - Distributing over OR changes OR to AND + * - A ⊽ B is reduced to ¬A ∧ ¬B in negation normal form + * + * @param xs `NULL`-terminated list of sentences that should be + * joined by the connective NOR; this can be any positive + * number of arguments; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * @throws EDOM `xs` contains no term (but only `NULL`) + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_NOR + * @seealso libnormalform_nor_checked + * @seealso libnormalform_norl_checked + * @seealso libnormalform_vnor_checked + * @seealso libnormalform_norl + * @seealso libnormalform_vnor + * @seealso libnormalform_nor2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_nor)(LIBNORMALFORM_SENTENCE **xs); + + +/** + * XNOR: Logical bicondition + * + * Also known as ENOR, EXNOR, NXOR, XAND, EQ, IFF, Exclusive NOR, + * Material biconditional, Logical equivalence, Biimplication, + * Bientailment, If and only if, Negated XOR, or Exclusive non-disjunction + * + * Creates a left-associated XNOR-chain; in XNOR, the + * two conditions must equally true + * + * 0 ⊙ 0 = 1 + * 0 ⊙ 1 = 0 + * 1 ⊙ 0 = 0 + * 1 ⊙ 1 = 1 + * + * The interpretation of a XNOR-chain depends on it's length, + * if it's length is even, is equivalent to XOR, otherwise, + * it's its complement + * + * Commonly written "⊙" ("⨀" if n-ary), "↔", "⇔", "≡", "==", or + * as XOR with the expression over-struck + * + * Properties: + * - Vacuous truth + * - Identity singleton + * - Commutative + * - Associative + * - ⊤ as identity + * - Complemented by ⊥ + * - Reflexive: A ⊙ A = ⊤ + * - Annihilated by ⊤: A ⊽ ⊤ = ⊥ + * - Connecting complement results in contradiction + * - A ⊙ B is reduced to (A ∨ ¬B) ∧ (¬A ∨ B) or + * (A ∧ B) ∨ (¬A ∧ ¬B) in negation normal form + * + * @param xs `NULL`-terminated list of sentences that should + * be joined by the connective XNOR; this can be any + * number of arguments; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_NXOR + * @seealso libnormalform_nxor_checked + * @seealso libnormalform_nxorl_checked + * @seealso libnormalform_vxnor_checked + * @seealso libnormalform_xnorl + * @seealso libnormalform_vxnor + * @seealso libnormalform_xnor2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_xnor)(LIBNORMALFORM_SENTENCE **xs); + + +/** + * NIF: Converse nonimplication + * + * Also known as NOT..BUT, Converse abjunction + * + * Creates a chain where any condition must be less + * satisfied than the condition left to it (xs[0] > xs[1]) + * + * 0 ↚ 0 = 0 + * 0 ↚ 1 = 1 + * 1 ↚ 0 = 0 + * 1 ↚ 1 = 0 + * + * Properties: + * - Vacuous falsity + * - Identity singleton + * - Non-commutative + * - Left-associative: A ↚ B ↚ C = (A ↚ B) ↚ C ≠ A ↚ (B ↚ C) + * - Complemented by right ⊤ + * - Dominated by right ⊥ + * - Left truth results in contradiction + * - ⊥ as left identity + * - Contrapositivity: A ↚ B = ¬B ↚ ¬A + * - Antireflexive: A ↚ A = ⊥ + * - Left-distributive: A ↚ (B ↚ C) = (A ↚ B) ↚ (A ↚ C) + * - Distributive over right AND + * - Distributive over right OR + * - Distributing over left AND changes AND to OR + * - Distributing over left OR changes OR to AND + * - A ↚ B is reduced to ¬A ∧ B in negation normal form + * + * Commonly written "↚", "⇍", "!<-", "<-~", "</-", "!<=", "<=~", "</=", "⊄", "⭉" (or "←̃"), or ">" + * + * @param xs `NULL`-terminated list of sentences that should be + * joined by the connective NIF; this can be any number + * of arguments, even none; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_NIF + * @seealso libnormalform_nif_checked + * @seealso libnormalform_nifl_checked + * @seealso libnormalform_vnif_checked + * @seealso libnormalform_nifl + * @seealso libnormalform_vnif + * @seealso libnormalform_nif2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_nif)(LIBNORMALFORM_SENTENCE **xs); + + +/** + * NIMPLY: Material nonimplication + * + * Also known as NIMPLIES, 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]) + * + * 0 ↛ 0 = 0 + * 0 ↛ 1 = 0 + * 1 ↛ 0 = 1 + * 1 ↛ 1 = 0 + * + * Properties: + * - Identity singleton + * - Non-commutative + * - Right-associative: A ↛ B ↛ C = A ↛ (B ↛ C) ≠ (A ↛ B) → C + * - Complemented by left ⊤ + * - Dominated by left ⊥ + * - Right truth results in contradiction + * - ⊥ as right identity + * - Contrapositivity: A ↛ B = ¬B ↛ ¬A + * - Antireflexive: A ↛ A = ⊥ + * - Right-distributive: (A ↛ B) ↛ C = (A ↛ C) ↛ (B → C) + * - Distributive over left AND + * - Distributive over left OR + * - Distributing over right AND changes AND to OR + * - Distributing over right OR changes OR to AND + * - A ↛ B is reduced to A ∧ ¬B in negation normal form + * + * Commonly written "↛", "⇏", "!->", "->~", "-/>", "!=>", "=>~", "=/>", "⊅", "⥲" (or "→̃"), or "<" + * + * @param xs `NULL`-terminated list of sentences that should be + * joined by the connective NIMPLY; this can be any positive + * number of arguments; ownership of object in the array + * (but not the array itself) is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * @throws EDOM `xs` contains no term (but only `NULL`) + * + * Ownership is always transfered, even on failure + * + * @seealso LIBNORMALFORM_NIMPLY + * @seealso libnormalform_nimply_checked + * @seealso libnormalform_nimplyl_checked + * @seealso libnormalform_vnimply_checked + * @seealso libnormalform_nimplyl + * @seealso libnormalform_vnimply + * @seealso libnormalform_nimply2 + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +LIBNORMALFORM_SENTENCE *(libnormalform_nimply)(LIBNORMALFORM_SENTENCE **xs); + + + + + +/** + * THERE EXISTS: Existential quantification (unmapped) + * + * The predicate must be met for at least one element; + * if no element exists, the sentence is false + * + * Commonly written "∃" ("∃x∈d.k", "∃x∈d k(x)", or "∃ x∈d : k(x)") + * + * The predicate is tested on `.key` in each element + * in the domain, and `.value` is unused + * + * @param d The domain of interest; ownership is retained by the caller + * @param k The predicate; ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `k` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `k` to + * be `NULL`) + * + * @seealso libnormalform_any + * @seealso libnormalform_existentially + * @seealso libnormalform_nexists + * @seealso libnormalform_unique + * @seealso libnormalform_nonempty + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_exists)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *k) +{ return libnormalform_any(d, k, libnormalform_true()); } + + +/** + * THERE EXISTS NO: Negated existential quantification (unmapped) + * + * The predicate must be unmet for every element; + * if no element exists, the sentence is true + * + * Commonly written "∄" ("∄x∈d.k", "∄x∈d k(x)", or "∄ x∈d : k(x)"), + * "~∃", "¬∃", or as THERE EXISTS with overline + * + * The predicate is tested on `.key` in each element + * in the domain, and `.value` is unused + * + * @param d The domain of interest; ownership is retained by the caller + * @param k The predicate; ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `k` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `k` to + * be `NULL`) + * + * @seealso libnormalform_all + * @seealso libnormalform_exists + * @seealso libnormalform_empty + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nexists)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *k) +{ return libnormalform_all(d, k, libnormalform_false()); } + + +/** + * THERE EXISTS ONE: Unique existential quantification (unmapped) + * + * The predicate must be met for exactly one element; + * if no element exists, the sentence is false + * + * Commonly written "∃!" ("∃!x∈d.k", "∃!x∈d k(x)", or "∃! x∈d : k(x)") + * + * The predicate is tested on `.key` in each element + * in the domain, and `.value` is unused + * + * @param d The domain of interest; ownership is retained by the caller + * @param k The predicate; ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `k` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `k` to + * be `NULL`) + * + * @seealso libnormalform_one + * @seealso libnormalform_uniquely + * @seealso libnormalform_exists + * @seealso libnormalform_singleton + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_unique)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *k) +{ return libnormalform_one(d, k, libnormalform_true()); } + + + + + +/** + * SOMEWHERE: Existential quantification (premapped) + * + * The predicate must be met for at least one element; + * if no element exists, the sentence is false + * + * Commonly written "∃" ("∃x∈d.v", "∃x∈d v(x)", or "v(x) ∃ x∈d") + * + * The predicate is tested on `.value` in each element + * in the domain, and `.key` is unused + * + * @param d The domain of interest; ownership is retained by the caller + * @param v The predicate; ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `v` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `v` to + * be `NULL`) + * + * @seealso libnormalform_any + * @seealso libnormalform_exists + * @seealso libnormalform_universally + * @seealso libnormalform_uniquely + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_existentially)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *v) +{ return libnormalform_any(d, libnormalform_true(), v); } + + +/** + * EVERYWHERE: Universal quantification (premapped) + * + * The predicate must be unmet for every element; + * if no element exists, the sentence is true + * + * Commonly written "∀" ("∀x∈d.v", "∀x∈d v(x)", or "v(x) ∀ x∈d") + * + * The predicate is tested on `.value` in each element + * in the domain, and `.key` is unused + * + * @param d The domain of interest; ownership is retained by the caller + * @param v The predicate; ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `v` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `v` to + * be `NULL`) + * + * @seealso libnormalform_all + * @seealso libnormalform_existentially + * @seealso libnormalform_uniquely + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_universally)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *v) +{ return libnormalform_all(d, libnormalform_true(), v); } + + +/** + * ONEWHERE: Unique existential quantification (premapped) + * + * The predicate must be met for exactly one element; + * if no element exists, the sentence is false + * + * Commonly written "∃!" ("∃!x∈d.v", "∃!x∈d v(x)", or "v(x) ∃! x∈d") + * + * The predicate is tested on `.value` in each element + * in the domain, and `.key` is unused + * + * @param d The domain of interest; ownership is retained by the caller + * @param v The predicate; ownership is transfered to this function + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * Ownership is always transfered, even on failure + * + * If `v` is `NULL`, this function will return `NULL` + * (indicating failure) without modified `errno` (expecting + * it to be set by a function that failed, causing `v` to + * be `NULL`) + * + * @seealso libnormalform_one + * @seealso libnormalform_unique + * @seealso libnormalform_existentially + * @seealso libnormalform_universially + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_uniquely)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *v) +{ return libnormalform_one(d, libnormalform_true(), v); } + + + + + +/** + * Create a sentence that checks whether a set is empty + * + * The sentence technically classifies as a qualification + * + * Commonly written "X=∅" or "|X|=0" + * + * @param d The set; ownership is retained by the caller + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * @seealso libnormalform_nonempty + * @seealso libnormalform_singleton + * @seealso libnormalform_nexists + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_empty)(struct libnormalform_map *d) +{ return libnormalform_all(d, libnormalform_true(), libnormalform_false()); } + + +/** + * Create a sentence that checks whether a set is non-empty + * + * The sentence technically classifies as a qualification + * + * Commonly written "X≠∅", "X⊃∅", "X⊋∅" "|X|≠0", or "|X|>0" + * + * @param d The set; ownership is retained by the caller + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * @seealso libnormalform_empty + * @seealso libnormalform_singleton + * @seealso libnormalform_exists + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nonempty)(struct libnormalform_map *d) +{ return libnormalform_any(d, libnormalform_true(), libnormalform_true()); } + + +/** + * Create a sentence that checks whether a set is a singleton + * (contains exactly one element) + * + * The sentence technically classifies as a qualification + * + * Commonly written "|X|=1", "∃! x : X={x}", "X={x} ∃! x", or just "X={x}" + * + * @param d The set; ownership is retained by the caller + * @return The constructed sentence, `NULL` on failure + * + * @throws ENOMEM Insufficient memory available to create the sentence + * + * @seealso libnormalform_empty + * @seealso libnormalform_nonempty + * @seealso libnormalform_unique + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_singleton)(struct libnormalform_map *d) +{ return libnormalform_one(d, libnormalform_true(), libnormalform_true()); } + + + + + +/** + * Variant of `libnormalform_and` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_and` + * @return See `libnormalform_and` + * @throws See `libnormalform_and` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_and_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(and) } + + +/** + * Variant of `libnormalform_or` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_or` + * @return See `libnormalform_or` + * @throws See `libnormalform_or` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_or_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(or) } + + +/** + * Variant of `libnormalform_xor` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_xor` + * @return See `libnormalform_xor` + * @throws See `libnormalform_xor` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_xor_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(xor) } + + +/** + * Variant of `libnormalform_if` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_if` + * @return See `libnormalform_if` + * @throws See `libnormalform_if` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_if_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(if) } + + +/** + * Variant of `libnormalform_imply` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_imply` + * @return See `libnormalform_imply` + * @throws See `libnormalform_imply` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_imply_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(imply) } + + +/** + * Variant of `libnormalform_nand` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_nand` + * @return See `libnormalform_nand` + * @throws See `libnormalform_nand` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nand_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(nand) } + + +/** + * Variant of `libnormalform_nor` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_nor` + * @return See `libnormalform_nor` + * @throws See `libnormalform_nor` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nor_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(nor) } + + +/** + * Variant of `libnormalform_xnor` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_xnor` + * @return See `libnormalform_xnor` + * @throws See `libnormalform_xnor` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_xnor_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(xnor) } + + +/** + * Variant of `libnormalform_nif` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_nif` + * @return See `libnormalform_nif` + * @throws See `libnormalform_nif` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nif_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(nif) } + + +/** + * Variant of `libnormalform_nimply` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param xs See `libnormalform_nimply` + * @return See `libnormalform_nimply` + * @throws See `libnormalform_nimply` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NONNULL_INPUT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nimply_checked)(size_t n, LIBNORMALFORM_SENTENCE **xs) +{ LIBNORMALFORM_CHECKED__(nimply) } + + + + + +/** + * Variant of `libnormalform_andl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_andl` + * @throws See `libnormalform_andl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vand)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(and) } + + +/** + * Variant of `libnormalform_orl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_orl` + * @throws See `libnormalform_orl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vor)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(or) } + + +/** + * Variant of `libnormalform_xorl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_xorl` + * @throws See `libnormalform_xorl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vxor)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(xor) } + + +/** + * Variant of `libnormalform_ifl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_ifl` + * @throws See `libnormalform_ifl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vif)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(if) } + + +/** + * Variant of `libnormalform_implyl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_implyl` + * @throws See `libnormalform_implyl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vimply)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(imply) } + + +/** + * Variant of `libnormalform_nandl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nandl` + * @throws See `libnormalform_nandl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vnand)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(nand) } + + +/** + * Variant of `libnormalform_norl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_norl` + * @throws See `libnormalform_norl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vnor)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(nor) } + + +/** + * Variant of `libnormalform_xnorl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_xnorl` + * @throws See `libnormalform_xnorl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vxnor)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(xnor) } + + +/** + * Variant of `libnormalform_nifl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nifl` + * @throws See `libnormalform_nifl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vnif)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(nif) } + + +/** + * Variant of `libnormalform_nimplyl` that uses `va_list` + * instead of variadic arguments + * + * @param a, args `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nimplyl` + * @throws See `libnormalform_nimplyl` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vnimply)(LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST__(nimply) } + + + + + +/** + * Variant of `libnormalform_and` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_and` + * @throws See `libnormalform_and` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_andl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(and) } + + +/** + * Variant of `libnormalform_or` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_or` + * @throws See `libnormalform_or` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_orl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(or) } + + +/** + * Variant of `libnormalform_xor` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_xor` + * @throws See `libnormalform_xor` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_xorl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(xor) } + + +/** + * Variant of `libnormalform_if` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_if` + * @throws See `libnormalform_if` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_ifl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(if) } + + +/** + * Variant of `libnormalform_imply` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_imply` + * @throws See `libnormalform_imply` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_implyl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(imply) } + + +/** + * Variant of `libnormalform_nand` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nand` + * @throws See `libnormalform_nand` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nandl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(nand) } + + +/** + * Variant of `libnormalform_nor` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nor` + * @throws See `libnormalform_nor` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_norl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(nor) } + + +/** + * Variant of `libnormalform_xnor` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_xnor` + * @throws See `libnormalform_xnor` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_xnorl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(xnor) } + + +/** + * Variant of `libnormalform_nif` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nif` + * @throws See `libnormalform_nif` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nifl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(nif) } + + +/** + * Variant of `libnormalform_nimply` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nimply` + * @throws See `libnormalform_nimply` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nimplyl)(LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS__(nimply) } + + + + + +/** + * Variant of `libnormalform_vand` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vand` + * @return See `libnormalform_vand` + * @throws See `libnormalform_vand` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vand_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(and) } + + +/** + * Variant of `libnormalform_vor` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vor` + * @return See `libnormalform_vor` + * @throws See `libnormalform_vor` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vor_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(or) } + + +/** + * Variant of `libnormalform_vxor` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vxor` + * @return See `libnormalform_vxor` + * @throws See `libnormalform_vxor` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vxor_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(xor) } + + +/** + * Variant of `libnormalform_vif` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vif` + * @return See `libnormalform_vif` + * @throws See `libnormalform_vif` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vif_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(if) } + + +/** + * Variant of `libnormalform_vimply` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vimply` + * @return See `libnormalform_vimply` + * @throws See `libnormalform_vimply` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vimply_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(imply) } + + +/** + * Variant of `libnormalform_vnand` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vnand` + * @return See `libnormalform_vnand` + * @throws See `libnormalform_vnand` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vnand_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(nand) } + + +/** + * Variant of `libnormalform_vnor` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vnor` + * @return See `libnormalform_vnor` + * @throws See `libnormalform_vnor` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vnor_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(nor) } + + +/** + * Variant of `libnormalform_vxnor` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vxnor` + * @return See `libnormalform_vxnor` + * @throws See `libnormalform_vxnor` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vxnor_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(xnor) } + + +/** + * Variant of `libnormalform_vnif` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vnif` + * @return See `libnormalform_vnif` + * @throws See `libnormalform_vnif` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vnif_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(nif) } + + +/** + * Variant of `libnormalform_vnimply` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, args See `libnormalform_vnimply` + * @return See `libnormalform_vnimply` + * @throws See `libnormalform_vnimply` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_vnimply_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, va_list args) +{ LIBNORMALFORM_VALIST_CHECKED__(nimply) } + + + + + +/** + * Variant of `libnormalform_andl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_andl` + * @return See `libnormalform_andl` + * @throws See `libnormalform_andl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_andl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(and) } + + +/** + * Variant of `libnormalform_orl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_orl` + * @return See `libnormalform_orl` + * @throws See `libnormalform_orl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_orl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(or) } + + +/** + * Variant of `libnormalform_xorl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_xorl` + * @return See `libnormalform_xorl` + * @throws See `libnormalform_xorl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_xorl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(xor) } + + +/** + * Variant of `libnormalform_ifl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_ifl` + * @return See `libnormalform_ifl` + * @throws See `libnormalform_ifl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_ifl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(if) } + + +/** + * Variant of `libnormalform_implyl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_implyl` + * @return See `libnormalform_implyl` + * @throws See `libnormalform_implyl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_implyl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(imply) } + + +/** + * Variant of `libnormalform_nandl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_nandl` + * @return See `libnormalform_nandl` + * @throws See `libnormalform_nandl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nandl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(nand) } + + +/** + * Variant of `libnormalform_norl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_norl` + * @return See `libnormalform_norl` + * @throws See `libnormalform_norl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_norl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(nor) } + + +/** + * Variant of `libnormalform_xnorl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_xnorl` + * @return See `libnormalform_xnorl` + * @throws See `libnormalform_xnorl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_xnorl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(xnor) } + + +/** + * Variant of `libnormalform_nifl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_nifl` + * @return See `libnormalform_nifl` + * @throws See `libnormalform_nifl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nifl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(nif) } + + +/** + * Variant of `libnormalform_nimplyl` that checks + * for unexpected `NULL` input + * + * @param n The number of expected non-`NULL` terms + * @param a, ... See `libnormalform_nimplyl` + * @return See `libnormalform_nimplyl` + * @throws See `libnormalform_nimplyl` + * + * If the function finds any unexpected `NULL` terms, + * it will call `libnormalform_free` on all non-`NULL` terms + * and return `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ LIBNORMALFORM_NULL_LAST__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nimplyl_checked)(size_t n, LIBNORMALFORM_SENTENCE *a, ...) +{ LIBNORMALFORM_ELLIPSIS_CHECKED__(nimply) } + + + + + +/** + * Variant of `libnormalform_and` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_and` + * @throws See `libnormalform_and` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +LIBNORMALFORM_SENTENCE *(libnormalform_and2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r); + + +/** + * Variant of `libnormalform_or` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_or` + * @throws See `libnormalform_or` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +LIBNORMALFORM_SENTENCE *(libnormalform_or2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r); + + +/** + * Variant of `libnormalform_xor` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_xor` + * @throws See `libnormalform_xor` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +LIBNORMALFORM_SENTENCE *(libnormalform_xor2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r); + + +/** + * Variant of `libnormalform_if` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_if` + * @throws See `libnormalform_if` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_if2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ LIBNORMALFORM_TWO__(if) } + + +/** + * Variant of `libnormalform_imply` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_imply` + * @throws See `libnormalform_imply` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_imply2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ LIBNORMALFORM_TWO__(imply) } + + +/** + * Variant of `libnormalform_nand` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_nand` + * @throws See `libnormalform_nand` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nand2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ LIBNORMALFORM_TWO__(nand) } + + +/** + * Variant of `libnormalform_nor` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_nor` + * @throws See `libnormalform_nor` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nor2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ LIBNORMALFORM_TWO__(nor) } + + +/** + * Variant of `libnormalform_xnor` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_xnor` + * @throws See `libnormalform_xnor` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_xnor2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ LIBNORMALFORM_TWO__(xnor) } + + +/** + * Variant of `libnormalform_nif` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_nif` + * @throws See `libnormalform_nif` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nif2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ LIBNORMALFORM_TWO__(nif) } + + +/** + * Variant of `libnormalform_nimply` that takes exactly two terms + * + * @param l The left-hand term + * @param r The right-hand term + * @return See `libnormalform_nimply` + * @throws See `libnormalform_nimply` + * + * If `l` or `r` is `NULL`, the function will call + * `libnormalform_free` on both arguments and return + * `NULL` (indicating failure) without setting + * `errno` (expected to already be set in indicate the + * error that caused one of the terms to be `NULL`) + */ +LIBNORMALFORM_USE_RESULT__ +inline LIBNORMALFORM_SENTENCE * +(libnormalform_nimply2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ LIBNORMALFORM_TWO__(nimply) } + + + + + +/* These are macro versions of function with the same name */ + +/** + * Variant of `libnormalform_and` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_and` + * @throws See `libnormalform_and` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_andl(...) (libnormalform_and((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + +/** + * Variant of `libnormalform_or` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_or` + * @throws See `libnormalform_or` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_orl(...) (libnormalform_or((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + +/** + * Variant of `libnormalform_xor` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_xor` + * @throws See `libnormalform_xor` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_xorl(...) (libnormalform_xor((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + +/** + * Variant of `libnormalform_if` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_if` + * @throws See `libnormalform_if` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_ifl(...) (libnormalform_if((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + +/** + * Variant of `libnormalform_imply` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_imply` + * @throws See `libnormalform_imply` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_implyl(...) (libnormalform_imply((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + +/** + * Variant of `libnormalform_nand` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nand` + * @throws See `libnormalform_nand` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_nandl(...) (libnormalform_nand((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + +/** + * Variant of `libnormalform_nor` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nor` + * @throws See `libnormalform_nor` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_norl(...) (libnormalform_nor((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + +/** + * Variant of `libnormalform_xnor` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_xnor` + * @throws See `libnormalform_xnor` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_xnorl(...) (libnormalform_xnor((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + +/** + * Variant of `libnormalform_nif` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nif` + * @throws See `libnormalform_nif` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_nifl(...) (libnormalform_nif((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + +/** + * Variant of `libnormalform_nimply` that uses variadic arguments + * + * @param a, ... `NULL`-terminated list of terms; the function + * will take ownership of each input reference, + * so the caller shall not attempt to deallocate + * or use them again. + * @return See `libnormalform_nimply` + * @throws See `libnormalform_nimply` + * + * Be careful not in inputing any `NULL` apart from + * the list terminator as the function will not be + * able to see them. + */ +#define libnormalform_nimplyl(...) (libnormalform_nimply((LIBNORMALFORM_SENTENCE *[]){__VA_ARGS__})) + + + + + +/** + * Macro variant of `libnormalform_andl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_andl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_AND(...) LIBNORMALFORM_MACRO__(and, __VA_ARGS__) + + +/** + * Macro variant of `libnormalform_orl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_orl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_OR(...) LIBNORMALFORM_MACRO__(or, __VA_ARGS__) + + +/** + * Macro variant of `libnormalform_xorl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_xorl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_XOR(...) LIBNORMALFORM_MACRO__(xor, __VA_ARGS__) + + +/** + * Macro variant of `libnormalform_ifl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_ifl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_IF(...) LIBNORMALFORM_MACRO__(if, __VA_ARGS__) + + +/** + * Macro variant of `libnormalform_implyl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_implyl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_IMPLY(...) LIBNORMALFORM_MACRO__(imply, __VA_ARGS__) + + +/** + * Macro variant of `libnormalform_nandl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_nandl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_NAND(...) LIBNORMALFORM_MACRO__(nand, __VA_ARGS__) + + +/** + * Macro variant of `libnormalform_norl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_norl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_NOR(...) LIBNORMALFORM_MACRO__(nor, __VA_ARGS__) + + +/** + * Macro variant of `libnormalform_xnorl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_xnorl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_XNOR(...) LIBNORMALFORM_MACRO__(xnor, __VA_ARGS__) + + +/** + * Macro variant of `libnormalform_nifl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_nifl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_NIF(...) LIBNORMALFORM_MACRO__(nif, __VA_ARGS__) + + +/** + * Macro variant of `libnormalform_nimplyl_checked`, that + * will add the `NULL` to the end of the list and, before + * the list, the number of terms in the list + * + * @param ... List of terms, _without_ `NULL`-terinator + * @return See `libnormalform_nimplyl_checked` + * + * Either any argument is `NULL`, `libnormalform_free` on + * will be called for all non-`NULL` terms, the `NULL` + * with be return (indicating failure) without `errno` + * being modified (it is expected to already be set in + * indicate the error that caused one of the terms to be + * `NULL`) + */ +#define LIBNORMALFORM_NIMPLY(...) LIBNORMALFORM_MACRO__(nimply, __VA_ARGS__) + + + + + +#undef LIBNORMALFORM_CHECKED__ +#undef LIBNORMALFORM_VALIST__ +#undef LIBNORMALFORM_ELLIPSIS__ +#undef LIBNORMALFORM_VALIST_CHECKED__ +#undef LIBNORMALFORM_ELLIPSIS_CHECKED__ +#undef LIBNORMALFORM_TWO__ + +#undef LIBNORMALFORM_USE_RESULT__ +#undef LIBNORMALFORM_FREE_WITH__ +#undef LIBNORMALFORM_USE_FREE__ +#undef LIBNORMALFORM_NONNULL_INPUT__ +#undef LIBNORMALFORM_ONE_NONNULL_INPUT__ +#undef LIBNORMALFORM_NULL_LAST__ + + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#endif diff --git a/libnormalform_all.c b/libnormalform_all.c new file mode 100644 index 0000000..1b8d6fc --- /dev/null +++ b/libnormalform_all.c @@ -0,0 +1,452 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (FOR ALL implementation) + */ +static LIBNORMALFORM_SENTENCE * +all_inverse(LIBNORMALFORM_SENTENCE *this) +{ + /* ¬∀x.φ ⇒ ∃x.¬φ */ + LIBNORMALFORM_SENTENCE *ret; + ret = libnormalform_any(this->data.qualifier.domain, + libnormalform_ref(this->data.qualifier.antecedent), + this->data.qualifier.predicate->inverse(this->data.qualifier.predicate)); + if (ret && this->atom) { + ret->atom = this->atom; + ret->atom->refcount += 1; + } + return ret; +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (FOR ALL implementation) + */ +static int +all_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + int r, inv_expect; + + if (this->hash != other->hash) + return 0; + + if (other->type == TYPE_ALL) + inv_expect = 0; + else if (other->type == TYPE_ANY) + inv_expect = 1; + else + return 0; + + if (this->data.qualifier.domain != other->data.qualifier.domain) + return 0; + + r = this->data.qualifier.antecedent->equals(this->data.qualifier.antecedent, other->data.qualifier.antecedent, inv_out); + if (r <= 0) + return r; + if (*inv_out) + return 0; + + r = this->data.qualifier.predicate->equals(this->data.qualifier.predicate, other->data.qualifier.predicate, inv_out); + if (r <= 0) + return r; + if (*inv_out != inv_expect) + return 0; + + return r; +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (FOR ALL implementation) + */ +static int +all_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + size_t i, n = this->data.qualifier.domain->nmappings; + void *k, *v; + int r; + + (void) input; + + for (i = 0; i < n; i++) { + k = this->data.qualifier.domain->mappings[i].key; + v = this->data.qualifier.domain->mappings[i].value; + r = this->data.qualifier.antecedent->evaluate(this->data.qualifier.antecedent, k); + if (r <= 0) { + if (!r) + continue; + return r; + } + r = this->data.qualifier.predicate->evaluate(this->data.qualifier.predicate, v); + if (r <= 0) + return r; + } + + return 1; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_all)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *k, LIBNORMALFORM_SENTENCE *v) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_ALL, + .inverse = &all_inverse, + .equals = &all_equals, + .evaluate = &all_evaluate + }; + + LIBNORMALFORM_SENTENCE *ret; + + if (!k || !v) { + libnormalform_free(k); + libnormalform_free(v); + return NULL; + } + + if (v->type == TYPE_TRUE) { + libnormalform_free(k); + return v; + } + if (k->type == TYPE_FALSE) { + libnormalform_free(k); + libnormalform_free(v); + return libnormalform_true(); + } + + ret = malloc(sizeof(*ret)); + if (ret) { + *ret = prototype; + ret->hash = ANY_ALL_HASH(d, k, v); + ret->data.qualifier.domain = d; + ret->data.qualifier.antecedent = k; + ret->data.qualifier.predicate = v; + } + return ret; +} + + +#else + + +static int +evalbool(void *user_data, void *input) +{ + int *vp = input; + (void) user_data; + return *vp; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2; + struct libnormalform_function fun1; + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[2]; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *c, *v1, *v2; + int t = 1, f = 0; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + + errno = 0; + ASSERT(!libnormalform_all(&dom1, NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!libnormalform_all(&dom1, NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!libnormalform_all(&dom1, REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_all(&dom1, REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!libnormalform_all(&dom1, NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_all(&dom1, NULL, NULL) && errno == 1); + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + /* ∀x.(⊥ → φ(x)) = ⊤ */ + ASSUME(a = libnormalform_all(&dom1, libnormalform_false(), REF(v1))); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* ∀x.(⊤ → φ(x)) irreducable */ + ASSUME(a = libnormalform_all(&dom1, libnormalform_true(), REF(v1))); + ASSERT(a->type == TYPE_ALL); + libnormalform_free(a); + + /* ∀x.(φ(x) → ⊤) = ⊤ */ + ASSUME(a = libnormalform_all(&dom1, REF(v1), libnormalform_true())); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* ∀x.(φ(x) → ⊥) irreducable */ + ASSUME(a = libnormalform_all(&dom1, REF(v1), libnormalform_false())); + ASSERT(a->type == TYPE_ALL); + libnormalform_free(a); + + ASSUME(a = libnormalform_all(&dom1, REF(v1), REF(v2))); + ASSERT(a->type == TYPE_ALL); + ASSERT(a->refcount == 1); + + dom1.nmappings = 1; + + /* ∀x∈{a}.(⊥ → ⊥) = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈{a}.(⊥ → ⊤) = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈{a}.(⊤ → ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{a}.(⊤ → ⊤) = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + + /* ∀x∈{a,b}.(⊥ → ⊥) = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈{a,b}.(⊥ → ⊤) = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈{a,b}.(⊤ → ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{a,b}.(⊤ → ⊤) = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 0; + + /* ∀x∈∅.(⊥ → ⊥) = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈∅.(⊥ → ⊤) = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈∅.(⊤ → ⊥) = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈∅.(⊤ → ⊤) = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 0; + + libnormalform_free(a); + + fun1.evaluate = &evalbool; + dom1.nmappings = 2; + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(a = libnormalform_all(&dom1, libnormalform_true(), a)); + ASSERT(a->type == TYPE_ALL); + map[0].key = NULL; + map[1].key = NULL; + + /* ∀x∈{⊥, ⊥}.(⊤ → x) = ⊥ */ + map[0].value = &f; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{⊥, ⊤}.(⊤ → x) = ⊥ */ + map[0].value = &f; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{⊤, ⊥}.(⊤ → x) = ⊥ */ + map[0].value = &t; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{⊤, ⊤}.(⊤ → x) = ⊤ */ + map[0].value = &t; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + libnormalform_free(a); + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(a = libnormalform_all(&dom1, a, libnormalform_false())); + ASSERT(a->type == TYPE_ALL); + map[0].value = NULL; + map[1].value = NULL; + + /* ∀x∈{⊥, ⊥}.(x → ⊥) = ⊤ */ + map[0].key = &f; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈{⊥, ⊤}.(x → ⊥) = ⊥ */ + map[0].key = &f; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{⊤, ⊥}.(x → ⊥) = ⊥ */ + map[0].key = &t; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{⊤, ⊤}.(x → ⊥) = ⊥ */ + map[0].key = &t; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + libnormalform_free(a); + + ASSUME(a = libnormalform_all(&dom1, REF(v1), REF(v2))); + + /* ∀x∈X.(P(x) → Q(x)) = ∀x∈X.(P(x) → Q(x)) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from ∀x∈X.(Q(x) → P(x)) */ + ASSUME(b = libnormalform_all(&dom1, REF(v2), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from ∀x∈Y.(P(x) → Q(x)) */ + ASSUME(b = libnormalform_all(&dom2, REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) = ¬(∃x∈X.(P(x) → ¬Q(x))) */ + ASSUME(b = libnormalform_any(&dom1, REF(v1), libnormalform_not(REF(v2)))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + /* ¬∀x∈X.(P(x) → Q(x)) = ∃x∈X.(P(x) → ¬Q(x)) */ + ASSUME(c = libnormalform_not(REF(a))); + ASSUME(b = libnormalform_any(&dom1, REF(v1), libnormalform_not(REF(v2)))); + ASSERT_EQUAL(c, b); + ASSERT(c->type == TYPE_ANY); + libnormalform_free(b); + libnormalform_free(c); + + /* ∀x∈X.(P(x) → Q(x)) independent from TRUE */ + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from FALSE */ + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from variable1 */ + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from NOT variable1 */ + ASSUME(b = libnormalform_not(libnormalform_variable(&var1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from function1 */ + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from NOT function1 */ + ASSUME(b = libnormalform_not(libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from T(function1) */ + ASSUME(b = libnormalform_transformation(&trans, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from ∃x∈X.(P(x) → Q(x)) */ + ASSUME(b = libnormalform_any(&dom1, REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from ∃!x∈X.(P(x) → Q(x)) */ + ASSUME(b = libnormalform_one(&dom1, REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + + /* ∀x∈X.(P(x) → Q(x)) independent from ¬∃!x∈X.(P(x) → Q(x)) */ + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from AND (P, Q) */ + ASSUME(b = libnormalform_and2(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from OR (P, Q) */ + ASSUME(b = libnormalform_or2(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.(P(x) → Q(x)) independent from XOR (P, Q) */ + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + 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) */ + ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), libnormalform_true())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + ASSUME(b = libnormalform_true()); + 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) */ + ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), libnormalform_not(REF(v1)))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + libnormalform_free(v1); + libnormalform_free(v2); + + /* cascading of evaluation failure is tested in libnormalform_function.c */ + + TEST_END; +} + + +#endif diff --git a/libnormalform_and.c b/libnormalform_and.c new file mode 100644 index 0000000..d94836d --- /dev/null +++ b/libnormalform_and.c @@ -0,0 +1,578 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * Check if a sentence equivalent to a specific sentence + * or its inverse is in an array of sentences + * + * @param x The sentence to look for + * @param among The array of sentences to look in + * @param n The number of sentences in `among` + * @param inv_out Output parameter for equivalency of `x` and the + * sentence found in `among`; set to 0 if the two + * are equivalent or 1 if `x` is equivalent to the + * inverse of the sentence found in `among` + * @return 1 if a sentence equivalent to `x` or its inverse + * was found, 0 otherwise + */ +static int +is_in(LIBNORMALFORM_SENTENCE *x, LIBNORMALFORM_SENTENCE **among, size_t n, int *inv_out) +{ + while (n--) + if (x->equals(x, among[n], inv_out)) + return 1; + return 0; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_and)(LIBNORMALFORM_SENTENCE **xs) +{ + LIBNORMALFORM_SENTENCE *x, *ret, **saved = xs; + size_t n = 0; + int inv; + + for (; (x = *xs); xs++) { + if (x->type == TYPE_FALSE) { + /* ⋀X ∧ 0 = 0 */ + ret = *xs++; + goto return_asis; + + } else if (x->type == TYPE_TRUE) { + redundant: + /* ⋀X ∧ 1 = ⋀X */ + libnormalform_free(x); + + } else if (is_in(x, saved, n, &inv)) { + if (!inv) { + /* x ∧ x = x */ + goto redundant; + + } else { + /* x ∧ ¬x = 0 */ + libnormalform_free(*xs++); + ret = libnormalform_false(); + goto return_asis; + } + + } else { + saved[n++] = x; + } + } + + if (!n) { + /* ⋀∅ = ∀x∈∅.x = {vacuous truth} = 1 */ + return libnormalform_true(); + } + + saved[n] = NULL; + /* ⋀{x} = x */ + ret = *saved++; + /* ⋀(X ∪ x) = ⋀X ∧ x */ + for (; *saved; saved++) + ret = libnormalform_and2(ret, *saved); + + return ret; + +return_asis: + while (*xs) + libnormalform_free(*xs++); + while (n) + libnormalform_free(saved[--n]); + return ret; +} + + +#else + + +#define AND(...) LIBNORMALFORM_AND(__VA_ARGS__) + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3, var4; + struct libnormalform_function fun1, fun2; + struct libnormalform_map domain; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3, *v4, *f1, *f2; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(v4 = libnormalform_variable(&var4)); + ASSUME(f1 = libnormalform_function(&fun1)); + ASSUME(f2 = libnormalform_function(&fun2)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!AND(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!AND(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!AND(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!AND(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!AND(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!AND(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = AND(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = AND(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL3(VALUE, V1, V2, V3)\ + do {\ + ASSUME(a = AND(REF(v1), REF(v2), REF(v3)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + var3.value = V3##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + ASSERT_CONST(TRUE); /* AND () -> TRUE */ + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* AND (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* AND (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(FALSE, F, F); /* AND (FALSE, FALSE) -> FALSE */ + ASSERT_CONST(FALSE, F, T); /* AND (FALSE, TRUE) -> FALSE */ + ASSERT_CONST(FALSE, T, F); /* AND (TRUE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, T, T); /* AND (TRUE, TRUE) -> TRUE */ + + ASSERT_EVAL2(FALSE, F, F); /* AND (var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(FALSE, F, T); /* AND (var(FALSE), var(TRUE)) => FALSE */ + ASSERT_EVAL2(FALSE, T, F); /* AND (var(TRUE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(TRUE, T, T); /* AND (var(TRUE), var(TRUE)) => TRUE */ + +#ifndef USE_TWO + + ASSERT_CONST(FALSE, F, F, F); /* AND (FALSE, FALSE, FALSE) -> FALSE */ + ASSERT_CONST(FALSE, F, F, T); /* AND (FALSE, FALSE, TRUE) -> FALSE */ + ASSERT_CONST(FALSE, F, T, F); /* AND (FALSE, TRUE, FALSE) -> FALSE */ + ASSERT_CONST(FALSE, F, T, T); /* AND (FALSE, TRUE, TRUE) -> FALSE */ + ASSERT_CONST(FALSE, T, F, F); /* AND (TRUE, FALSE, FALSE) -> FALSE */ + ASSERT_CONST(FALSE, T, F, T); /* AND (TRUE, FALSE, TRUE) -> FALSE */ + ASSERT_CONST(FALSE, T, T, F); /* AND (TRUE, TRUE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, T, T, T); /* AND (TRUE, TRUE, TRUE) -> TRUE */ + + ASSERT_EVAL3(FALSE, F, F, F); /* AND (var(FALSE), var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL3(FALSE, F, F, T); /* AND (var(FALSE), var(FALSE), var(TRUE)) => FALSE */ + ASSERT_EVAL3(FALSE, F, T, F); /* AND (var(FALSE), var(TRUE), var(FALSE)) => FALSE */ + ASSERT_EVAL3(FALSE, F, T, T); /* AND (var(FALSE), var(TRUE), var(TRUE)) => FALSE */ + ASSERT_EVAL3(FALSE, T, F, F); /* AND (var(TRUE), var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL3(FALSE, T, F, T); /* AND (var(TRUE), var(FALSE), var(TRUE)) => FALSE */ + ASSERT_EVAL3(FALSE, T, T, F); /* AND (var(TRUE), var(TRUE), var(FALSE)) => FALSE */ + ASSERT_EVAL3(TRUE, T, T, T); /* AND (var(TRUE), var(TRUE), var(TRUE)) => TRUE */ + + /* AND (x) -> x */ + ASSUME(a = AND(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* AND (x, FALSE) -> FALSE */ + ASSUME(a = AND(REF(v1), F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (FALSE, x) -> FALSE */ + ASSUME(a = AND(F, REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (x, TRUE) -> x */ + ASSUME(a = AND(REF(v1), T)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* AND (TRUE, x) -> x */ + ASSUME(a = AND(T, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + +#ifndef USE_TWO + + /* AND (x, FALSE, FALSE) -> FALSE */ + ASSUME(a = AND(REF(v1), F, F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (x, FALSE, TRUE) -> FALSE x */ + ASSUME(a = AND(REF(v1), F, T)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (x, TRUE, FALSE) -> FALSE */ + ASSUME(a = AND(REF(v1), T, F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (x, TRUE, TRUE) -> x */ + ASSUME(a = AND(REF(v1), T, T)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* AND (FALSE, x, FALSE) -> FALSE */ + ASSUME(a = AND(F, REF(v1), F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (FALSE, x, TRUE) -> FALSE */ + ASSUME(a = AND(F, REF(v1), T)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (TRUE, x, FALSE) -> FALSE */ + ASSUME(a = AND(T, REF(v1), F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (TRUE, x, TRUE) -> x */ + ASSUME(a = AND(T, REF(v1), T)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* AND (FALSE, FALSE, x) -> FALSE */ + ASSUME(a = AND(F, F, REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (FALSE, TRUE, x) -> FALSE */ + ASSUME(a = AND(F, T, REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (TRUE, FALSE, x) -> FALSE */ + ASSUME(a = AND(T, F, REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (TRUE, TRUE, x) -> x */ + ASSUME(a = AND(T, T, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + +#endif + + /* AND (x, x) -> x */ + ASSUME(a = AND(REF(v1), REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* AND (x, NOT x) -> FALSE */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = AND(REF(v1), a)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* AND (x, y) -> AND (x, y) */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = AND(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (y, x) -> AND (x, y) */ + ASSUME(a = AND(REF(v2), REF(v1))); + ASSUME(b = AND(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* NOT AND (x, y) -> OR (NOT x, NOT y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(a = libnormalform_not(a)); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from AND (x, z) */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_and2(REF(v1), REF(v3))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from AND (z, x) */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_and2(REF(v3), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from AND (z, w) */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_and2(REF(v3), REF(v4))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from OR (x, y) */ + ASSUME(a = REF(v1)); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = AND(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from or (x, NOT y) */ + ASSUME(a = REF(v1)); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = AND(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from OR (NOT x, y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = AND(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from XOR (x, y) */ + ASSUME(a = REF(v1)); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_xor2(a, b)); + ASSUME(a = AND(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from XOR (x, NOT y) */ + ASSUME(a = REF(v1)); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_xor2(a, b)); + ASSUME(a = AND(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from XOR (NOT x, y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_xor2(a, b)); + ASSUME(a = AND(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from XOR (NOT x, NOT y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_xor2(a, b)); + ASSUME(a = AND(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from TRUE */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from FALSE */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from variable1 */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, v1); + libnormalform_free(a); + + /* AND (x, y) -> independence from NOT variable1 */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from function1 */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, f1); + libnormalform_free(a); + + /* AND (x, y) -> independence from NOT function1 */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_not(REF(f1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from T(function1) */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_transformation(&trans, REF(f1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from ALL (domain1, function1, function2) */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_all(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from ANY (domain1, function1, function2) */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_any(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from ONE (domain1, function1, function2) */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_one(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (x, y) -> independence from NOT ONE (domain1, function1, function2) */ + ASSUME(a = AND(REF(v1), REF(v2))); + ASSUME(b = libnormalform_one(&domain, REF(f1), REF(f2))); + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (OR (x, NOT y), OR (NOT x, y)) -> NOT XOR (x, y) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(a = libnormalform_or2(REF(v1), a)); + ASSUME(b = libnormalform_or2(b, REF(v2))); + ASSUME(a = AND(a, b)); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + ASSERT(a->type == TYPE_XOR); + ASSERT(a->type == TYPE_XOR); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (OR (NOT x, y), OR (x, NOT y)) -> NOT XOR (x, y) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(a = libnormalform_or2(REF(v1), a)); + ASSUME(b = libnormalform_or2(b, REF(v2))); + ASSUME(a = AND(b, a)); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + ASSERT(a->type == TYPE_XOR); + ASSERT(a->type == TYPE_XOR); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (OR (x, y), OR (NOT x, NOT y)) -> XOR (x, y) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = libnormalform_or2(REF(v1), REF(v2))); + ASSUME(a = AND(a, b)); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + ASSERT(a->type == TYPE_XOR); + ASSERT(a->type == TYPE_XOR); + libnormalform_free(a); + libnormalform_free(b); + + /* AND (OR (NOT x, NOT y), OR (x, y)) -> XOR (x, y) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = libnormalform_or2(REF(v1), REF(v2))); + ASSUME(a = AND(b, a)); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + ASSERT(a->type == TYPE_XOR); + ASSERT(a->type == TYPE_XOR); + libnormalform_free(a); + libnormalform_free(b); + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 +#undef ASSERT_EVAL3 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(v4); + libnormalform_free(f1); + libnormalform_free(f2); + libnormalform_free(ts); + libnormalform_free(fs); + + /* cascading of evaluation failure is tested in libnormalform_function.c */ + + TEST_END; +} + + +#endif diff --git a/libnormalform_and2.c b/libnormalform_and2.c new file mode 100644 index 0000000..62d8db3 --- /dev/null +++ b/libnormalform_and2.c @@ -0,0 +1,82 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +LIBNORMALFORM_SENTENCE * +(libnormalform_and2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ + LIBNORMALFORM_SENTENCE *ll, *lr; + LIBNORMALFORM_SENTENCE *rl, *rr; + int inv; + + if (!l || !r) { + libnormalform_free(l); + libnormalform_free(r); + return NULL; + } + + if (l->equals(l, r, &inv)) { + if (!inv) { + /* x ∧ x = x */ + goto return_either; + + } else { + /* x ∧ ¬x = 0 */ + libnormalform_free(l); + libnormalform_free(r); + return libnormalform_false(); + } + + } else if (l->type == TYPE_FALSE || r->type == TYPE_TRUE) { + /* 0 ∧ x = 0 */ + /* x ∧ 1 = x */ + return_either: + libnormalform_free(r); + return l; + + } else if (r->type == TYPE_FALSE || l->type == TYPE_TRUE) { + /* x ∧ 0 = 0 */ + /* 1 ∧ x = x */ + libnormalform_free(l); + return r; + + } else if (l->hash == r->hash && l->type == TYPE_OR && r->type == TYPE_OR) { + /* (x ∨ y) ∧ (¬x ∨ ¬y) = (x ∨ y) ∧ ¬(x ∧ y) = x ⊕ y */ + ll = l->data.binary.l; + lr = l->data.binary.r; + rl = r->data.binary.l; + rr = r->data.binary.r; + if (ll->hash != rl->hash || lr->hash != rr->hash) + goto fallback; + if (!ll->equals(ll, rl, &inv)) { + if (ll->hash == lr->hash) { + rl = r->data.binary.r; + rr = r->data.binary.l; + if (!ll->equals(ll, rl, &inv)) + goto fallback; + } else { + goto fallback; + } + } + if (!inv || !lr->equals(lr, rr, &inv) || !inv) + goto fallback; + l->data.binary.l = NULL; + l->data.binary.r = NULL; + libnormalform_free(l); + libnormalform_free(r); + return libnormalform_xor2__(ll, lr); + + } else { + fallback: + return libnormalform_and2__(l, r); + } +} + + +#else + +#define USE_TWO +#include "libnormalform_and.c" + +#endif diff --git a/libnormalform_and2__.c b/libnormalform_and2__.c new file mode 100644 index 0000000..c20eb3e --- /dev/null +++ b/libnormalform_and2__.c @@ -0,0 +1,119 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (AND implementation) + */ +static LIBNORMALFORM_SENTENCE * +and_inverse(LIBNORMALFORM_SENTENCE *this) +{ + /* ¬(ab) = ¬a + ¬b */ + LIBNORMALFORM_SENTENCE *l, *r, *ret; + l = this->data.binary.l->inverse(this->data.binary.l); + if (!l) + return NULL; + r = this->data.binary.r->inverse(this->data.binary.r); + if (!r) { + libnormalform_free(l); + return NULL; + } + ret = libnormalform_or2__(l, r); + if (ret && this->atom) { + ret->atom = this->atom; + ret->atom->refcount += 1; + } + return ret; +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (AND implementation) + */ +static int +and_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + LIBNORMALFORM_SENTENCE *tl, *tr; + LIBNORMALFORM_SENTENCE *ol, *or; + int inv; + + if (other->type != TYPE_AND && other->type != TYPE_OR) + return other->type == TYPE_XOR && other->equals(other, this, inv_out); + + tl = this->data.binary.l; + tr = this->data.binary.r; + ol = other->data.binary.l; + or = other->data.binary.r; + + if (tl->hash != ol->hash || tr->hash != or->hash) + return 0; + + if (!tl->equals(tl, ol, inv_out)) { + if (tl->hash == tr->hash) { + ol = other->data.binary.r; + or = other->data.binary.l; + if (!tl->equals(tl, ol, inv_out)) + return 0; + } else { + return 0; + } + } + if (!tr->equals(tr, or, &inv)) + return 0; + return inv == *inv_out && inv == (other->type == TYPE_OR); +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (AND implementation) + */ +static int +and_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + int r = this->data.binary.l->evaluate(this->data.binary.l, input); + return r <= 0 ? r : this->data.binary.r->evaluate(this->data.binary.r, input); +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_and2__)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_AND, + .inverse = &and_inverse, + .equals = &and_equals, + .evaluate = &and_evaluate + }; + + LIBNORMALFORM_SENTENCE *ret = malloc(sizeof(*ret)); + if (!ret) { + libnormalform_free(l); + libnormalform_free(r); + return NULL; + } + *ret = prototype; + ret->hash = AND_OR_HASH(l, r); + if (l->hash <= r->hash) { + ret->data.binary.l = l; + ret->data.binary.r = r; + } else { + ret->data.binary.l = r; + ret->data.binary.r = l; + } + return ret; +} + + +#else + + +CONST int +main(void) +{ + return 0; /* indirectly tested */ +} + + +#endif diff --git a/libnormalform_and_checked.c b/libnormalform_and_checked.c new file mode 100644 index 0000000..5a1b1d2 --- /dev/null +++ b/libnormalform_and_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_and_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_and.c" + +#endif diff --git a/libnormalform_andl.c b/libnormalform_andl.c new file mode 100644 index 0000000..7388efb --- /dev/null +++ b/libnormalform_andl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_andl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_and.c" + +#endif diff --git a/libnormalform_andl_checked.c b/libnormalform_andl_checked.c new file mode 100644 index 0000000..aa72709 --- /dev/null +++ b/libnormalform_andl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_andl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_and.c" + +#endif diff --git a/libnormalform_andl_macro_test.c b/libnormalform_andl_macro_test.c new file mode 100644 index 0000000..63a40c8 --- /dev/null +++ b/libnormalform_andl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_and.c" +#endif diff --git a/libnormalform_any.c b/libnormalform_any.c new file mode 100644 index 0000000..7e69210 --- /dev/null +++ b/libnormalform_any.c @@ -0,0 +1,451 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (FOR ANY implementation) + */ +static LIBNORMALFORM_SENTENCE * +any_inverse(LIBNORMALFORM_SENTENCE *this) +{ + /* ¬∃x.φ ⇒ ∀x.¬φ */ + LIBNORMALFORM_SENTENCE *ret; + ret = libnormalform_all(this->data.qualifier.domain, + libnormalform_ref(this->data.qualifier.antecedent), + this->data.qualifier.predicate->inverse(this->data.qualifier.predicate)); + if (ret && this->atom) { + ret->atom = this->atom; + ret->atom->refcount += 1; + } + return ret; +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (FOR ANY implementation) + */ +static int +any_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + int r, inv_expect; + + if (this->hash != other->hash) + return 0; + + if (other->type == TYPE_ANY) + inv_expect = 0; + else if (other->type == TYPE_ALL) + inv_expect = 1; + else + return 0; + + if (this->data.qualifier.domain != other->data.qualifier.domain) + return 0; + + r = this->data.qualifier.antecedent->equals(this->data.qualifier.antecedent, other->data.qualifier.antecedent, inv_out); + if (r <= 0) + return r; + if (*inv_out) + return 0; + + r = this->data.qualifier.predicate->equals(this->data.qualifier.predicate, other->data.qualifier.predicate, inv_out); + if (r <= 0) + return r; + if (*inv_out != inv_expect) + return 0; + + return r; +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (FOR ANY implementation) + */ +static int +any_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + size_t i, n = this->data.qualifier.domain->nmappings; + void *k, *v; + int r; + + (void) input; + + for (i = 0; i < n; i++) { + k = this->data.qualifier.domain->mappings[i].key; + v = this->data.qualifier.domain->mappings[i].value; + r = this->data.qualifier.antecedent->evaluate(this->data.qualifier.antecedent, k); + if (r <= 0) { + if (!r) + continue; + return r; + } + r = this->data.qualifier.predicate->evaluate(this->data.qualifier.predicate, v); + if (r) + return r; + } + + return 0; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_any)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *k, LIBNORMALFORM_SENTENCE *v) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_ANY, + .inverse = &any_inverse, + .equals = &any_equals, + .evaluate = &any_evaluate + }; + + LIBNORMALFORM_SENTENCE *ret; + + if (!k || !v) { + libnormalform_free(k); + libnormalform_free(v); + return NULL; + } + + if (k->type == TYPE_FALSE) { + libnormalform_free(v); + return k; + } + if (v->type == TYPE_FALSE) { + libnormalform_free(k); + return v; + } + + ret = malloc(sizeof(*ret)); + if (ret) { + *ret = prototype; + ret->hash = ANY_ALL_HASH(d, k, v); + ret->data.qualifier.domain = d; + ret->data.qualifier.antecedent = k; + ret->data.qualifier.predicate = v; + } + return ret; +} + + +#else + + +static int +evalbool(void *user_data, void *input) +{ + int *vp = input; + (void) user_data; + return *vp; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2; + struct libnormalform_function fun1; + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[2]; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *c, *v1, *v2; + int t = 1, f = 0; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + + errno = 0; + ASSERT(!libnormalform_any(&dom1, NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!libnormalform_any(&dom1, NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!libnormalform_any(&dom1, REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_any(&dom1, REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!libnormalform_any(&dom1, NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_any(&dom1, NULL, NULL) && errno == 1); + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + /* ∃x.(⊥ ∧ φ(x)) = ⊥ */ + ASSUME(a = libnormalform_any(&dom1, libnormalform_false(), REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* ∃x.(⊤ ∧ φ(x)) irreducable */ + ASSUME(a = libnormalform_any(&dom1, libnormalform_true(), REF(v1))); + ASSERT(a->type == TYPE_ANY); + libnormalform_free(a); + + /* ∃x.(φ(x) ∧ ⊤) irreducable */ + ASSUME(a = libnormalform_any(&dom1, REF(v1), libnormalform_true())); + ASSERT(a->type == TYPE_ANY); + libnormalform_free(a); + + /* ∃x.(φ(x) ∧ ⊥) = ⊥ */ + ASSUME(a = libnormalform_any(&dom1, REF(v1), libnormalform_false())); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + ASSUME(a = libnormalform_any(&dom1, REF(v1), REF(v2))); + ASSERT(a->type == TYPE_ANY); + ASSERT(a->refcount == 1); + + dom1.nmappings = 1; + + /* ∃x∈{a}.(⊥ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a}.(⊥ ∧ ⊤) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a}.(⊤ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a}.(⊤ ∧ ⊤) = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + + /* ∃x∈{a,b}.(⊥ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a,b}.(⊥ ∧ ⊤) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a,b}.(⊤ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a,b}.(⊤ ∧ ⊤) = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 0; + + /* ∃x∈∅.(⊥ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈∅.(⊥ ∧ ⊤) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈∅.(⊤ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈∅.(⊤ ∧ ⊤) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + libnormalform_free(a); + + fun1.evaluate = &evalbool; + dom1.nmappings = 2; + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(a = libnormalform_any(&dom1, libnormalform_true(), a)); + ASSERT(a->type == TYPE_ANY); + map[0].key = NULL; + map[1].key = NULL; + + /* ∃x∈{⊥, ⊥}.(⊤ ∧ x) = ⊥ */ + map[0].value = &f; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{⊥, ⊤}.(⊤ ∧ x) = ⊤ */ + map[0].value = &f; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊥}.(⊤ ∧ x) = ⊤ */ + map[0].value = &t; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊤}.(⊤ ∧ x) = ⊤ */ + map[0].value = &t; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + libnormalform_free(a); + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(a = libnormalform_any(&dom1, a, libnormalform_true())); + ASSERT(a->type == TYPE_ANY); + map[0].value = NULL; + map[1].value = NULL; + + /* ∃x∈{⊥, ⊥}.(x ∧ ⊤) = ⊥ */ + map[0].key = &f; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{⊥, ⊤}.(x ∧ ⊤) = ⊤ */ + map[0].key = &f; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊥}.(x ∧ ⊤) = ⊤ */ + map[0].key = &t; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊤}.(x ∧ ⊤) = ⊤ */ + map[0].key = &t; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + libnormalform_free(a); + + ASSUME(a = libnormalform_any(&dom1, REF(v1), REF(v2))); + + /* ∃x∈X.(P(x) ∧ Q(x)) = ∃x∈X.(P(x) ∧ Q(x)) */ + ASSUME(b = libnormalform_any(&dom1, REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from ∃x∈X.(Q(x) ∧ P(x)) */ + ASSUME(b = libnormalform_any(&dom1, REF(v2), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from ∃x∈Y.(P(x) ∧ Q(x)) */ + ASSUME(b = libnormalform_any(&dom2, REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) = ¬(∀x∈X.(P(x) → ¬Q(x))) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), libnormalform_not(REF(v2)))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + /* ¬∃x∈X.(P(x) ∧ Q(x)) = ∀x∈X.(P(x) → ¬Q(x)) */ + ASSUME(c = libnormalform_not(REF(a))); + ASSUME(b = libnormalform_all(&dom1, REF(v1), libnormalform_not(REF(v2)))); + ASSERT_EQUAL(c, b); + ASSERT(c->type == TYPE_ALL); + libnormalform_free(b); + libnormalform_free(c); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from TRUE */ + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from FALSE */ + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from variable1 */ + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from NOT variable1 */ + ASSUME(b = libnormalform_not(libnormalform_variable(&var1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from function1 */ + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from NOT function1 */ + ASSUME(b = libnormalform_not(libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from T(function1) */ + ASSUME(b = libnormalform_transformation(&trans, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) = ∀x∈X.(P(x) → Q(x)) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from ∃!x∈X.(P(x) ∧ Q(x)) */ + ASSUME(b = libnormalform_one(&dom1, REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from ¬∃!x∈X.(P(x) ∧ Q(x)) */ + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from AND (P, Q) */ + ASSUME(b = libnormalform_and2(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from OR (P, Q) */ + ASSUME(b = libnormalform_or2(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.(P(x) ∧ Q(x)) independent from XOR (P, Q) */ + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + 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) */ + ASSUME(b = libnormalform_any(&dom1, libnormalform_true(), libnormalform_true())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + ASSUME(b = libnormalform_true()); + 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) */ + ASSUME(b = libnormalform_any(&dom1, libnormalform_false(), libnormalform_false())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + libnormalform_free(v1); + libnormalform_free(v2); + + /* cascading of evaluation failure is tested in libnormalform_function.c */ + + TEST_END; +} + + +#endif diff --git a/libnormalform_clone.c b/libnormalform_clone.c new file mode 100644 index 0000000..3f8d01f --- /dev/null +++ b/libnormalform_clone.c @@ -0,0 +1,558 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * Create a shallow clone of a sentence + * + * Any non-`NULL` `.copy_for_clone` in any + * `struct libnormalform_variable *`, + * `struct libnormalform_function *`, or + * `struct libnormalform_map *` will be applied + * + * @param this The sentence + * @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; + * `NULL` will be stored in `*ap_out` if `this` does not + * have any substatements + * @param bp_out Output parameter for `this`'s other substatement; + * `NULL` will be stored in `*bp_out` if `this` does not + * have any substatements + * @return The clone of `this`, `NULL` on failure + */ +USE_RESULT SOME_NONNULL_INPUT(1, 3, 4) +static LIBNORMALFORM_SENTENCE * +shallow_clone(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE **map, + LIBNORMALFORM_SENTENCE ***ap_out, LIBNORMALFORM_SENTENCE ***bp_out) +{ + LIBNORMALFORM_SENTENCE *ret; + + *ap_out = NULL; + *bp_out = NULL; + + if (this->travel_count > 1 && map[this->travel_index]) { + map[this->travel_index]->refcount += 1; + return map[this->travel_index]; + } + + ret = malloc(sizeof(*ret)); + if (!ret) + return NULL; + + memcpy(ret, this, sizeof(*ret)); + ret->refcount = 1; + ret->atom = NULL; + + switch (ret->type) { + case TYPE_ALL: + case TYPE_ANY: + case TYPE_ONE: + case TYPE_NOT_ONE: + if (ret->data.qualifier.domain->copy_for_clone) + ret->data.qualifier.domain = ret->data.qualifier.domain->copy_for_clone; + /* fall through */ + + case TYPE_AND: + case TYPE_OR: + case TYPE_XOR: + *ap_out = &LEFT(ret); + *bp_out = &RIGHT(ret); + /* fall through */ + + case TYPE_TRUE: + case TYPE_FALSE: + break; + + case TYPE_VARIABLE: + if (ret->data.literal.atom.variable->copy_for_clone) + ret->data.literal.atom.variable = ret->data.literal.atom.variable->copy_for_clone; + break; + + case TYPE_FUNCTION: + if (ret->data.literal.atom.function->copy_for_clone) + ret->data.literal.atom.function = ret->data.literal.atom.function->copy_for_clone; + break; + + case TYPE_TRANS: + if (ret->data.trans.function->copy_for_clone) + ret->data.trans.function = ret->data.trans.function->copy_for_clone; + *bp_out = &ret->data.trans.input; + break; + + default: + abort(); + } + + if (this->travel_count > 1) + map[this->travel_index] = ret; + + return ret; +} + + +/** + * Create a deep clone of a sentence + * + * @param ret_out Output parameter for the clone + * @param this The sentence + * @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 + * @return 0 on success, -1 on failure + */ +USE_RESULT SOME_NONNULL_INPUT(1, 2) +static int +deep_clone(LIBNORMALFORM_SENTENCE **ret_out, LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE **map) +{ + LIBNORMALFORM_SENTENCE **ap, **bp; + LIBNORMALFORM_SENTENCE *a = NULL, *b = NULL; /* initialised to silence compiler */ + + for (;;) { + *ret_out = shallow_clone(this, map, &ap, &bp); + if (ap && !bp) + bp = ap, ap = NULL; + if (ap) + a = *ap, *ap = NULL; + if (bp) + b = *bp, *bp = NULL; + if (!*ret_out) + return -1; + + if (ap) + if (deep_clone(ap, a, map)) + return -1; + + if (!bp) + break; + ret_out = bp; + this = b; + } + + return 0; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_clone)(LIBNORMALFORM_SENTENCE *this) +{ + LIBNORMALFORM_SENTENCE *ret = NULL; + LIBNORMALFORM_SENTENCE **map; + size_t dupcount; + + if (!this) + return ret; + + map = NULL; + dupcount = libnormalform_set_indices_and_counts__(this); + if (dupcount) { + /* ideality, we would skip this part and let shallow_clone, + * store the first clone, however since there is no guarantee + * that NULL and 0 have the same representation, we cannot reuse + * the memory of `.travel_index` (needed by libnormalform_to_string) + * for this purpose, so either we do this, or we add another + * seldomly used member to `struct libnormalform_sentence` */ + map = malloc(dupcount * sizeof(*map)); + if (!map) + goto out; + while (dupcount--) + map[dupcount] = NULL; + } + if (deep_clone(&ret, this, map)) { + libnormalform_free(ret); + ret = NULL; + } + free(map); +out: + libnormalform_reset_indices_and_counts__(this); + return ret; +} + + +#else + + +static void +test_simple(void) +{ + struct libnormalform_variable var1, var2; + struct libnormalform_function fun1, fun2; + struct libnormalform_map dom1, dom2; + LIBNORMALFORM_SENTENCE *a, *b; + + ASSUME(a = libnormalform_true()); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(a = libnormalform_false()); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_variable(&var1)); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + var1.copy_for_clone = &var2; + ASSUME(a = libnormalform_variable(&var1)); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT(a->type == TYPE_VARIABLE); + ASSERT(b->type == TYPE_VARIABLE); + ASSERT(a->data.literal.atom.variable == &var1); + ASSERT(b->data.literal.atom.variable == &var2); + ASSERT_NOTEQUAL(a, b); + b->data.literal.atom.variable = &var1; + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + fun1.copy_for_clone = NULL; + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + fun1.copy_for_clone = &fun2; + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT(a->type == TYPE_FUNCTION); + ASSERT(b->type == TYPE_FUNCTION); + ASSERT(a->data.literal.atom.function == &fun1); + ASSERT(b->data.literal.atom.function == &fun2); + ASSERT_NOTEQUAL(a, b); + b->data.literal.atom.function = &fun1; + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + var1.copy_for_clone = NULL; + fun1.copy_for_clone = NULL; + ASSUME(a = libnormalform_and2(libnormalform_variable(&var1), libnormalform_function(&fun1))); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + var1.copy_for_clone = NULL; + fun1.copy_for_clone = NULL; + ASSUME(a = libnormalform_or2(libnormalform_variable(&var1), libnormalform_function(&fun1))); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + var1.copy_for_clone = NULL; + fun1.copy_for_clone = NULL; + ASSUME(a = libnormalform_xor2(libnormalform_variable(&var1), libnormalform_function(&fun1))); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + dom1.copy_for_clone = NULL; + fun1.copy_for_clone = NULL; + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_all(&dom1, libnormalform_function(&fun1), libnormalform_variable(&var1))); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + dom1.copy_for_clone = &dom2; + fun1.copy_for_clone = NULL; + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_all(&dom1, libnormalform_function(&fun1), libnormalform_variable(&var1))); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT(a->type == TYPE_ALL); + ASSERT(b->type == TYPE_ALL); + ASSERT(a->data.qualifier.domain == &dom1); + ASSERT(b->data.qualifier.domain == &dom2); + ASSERT_NOTEQUAL(a, b); + b->data.qualifier.domain = &dom1; + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + dom1.copy_for_clone = NULL; + fun1.copy_for_clone = NULL; + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_any(&dom1, libnormalform_function(&fun1), libnormalform_variable(&var1))); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + dom1.copy_for_clone = &dom2; + fun1.copy_for_clone = NULL; + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_any(&dom1, libnormalform_function(&fun1), libnormalform_variable(&var1))); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT(a->type == TYPE_ANY); + ASSERT(b->type == TYPE_ANY); + ASSERT(a->data.qualifier.domain == &dom1); + ASSERT(b->data.qualifier.domain == &dom2); + ASSERT_NOTEQUAL(a, b); + b->data.qualifier.domain = &dom1; + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + dom1.copy_for_clone = NULL; + fun1.copy_for_clone = NULL; + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_one(&dom1, libnormalform_function(&fun1), libnormalform_variable(&var1))); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + dom1.copy_for_clone = &dom2; + fun1.copy_for_clone = NULL; + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_one(&dom1, libnormalform_function(&fun1), libnormalform_variable(&var1))); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT(a->type == TYPE_ONE); + ASSERT(b->type == TYPE_ONE); + ASSERT(a->data.qualifier.domain == &dom1); + ASSERT(b->data.qualifier.domain == &dom2); + ASSERT_NOTEQUAL(a, b); + b->data.qualifier.domain = &dom1; + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + dom1.copy_for_clone = NULL; + fun1.copy_for_clone = NULL; + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_one(&dom1, libnormalform_function(&fun1), libnormalform_variable(&var1))); + ASSUME(a = libnormalform_not(a)); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + dom1.copy_for_clone = &dom2; + fun1.copy_for_clone = NULL; + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_one(&dom1, libnormalform_function(&fun1), libnormalform_variable(&var1))); + ASSUME(a = libnormalform_not(a)); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT(a->type == TYPE_NOT_ONE); + ASSERT(b->type == TYPE_NOT_ONE); + ASSERT(a->data.qualifier.domain == &dom1); + ASSERT(b->data.qualifier.domain == &dom2); + ASSERT_NOTEQUAL(a, b); + b->data.qualifier.domain = &dom1; + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); +} + + +static void +test_multientry(void) +{ + struct libnormalform_variable var1; + struct libnormalform_map dom1; + LIBNORMALFORM_SENTENCE *a, *b; + + dom1.copy_for_clone = NULL; + var1.copy_for_clone = NULL; + ASSUME(a = libnormalform_variable(&var1)); + ASSUME(a = libnormalform_all(&dom1, REF(a), a)); + ASSERT(a->type == TYPE_ALL); + ASSERT(LEFT(a) == RIGHT(a)); + ASSERT(LEFT(a)->refcount = 2); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT(a->refcount == 1); + ASSERT(b->refcount == 1); + ASSERT(b->type == TYPE_ALL); + ASSERT(LEFT(b) == RIGHT(b)); + ASSERT(LEFT(b) != LEFT(a)); + ASSERT(LEFT(b)->refcount = 2); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + libnormalform_free(a); +} + + +static void +test_complex(void) +{ +#define U(NAME) NAME = {.copy_for_clone = NULL, .identifier = #NAME} + + struct libnormalform_variable U(var1), U(var2); + struct libnormalform_function U(fun1), U(fun2); + struct libnormalform_map U(dom1); + LIBNORMALFORM_SENTENCE *a, *b; + + ASSUME(a = + libnormalform_any(&dom1, + libnormalform_true(), + LIBNORMALFORM_AND( + libnormalform_function(&fun1), + libnormalform_function(&fun2), + libnormalform_variable(&var1), + libnormalform_variable(&var2) + ) + ) + ); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT_EQUAL(a, b); + + ASSUME(a = + LIBNORMALFORM_OR( + a, + libnormalform_xor2( + libnormalform_function(&fun1), + libnormalform_variable(&var1)), + libnormalform_function(&fun2), + libnormalform_all(&dom1, + LIBNORMALFORM_AND( + libnormalform_function(&fun1), + libnormalform_function(&fun2), + libnormalform_variable(&var1), + libnormalform_variable(&var2) + ), + libnormalform_xor2( + libnormalform_function(&fun1), + b) + ) + ) + ); + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT_EQUAL(a, b); + + libnormalform_free(b); + libnormalform_free(a); + +#undef U +} + + +static void +test_complex_multientry(void) +{ +#define U(NAME) NAME = {.copy_for_clone = NULL, .identifier = #NAME} + + struct libnormalform_variable U(var1), U(var2); + struct libnormalform_function U(fun1), U(fun2); + struct libnormalform_map U(dom1); + LIBNORMALFORM_SENTENCE *a, *b, *ref1, *ref2, *ref3, *ref4; + + ASSUME(a = + libnormalform_any(&dom1, + libnormalform_true(), + ref4 = REF(LIBNORMALFORM_AND( + ref1 = REF(libnormalform_function(&fun1)), + ref2 = REF(libnormalform_function(&fun2)), + ref3 = REF(libnormalform_variable(&var1)), + libnormalform_variable(&var2) + )) + ) + ); + ASSUME(a = + LIBNORMALFORM_OR( + a, + libnormalform_xor2(REF(ref1), REF(ref3)), + REF(ref2), + libnormalform_all(&dom1, + REF(ref4), + libnormalform_xor2(REF(ref1), REF(a)) + ) + ) + ); + + libnormalform_free(ref1); + libnormalform_free(ref2); + libnormalform_free(ref3); + libnormalform_free(ref4); + + ASSUME(b = libnormalform_clone(a)); + ASSERT(a != b); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + libnormalform_free(a); + +#undef U +} + + +int +main(void) +{ + TEST_BEGIN; + + test_simple(); + test_multientry(); + test_complex(); + test_complex_multientry(); + + TEST_END; +} + + +#endif diff --git a/libnormalform_empty.c b/libnormalform_empty.c new file mode 100644 index 0000000..e9f4622 --- /dev/null +++ b/libnormalform_empty.c @@ -0,0 +1,69 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_empty)(struct libnormalform_map *); + + +#else + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + LIBNORMALFORM_SENTENCE *a, *b; + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + ASSUME(a = libnormalform_empty(&dom1)); + ASSERT(a->type == TYPE_ALL); + + ASSUME(b = libnormalform_empty(&dom2)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_empty(&dom1)); + ASSERT(b != a); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_nonempty(&dom1)); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_singleton(&dom1)); + ASSERT_NOTEQUAL(a, b); + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_not(libnormalform_empty(&dom1))); + ASSERT(b->type == TYPE_ANY); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + dom1.nmappings = 0; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 1; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 2; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 3; + ASSERT(libnormalform_evaluate(a) == 0); + + libnormalform_free(a); + + TEST_END; +} + + +#endif diff --git a/libnormalform_evaluate.c b/libnormalform_evaluate.c new file mode 100644 index 0000000..80749ed --- /dev/null +++ b/libnormalform_evaluate.c @@ -0,0 +1,44 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +(libnormalform_evaluate)(LIBNORMALFORM_SENTENCE *this) +{ + return this->evaluate(this, NULL); +} + + +#else + + +#define EVALUATION_RESULT 5 + +static LIBNORMALFORM_SENTENCE *evaluation_this; + +static int +evaluation(LIBNORMALFORM_SENTENCE *this, void *input) +{ + ASSERT(this); + ASSERT(this == evaluation_this); + ASSERT(input == NULL); + return EVALUATION_RESULT; +} + + +int +main(void) +{ + TEST_BEGIN; + + LIBNORMALFORM_SENTENCE a; + evaluation_this = &a; + a.evaluate = &evaluation; + ASSERT(libnormalform_evaluate(&a) == EVALUATION_RESULT); + + TEST_END; +} + + +#endif diff --git a/libnormalform_existentially.c b/libnormalform_existentially.c new file mode 100644 index 0000000..16ff9fa --- /dev/null +++ b/libnormalform_existentially.c @@ -0,0 +1,235 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_existentially)(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); + + +#else + + +static int +evalbool(void *user_data, void *input) +{ + int *vp = input; + (void) user_data; + return *vp; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1; + struct libnormalform_function fun1; + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *c, *v1; + int t = 1, f = 0; + + ASSUME(v1 = libnormalform_variable(&var1)); + + errno = 0; + ASSERT(!libnormalform_existentially(&dom1, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_existentially(&dom1, NULL) && errno == 1); + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + /* ∃x.⊥ = ⊥ */ + ASSUME(a = libnormalform_existentially(&dom1, libnormalform_false())); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* ∃x.φ irreducable */ + ASSUME(a = libnormalform_existentially(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ANY); + libnormalform_free(a); + + /* ∃x.⊤ irreducable */ + ASSUME(a = libnormalform_existentially(&dom1, libnormalform_true())); + ASSERT(a->type == TYPE_ANY); + libnormalform_free(a); + + ASSUME(a = libnormalform_existentially(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ANY); + ASSERT(a->refcount == 1); + + dom1.nmappings = 1; + + /* ∃x∈{a}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a}.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + + /* ∃x∈{a,b}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a,b}.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 0; + + /* ∃x∈∅.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈∅.⊤ = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + libnormalform_free(a); + + fun1.evaluate = &evalbool; + dom1.nmappings = 2; + + ASSUME(a = libnormalform_existentially(&dom1, libnormalform_function(&fun1))); + ASSERT(a->type == TYPE_ANY); + map[0].key = NULL; + map[1].key = NULL; + + /* ∃x∈{⊥, ⊥}.x = ⊥ */ + map[0].value = &f; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{⊥, ⊤}.x = ⊤ */ + map[0].value = &f; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊥}.x = ⊤ */ + map[0].value = &t; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊤}.x = ⊤ */ + map[0].value = &t; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊤, ⊤}.x = ⊤ */ + map[0].value = &t; + map[1].value = &t; + map[2].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + libnormalform_free(a); + + ASSUME(a = libnormalform_existentially(&dom1, REF(v1))); + + /* ∃x∈X.P(x) = ∃x∈X.(⊤ ∧ P(x)) */ + ASSUME(b = libnormalform_any(&dom1, libnormalform_true(), REF(v1))); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from ∃x∈X.Q(x) */ + ASSUME(b = libnormalform_existentially(&dom1, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from ∃x∈Y.P(x)) */ + ASSUME(b = libnormalform_existentially(&dom2, REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) = ¬(∀x∈X.(⊤ → ¬P(x))) */ + ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), libnormalform_not(REF(v1)))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + /* ¬∃x∈X.P(x) = ∀x∈X.(⊤ → ¬P(x)) */ + ASSUME(c = libnormalform_not(REF(a))); + ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), libnormalform_not(REF(v1)))); + ASSERT_EQUAL(c, b); + ASSERT(c->type == TYPE_ALL); + libnormalform_free(b); + libnormalform_free(c); + + /* ∃x∈X.P(x) independent from TRUE */ + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from FALSE */ + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from variable1 */ + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from NOT variable1 */ + ASSUME(b = libnormalform_not(libnormalform_variable(&var1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from function1 */ + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from NOT function1 */ + ASSUME(b = libnormalform_not(libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from T(function1) */ + ASSUME(b = libnormalform_transformation(&trans, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from ∀x∈X.(⊤ → P(x)) */ + ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from ∃!x∈X.(⊤ ∧ P(x)) */ + ASSUME(b = libnormalform_one(&dom1, libnormalform_true(), REF(v1))); + ASSERT_NOTEQUAL(a, b); + + /* ∃x∈X.P(x) independent from ¬∃!x∈X.(⊤ ∧ P(x)) */ + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from AND (P, P) */ + ASSUME(b = libnormalform_and2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(X) independent from OR (P, P) */ + ASSUME(b = libnormalform_or2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from XOR (P, P) */ + ASSUME(b = libnormalform_xor2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + libnormalform_free(v1); + + TEST_END; +} + + +#endif diff --git a/libnormalform_exists.c b/libnormalform_exists.c new file mode 100644 index 0000000..0c40729 --- /dev/null +++ b/libnormalform_exists.c @@ -0,0 +1,243 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_exists)(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); + + +#else + + +static int +evalbool(void *user_data, void *input) +{ + int *vp = input; + (void) user_data; + return *vp; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1; + struct libnormalform_function fun1; + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *c, *v1; + int t = 1, f = 0; + + ASSUME(v1 = libnormalform_variable(&var1)); + + errno = 0; + ASSERT(!libnormalform_exists(&dom1, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_exists(&dom1, NULL) && errno == 1); + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + /* ∃x.⊥ = ⊥ */ + ASSUME(a = libnormalform_exists(&dom1, libnormalform_false())); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* ∃x.φ irreducable */ + ASSUME(a = libnormalform_exists(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ANY); + libnormalform_free(a); + + /* ∃x.⊤ irreducable */ + ASSUME(a = libnormalform_exists(&dom1, libnormalform_true())); + ASSERT(a->type == TYPE_ANY); + libnormalform_free(a); + + ASSUME(a = libnormalform_exists(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ANY); + ASSERT(a->refcount == 1); + + dom1.nmappings = 1; + + /* ∃x∈{a}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a}.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + + /* ∃x∈{a,b}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{a,b}.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 0; + + /* ∃x∈∅.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈∅.⊤ = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + libnormalform_free(a); + + fun1.evaluate = &evalbool; + dom1.nmappings = 2; + + ASSUME(a = libnormalform_exists(&dom1, libnormalform_function(&fun1))); + ASSERT(a->type == TYPE_ANY); + map[0].value = NULL; + map[1].value = NULL; + + /* ∃x∈{⊥, ⊥}.x = ⊥ */ + map[0].key = &f; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃x∈{⊥, ⊤}.x = ⊤ */ + map[0].key = &f; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊥}.x = ⊤ */ + map[0].key = &t; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊤}.x = ⊤ */ + map[0].key = &t; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃x∈{⊤, ⊤, ⊤}.x = ⊤ */ + map[0].key = &t; + map[1].key = &t; + map[2].key = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + libnormalform_free(a); + + ASSUME(a = libnormalform_exists(&dom1, REF(v1))); + + /* ∃x∈X.P(x) = ∃x∈X.(P(x) ∧ ⊤) */ + ASSUME(b = libnormalform_any(&dom1, REF(v1), libnormalform_true())); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from ∃x∈X.Q(x) */ + ASSUME(b = libnormalform_exists(&dom1, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from ∃x∈Y.P(x)) */ + ASSUME(b = libnormalform_exists(&dom2, REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) = ¬(∀x∈X.(P(x) → ⊥)) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), libnormalform_false())); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + /* ¬∃x∈X.P(x) = ∀x∈X.(P(x) → ⊥) */ + ASSUME(c = libnormalform_not(REF(a))); + ASSUME(b = libnormalform_all(&dom1, REF(v1), libnormalform_false())); + ASSERT_EQUAL(c, b); + ASSERT(c->type == TYPE_ALL); + libnormalform_free(b); + libnormalform_free(c); + + /* ∃x∈X.P(x) independent from TRUE */ + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from FALSE */ + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from variable1 */ + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from NOT variable1 */ + ASSUME(b = libnormalform_not(libnormalform_variable(&var1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from function1 */ + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from NOT function1 */ + ASSUME(b = libnormalform_not(libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from T(function1) */ + ASSUME(b = libnormalform_transformation(&trans, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from ∀x∈X.(P(x) → ⊤) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), libnormalform_true())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from ∃!x∈X.(P(x) ∧ ⊤) */ + ASSUME(b = libnormalform_one(&dom1, REF(v1), libnormalform_true())); + ASSERT_NOTEQUAL(a, b); + + /* ∃x∈X.P(x) independent from ¬∃!x∈X.(P(x) ∧ ⊤) */ + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from AND (P, P) */ + ASSUME(b = libnormalform_and2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(X) independent from OR (P, P) */ + ASSUME(b = libnormalform_or2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) independent from XOR (P, P) */ + ASSUME(b = libnormalform_xor2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃x∈X.P(x) = ¬∄x∈X.P(x) */ + ASSUME(b = libnormalform_nexists(&dom1, REF(v1))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + ASSUME(b = libnormalform_not(libnormalform_nexists(&dom1, REF(v1)))); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + libnormalform_free(v1); + + TEST_END; +} + + +#endif diff --git a/libnormalform_express.c b/libnormalform_express.c new file mode 100644 index 0000000..2532488 --- /dev/null +++ b/libnormalform_express.c @@ -0,0 +1,849 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#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; +} + + +#else + +TODO_TEST + +#endif diff --git a/libnormalform_false.c b/libnormalform_false.c new file mode 100644 index 0000000..e0708f5 --- /dev/null +++ b/libnormalform_false.c @@ -0,0 +1,155 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (FALSE implementation) + */ +static LIBNORMALFORM_SENTENCE * +false_inverse(LIBNORMALFORM_SENTENCE *this) +{ + (void) this; + return libnormalform_true(); +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (FALSE implementation) + */ +static int +false_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + (void) this; + if (other->type == TYPE_FALSE) { + *inv_out = 0; + return 1; + } else if (other->type == TYPE_TRUE) { + *inv_out = 1; + return 1; + } else { + return 0; + } +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (FALSE implementation) + */ +CONST static int +false_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + (void) this; + (void) input; + return 0; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_false)(void) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_FALSE, + .hash = TRUE_FALSE_HASH, + .inverse = &false_inverse, + .equals = &false_equals, + .evaluate = &false_evaluate + }; + + /* + * During normalisation, some fields may be set, + * therefore a new allocation is returned instead + * of a reference to a static allocation, so that + * converation can be done from the threads at + * the same time on different sentences, both + * including this constant. + */ + + LIBNORMALFORM_SENTENCE *ret = malloc(sizeof(*ret)); + if (ret) + *ret = prototype; + return ret; +} + + +#else + + +static int +tautology(void *user_data, void *input) +{ + (void) user_data; + (void) input; + return 1; +} + + +static int +contradiction(void *user_data, void *input) +{ + (void) user_data; + (void) input; + return 0; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2; + struct libnormalform_function fun1, fun2; + struct libnormalform_map domain; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *f1, *f2; + + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + fun1.evaluate = &tautology; + fun2.evaluate = &contradiction; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(f1 = libnormalform_function(&fun1)); + ASSUME(f2 = libnormalform_function(&fun2)); + + ASSUME(a = libnormalform_false()); + ASSERT(a->type == TYPE_FALSE); + ASSERT(a->refcount == 1); + ASSERT_EQUAL(a, a); + + ASSERT(a->evaluate(a, NULL) == 0); + +#define CHECK(CMP, X)\ + do {\ + ASSUME(b = (X));\ + ASSERT_##CMP(a, b);\ + libnormalform_free(b);\ + } while (0) + + CHECK(EQUAL, libnormalform_false()); + CHECK(INVEQUAL, libnormalform_true()); + CHECK(NOTEQUAL, libnormalform_and2(REF(v1), REF(v2))); + CHECK(NOTEQUAL, libnormalform_or2(REF(v1), REF(v2))); + CHECK(NOTEQUAL, libnormalform_xor2(REF(v1), REF(v2))); + CHECK(NOTEQUAL, libnormalform_all(&domain, REF(f1), REF(f2))); + CHECK(NOTEQUAL, libnormalform_any(&domain, REF(f1), REF(f2))); + CHECK(NOTEQUAL, libnormalform_one(&domain, REF(f1), REF(f2))); + CHECK(NOTEQUAL, libnormalform_not(libnormalform_one(&domain, REF(f1), REF(f2)))); + CHECK(NOTEQUAL, REF(v1)); + CHECK(NOTEQUAL, REF(f1)); + +#undef CHECK + + libnormalform_free(a); + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(f1); + libnormalform_free(f2); + + TEST_END; +} + +#endif diff --git a/libnormalform_free.c b/libnormalform_free.c new file mode 100644 index 0000000..3bce4df --- /dev/null +++ b/libnormalform_free.c @@ -0,0 +1,123 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * Recursively deallocate a `LIBNORMALFORM_SENTENCE *` + * according to `libnormalform_free` + * + * @param this The object to deallocate + */ +NONNULL_INPUT static void +free_sentence(LIBNORMALFORM_SENTENCE *this) +{ + LIBNORMALFORM_SENTENCE *head = NULL, *a, *b; + + if (--this->refcount) + return; + + do { + if (this->atom && !--this->atom->refcount) + free(this->atom); + + if (IS_BRANCH(this)) { + a = LEFT(this); + b = RIGHT(this); + if (b && !--b->refcount) + PUSH(&head, b); + push_a: + if (a && !--a->refcount) + PUSH(&head, a); + } else if (this->type == TYPE_TRANS) { + a = this->data.trans.input; + goto push_a; + } + + free(this); + } while (POP(&head, &this)); +} + + +/** + * Recursively deallocate a `LIBNORMALFORM_SENTENCE *` + * according to `libnormalform_free`, but do not + * deallocate the pointer itself + * + * @param this The object to deallocate + */ +NONNULL_INPUT static void +destroy_term(struct libnormalform_term *this) +{ + switch (this->type) { + case LIBNORMALFORM_DISJUNCTION: + case LIBNORMALFORM_CONJUNCTION: + case LIBNORMALFORM_EXCLUSIVE_DISJUNCTION: + 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: + free(this->term.transformation.sentence); + free(this); + return; + + 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: + destroy_term(this->term.qualification.antecedent); + destroy_term(this->term.qualification.predicate); + free(this->term.qualification.antecedent); + free(this->term.qualification.predicate); + /* fall through */ + + case LIBNORMALFORM_VARIABLE: + case LIBNORMALFORM_NEGATED_VARIABLE: + case LIBNORMALFORM_FUNCTION: + case LIBNORMALFORM_NEGATED_FUNCTION: + free(this); + break; + + default: + abort(); + } +} + + +void +(libnormalform_free)(void *this) +{ + if (!this) + return; + + if (*(enum libnormalform_term_type *)this >= SENTENCE_TYPE_OFFSET) { + free_sentence(this); + } else { + destroy_term(this); + free(this); + } +} + + +#else + + +int +main(void) +{ + TEST_BEGIN; + + libnormalform_free(NULL); + + /* Tested in other tests */ + + TEST_END; +} + + +#endif diff --git a/libnormalform_from_string.c b/libnormalform_from_string.c new file mode 100644 index 0000000..8d64c41 --- /dev/null +++ b/libnormalform_from_string.c @@ -0,0 +1,918 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#define OPT_SPACE\ + do {\ + while (isspace(*s))\ + s++;\ + } while (0); + +#define LEFT_BRACKET\ + do {\ + if (!STARTS("("))\ + goto einval;\ + OPT_SPACE;\ + } while (0) + +#define COMMA\ + do {\ + OPT_SPACE;\ + if (!STARTS(","))\ + goto einval;\ + OPT_SPACE;\ + } while (0) + +#define RIGHT_BRACKET\ + do {\ + OPT_SPACE;\ + if (!STARTS(")"))\ + goto einval;\ + } while (0) + + +/** + * Parse a reference number + * + * @param s String beginning with the first digit in the number + * @param end_out Output parameter for the end of the number + * @param id_out Output parameter for the reference number + * @return 0 on success, -1 on failure + * + * @throws EINVAL The string does not begin with a number + * @throws EINVAL The number is too large + */ +USE_RESULT NONNULL_INPUT +static int +get_reference_id(char *s, char **end_out, size_t *id_out) +{ + size_t digit; + + *id_out = 0; + + if (*s == '0') { + *end_out = &s[1]; + return 0; + } + + if ('1' > *s || *s > '9') + goto einval; + + while (isdigit(*s)) { + digit = (size_t)(*s & 15); + + if (*id_out > (SIZE_MAX / sizeof(void *) - 1U - digit) / 10U) + goto einval; + *id_out = *id_out * 10U + digit; + } + + *end_out = s; + return 0; + +einval: + errno = EINVAL; + return -1; +} + + +/** + * Parse a reference number and allocate a reference slot for it + * + * The reference slot will be initialised to `NULL` + * + * @param s String beginning with the first digit in the number + * @param end_out Output parameter for the end of the number + * @param id_out Output parameter for the reference number + * @param referencesp Reference to array of references points + * @param nreferencesp Reference to the number of elements in `*referencesp`, + * will be incremented by 1 on success + * @return 0 on success, -1 on failure + * + * @throws EINVAL The string does not begin with a number + * @throws EINVAL The number is not `*nreferencesp` + * @throws ENOMEM Insufficient memory available to allocate the slot + */ +USE_RESULT NONNULL_INPUT +static int +get_reference_id_and_allocate_slot(char *s, char **end_out, size_t *id_out, + LIBNORMALFORM_SENTENCE ***referencesp, size_t *nreferencesp) +{ + void *new; + + OPT_SPACE; + if (get_reference_id(s, &s, id_out)) + return -1; + if (*id_out != *nreferencesp) { + errno = EINVAL; + return -1; + } + OPT_SPACE; + + new = realloc(*referencesp, (*id_out + 1U) * sizeof(**referencesp)); + if (!new) + return -1; + *referencesp = new; + (*referencesp)[(*nreferencesp)++] = NULL; + + *end_out = s; + return 0; +} + + +/** + * `libnormalform_from_string` with some additional parameters: + * + * @param truep Reference to any already constructed TRUE sentence (`*truep` is `NULL` until then) + * @param falsep Reference to any already constructed FALSE sentence (`*falsep` is `NULL` until then) + * @param referencesp Reference to array of references points + * @param nreferencesp Reference to the number of elements in `*referencesp` + */ +USE_RESULT SOME_NONNULL_INPUT(1, 3, 4, 5, 6, 7) +static LIBNORMALFORM_SENTENCE * +from_string(char *s, char **end_out, const struct libnormalform_representation_spec *spec, + LIBNORMALFORM_SENTENCE **truep, LIBNORMALFORM_SENTENCE **falsep, + LIBNORMALFORM_SENTENCE ***referencesp, size_t *nreferencesp) +{ +#define STARTS(WANT) (!strncmp(s, WANT, (n = sizeof(WANT) - 1U)) ? (s = &s[n]) : NULL) + + LIBNORMALFORM_SENTENCE *(*qualifier2)(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *) = NULL; + LIBNORMALFORM_SENTENCE *(*qualifier1)(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *) = NULL; + LIBNORMALFORM_SENTENCE *(*qualifier0)(struct libnormalform_map *) = NULL; + LIBNORMALFORM_SENTENCE *(*variadic)(LIBNORMALFORM_SENTENCE **) = NULL; + LIBNORMALFORM_SENTENCE *(*unary)(LIBNORMALFORM_SENTENCE *) = NULL; + LIBNORMALFORM_SENTENCE *ret = NULL, *k = NULL, *v = NULL, *x = NULL; + size_t n, refid = SIZE_MAX; + + if (STARTS("TRUE")) { + if (*truep) + ret = libnormalform_ref(*truep); + else + ret = *truep = libnormalform_true(); + } else if (STARTS("FALSE")) { + if (*falsep) + ret = libnormalform_ref(*falsep); + else + ret = *falsep = libnormalform_false(); + } else if (STARTS("REF")) { + LEFT_BRACKET; + if (get_reference_id(s, &s, &refid)) + return NULL; + RIGHT_BRACKET; + if (refid >= *nreferencesp) + goto einval; + ret = (*referencesp)[refid]; + if (!ret) + goto einval; + ret = libnormalform_ref(ret); + if (!ret) + return NULL; + } else if (STARTS("NOT")) { + unary = &libnormalform_not; + } else if (STARTS("AND")) { + variadic = &libnormalform_and; + } else if (STARTS("OR")) { + variadic = &libnormalform_or; + } else if (STARTS("XOR")) { + variadic = &libnormalform_xor; + } else if (STARTS("IF")) { + variadic = &libnormalform_if; + } else if (STARTS("IMPLY")) { + variadic = &libnormalform_imply; + } else if (STARTS("NAND")) { + variadic = &libnormalform_nand; + } else if (STARTS("NOR")) { + variadic = &libnormalform_nor; + } else if (STARTS("XNOR")) { + variadic = &libnormalform_xnor; + } else if (STARTS("NIF")) { + variadic = &libnormalform_nif; + } else if (STARTS("NIMPLY")) { + variadic = &libnormalform_nimply; + } else if (STARTS("ALL")) { + qualifier2 = &libnormalform_all; + } else if (STARTS("ANY")) { + qualifier2 = &libnormalform_any; + } else if (STARTS("ONE")) { + qualifier2 = &libnormalform_one; + } else if (STARTS("EXISTENTIALLY")) { + qualifier1 = &libnormalform_existentially; + } else if (STARTS("UNIVERSALLY")) { + qualifier1 = &libnormalform_universally; + } else if (STARTS("UNIQUELY")) { + qualifier1 = &libnormalform_uniquely; + } else if (STARTS("EXISTS")) { + qualifier1 = &libnormalform_exists; + } else if (STARTS("NEXISTS")) { + qualifier1 = &libnormalform_nexists; + } else if (STARTS("UNIQUE")) { + qualifier1 = &libnormalform_unique; + } else if (STARTS("EMPTY")) { + qualifier0 = &libnormalform_empty; + } else if (STARTS("NONEMPTY")) { + qualifier0 = &libnormalform_nonempty; + } else if (STARTS("SINGLETON")) { + qualifier0 = &libnormalform_singleton; + } else if (STARTS("VARIABLE")) { + struct libnormalform_variable *a; + OPT_SPACE; + if (*s == '@' && get_reference_id_and_allocate_slot(&s[1], &s, &refid, referencesp, nreferencesp)) + return NULL; + LEFT_BRACKET; + if (!spec->get_variable) + goto enoent; + a = spec->get_variable(s, &s, spec->user_data); + if (!a) + return NULL; + RIGHT_BRACKET; + ret = libnormalform_variable(a); + } else if (STARTS("FUNCTION")) { + struct libnormalform_function *a; + OPT_SPACE; + if (*s == '@' && get_reference_id_and_allocate_slot(&s[1], &s, &refid, referencesp, nreferencesp)) + return NULL; + LEFT_BRACKET; + if (!spec->get_function) + goto enoent; + a = spec->get_function(s, &s, spec->user_data); + if (!a) + return NULL; + RIGHT_BRACKET; + ret = libnormalform_function(a); + } else if (STARTS("TRANSFORMATION")) { + struct libnormalform_transformer *a; + OPT_SPACE; + if (*s == '@' && get_reference_id_and_allocate_slot(&s[1], &s, &refid, referencesp, nreferencesp)) + return NULL; + LEFT_BRACKET; + if (!spec->get_transformer) + goto enoent; + a = spec->get_transformer(s, &s, spec->user_data); + if (!a) + return NULL; + COMMA; + x = from_string(s, &s, spec, truep, falsep, referencesp, nreferencesp); + if (!x) + return NULL; + RIGHT_BRACKET; + ret = libnormalform_transformation(a, x); + x = NULL; + } else { + goto einval; + } + + if (!ret) { + OPT_SPACE; + if (*s == '@' && get_reference_id_and_allocate_slot(&s[1], &s, &refid, referencesp, nreferencesp)) + return NULL; + LEFT_BRACKET; + } + + if (qualifier2) { + struct libnormalform_map *d; + if (!spec->get_map) + goto enoent; + d = spec->get_map(s, &s, spec->user_data); + if (!d) + return NULL; + COMMA; + k = from_string(s, &s, spec, truep, falsep, referencesp, nreferencesp); + if (!k) + return NULL; + COMMA; + v = from_string(s, &s, spec, truep, falsep, referencesp, nreferencesp); + if (!v) { + libnormalform_free(k); + return NULL; + } + RIGHT_BRACKET; + ret = (*qualifier2)(d, k, v); + k = v = NULL; + + } else if (qualifier1) { + struct libnormalform_map *d; + if (!spec->get_map) + goto enoent; + d = spec->get_map(s, &s, spec->user_data); + if (!d) + return NULL; + COMMA; + k = from_string(s, &s, spec, truep, falsep, referencesp, nreferencesp); + if (!k) + return NULL; + RIGHT_BRACKET; + ret = (*qualifier1)(d, k); + + } else if (qualifier0) { + struct libnormalform_map *d; + if (!spec->get_map) + goto enoent; + d = spec->get_map(s, &s, spec->user_data); + if (!d) + return NULL; + RIGHT_BRACKET; + ret = (*qualifier0)(d); + + } else if (unary) { + x = from_string(s, &s, spec, truep, falsep, referencesp, nreferencesp); + if (!x) + return NULL; + RIGHT_BRACKET; + ret = (*unary)(x); + x = NULL; + + } else if (variadic) { + LIBNORMALFORM_SENTENCE **xs = NULL; + size_t nxs = 0; + void *new; + goto fit_one_more; + do { + xs[nxs] = from_string(s, &s, spec, truep, falsep, referencesp, nreferencesp); + if (!xs[nxs]) + goto fail_variadic; + nxs++; + + OPT_SPACE; + if (STARTS(",")) { + OPT_SPACE; + } else if (*s != ')') { + errno = EINVAL; + fail_variadic: + while (nxs--) + libnormalform_free(xs[nxs]); + free(xs); + return NULL; + } + + fit_one_more: + new = realloc(xs, (nxs + 1U) * sizeof(*xs)); + if (!new) + goto fail_variadic; + xs = new; + } while (!STARTS(")")); + xs[nxs] = NULL; + ret = (*variadic)(xs); + free(xs); + } + + if (end_out) { + *end_out = s; + } else if (*s) { + OPT_SPACE; + if (*s) { + libnormalform_free(ret); + einval: + libnormalform_free(k); + libnormalform_free(v); + libnormalform_free(x); + errno = EINVAL; + return NULL; + } + } + + if (refid != SIZE_MAX) + (*referencesp)[refid] = ret; + + return ret; + +enoent: + errno = ENOENT; + return NULL; + +#undef STARTS +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_from_string)(char *s, char **end_out, const struct libnormalform_representation_spec *spec) +{ + LIBNORMALFORM_SENTENCE *true_obj = NULL; + LIBNORMALFORM_SENTENCE *false_obj = NULL; + LIBNORMALFORM_SENTENCE **references = NULL; + size_t nreferences = 0; + LIBNORMALFORM_SENTENCE *ret; + while (isspace(*s)) + s++; + ret = from_string(s, end_out, spec, &true_obj, &false_obj, &references, &nreferences); + free(references); + return ret; +} + + +#undef OPT_SPACE +#undef LEFT_BRACKET +#undef COMMA +#undef RIGHT_BRACKET + + +#else + + +static struct libnormalform_variable var1 = {.identifier = "var1"}; +static struct libnormalform_variable var2 = {.identifier = "var2"}; +static struct libnormalform_variable var3 = {.identifier = "var3"}; +static struct libnormalform_function fun1 = {.identifier = "fun1"}; +static struct libnormalform_function fun2 = {.identifier = "fun2"}; +static struct libnormalform_map dom1 = {.identifier = "dom1"}; +static struct libnormalform_map dom2 = {.identifier = "dom2"}; +static struct libnormalform_transformer trans1 = {.identifier = "trans1"}; + + +static struct libnormalform_variable * +get_variable(char *s, char **end_out, void *user_data) +{ + struct libnormalform_variable *ret; + (void) user_data; + if (!strncmp(s, "var1", 4)) + ret = &var1; + else if (!strncmp(s, "var2", 4)) + ret = &var2; + else if (!strncmp(s, "var3", 4)) + ret = &var3; + else + return NULL; + *end_out = &s[4]; + return ret; +} + + +static struct libnormalform_function * +get_function(char *s, char **end_out, void *user_data) +{ + struct libnormalform_function *ret; + (void) user_data; + if (!strncmp(s, "fun1", 4)) + ret = &fun1; + else if (!strncmp(s, "fun2", 4)) + ret = &fun2; + else + return NULL; + *end_out = &s[4]; + return ret; +} + + +static struct libnormalform_map * +get_map(char *s, char **end_out, void *user_data) +{ + struct libnormalform_map *ret; + (void) user_data; + if (!strncmp(s, "dom1", 4)) + ret = &dom1; + else if (!strncmp(s, "dom2", 4)) + ret = &dom2; + else + return NULL; + *end_out = &s[4]; + return ret; +} + + +static struct libnormalform_transformer * +get_transformer(char *s, char **end_out, void *user_data) +{ + struct libnormalform_transformer *ret; + (void) user_data; + if (!strncmp(s, "trans1", 6)) + ret = &trans1; + else + return NULL; + *end_out = &s[6]; + return ret; +} + + +struct libnormalform_representation_spec spec = { + .get_variable = get_variable, + .get_function = get_function, + .get_map = get_map, + .get_transformer = get_transformer +}; + + +int +main(void) +{ + TEST_BEGIN; + + LIBNORMALFORM_SENTENCE *a, *b; + char *s, *p; + + ASSUME(s = strdup("AND(VARIABLE(var1), VARIABLE(var2))")); + ASSUME(a = libnormalform_and2(libnormalform_variable(&var1), libnormalform_variable(&var2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("AND(VARIABLE(var1), VARIABLE(var2))")); + spec.get_variable = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_variable = &get_variable; + free(s); + + ASSUME(s = strdup("OR(VARIABLE(var1), VARIABLE(var2))")); + ASSUME(a = libnormalform_or2(libnormalform_variable(&var1), libnormalform_variable(&var2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("XOR(VARIABLE(var1), VARIABLE(var2))")); + ASSUME(a = libnormalform_xor2(libnormalform_variable(&var1), libnormalform_variable(&var2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("ALL(dom1, FUNCTION(fun1), FUNCTION(fun2))")); + ASSUME(a = libnormalform_all(&dom1, libnormalform_function(&fun1), libnormalform_function(&fun2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("ALL(dom1, FUNCTION(fun1), FUNCTION(fun2))")); + spec.get_function = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_function = &get_function; + free(s); + + ASSUME(s = strdup("ANY(dom1, FUNCTION(fun1), FUNCTION(fun2))")); + ASSUME(a = libnormalform_any(&dom1, libnormalform_function(&fun1), libnormalform_function(&fun2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("ANY(dom1, FUNCTION(fun1), FUNCTION(fun2))")); + spec.get_function = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_function = &get_function; + free(s); + + ASSUME(s = strdup("ONE(dom1, FUNCTION(fun1), FUNCTION(fun2))")); + ASSUME(a = libnormalform_one(&dom1, libnormalform_function(&fun1), libnormalform_function(&fun2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("ONE(dom1, FUNCTION(fun1), FUNCTION(fun2))")); + spec.get_function = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_function = &get_function; + free(s); + + ASSUME(s = strdup("NOT(ONE(dom1, TRUE, FALSE))")); + ASSUME(a = libnormalform_not(libnormalform_one(&dom1, libnormalform_true(), libnormalform_false()))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("NOT(ONE(dom1, FUNCTION(fun1), FUNCTION(fun2)))")); + spec.get_function = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_function = &get_function; + free(s); + + ASSUME(s = strdup(" XNOR(VARIABLE(var1), VARIABLE(var2))")); + ASSUME(a = libnormalform_xnor2(libnormalform_variable(&var1), libnormalform_variable(&var2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("IF(VARIABLE(var1), VARIABLE(var2)) ")); + ASSUME(a = libnormalform_if2(libnormalform_variable(&var1), libnormalform_variable(&var2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("IMPLY ( VARIABLE ( var1 ) , VARIABLE ( var2 ) )")); + ASSUME(a = libnormalform_imply2(libnormalform_variable(&var1), libnormalform_variable(&var2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("\nNIF(VARIABLE(var1),VARIABLE(var2))\t")); + ASSUME(a = libnormalform_nif2(libnormalform_variable(&var1), libnormalform_variable(&var2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("\tNIMPLY(VARIABLE(var1)\n,\t VARIABLE(var2))\n")); + ASSUME(a = libnormalform_nimply2(libnormalform_variable(&var1), libnormalform_variable(&var2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("EXISTS(dom1, FUNCTION(fun1))")); + ASSUME(a = libnormalform_exists(&dom1, libnormalform_function(&fun1))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("NEXISTS(dom1, FUNCTION(fun1))")); + ASSUME(a = libnormalform_nexists(&dom1, libnormalform_function(&fun1))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("UNIQUE(dom2, FUNCTION(fun2))")); + ASSUME(a = libnormalform_unique(&dom2, libnormalform_function(&fun2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("EXISTENTIALLY(dom1, FUNCTION(fun1))")); + ASSUME(a = libnormalform_existentially(&dom1, libnormalform_function(&fun1))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("UNIVERSALLY(dom1, FUNCTION(fun1))")); + ASSUME(a = libnormalform_universally(&dom1, libnormalform_function(&fun1))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("UNIQUELY(dom2, FUNCTION(fun2))")); + ASSUME(a = libnormalform_uniquely(&dom2, libnormalform_function(&fun2))); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("EMPTY(dom1)")); + ASSUME(a = libnormalform_empty(&dom1)); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("NONEMPTY(dom1)")); + ASSUME(a = libnormalform_nonempty(&dom1)); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("SINGLETON(dom2)")); + ASSUME(a = libnormalform_singleton(&dom2)); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("ALL(dom1, FUNCTION@0(fun1), REF(0))")); + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_ref(a)); + ASSUME(a = libnormalform_all(&dom1, a, b)); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + free(s); + ASSERT_EQUAL(a, b); + ASSERT(a->type == TYPE_ALL); + ASSERT(b->type == TYPE_ALL); + ASSERT(a->data.qualifier.antecedent->refcount == 2); + ASSERT(a->data.qualifier.predicate->refcount == 2); + ASSERT(a->data.qualifier.antecedent == a->data.qualifier.predicate); + ASSERT(b->data.qualifier.antecedent->refcount == 2); + ASSERT(b->data.qualifier.predicate->refcount == 2); + ASSERT(b->data.qualifier.antecedent == b->data.qualifier.predicate); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("ALL(dom1, VARIABLE(var1), FALSE)")); + ASSUME(a = libnormalform_from_string(s, NULL, &spec)); + libnormalform_free(a); + free(s); + + ASSUME(s = strdup("ALL(dom1, ZZRIABLE(var1), FALSE)")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1, VARIABLE(var1), FALSE) X")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1, VARIABLE(var1), FALSE) X")); + ASSERT(a = libnormalform_from_string(s, &p, &spec)); + libnormalform_free(a); + ASSERT(!strcmp(p, " X")); + free(s); + + ASSUME(s = strdup("ALL(dom1, VARIABLE(var1), FALSE")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("AL")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1, VARIABLE(var1), FALSE,")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1, VARIABLE(var1) FALSE)")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1 VARIABLE(var1), FALSE)")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1, REF(0), FUNCTION@0(fun1))")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1, FUNCTION@1(fun1), FALSE)")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1, REF(2), FALSE)")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1, AND@0(VARIABLE(var1), REF(0)), FALSE)")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EINVAL); + free(s); + + ASSUME(s = strdup("ALL(dom1, VARIABLE(varx), FALSE)")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == 0); + errno = 1; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == 1); + free(s); + + ASSUME(s = strdup("ALL(dom1, FUNCTION(funx), FALSE)")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == 0); + errno = 1; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == 1); + free(s); + + ASSUME(s = strdup("ALL(domx, VARIABLE(var1), FALSE)")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == 0); + errno = 1; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == 1); + free(s); + + ASSUME(s = strdup("ALL(dom1, FUNCTION(fun1), FUNCTION(fun2))")); + spec.get_map = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_map = &get_map; + free(s); + + ASSUME(s = strdup("ANY(dom1, FUNCTION(fun1), FUNCTION(fun2))")); + spec.get_map = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_map = &get_map; + free(s); + + ASSUME(s = strdup("ONE(dom1, FUNCTION(fun1), FUNCTION(fun2))")); + spec.get_map = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_map = &get_map; + free(s); + + ASSUME(s = strdup("NOT(ONE(dom1, FUNCTION(fun1), FUNCTION(fun2)))")); + spec.get_map = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_map = &get_map; + free(s); + + ASSUME(s = strdup("AND()")); + errno = 0; + ASSUME(a = libnormalform_from_string(s, NULL, &spec)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + free(s); + + ASSUME(s = strdup("OR()")); + errno = 0; + ASSUME(a = libnormalform_from_string(s, NULL, &spec)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + free(s); + + ASSUME(s = strdup("XOR()")); + errno = 0; + ASSUME(a = libnormalform_from_string(s, NULL, &spec)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + free(s); + + ASSUME(s = strdup("XNOR()")); + errno = 0; + ASSUME(a = libnormalform_from_string(s, NULL, &spec)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + free(s); + + ASSUME(s = strdup("IMPLY()")); + errno = 0; + ASSUME(a = libnormalform_from_string(s, NULL, &spec)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + free(s); + + ASSUME(s = strdup("NIF()")); + errno = 0; + ASSUME(a = libnormalform_from_string(s, NULL, &spec)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + free(s); + + ASSUME(s = strdup("NIMPLY()")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EDOM); + free(s); + + ASSUME(s = strdup("IF()")); + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == EDOM); + free(s); + + ASSUME(s = strdup("XOR(ALL(dom1, TRUE, FALSE), ALL(dom2, TRUE, FALSE))")); + ASSUME(a = libnormalform_all(&dom1, libnormalform_true(), libnormalform_false())); + ASSUME(b = libnormalform_all(&dom2, libnormalform_true(), libnormalform_false())); + ASSUME(a = libnormalform_xor2(a, b)); + ASSUME(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + ASSUME(s = strdup("TRANSFORMATION(trans1, FUNCTION(fun1))")); + spec.get_transformer = NULL; + errno = 0; + ASSERT(!libnormalform_from_string(s, NULL, &spec) && errno == ENOENT); + spec.get_transformer = &get_transformer; + free(s); + + ASSUME(s = strdup("TRANSFORMATION(trans1, FUNCTION(fun1))")); + ASSUME(a = libnormalform_transformation(&trans1, libnormalform_function(&fun1))); + ASSERT(b = libnormalform_from_string(s, NULL, &spec)); + ASSERT_EQUAL(a, b); + free(s); + libnormalform_free(a); + libnormalform_free(b); + + TEST_END; +} + + +#endif diff --git a/libnormalform_function.c b/libnormalform_function.c new file mode 100644 index 0000000..3978a4a --- /dev/null +++ b/libnormalform_function.c @@ -0,0 +1,287 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (Function literal implementation) + */ +static LIBNORMALFORM_SENTENCE * +function_inverse(LIBNORMALFORM_SENTENCE *this) +{ + LIBNORMALFORM_SENTENCE *ret = libnormalform_function(this->data.literal.atom.function); + if (ret) + ret->data.literal.inverted = this->data.literal.inverted ^ 1; + return ret; +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (Function literal implementation) + */ +static int +function_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + if (other->type != TYPE_FUNCTION || this->data.literal.atom.function != other->data.literal.atom.function) + return 0; + *inv_out = this->data.literal.inverted ^ other->data.literal.inverted; + return 1; +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (Function literal implementation) + */ +static int +function_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + int r = this->data.literal.atom.function->evaluate(this->data.literal.atom.function->user_data, input); + return r < 0 ? r : ((r > 0) ^ this->data.literal.inverted); +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_function)(struct libnormalform_function *function) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_FUNCTION, + .inverse = &function_inverse, + .equals = &function_equals, + .evaluate = &function_evaluate + }; + + LIBNORMALFORM_SENTENCE *ret = malloc(sizeof(*ret)); + if (ret) { + *ret = prototype; + ret->hash = LITERAL_HASH(function); + ret->data.literal.atom.function = function; + ret->data.literal.inverted = 0; + } + return ret; +} + + +#else + + +static int +tautology(void *user_data, void *input) +{ + (void) user_data; + (void) input; + return 1; +} + + +static int +contradiction(void *user_data, void *input) +{ + (void) user_data; + (void) input; + return 0; +} + + +static int +einval(void *user_data, void *input) +{ + (void) user_data; + (void) input; + errno = EINVAL; + return -1; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_function fun1, fun2; + struct libnormalform_variable var1; + struct libnormalform_mapping map[1]; + struct libnormalform_map domain; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *f1, *f2; + + domain.nmappings = 0; + + ASSUME(a = libnormalform_function(&fun1)); + ASSERT(a->refcount == 1); + ASSERT(a->travel_index == 0); + ASSERT(a->travel_count == 0); + ASSERT(a->type == TYPE_FUNCTION); + ASSERT(a->data.literal.inverted == 0); + ASSERT(a->data.literal.atom.function == &fun1); + ASSERT_EQUAL(a, a); + fun1.evaluate = tautology; + ASSERT(libnormalform_evaluate(a) == 1); + fun1.evaluate = contradiction; + ASSERT(libnormalform_evaluate(a) == 0); + fun1.evaluate = einval; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_EQUAL(a, b); + ASSUME(b = libnormalform_not(b)); + ASSERT_INVEQUAL(a, b); + ASSERT_INVEQUAL(b, a); + libnormalform_free(b); + + ASSUME(b = libnormalform_function(&fun2)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(f1 = libnormalform_function(&fun1)); + ASSUME(f2 = libnormalform_function(&fun2)); + + ASSERT_NOTEQUAL(a, v1); + + ASSUME(b = libnormalform_and2(REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_or2(REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_xor2(REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_all(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_any(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_one(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_and2(a, b)); + fun1.evaluate = tautology; + fun2.evaluate = einval; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = einval; + fun2.evaluate = tautology; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = tautology; + fun2.evaluate = tautology; + ASSERT(libnormalform_evaluate(a) == 1); + libnormalform_free(a); + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_or2(a, b)); + fun1.evaluate = contradiction; + fun2.evaluate = einval; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = einval; + fun2.evaluate = contradiction; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = contradiction; + fun2.evaluate = contradiction; + ASSERT(libnormalform_evaluate(a) == 0); + libnormalform_free(a); + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_xor2(a, b)); + fun1.evaluate = contradiction; + fun2.evaluate = einval; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = einval; + fun2.evaluate = contradiction; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = contradiction; + fun2.evaluate = contradiction; + ASSERT(libnormalform_evaluate(a) == 0); + libnormalform_free(a); + + domain.nmappings = 1; + domain.mappings = map; + map[0].key = NULL; + map[0].value = NULL; + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_all(&domain, a, b)); + fun1.evaluate = tautology; + fun2.evaluate = einval; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = einval; + fun2.evaluate = tautology; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = tautology; + fun2.evaluate = tautology; + ASSERT(libnormalform_evaluate(a) == 1); + libnormalform_free(a); + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_any(&domain, a, b)); + fun1.evaluate = tautology; + fun2.evaluate = einval; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = einval; + fun2.evaluate = tautology; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = tautology; + fun2.evaluate = tautology; + ASSERT(libnormalform_evaluate(a) == 1); + libnormalform_free(a); + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_one(&domain, a, b)); + fun1.evaluate = tautology; + fun2.evaluate = einval; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = einval; + fun2.evaluate = tautology; + errno = 0; + ASSERT(libnormalform_evaluate(a) == -1 && errno == EINVAL); + fun1.evaluate = tautology; + fun2.evaluate = tautology; + ASSERT(libnormalform_evaluate(a) == 1); + libnormalform_free(a); + + libnormalform_free(v1); + libnormalform_free(f1); + libnormalform_free(f2); + + TEST_END; +} + + +#endif diff --git a/libnormalform_if.c b/libnormalform_if.c new file mode 100644 index 0000000..17bb380 --- /dev/null +++ b/libnormalform_if.c @@ -0,0 +1,249 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +LIBNORMALFORM_SENTENCE * +(libnormalform_if)(LIBNORMALFORM_SENTENCE **xs) +{ + LIBNORMALFORM_SENTENCE *x, *ret; + int inv; + + /* 0 ← x = ¬x, but 1 ← x = 1, so at least one argument is required */ + ret = *xs++; + if (!ret) { + errno = EDOM; + return NULL; + } + + if (ret->type == TYPE_TRUE) { + /* 1 ← x = 1 */ + goto return_asis; + } + + for (; (x = *xs); xs++) { + if (!ret) { + /* error handling */ + libnormalform_free(x); + + } else if (x->type == TYPE_FALSE) { + /* x ← 0 ← y = 1 ← y = 1 */ + goto return_true; + + } else if (x->type == TYPE_TRUE) { + /* x ← 1 = x */ + libnormalform_free(x); + + } else if (x->equals(x, ret, &inv)) { + if (!inv) { + /* x ← x ← y = 1 ← y = 1 */ + goto return_true; + + } else { + /* x ← ¬x = x */ + libnormalform_free(x); + } + + } else if (ret->type == TYPE_FALSE) { + /* 0 ← x = ¬x */ + libnormalform_free(ret); + ret = libnormalform_not(x); + + } else { + /* l ← r = r → l = ¬(r ∧ ¬l) = ¬r ∨ l = l ∨ ¬r */ + ret = libnormalform_or2(ret, libnormalform_not(x)); + if (ret && ret->type == TYPE_TRUE) { + /* 1 ← x = 1 */ + goto return_true; + } + } + } + + return ret; + +return_true: + libnormalform_free(ret); + ret = libnormalform_true(); +return_asis: + while (*xs) + libnormalform_free(*xs++); + return ret; +} + + +#else + + +#define IF(...) LIBNORMALFORM_IF(__VA_ARGS__) + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!IF(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!IF(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!IF(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!IF(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!IF(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!IF(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = IF(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = IF(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + /* IF () -> EDOM */ + errno = 0; + ASSERT(!IF() && errno == EDOM); + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* IF (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* IF (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(TRUE, F, F); /* IF (FALSE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, F, T); /* IF (FALSE, TRUE) -> FALSE */ + ASSERT_CONST(TRUE, T, F); /* IF (TRUE, FALSE) -> TRUE */ + ASSERT_CONST(TRUE, T, T); /* IF (TRUE, TRUE) -> TRUE */ + + ASSERT_EVAL2(TRUE, F, F); /* IF (var(FALSE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(FALSE, F, T); /* IF (var(FALSE), var(TRUE)) => FALSE */ + ASSERT_EVAL2(TRUE, T, F); /* IF (var(TRUE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(TRUE, T, T); /* IF (var(TRUE), var(TRUE)) => TRUE */ + +#ifndef USE_TWO + + /* IF (x) -> x */ + ASSUME(a = IF(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* IF (x, TRUE) -> x */ + ASSUME(a = IF(REF(v1), T)); + ASSERT(a == v1); + libnormalform_free(a); + + /* IF (TRUE, x) -> TRUE */ + ASSUME(a = IF(T, REF(v1))); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* IF (x, FALSE) -> TRUE */ + ASSUME(a = IF(REF(v1), F)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* IF (FALSE, x) -> NOT x */ + ASSUME(a = IF(F, REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* IF (x, x) -> TRUE */ + ASSUME(a = IF(REF(v1), REF(v1))); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* IF (x, NOT x) -> x */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = IF(REF(v1), a)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* IF (x, y) -> OR (x, NOT y) */ + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_or2(REF(v1), b)); + ASSUME(a = IF(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#ifndef USE_TWO + + /* IF (x, y, z) -> NOT AND (NOT x, y, z) */ + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_andl(b, REF(v2), REF(v3), NULL)); + ASSUME(a = IF(REF(v1), REF(v2), REF(v3))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* IF (x, y) -> independence from IF (y, x) */ + ASSUME(a = IF(REF(v1), REF(v2))); + ASSUME(b = IF(REF(v2), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#endif + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(ts); + libnormalform_free(fs); + + TEST_END; +} + + +#endif diff --git a/libnormalform_if2.c b/libnormalform_if2.c new file mode 100644 index 0000000..208c789 --- /dev/null +++ b/libnormalform_if2.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_if2)(LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); + + +#else + +#define USE_TWO +#include "libnormalform_if.c" + +#endif diff --git a/libnormalform_if_checked.c b/libnormalform_if_checked.c new file mode 100644 index 0000000..2a91f2b --- /dev/null +++ b/libnormalform_if_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_if_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_if.c" + +#endif diff --git a/libnormalform_ifl.c b/libnormalform_ifl.c new file mode 100644 index 0000000..b811d7e --- /dev/null +++ b/libnormalform_ifl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_ifl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_if.c" + +#endif diff --git a/libnormalform_ifl_checked.c b/libnormalform_ifl_checked.c new file mode 100644 index 0000000..25ab4f9 --- /dev/null +++ b/libnormalform_ifl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_ifl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_if.c" + +#endif diff --git a/libnormalform_ifl_macro_test.c b/libnormalform_ifl_macro_test.c new file mode 100644 index 0000000..a911999 --- /dev/null +++ b/libnormalform_ifl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_if.c" +#endif diff --git a/libnormalform_imply.c b/libnormalform_imply.c new file mode 100644 index 0000000..823abf7 --- /dev/null +++ b/libnormalform_imply.c @@ -0,0 +1,166 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +LIBNORMALFORM_SENTENCE * +(libnormalform_imply)(LIBNORMALFORM_SENTENCE **xs) +{ + LIBNORMALFORM_SENTENCE *t; + size_t n = 0, i; + + while (xs[n]) + n += 1; + + if (!n) { + /* 1 → x = x and 0 → x = 1 */ + return libnormalform_true(); + } + + /* a → b → c = a → (b → c) = (b → c) ← a = (c ← b) ← a = c ← b ← a */ + for (i = 0; i < n--; i++) { + t = xs[i]; + xs[i] = xs[n]; + xs[n] = t; + } + return libnormalform_if(xs); +} + + +#else + + +#define IMPLY(...) LIBNORMALFORM_IMPLY(__VA_ARGS__) + + +int +main(void) +{ + struct libnormalform_variable var1, var2, var3; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!IMPLY(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!IMPLY(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!IMPLY(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!IMPLY(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!IMPLY(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!IMPLY(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = IMPLY(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = IMPLY(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + ASSERT_CONST(TRUE); /* IMPLY () -> TRUE */ + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* IMPLY (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* IMPLY (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(TRUE, F, F); /* IMPLY (FALSE, FALSE) -> TRUE */ + ASSERT_CONST(TRUE, F, T); /* IMPLY (FALSE, TRUE) -> TRUE */ + ASSERT_CONST(FALSE, T, F); /* IMPLY (TRUE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, T, T); /* IMPLY (TRUE, TRUE) -> TRUE */ + + ASSERT_EVAL2(TRUE, F, F); /* IMPLY (var(FALSE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(TRUE, F, T); /* IMPLY (var(FALSE), var(TRUE)) => TRUE */ + ASSERT_EVAL2(FALSE, T, F); /* IMPLY (var(TRUE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(TRUE, T, T); /* IMPLY (var(TRUE), var(TRUE)) => TRUE */ + +#ifndef USE_TWO + + /* IMPLY (x) -> x */ + ASSUME(a = IMPLY(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* IMPLY (x, y) -> IF (y, x) */ + ASSUME(a = IMPLY(REF(v1), REF(v2))); + ASSUME(b = libnormalform_if2(REF(v2), REF(v1))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#ifndef USE_TWO + + /* IMPLY (x, y, z) -> IF (z, y, x) */ + ASSUME(a = IMPLY(REF(v1), REF(v2), REF(v3))); + ASSUME(b = libnormalform_ifl(REF(v3), REF(v2), REF(v1), NULL)); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* IMPLY (x, y) -> independence from IMPLY (y, x) */ + ASSUME(a = IMPLY(REF(v1), REF(v2))); + ASSUME(b = IMPLY(REF(v2), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#endif + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(ts); + libnormalform_free(fs); + return 0; +} + + +#endif diff --git a/libnormalform_imply2.c b/libnormalform_imply2.c new file mode 100644 index 0000000..63b7562 --- /dev/null +++ b/libnormalform_imply2.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_imply2)(LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); + + +#else + +#define USE_TWO +#include "libnormalform_imply.c" + +#endif diff --git a/libnormalform_imply_checked.c b/libnormalform_imply_checked.c new file mode 100644 index 0000000..8bf9a3e --- /dev/null +++ b/libnormalform_imply_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_imply_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_imply.c" + +#endif diff --git a/libnormalform_implyl.c b/libnormalform_implyl.c new file mode 100644 index 0000000..9e13bbe --- /dev/null +++ b/libnormalform_implyl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_implyl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_imply.c" + +#endif diff --git a/libnormalform_implyl_checked.c b/libnormalform_implyl_checked.c new file mode 100644 index 0000000..0a538f9 --- /dev/null +++ b/libnormalform_implyl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_implyl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_imply.c" + +#endif diff --git a/libnormalform_implyl_macro_test.c b/libnormalform_implyl_macro_test.c new file mode 100644 index 0000000..960b4b1 --- /dev/null +++ b/libnormalform_implyl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_imply.c" +#endif diff --git a/libnormalform_nand.c b/libnormalform_nand.c new file mode 100644 index 0000000..efe7161 --- /dev/null +++ b/libnormalform_nand.c @@ -0,0 +1,235 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +LIBNORMALFORM_SENTENCE * +(libnormalform_nand)(LIBNORMALFORM_SENTENCE **xs) +{ + LIBNORMALFORM_SENTENCE *x, *ret; + int inv; + + /* 1 ⊼ x = ¬x, but 0 ⊼ x = 1, so at least one argument is required */ + ret = *xs++; + if (!ret) { + errno = EDOM; + return NULL; + } + + for (; (x = *xs); xs++) { + if (!ret) { + /* error handling */ + libnormalform_free(x); + + } else if (x->type == TYPE_FALSE || ret->type == TYPE_FALSE) { + set_to_true: + /* x ⊼ 0 = 0 ⊼ x = 1 */ + libnormalform_free(ret); + libnormalform_free(x); + ret = libnormalform_true(); + + } else if (x->type == TYPE_TRUE) { + /* x ⊼ 1 = ¬x */ + libnormalform_free(x); + ret = libnormalform_not(ret); + + } else if (ret->type == TYPE_TRUE) { + set_to_not_x: + /* 1 ⊼ x = ¬x */ + libnormalform_free(ret); + ret = libnormalform_not(x); + + } else if (ret->equals(ret, x, &inv)) { + if (!inv) { + /* x ⊼ x = ¬x */ + goto set_to_not_x; + } else { + /* x ⊼ ¬x = 1 */ + goto set_to_true; + } + + } else { + /* a ⊼ b = ¬(a ∧ b) = ¬a ∨ ¬b */ + ret = libnormalform_or2(libnormalform_not(ret), libnormalform_not(x)); + } + } + + return ret; +} + + +#else + + +#define NAND(...) LIBNORMALFORM_NAND(__VA_ARGS__) + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!NAND(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!NAND(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!NAND(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!NAND(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!NAND(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!NAND(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = NAND(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = NAND(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + /* NAND () -> EDOM */ + errno = 0; + ASSERT(!NAND() && errno == EDOM); + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* NAND (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* NAND (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(TRUE, F, F); /* NAND (FALSE, FALSE) -> TRUE */ + ASSERT_CONST(TRUE, F, T); /* NAND (FALSE, TRUE) -> TRUE */ + ASSERT_CONST(TRUE, T, F); /* NAND (TRUE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, T, T); /* NAND (TRUE, TRUE) -> FALSE */ + + ASSERT_EVAL2(TRUE, F, F); /* NAND (var(FALSE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(TRUE, F, T); /* NAND (var(FALSE), var(TRUE)) => TRUE */ + ASSERT_EVAL2(TRUE, T, F); /* NAND (var(TRUE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(FALSE, T, T); /* NAND (var(TRUE), var(TRUE)) => FALSE */ + +#ifndef USE_TWO + + /* NAND (x) -> x */ + ASSUME(a = NAND(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* NAND (x, FALSE) -> TRUE */ + ASSUME(a = NAND(REF(v1), F)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* NAND (FALSE, x) -> TRUE */ + ASSUME(a = NAND(F, REF(v1))); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* NAND (x, TRUE) -> NOT x */ + ASSUME(a = NAND(REF(v1), T)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* NAND (TRUE, x) -> NOT x */ + ASSUME(a = NAND(T, REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* NAND (x, x) -> NOT x */ + ASSUME(a = NAND(REF(v1), REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* NAND (x, NOT x) -> TRUE */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = NAND(REF(v1), a)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* NAND (x, y) -> NOT AND (x, y) */ + ASSUME(b = libnormalform_and2(REF(v1), REF(v2))); + ASSUME(a = NAND(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* NAND (y, x) -> NAND (x, y) */ + ASSUME(a = NAND(REF(v2), REF(v1))); + ASSUME(b = NAND(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#ifndef USE_TWO + + /* NAND (x, y, z) -> NOT AND (NOT AND (x, y), z) */ + ASSUME(b = libnormalform_and2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_not(b)); + ASSUME(b = libnormalform_and2(b, REF(v3))); + ASSUME(a = NAND(REF(v1), REF(v2), REF(v3))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#endif + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(ts); + libnormalform_free(fs); + + TEST_END; +} + + +#endif diff --git a/libnormalform_nand2.c b/libnormalform_nand2.c new file mode 100644 index 0000000..dbc94f2 --- /dev/null +++ b/libnormalform_nand2.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nand2)(LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); + + +#else + +#define USE_TWO +#include "libnormalform_nand.c" + +#endif diff --git a/libnormalform_nand_checked.c b/libnormalform_nand_checked.c new file mode 100644 index 0000000..4b80cb9 --- /dev/null +++ b/libnormalform_nand_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nand_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_nand.c" + +#endif diff --git a/libnormalform_nandl.c b/libnormalform_nandl.c new file mode 100644 index 0000000..a785dd1 --- /dev/null +++ b/libnormalform_nandl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nandl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_nand.c" + +#endif diff --git a/libnormalform_nandl_checked.c b/libnormalform_nandl_checked.c new file mode 100644 index 0000000..eea01cc --- /dev/null +++ b/libnormalform_nandl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nandl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_nand.c" + +#endif diff --git a/libnormalform_nandl_macro_test.c b/libnormalform_nandl_macro_test.c new file mode 100644 index 0000000..2c181a0 --- /dev/null +++ b/libnormalform_nandl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_nand.c" +#endif diff --git a/libnormalform_nexists.c b/libnormalform_nexists.c new file mode 100644 index 0000000..3996498 --- /dev/null +++ b/libnormalform_nexists.c @@ -0,0 +1,243 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nexists)(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); + + +#else + + +static int +evalbool(void *user_data, void *input) +{ + int *vp = input; + (void) user_data; + return *vp; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1; + struct libnormalform_function fun1; + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *c, *v1; + int t = 1, f = 0; + + ASSUME(v1 = libnormalform_variable(&var1)); + + errno = 0; + ASSERT(!libnormalform_exists(&dom1, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_exists(&dom1, NULL) && errno == 1); + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + /* ∄x.⊥ = ⊤ */ + ASSUME(a = libnormalform_nexists(&dom1, libnormalform_false())); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* ∄x.φ irreducable */ + ASSUME(a = libnormalform_nexists(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ALL); + libnormalform_free(a); + + /* ∄x.⊤ irreducable */ + ASSUME(a = libnormalform_nexists(&dom1, libnormalform_true())); + ASSERT(a->type == TYPE_ALL); + libnormalform_free(a); + + ASSUME(a = libnormalform_nexists(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ALL); + ASSERT(a->refcount == 1); + + dom1.nmappings = 1; + + /* ∄x∈{a}.⊥ = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∄x∈{a}.⊤ = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 2; + + /* ∄x∈{a,b}.⊥ = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∄x∈{a,b}.⊤ = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + /* ∄x∈∅.⊥ = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∄x∈∅.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 0; + + libnormalform_free(a); + + fun1.evaluate = &evalbool; + dom1.nmappings = 2; + + ASSUME(a = libnormalform_nexists(&dom1, libnormalform_function(&fun1))); + ASSERT(a->type == TYPE_ALL); + map[0].value = NULL; + map[1].value = NULL; + + /* ∄x∈{⊥, ⊥}.x = ⊤ */ + map[0].key = &f; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∄x∈{⊥, ⊤}.x = ⊥ */ + map[0].key = &f; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∄x∈{⊤, ⊥}.x = ⊥ */ + map[0].key = &t; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∄x∈{⊤, ⊤}.x = ⊥ */ + map[0].key = &t; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∄x∈{⊤, ⊤, ⊤}.x = ⊥ */ + map[0].key = &t; + map[1].key = &t; + map[2].key = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + libnormalform_free(a); + + ASSUME(a = libnormalform_nexists(&dom1, REF(v1))); + + /* ∄x∈X.P(x) = ¬∃x∈X.(P(x) ∧ ⊤) */ + ASSUME(b = libnormalform_any(&dom1, REF(v1), libnormalform_true())); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from ∄x∈X.Q(x) */ + ASSUME(b = libnormalform_nexists(&dom1, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from ∄x∈Y.P(x)) */ + ASSUME(b = libnormalform_nexists(&dom2, REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) = ∀x∈X.(P(x) → ⊥) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), libnormalform_false())); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + /* ¬∄x∈X.P(x) = ∃x∈X.(P(x) ∧ ⊤) */ + ASSUME(c = libnormalform_not(REF(a))); + ASSUME(b = libnormalform_any(&dom1, REF(v1), libnormalform_true())); + ASSERT_EQUAL(c, b); + ASSERT(c->type == TYPE_ANY); + libnormalform_free(b); + libnormalform_free(c); + + /* ∄x∈X.P(x) independent from TRUE */ + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from FALSE */ + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from variable1 */ + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from NOT variable1 */ + ASSUME(b = libnormalform_not(libnormalform_variable(&var1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from function1 */ + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from NOT function1 */ + ASSUME(b = libnormalform_not(libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from T(function1) */ + ASSUME(b = libnormalform_transformation(&trans, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from ∀x∈X.(P(x) → ⊤) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), libnormalform_true())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from ∃!x∈X.(P(x) ∧ ⊤) */ + ASSUME(b = libnormalform_one(&dom1, REF(v1), libnormalform_true())); + ASSERT_NOTEQUAL(a, b); + + /* ∄x∈X.P(x) independent from ¬∃!x∈X.(P(x) ∧ ⊤) */ + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from AND (P, P) */ + ASSUME(b = libnormalform_and2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(X) independent from OR (P, P) */ + ASSUME(b = libnormalform_or2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) independent from XOR (P, P) */ + ASSUME(b = libnormalform_xor2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∄x∈X.P(x) = ¬∃x∈X.P(x) */ + ASSUME(b = libnormalform_exists(&dom1, REF(v1))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + ASSUME(b = libnormalform_not(libnormalform_exists(&dom1, REF(v1)))); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + libnormalform_free(v1); + + TEST_END; +} + + +#endif diff --git a/libnormalform_nif.c b/libnormalform_nif.c new file mode 100644 index 0000000..5b2ae24 --- /dev/null +++ b/libnormalform_nif.c @@ -0,0 +1,228 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +LIBNORMALFORM_SENTENCE * +(libnormalform_nif)(LIBNORMALFORM_SENTENCE **xs) +{ + LIBNORMALFORM_SENTENCE *x, *ret; + int inv; + + /* 0 ↚ x = x, but 1 ↚ x = 0 */ + ret = libnormalform_false(); + + for (; (x = *xs); xs++) { + if (!ret) { + /* error handling */ + libnormalform_free(x); + + } else if (ret->type == TYPE_TRUE || x->type == TYPE_FALSE) { + set_to_false: + /* 1 ↚ x = 0 */ + /* x ↚ 0 = 0 */ + libnormalform_free(x); + libnormalform_free(ret); + ret = libnormalform_false(); + + } else if (ret->type == TYPE_FALSE) { + set_to_x: + /* 0 ↚ x = x */ + libnormalform_free(ret); + ret = x; + + } else if (ret->equals(ret, x, &inv)) { + if (!inv) { + /* x ↚ x = 0 */ + goto set_to_false; + + } else { + /* ¬x ↚ x = x (0 ↚ 1 = 1, 1 ↚ 0 = 0) */ + goto set_to_x; + } + + } else { + /* l ↚ r = ¬(l ← r) = ¬(r → l) = ¬¬(r ∧ ¬l) = r ∧ ¬l = ¬l ∧ r */ + ret = libnormalform_and2(libnormalform_not(ret), x); + } + } + + return ret; +} + + +#else + + +#define NIF(...) LIBNORMALFORM_NIF(__VA_ARGS__) + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!NIF(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!NIF(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!NIF(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!NIF(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!NIF(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!NIF(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = NIF(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = NIF(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + ASSERT_CONST(FALSE); /* NIF () -> FALSE */ + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* NIF (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* NIF (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(FALSE, F, F); /* NIF (FALSE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, F, T); /* NIF (FALSE, TRUE) -> TRUE */ + ASSERT_CONST(FALSE, T, F); /* NIF (TRUE, FALSE) -> FALSE */ + ASSERT_CONST(FALSE, T, T); /* NIF (TRUE, TRUE) -> FALSE */ + + ASSERT_EVAL2(FALSE, F, F); /* NIF (var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(TRUE, F, T); /* NIF (var(FALSE), var(TRUE)) => TRUE */ + ASSERT_EVAL2(FALSE, T, F); /* NIF (var(TRUE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(FALSE, T, T); /* NIF (var(TRUE), var(TRUE)) => FALSE */ + +#ifndef USE_TWO + + /* NIF (x) -> x */ + ASSUME(a = NIF(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* NIF (x, TRUE) -> NOT x */ + ASSUME(a = NIF(REF(v1), T)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* NIF (TRUE, x) -> FALSE */ + ASSUME(a = NIF(T, REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* NIF (x, FALSE) -> FALSE */ + ASSUME(a = NIF(REF(v1), F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* NIF (FALSE, x) -> x */ + ASSUME(a = NIF(F, REF(v1))); + ASSERT_EQUAL(a, v1); + libnormalform_free(a); + + /* NIF (x, x) -> FALSE */ + ASSUME(a = NIF(REF(v1), REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* NIF (NOT x, x) -> x */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = NIF(a, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* NIF (x, y) -> AND (NOT x, y) */ + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_and2(b, REF(v2))); + ASSUME(a = NIF(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#ifndef USE_TWO + + /* NIF (x, y, z) -> AND (NOT AND (NOT x, y), z) */ + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_nand2(b, REF(v2))); + ASSUME(b = libnormalform_and2(b, REF(v3))); + ASSUME(a = NIF(REF(v1), REF(v2), REF(v3))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* NIF (x, y) -> independence from NIF (y, x) */ + ASSUME(a = NIF(REF(v1), REF(v2))); + ASSUME(b = NIF(REF(v2), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#endif + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(ts); + libnormalform_free(fs); + + TEST_END; +} + + +#endif diff --git a/libnormalform_nif2.c b/libnormalform_nif2.c new file mode 100644 index 0000000..7a10793 --- /dev/null +++ b/libnormalform_nif2.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nif2)(LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); + + +#else + +#define USE_TWO +#include "libnormalform_nif.c" + +#endif diff --git a/libnormalform_nif_checked.c b/libnormalform_nif_checked.c new file mode 100644 index 0000000..b197188 --- /dev/null +++ b/libnormalform_nif_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nif_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_nif.c" + +#endif diff --git a/libnormalform_nifl.c b/libnormalform_nifl.c new file mode 100644 index 0000000..d2c401f --- /dev/null +++ b/libnormalform_nifl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nifl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_nif.c" + +#endif diff --git a/libnormalform_nifl_checked.c b/libnormalform_nifl_checked.c new file mode 100644 index 0000000..dac7871 --- /dev/null +++ b/libnormalform_nifl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nifl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_nif.c" + +#endif diff --git a/libnormalform_nifl_macro_test.c b/libnormalform_nifl_macro_test.c new file mode 100644 index 0000000..641d40e --- /dev/null +++ b/libnormalform_nifl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_nif.c" +#endif diff --git a/libnormalform_nimply.c b/libnormalform_nimply.c new file mode 100644 index 0000000..5469e62 --- /dev/null +++ b/libnormalform_nimply.c @@ -0,0 +1,172 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +LIBNORMALFORM_SENTENCE * +(libnormalform_nimply)(LIBNORMALFORM_SENTENCE **xs) +{ + LIBNORMALFORM_SENTENCE *t; + size_t n = 0, i; + + while (xs[n]) + n += 1; + + if (!n) { + /* 0 ↛ x = 0, but 1 ↛ x = ¬x, so at least one argument is required */ + errno = EDOM; + return NULL; + } + + /* a ↛ b ↛ c = a ↛ (b ↛ c) = (b ↛ c) ↚ a = (c ↚ b) ↚ a = c ↚ b ↚ a */ + for (i = 0; i < n--; i++) { + t = xs[i]; + xs[i] = xs[n]; + xs[n] = t; + } + return libnormalform_nif(xs); +} + + +#else + + +#define NIMPLY(...) LIBNORMALFORM_NIMPLY(__VA_ARGS__) + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!NIMPLY(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!NIMPLY(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!NIMPLY(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!NIMPLY(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!NIMPLY(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!NIMPLY(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = NIMPLY(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = NIMPLY(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + /* NIMPLY () -> EDOM */ + errno = 0; + ASSERT(!NIMPLY() && errno == EDOM); + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* NIMPLY (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* NIMPLY (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(FALSE, F, F); /* NIMPLY (FALSE, FALSE) -> FALSE */ + ASSERT_CONST(FALSE, F, T); /* NIMPLY (FALSE, TRUE) -> FALSE */ + ASSERT_CONST(TRUE, T, F); /* NIMPLY (TRUE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, T, T); /* NIMPLY (TRUE, TRUE) -> FALSE */ + + ASSERT_EVAL2(FALSE, F, F); /* NIMPLY (var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(FALSE, F, T); /* NIMPLY (var(FALSE), var(TRUE)) => FALSE */ + ASSERT_EVAL2(TRUE, T, F); /* NIMPLY (var(TRUE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(FALSE, T, T); /* NIMPLY (var(TRUE), var(TRUE)) => FALSE */ + +#ifndef USE_TWO + + /* NIMPLY (x) -> x */ + ASSUME(a = NIMPLY(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* NIMPLY (x, y) -> NIF (y, x) */ + ASSUME(a = NIMPLY(REF(v1), REF(v2))); + ASSUME(b = libnormalform_nif2(REF(v2), REF(v1))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#ifndef USE_TWO + + /* NIMPLY (x, y, z) -> NIF (z, y, x) */ + ASSUME(a = NIMPLY(REF(v1), REF(v2), REF(v3))); + ASSUME(b = libnormalform_nifl(REF(v3), REF(v2), REF(v1), NULL)); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* NIMPLY (x, y) -> independence from NIMPLY (y, x) */ + ASSUME(a = NIMPLY(REF(v1), REF(v2))); + ASSUME(b = NIMPLY(REF(v2), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#endif + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(ts); + libnormalform_free(fs); + + TEST_END; +} + + +#endif diff --git a/libnormalform_nimply2.c b/libnormalform_nimply2.c new file mode 100644 index 0000000..eb0a8df --- /dev/null +++ b/libnormalform_nimply2.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nimply2)(LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); + + +#else + +#define USE_TWO +#include "libnormalform_nimply.c" + +#endif diff --git a/libnormalform_nimply_checked.c b/libnormalform_nimply_checked.c new file mode 100644 index 0000000..74736e2 --- /dev/null +++ b/libnormalform_nimply_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nimply_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_nimply.c" + +#endif diff --git a/libnormalform_nimplyl.c b/libnormalform_nimplyl.c new file mode 100644 index 0000000..ba0786f --- /dev/null +++ b/libnormalform_nimplyl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nimplyl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_nimply.c" + +#endif diff --git a/libnormalform_nimplyl_checked.c b/libnormalform_nimplyl_checked.c new file mode 100644 index 0000000..fae5ce0 --- /dev/null +++ b/libnormalform_nimplyl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nimplyl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_nimply.c" + +#endif diff --git a/libnormalform_nimplyl_macro_test.c b/libnormalform_nimplyl_macro_test.c new file mode 100644 index 0000000..a343038 --- /dev/null +++ b/libnormalform_nimplyl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_nimply.c" +#endif diff --git a/libnormalform_nonempty.c b/libnormalform_nonempty.c new file mode 100644 index 0000000..a120bec --- /dev/null +++ b/libnormalform_nonempty.c @@ -0,0 +1,69 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nonempty)(struct libnormalform_map *); + + +#else + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + LIBNORMALFORM_SENTENCE *a, *b; + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + ASSUME(a = libnormalform_nonempty(&dom1)); + ASSERT(a->type == TYPE_ANY); + + ASSUME(b = libnormalform_nonempty(&dom2)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_nonempty(&dom1)); + ASSERT(b != a); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_empty(&dom1)); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_singleton(&dom1)); + ASSERT_NOTEQUAL(a, b); + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_not(libnormalform_nonempty(&dom1))); + ASSERT(b->type == TYPE_ALL); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + dom1.nmappings = 0; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 1; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 3; + ASSERT(libnormalform_evaluate(a) == 1); + + libnormalform_free(a); + + TEST_END; +} + + +#endif diff --git a/libnormalform_nor.c b/libnormalform_nor.c new file mode 100644 index 0000000..dbee41b --- /dev/null +++ b/libnormalform_nor.c @@ -0,0 +1,235 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +LIBNORMALFORM_SENTENCE * +(libnormalform_nor)(LIBNORMALFORM_SENTENCE **xs) +{ + LIBNORMALFORM_SENTENCE *x, *ret; + int inv; + + /* 0 ⊽ x = ¬x, but 1 ⊽ x = 0, so at least one argument is required */ + ret = *xs++; + if (!ret) { + errno = EDOM; + return NULL; + } + + for (; (x = *xs); xs++) { + if (!ret) { + /* error handling */ + libnormalform_free(x); + + } else if (x->type == TYPE_TRUE || ret->type == TYPE_TRUE) { + set_to_false: + /* x ⊽ 1 = 1 ⊽ x = 0 */ + libnormalform_free(ret); + libnormalform_free(x); + ret = libnormalform_false(); + + } else if (x->type == TYPE_FALSE) { + /* x ⊽ 0 = ¬x */ + libnormalform_free(x); + ret = libnormalform_not(ret); + + } else if (ret->type == TYPE_FALSE) { + set_to_not_x: + /* 0 ⊽ x = ¬x */ + libnormalform_free(ret); + ret = libnormalform_not(x); + + } else if (ret->equals(ret, x, &inv)) { + if (!inv) { + /* x ⊽ x = ¬x */ + goto set_to_not_x; + } else { + /* x ⊽ ¬x = 0 */ + goto set_to_false; + } + + } else { + /* a ⊽ b = ¬(a ∨ b) = ¬a ∧ ¬b */ + ret = libnormalform_and2(libnormalform_not(ret), libnormalform_not(x)); + } + } + + return ret; +} + + +#else + + +#define NOR(...) LIBNORMALFORM_NOR(__VA_ARGS__) + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!NOR(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!NOR(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!NOR(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!NOR(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!NOR(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!NOR(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = NOR(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = NOR(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + /* NOR () -> EDOM */ + errno = 0; + ASSERT(!NOR() && errno == EDOM); + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* NOR (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* NOR (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(TRUE, F, F); /* NOR (FALSE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, F, T); /* NOR (FALSE, TRUE) -> FALSE */ + ASSERT_CONST(FALSE, T, F); /* NOR (TRUE, FALSE) -> FALSE */ + ASSERT_CONST(FALSE, T, T); /* NOR (TRUE, TRUE) -> FALSE */ + + ASSERT_EVAL2(TRUE, F, F); /* NOR (var(FALSE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(FALSE, F, T); /* NOR (var(FALSE), var(TRUE)) => FALSE */ + ASSERT_EVAL2(FALSE, T, F); /* NOR (var(TRUE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(FALSE, T, T); /* NOR (var(TRUE), var(TRUE)) => FALSE */ + +#ifndef USE_TWO + + /* NOR (x) -> x */ + ASSUME(a = NOR(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* NOR (x, TRUE) -> FALSE */ + ASSUME(a = NOR(REF(v1), T)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* NOR (TRUE, x) -> FALSE */ + ASSUME(a = NOR(T, REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* NOR (x, FALSE) -> NOT x */ + ASSUME(a = NOR(REF(v1), F)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* NOR (FALSE, x) -> NOT x */ + ASSUME(a = NOR(F, REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* NOR (x, x) -> NOT x */ + ASSUME(a = NOR(REF(v1), REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* NOR (x, NOT x) -> FALSE x */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = NOR(REF(v1), a)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* NOR (x, y) -> NOT OR (x, y) */ + ASSUME(b = libnormalform_or2(REF(v1), REF(v2))); + ASSUME(a = NOR(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* NOR (y, x) -> NOR (x, y) */ + ASSUME(a = NOR(REF(v2), REF(v1))); + ASSUME(b = NOR(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#ifndef USE_TWO + + /* NOR (x, y, z) -> NOT OR (NOT OR (x, y), z) */ + ASSUME(b = libnormalform_or2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_not(b)); + ASSUME(b = libnormalform_or2(b, REF(v3))); + ASSUME(a = NOR(REF(v1), REF(v2), REF(v3))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#endif + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(ts); + libnormalform_free(fs); + + TEST_END; +} + + +#endif diff --git a/libnormalform_nor2.c b/libnormalform_nor2.c new file mode 100644 index 0000000..c9ee0aa --- /dev/null +++ b/libnormalform_nor2.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nor2)(LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); + + +#else + +#define USE_TWO +#include "libnormalform_nor.c" + +#endif diff --git a/libnormalform_nor_checked.c b/libnormalform_nor_checked.c new file mode 100644 index 0000000..8ba7a9f --- /dev/null +++ b/libnormalform_nor_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_nor_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_nor.c" + +#endif diff --git a/libnormalform_norl.c b/libnormalform_norl.c new file mode 100644 index 0000000..b6fa4df --- /dev/null +++ b/libnormalform_norl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_norl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_nor.c" + +#endif diff --git a/libnormalform_norl_checked.c b/libnormalform_norl_checked.c new file mode 100644 index 0000000..fb20f16 --- /dev/null +++ b/libnormalform_norl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_norl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_nor.c" + +#endif diff --git a/libnormalform_norl_macro_test.c b/libnormalform_norl_macro_test.c new file mode 100644 index 0000000..07b33d1 --- /dev/null +++ b/libnormalform_norl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_nor.c" +#endif diff --git a/libnormalform_not.c b/libnormalform_not.c new file mode 100644 index 0000000..fcc9604 --- /dev/null +++ b/libnormalform_not.c @@ -0,0 +1,115 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +LIBNORMALFORM_SENTENCE * +(libnormalform_not)(LIBNORMALFORM_SENTENCE *x) +{ + LIBNORMALFORM_SENTENCE *ret; + if (!x) + return NULL; + ret = x->inverse(x); + libnormalform_free(x); + return ret; +} + + +#else + + +static int +tautology(void *user_data, void *input) +{ + (void) user_data; + (void) input; + return 1; +} + + +static int +contradiction(void *user_data, void *input) +{ + (void) user_data; + (void) input; + return 0; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2; + struct libnormalform_function fun1, fun2; + struct libnormalform_map domain; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *f1, *f2; + int r1, r2; + + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + fun1.evaluate = &tautology; + fun2.evaluate = &contradiction; + domain.nmappings = 0; + + errno = 0; + ASSERT(!libnormalform_not(NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_not(NULL) && errno == 1); + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(f1 = libnormalform_function(&fun1)); + ASSUME(f2 = libnormalform_function(&fun2)); + +#define CHECK(X)\ + do {\ + ASSUME(a = libnormalform_not(REF(X)));\ + ASSERT((X)->refcount == 1);\ + ASSERT(a->refcount == 1);\ + ASSERT_INVEQUAL(a, (X));\ + ASSERT((r1 = a->evaluate(a, NULL)) >= 0);\ + ASSERT((r2 = (X)->evaluate((X), NULL)) >= 0);\ + ASSERT(r1 == 1 - r2);\ + libnormalform_free(a);\ + } while (0) + + CHECK(v1); + CHECK(f1); + +#undef CHECK + +#define CHECK(X)\ + do {\ + ASSUME(b = (X));\ + ASSUME(a = libnormalform_not(REF(b)));\ + ASSERT(b->refcount == 1);\ + ASSERT(a->refcount == 1);\ + ASSERT_INVEQUAL(a, b);\ + ASSERT((r1 = a->evaluate(a, NULL)) >= 0);\ + ASSERT((r2 = b->evaluate(b, NULL)) >= 0);\ + ASSERT(r1 == 1 - r2);\ + libnormalform_free(a);\ + libnormalform_free(b);\ + } while (0) + + CHECK(libnormalform_and2(REF(v1), REF(v2))); + CHECK(libnormalform_or2(REF(v1), REF(v2))); + CHECK(libnormalform_xor2(REF(v1), REF(v2))); + CHECK(libnormalform_all(&domain, REF(f1), REF(f2))); + CHECK(libnormalform_any(&domain, REF(f1), REF(f2))); + CHECK(libnormalform_one(&domain, REF(f1), REF(f2))); + +#undef CHECK + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(f1); + libnormalform_free(f2); + + TEST_END; +} + + +#endif diff --git a/libnormalform_one.c b/libnormalform_one.c new file mode 100644 index 0000000..b5c17f1 --- /dev/null +++ b/libnormalform_one.c @@ -0,0 +1,449 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (FOR ONE implementation) + */ +static LIBNORMALFORM_SENTENCE * +one_inverse(LIBNORMALFORM_SENTENCE *this) +{ + /* ¬∃x.φ ⇒ ∀x.¬φ */ + LIBNORMALFORM_SENTENCE *ret; + ret = libnormalform_one(this->data.qualifier.domain, + libnormalform_ref(this->data.qualifier.antecedent), + libnormalform_ref(this->data.qualifier.predicate)); + if (ret) { + if (this->atom) { + ret->atom = this->atom; + ret->atom->refcount += 1; + } + ret->type = TYPE_ONE ^ TYPE_NOT_ONE ^ this->type; + } + return ret; +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (FOR ONE implementation) + */ +static int +one_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + int r; + + if (this->hash != other->hash) + return 0; + + if (other->type != TYPE_ONE && other->type != TYPE_NOT_ONE) + return 0; + + if (this->data.qualifier.domain != other->data.qualifier.domain) + return 0; + + r = this->data.qualifier.antecedent->equals(this->data.qualifier.antecedent, other->data.qualifier.antecedent, inv_out); + if (r <= 0) + return r; + if (*inv_out) + return 0; + + r = this->data.qualifier.predicate->equals(this->data.qualifier.predicate, other->data.qualifier.predicate, inv_out); + if (r <= 0) + return r; + if (*inv_out) + return 0; + + *inv_out = this->type != other->type; + return r; +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (FOR ONE implementation) + */ +static int +one_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + size_t i, n = this->data.qualifier.domain->nmappings; + void *k, *v; + int r, ret = 0; + + (void) input; + + for (i = 0; i < n; i++) { + k = this->data.qualifier.domain->mappings[i].key; + v = this->data.qualifier.domain->mappings[i].value; + r = this->data.qualifier.antecedent->evaluate(this->data.qualifier.antecedent, k); + if (r <= 0) { + if (!r) + continue; + return r; + } + r = this->data.qualifier.predicate->evaluate(this->data.qualifier.predicate, v); + if (r) { + if (r < 0) + return r; + if (++ret == 2) + return this->type == TYPE_NOT_ONE; + } + } + + return ret ^ (this->type == TYPE_NOT_ONE); +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_one)(struct libnormalform_map *d, LIBNORMALFORM_SENTENCE *k, LIBNORMALFORM_SENTENCE *v) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_ONE, + .inverse = &one_inverse, + .equals = &one_equals, + .evaluate = &one_evaluate + }; + + LIBNORMALFORM_SENTENCE *ret; + + if (!k || !v) { + libnormalform_free(k); + libnormalform_free(v); + return NULL; + } + + if (k->type == TYPE_FALSE) { + libnormalform_free(v); + return k; + } + if (v->type == TYPE_FALSE) { + libnormalform_free(k); + return v; + } + + ret = malloc(sizeof(*ret)); + if (ret) { + *ret = prototype; + ret->hash = ONE_HASH(d, k, v); + ret->data.qualifier.domain = d; + ret->data.qualifier.antecedent = k; + ret->data.qualifier.predicate = v; + } + return ret; +} + + +#else + + +static int +evalbool(void *user_data, void *input) +{ + int *vp = input; + (void) user_data; + return *vp; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2; + struct libnormalform_function fun1; + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *c, *v1, *v2; + int t = 1, f = 0; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + + errno = 0; + ASSERT(!libnormalform_one(&dom1, NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!libnormalform_one(&dom1, NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!libnormalform_one(&dom1, REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_one(&dom1, REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!libnormalform_one(&dom1, NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_one(&dom1, NULL, NULL) && errno == 1); + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + /* ∃!x.(⊥ ∧ φ(x)) = ⊥ */ + ASSUME(a = libnormalform_one(&dom1, libnormalform_false(), REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* ∃!x.(⊤ ∧ φ(x)) irreducable */ + ASSUME(a = libnormalform_one(&dom1, libnormalform_true(), REF(v1))); + ASSERT(a->type == TYPE_ONE); + libnormalform_free(a); + + /* ∃!x.(φ(x) ∧ ⊤) irreducable */ + ASSUME(a = libnormalform_one(&dom1, REF(v1), libnormalform_true())); + ASSERT(a->type == TYPE_ONE); + libnormalform_free(a); + + /* ∃!x.(φ(x) ∧ ⊥) = ⊥ */ + ASSUME(a = libnormalform_one(&dom1, REF(v1), libnormalform_false())); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + ASSUME(a = libnormalform_one(&dom1, REF(v1), REF(v2))); + ASSERT(a->type == TYPE_ONE); + ASSERT(a->refcount == 1); + + dom1.nmappings = 1; + + /* ∃!x∈{a}.(⊥ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a}.(⊥ ∧ ⊤) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a}.(⊤ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a}.(⊤ ∧ ⊤) = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + + /* ∃!x∈{a,b}.(⊥ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a,b}.(⊥ ∧ ⊤) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a,b}.(⊤ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a,b}.(⊤ ∧ ⊤) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + /* ∃!x∈∅.(⊥ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈∅.(⊥ ∧ ⊤) = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈∅.(⊤ ∧ ⊥) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈∅.(⊤ ∧ ⊤) = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + var2.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + libnormalform_free(a); + + fun1.evaluate = &evalbool; + dom1.nmappings = 2; + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(a = libnormalform_one(&dom1, libnormalform_true(), a)); + ASSERT(a->type == TYPE_ONE); + map[0].key = NULL; + map[1].key = NULL; + + /* ∃!x∈{⊥, ⊥}.(⊤ ∧ x) = ⊥ */ + map[0].value = &f; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{⊥, ⊤}.(⊤ ∧ x) = ⊤ */ + map[0].value = &f; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃!x∈{⊤, ⊥}.(⊤ ∧ x) = ⊤ */ + map[0].value = &t; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃!x∈{⊤, ⊤}.(⊤ ∧ x) = ⊥ */ + map[0].value = &t; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + libnormalform_free(a); + + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(a = libnormalform_one(&dom1, a, libnormalform_true())); + ASSERT(a->type == TYPE_ONE); + map[0].value = NULL; + map[1].value = NULL; + + /* ∃!x∈{⊥, ⊥}.(x ∧ ⊤) = ⊥ */ + map[0].key = &f; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{⊥, ⊤}.(x ∧ ⊤) = ⊤ */ + map[0].key = &f; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃!x∈{⊤, ⊥}.(x ∧ ⊤) = ⊤ */ + map[0].key = &t; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃!x∈{⊤, ⊤}.(x ∧ ⊤) = ⊥ */ + map[0].key = &t; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{⊤, ⊤, ⊤}.(x ∧ ⊤) = ⊥ */ + dom1.nmappings = 3; + map[0].key = &t; + map[1].key = &t; + map[2].key = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + libnormalform_free(a); + + ASSUME(a = libnormalform_one(&dom1, REF(v1), REF(v2))); + + /* ∃!x∈X.(P(x) ∧ Q(x)) = ∃!x∈X.(P(x) ∧ Q(x)) */ + ASSUME(b = libnormalform_one(&dom1, REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from ∃!x∈X.(Q(x) ∧ P(x)) */ + ASSUME(b = libnormalform_one(&dom1, REF(v2), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from ∃!x∈Y.(P(x) ∧ Q(x)) */ + ASSUME(b = libnormalform_one(&dom2, REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ¬∃!x∈X.(P(x) ∧ Q(x)) = ¬∃!x∈X.(P(x) ∧ Q(x)) */ + ASSUME(c = libnormalform_not(REF(a))); + ASSUME(b = libnormalform_not(libnormalform_one(&dom1, REF(v1), REF(v2)))); + ASSERT_EQUAL(c, b); + ASSERT(c->type == TYPE_NOT_ONE); + libnormalform_free(b); + libnormalform_free(c); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from TRUE */ + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from FALSE */ + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from variable1 */ + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from NOT variable1 */ + ASSUME(b = libnormalform_not(libnormalform_variable(&var1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from function1 */ + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from NOT function1 */ + ASSUME(b = libnormalform_not(libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from T(function1) */ + ASSUME(b = libnormalform_transformation(&trans, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) = ∀x∈X.(P(x) → Q(x)) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) = ∃x∈X.(P(x) ∧ Q(x)) */ + ASSUME(b = libnormalform_any(&dom1, REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) = ¬∃x∈X.(P(x) ∧ Q(x)) */ + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from AND (P, Q) */ + ASSUME(b = libnormalform_and2(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from OR (P, Q) */ + ASSUME(b = libnormalform_or2(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.(P(x) ∧ Q(x)) independent from XOR (P, Q) */ + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + 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) */ + ASSUME(b = libnormalform_one(&dom1, libnormalform_false(), libnormalform_false())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + libnormalform_free(v1); + libnormalform_free(v2); + + /* cascading of evaluation failure is tested in libnormalform_function.c */ + + TEST_END; +} + + +#endif diff --git a/libnormalform_or.c b/libnormalform_or.c new file mode 100644 index 0000000..16a99a5 --- /dev/null +++ b/libnormalform_or.c @@ -0,0 +1,562 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * Check if a sentence equivalent to a specific sentence + * or its inverse is in an array of sentences + * + * @param x The sentence to look for + * @param among The array of sentences to look in + * @param n The number of sentences in `among` + * @param inv_out Output parameter for equivalency of `x` and the + * sentence found in `among`; set to 0 if the two + * are equivalent or 1 if `x` is equivalent to the + * inverse of the sentence found in `among` + * @return 1 if a sentence equivalent to `x` or its inverse + * was found, 0 otherwise + */ +static int +is_in(LIBNORMALFORM_SENTENCE *x, LIBNORMALFORM_SENTENCE **among, size_t n, int *inv_out) +{ + while (n--) + if (x->equals(x, among[n], inv_out)) + return 1; + return 0; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_or)(LIBNORMALFORM_SENTENCE **xs) +{ + LIBNORMALFORM_SENTENCE *x, *ret, **saved = xs; + size_t n = 0; + int inv; + + for (; (x = *xs); xs++) { + if (x->type == TYPE_TRUE) { + /* ⋁x ∨ 0 = 0 */ + ret = *xs++; + goto return_asis; + + } else if (x->type == TYPE_FALSE) { + redundant: + /* ⋁X ∨ 0 = ⋁X */ + libnormalform_free(x); + + } else if (is_in(x, saved, n, &inv)) { + if (!inv) { + /* x ∨ x = x */ + goto redundant; + + } else { + /* x ∨ ¬x = 1 */ + libnormalform_free(*xs++); + ret = libnormalform_true(); + goto return_asis; + } + + } else { + saved[n++] = x; + } + } + + if (!n) { + /* ⋁∅ = ¬¬⋁∅ = ¬⋀∅ = ¬∀x∈∅.x = {vacuous truth} = ¬1 = 0 */ + return libnormalform_false(); + } + + saved[n] = NULL; + /* ⋁{x} = x */ + ret = *saved++; + /* ⋁(X ∪ x) = ⋁X ∨ x */ + for (; *saved; saved++) + ret = libnormalform_or2(ret, *saved); + + return ret; + +return_asis: + while (*xs) + libnormalform_free(*xs++); + while (n) + libnormalform_free(saved[--n]); + return ret; +} + + +#else + + +#define OR(...) LIBNORMALFORM_OR(__VA_ARGS__) + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3, var4; + struct libnormalform_function fun1, fun2; + struct libnormalform_map domain; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3, *v4, *f1, *f2; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(v4 = libnormalform_variable(&var4)); + ASSUME(f1 = libnormalform_function(&fun1)); + ASSUME(f2 = libnormalform_function(&fun2)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!OR(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!OR(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!OR(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!OR(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!OR(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!OR(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = OR(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = OR(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL3(VALUE, V1, V2, V3)\ + do {\ + ASSUME(a = OR(REF(v1), REF(v2), REF(v3)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + var3.value = V3##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + ASSERT_CONST(FALSE); /* OR () -> FALSE */ + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* OR (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* OR (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(FALSE, F, F); /* OR (FALSE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, F, T); /* OR (FALSE, TRUE) -> TRUE */ + ASSERT_CONST(TRUE, T, F); /* OR (TRUE, FALSE) -> TRUE */ + ASSERT_CONST(TRUE, T, T); /* OR (TRUE, TRUE) -> TRUE */ + + ASSERT_EVAL2(FALSE, F, F); /* OR (var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(TRUE, F, T); /* OR (var(FALSE), var(TRUE)) => TRUE */ + ASSERT_EVAL2(TRUE, T, F); /* OR (var(TRUE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(TRUE, T, T); /* OR (var(TRUE), var(TRUE)) => TRUE */ + +#ifndef USE_TWO + + ASSERT_CONST(FALSE, F, F, F); /* OR (FALSE, FALSE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, F, F, T); /* OR (FALSE, FALSE, TRUE) -> TRUE */ + ASSERT_CONST(TRUE, F, T, F); /* OR (FALSE, TRUE, FALSE) -> TRUE */ + ASSERT_CONST(TRUE, F, T, T); /* OR (FALSE, TRUE, TRUE) -> TRUE */ + ASSERT_CONST(TRUE, T, F, F); /* OR (TRUE, FALSE, FALSE) -> TRUE */ + ASSERT_CONST(TRUE, T, F, T); /* OR (TRUE, FALSE, TRUE) -> TRUE */ + ASSERT_CONST(TRUE, T, T, F); /* OR (TRUE, TRUE, FALSE) -> TRUE */ + ASSERT_CONST(TRUE, T, T, T); /* OR (TRUE, TRUE, TRUE) -> TRUE */ + + ASSERT_EVAL3(FALSE, F, F, F); /* OR (var(FALSE), var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL3(TRUE, F, F, T); /* OR (var(FALSE), var(FALSE), var(TRUE)) => TRUE */ + ASSERT_EVAL3(TRUE, F, T, F); /* OR (var(FALSE), var(TRUE), var(FALSE)) => TRUE */ + ASSERT_EVAL3(TRUE, F, T, T); /* OR (var(FALSE), var(TRUE), var(TRUE)) => TRUE */ + ASSERT_EVAL3(TRUE, T, F, F); /* OR (var(TRUE), var(FALSE), var(FALSE)) => TRUE */ + ASSERT_EVAL3(TRUE, T, F, T); /* OR (var(TRUE), var(FALSE), var(TRUE)) => TRUE */ + ASSERT_EVAL3(TRUE, T, T, F); /* OR (var(TRUE), var(TRUE), var(FALSE)) => TRUE */ + ASSERT_EVAL3(TRUE, T, T, T); /* OR (var(TRUE), var(TRUE), var(TRUE)) => TRUE */ + + /* OR (x) -> x */ + ASSUME(a = OR(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* OR (x, TRUE) -> TRUE */ + ASSUME(a = OR(REF(v1), T)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* OR (TRUE, x) -> TRUE */ + ASSUME(a = OR(T, REF(v1))); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* OR (x, FALSE) -> x */ + ASSUME(a = OR(REF(v1), F)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* OR (FALSE, x) -> x */ + ASSUME(a = OR(F, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + +#ifndef USE_TWO + + /* OR (x, FALSE, FALSE) -> x */ + ASSUME(a = OR(REF(v1), F, F)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* OR (x, FALSE, TRUE) -> TRUE */ + ASSUME(a = OR(REF(v1), F, T)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* OR (x, TRUE, FALSE) -> TRUE */ + ASSUME(a = OR(REF(v1), T, F)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* OR (FALSE, x, FALSE) -> x */ + ASSUME(a = OR(F, REF(v1), F)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* OR (FALSE, x, TRUE) -> TRUE */ + ASSUME(a = OR(F, REF(v1), T)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* OR (TRUE, x, FALSE) -> TRUE */ + ASSUME(a = OR(T, REF(v1), F)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* OR (FALSE, FALSE, x) -> x */ + ASSUME(a = OR(F, F, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* OR (FALSE, TRUE, x) -> TRUE */ + ASSUME(a = OR(F, T, REF(v1))); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* OR (TRUE, FALSE, x) -> TRUE */ + ASSUME(a = OR(T, F, REF(v1))); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + +#endif + + /* OR (x, x) -> x */ + ASSUME(a = OR(REF(v1), REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* OR (x, NOT x) -> TRUE */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = OR(REF(v1), a)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* OR (x, y) -> OR (x, y) */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = OR(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (y, x) -> OR (x, y) */ + ASSUME(a = OR(REF(v2), REF(v1))); + ASSUME(b = OR(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* NOT OR (x, y) -> AND (NOT x, NOT y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(a = libnormalform_not(a)); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from OR (x, z) */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_or2(REF(v1), REF(v3))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from OR (z, x) */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_or2(REF(v3), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from OR (z, w) */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_or2(REF(v3), REF(v4))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from AND (x, y) */ + ASSUME(a = REF(v1)); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = OR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from AND (x, NOT y) */ + ASSUME(a = REF(v1)); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = OR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from AND (NOT x, y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = OR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from XOR (x, y) */ + ASSUME(a = REF(v1)); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_xor2(a, b)); + ASSUME(a = OR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from XOR (x, NOT y) */ + ASSUME(a = REF(v1)); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_xor2(a, b)); + ASSUME(a = OR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from XOR (NOT x, y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_xor2(a, b)); + ASSUME(a = OR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from XOR (NOT x, NOT y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_xor2(a, b)); + ASSUME(a = OR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from TRUE */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from FALSE */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from variable1 */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, v1); + libnormalform_free(a); + + /* OR (x, y) -> independence from NOT variable1 */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from function1 */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, f1); + libnormalform_free(a); + + /* OR (x, y) -> independence from NOT function2 */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_not(REF(f1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from T(function1) */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_transformation(&trans, REF(f1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from ALL (domain1, function1, function2) */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_all(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from ANY (domain1, function1, function2) */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_any(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from ONE (domain1, function1, function2) */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_one(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (x, y) -> independence from NOT ONE (domain1, function1, function2) */ + ASSUME(a = OR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_one(&domain, REF(f1), REF(f2))); + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (AND (x, NOT y), AND (NOT x, y)) -> XOR (x, y) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(a = libnormalform_and2(REF(v1), a)); + ASSUME(b = libnormalform_and2(b, REF(v2))); + ASSUME(a = OR(a, b)); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + ASSERT(a->type == TYPE_XOR); + ASSERT(a->type == TYPE_XOR); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (AND (NOT x, y), AND (x, NOT y)) -> XOR (x, y) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(a = libnormalform_and2(REF(v1), a)); + ASSUME(b = libnormalform_and2(b, REF(v2))); + ASSUME(a = OR(b, a)); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + ASSERT(a->type == TYPE_XOR); + ASSERT(a->type == TYPE_XOR); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (AND (x, y), AND (NOT x, NOT y)) -> NOT XOR (x, y) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = libnormalform_and2(REF(v1), REF(v2))); + ASSUME(a = OR(a, b)); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + ASSERT(a->type == TYPE_XOR); + ASSERT(a->type == TYPE_XOR); + libnormalform_free(a); + libnormalform_free(b); + + /* OR (AND (NOT x, NOT y), AND (x, y)) -> NOT XOR (x, y) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = libnormalform_and2(REF(v1), REF(v2))); + ASSUME(a = OR(b, a)); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + ASSERT(a->type == TYPE_XOR); + ASSERT(a->type == TYPE_XOR); + libnormalform_free(a); + libnormalform_free(b); + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 +#undef ASSERT_EVAL3 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(v4); + libnormalform_free(f1); + libnormalform_free(f2); + libnormalform_free(ts); + libnormalform_free(fs); + + /* cascading of evaluation failure is tested in libnormalform_function.c */ + + TEST_END; +} + + +#endif diff --git a/libnormalform_or2.c b/libnormalform_or2.c new file mode 100644 index 0000000..51ed628 --- /dev/null +++ b/libnormalform_or2.c @@ -0,0 +1,82 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +LIBNORMALFORM_SENTENCE * +(libnormalform_or2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ + LIBNORMALFORM_SENTENCE *ll, *lr; + LIBNORMALFORM_SENTENCE *rl, *rr; + int inv; + + if (!l || !r) { + libnormalform_free(l); + libnormalform_free(r); + return NULL; + } + + if (l->equals(l, r, &inv)) { + if (!inv) { + /* x ∨ x = x */ + goto return_either; + + } else { + /* x ∨ ¬x = 1 */ + libnormalform_free(l); + libnormalform_free(r); + return libnormalform_true(); + } + + } else if (l->type == TYPE_TRUE || r->type == TYPE_FALSE) { + /* 1 ∨ x = 1 */ + /* x ∨ 0 = x */ + return_either: + libnormalform_free(r); + return l; + + } else if (r->type == TYPE_TRUE || l->type == TYPE_FALSE) { + /* x ∨ 1 = 1 */ + /* 0 ∨ x = x */ + libnormalform_free(l); + return r; + + } else if (l->hash == r->hash && l->type == TYPE_AND && r->type == TYPE_AND) { + /* (x ∧ ¬y) ∨ (¬x ∧ y) = x ⊕ y */ + ll = l->data.binary.l; + lr = l->data.binary.r; + rl = r->data.binary.l; + rr = r->data.binary.r; + if (ll->hash != rl->hash || lr->hash != rr->hash) + goto fallback; + if (!ll->equals(ll, rl, &inv)) { + if (ll->hash == lr->hash) { + rl = r->data.binary.r; + rr = r->data.binary.l; + if (!ll->equals(ll, rl, &inv)) + goto fallback; + } else { + goto fallback; + } + } + if (!inv || !lr->equals(lr, rr, &inv) || !inv) + goto fallback; + l->data.binary.l = NULL; + r->data.binary.r = NULL; + libnormalform_free(l); + libnormalform_free(r); + return libnormalform_xor2__(ll, rr); + + } else { + fallback: + return libnormalform_or2__(l, r); + } +} + + +#else + +#define USE_TWO +#include "libnormalform_or.c" + +#endif diff --git a/libnormalform_or2__.c b/libnormalform_or2__.c new file mode 100644 index 0000000..7d093a0 --- /dev/null +++ b/libnormalform_or2__.c @@ -0,0 +1,119 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (OR implementation) + */ +static LIBNORMALFORM_SENTENCE * +or_inverse(LIBNORMALFORM_SENTENCE *this) +{ + /* ¬(a + b) = ¬a¬b */ + LIBNORMALFORM_SENTENCE *l, *r, *ret; + l = this->data.binary.l->inverse(this->data.binary.l); + if (!l) + return NULL; + r = this->data.binary.r->inverse(this->data.binary.r); + if (!r) { + libnormalform_free(l); + return NULL; + } + ret = libnormalform_and2__(l, r); + if (ret && this->atom) { + ret->atom = this->atom; + ret->atom->refcount += 1; + } + return ret; +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (OR implementation) + */ +static int +or_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + LIBNORMALFORM_SENTENCE *tl, *tr; + LIBNORMALFORM_SENTENCE *ol, *or; + int inv; + + if (other->type != TYPE_OR && other->type != TYPE_AND) + return other->type == TYPE_XOR && other->equals(other, this, inv_out); + + tl = this->data.binary.l; + tr = this->data.binary.r; + ol = other->data.binary.l; + or = other->data.binary.r; + + if (tl->hash != ol->hash || tr->hash != or->hash) + return 0; + + if (!tl->equals(tl, ol, inv_out)) { + if (tl->hash == tr->hash) { + ol = other->data.binary.r; + or = other->data.binary.l; + if (!tl->equals(tl, ol, inv_out)) + return 0; + } else { + return 0; + } + } + if (!tr->equals(tr, or, &inv)) + return 0; + return inv == *inv_out && inv == (other->type == TYPE_AND); +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (OR implementation) + */ +static int +or_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + int r = this->data.binary.l->evaluate(this->data.binary.l, input); + return r ? r : this->data.binary.r->evaluate(this->data.binary.r, input); +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_or2__)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_OR, + .inverse = &or_inverse, + .equals = &or_equals, + .evaluate = &or_evaluate + }; + + LIBNORMALFORM_SENTENCE *ret = malloc(sizeof(*ret)); + if (!ret) { + libnormalform_free(l); + libnormalform_free(r); + return NULL; + } + *ret = prototype; + ret->hash = AND_OR_HASH(l, r); + if (l->hash <= r->hash) { + ret->data.binary.l = l; + ret->data.binary.r = r; + } else { + ret->data.binary.l = r; + ret->data.binary.r = l; + } + return ret; +} + + +#else + + +CONST int +main(void) +{ + return 0; /* indirectly tested */ +} + + +#endif diff --git a/libnormalform_or_checked.c b/libnormalform_or_checked.c new file mode 100644 index 0000000..6223727 --- /dev/null +++ b/libnormalform_or_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_or_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_or.c" + +#endif diff --git a/libnormalform_orl.c b/libnormalform_orl.c new file mode 100644 index 0000000..540fd70 --- /dev/null +++ b/libnormalform_orl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_orl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_or.c" + +#endif diff --git a/libnormalform_orl_checked.c b/libnormalform_orl_checked.c new file mode 100644 index 0000000..227064a --- /dev/null +++ b/libnormalform_orl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_orl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_or.c" + +#endif diff --git a/libnormalform_orl_macro_test.c b/libnormalform_orl_macro_test.c new file mode 100644 index 0000000..8cc0d20 --- /dev/null +++ b/libnormalform_orl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_or.c" +#endif diff --git a/libnormalform_ref.c b/libnormalform_ref.c new file mode 100644 index 0000000..6d703eb --- /dev/null +++ b/libnormalform_ref.c @@ -0,0 +1,57 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +LIBNORMALFORM_SENTENCE * +(libnormalform_ref)(LIBNORMALFORM_SENTENCE *this) +{ + if (this) { + if (this->refcount == SIZE_MAX) { + errno = ENOMEM; + return NULL; + } + this->refcount += 1; + } + return this; +} + + +#else + + +int +main(void) +{ + TEST_BEGIN; + + LIBNORMALFORM_SENTENCE *a; + + errno = 0; + ASSERT(!libnormalform_ref(NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_ref(NULL) && errno == 1); + + ASSUME(a = libnormalform_true()); + + a->refcount = SIZE_MAX; + errno = 0; + ASSERT(!libnormalform_ref(a) && errno == ENOMEM); + a->refcount -= 1; + ASSERT(libnormalform_ref(a) == a); + ASSERT(a->refcount == SIZE_MAX); + + a->refcount = 1; + ASSERT(libnormalform_ref(a) == a); + ASSERT(a->refcount == 2); + ASSERT(libnormalform_ref(a) == a); + ASSERT(a->refcount == 3); + + a->refcount = 1; + libnormalform_free(a); + + TEST_END; +} + + +#endif diff --git a/libnormalform_reset_indices_and_counts__.c b/libnormalform_reset_indices_and_counts__.c new file mode 100644 index 0000000..f6b1790 --- /dev/null +++ b/libnormalform_reset_indices_and_counts__.c @@ -0,0 +1,44 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * Undo `libnormalform_set_indices_and_counts__` + * + * This is needed because `libnormalform_set_indices_and_counts__` + * requires some otherwise unused memory to be properly initialised + * + * @param this The sentence that shall be recursively restored + */ +void +(libnormalform_reset_indices_and_counts__)(LIBNORMALFORM_SENTENCE *this) +{ + LIBNORMALFORM_SENTENCE *head = NULL; + + this->travel_count -= 1; + do { + this->travel_index = 0; + if (IS_BRANCH(this)) { + if (!--RIGHT(this)->travel_count) + PUSH(&head, RIGHT(this)); + if (!--LEFT(this)->travel_count) + PUSH(&head, LEFT(this)); + } + } while (POP(&head, &this)); + +#undef UNMARK +} + + +#else + + +CONST int +main(void) +{ + return 0; /* indirectly tested */ +} + + +#endif diff --git a/libnormalform_set_indices_and_counts__.c b/libnormalform_set_indices_and_counts__.c new file mode 100644 index 0000000..2dbc9aa --- /dev/null +++ b/libnormalform_set_indices_and_counts__.c @@ -0,0 +1,56 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * Annotate every sentence that appear multiple times + * with a unique index (counted from 0 up) + * + * @param this The sentence that shall be inspected + * recursively for annotation + * @return The number of annotated sentences + */ +size_t +(libnormalform_set_indices_and_counts__)(LIBNORMALFORM_SENTENCE *this) +{ + LIBNORMALFORM_SENTENCE *head = NULL, *a, *b; + size_t count = 0; + + this->travel_count = 1; + do { + if (IS_BRANCH(this)) { + a = LEFT(this); + b = RIGHT(this); + + a->travel_count += 1; + if (a->travel_count == 2) + a->travel_index = count++; + + b->travel_count += 1; + if (b->travel_count == 2) + b->travel_index = count++; + + if (b->travel_count == 1) + PUSH(&head, b); + + if (a->travel_count == 1) + PUSH(&head, a); + } + } while (POP(&head, &this)); + + return count; +} + + +#else + + +CONST int +main(void) +{ + return 0; /* indirectly tested */ +} + + +#endif diff --git a/libnormalform_singleton.c b/libnormalform_singleton.c new file mode 100644 index 0000000..b850b98 --- /dev/null +++ b/libnormalform_singleton.c @@ -0,0 +1,71 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_singleton)(struct libnormalform_map *); + + +#else + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + LIBNORMALFORM_SENTENCE *a, *b; + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + ASSUME(a = libnormalform_singleton(&dom1)); + ASSERT(a->type == TYPE_ONE); + + ASSUME(b = libnormalform_singleton(&dom2)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_singleton(&dom1)); + ASSERT(b != a); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_empty(&dom1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_nonempty(&dom1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_not(libnormalform_singleton(&dom1))); + ASSERT(b->type == TYPE_NOT_ONE); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_not(libnormalform_singleton(&dom2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + dom1.nmappings = 0; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 1; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 3; + ASSERT(libnormalform_evaluate(a) == 0); + + libnormalform_free(a); + + TEST_END; +} + + +#endif diff --git a/libnormalform_to_string.c b/libnormalform_to_string.c new file mode 100644 index 0000000..74aa839 --- /dev/null +++ b/libnormalform_to_string.c @@ -0,0 +1,519 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * String under construct + */ +struct string_construction { + const char **strings; /**< Strings to right-concatenate one-by-one */ + char **dyn_strings; /**< List of dynamically allocated strings that appear in `.strings` */ + size_t n; /**< The number of elements in `.strings` */ + size_t size; /**< The number of elements allocated to `.strings` */ + size_t dyn_n; /**< The number of elements in `.dyn_strings` */ + size_t dyn_size; /**< The number of elements allocated to `.dyn_strings` */ + size_t length; /**< The total length of the string */ +}; + + +/** + * Append a string (with its length already known) + * + * @param s The string to append `str` to + * @param str The string to append to `s` + * @param len The length of `str` + * @return 0 on sucess, -1 on failure + */ +USE_RESULT NONNULL_INPUT +static int +add_string_n(struct string_construction *s, const char *str, size_t len) +{ + if (s->n == s->size) { + void *new = realloc(s->strings, (s->size += 128) * sizeof(*s->strings)); + if (!new) + return -1; + s->strings = new; + } + s->strings[s->n++] = str; + s->length += len; + return 0; +} + + +/** + * Append a string + * + * @param s The string to append `str` to + * @param str The string to append to `s` + * @return 0 on sucess, -1 on failure + */ +USE_RESULT NONNULL_INPUT +static int +add_string(struct string_construction *s, const char *str) +{ + return add_string_n(s, str, strlen(str)); +} + + +/** + * Append `size_t` encoded in decimal + * + * @param s The string to append `num` to + * @param num The number to append to `s` + * @return 0 on sucess, -1 on failure + */ +USE_RESULT NONNULL_INPUT +static int +add_number(struct string_construction *s, size_t n) +{ + char buf[3 * sizeof(size_t)], *str; + int len = sprintf(buf, "%zu", n); + if (len < 0) + abort(); + if (s->dyn_n == s->dyn_size) { + void *new = realloc(s->dyn_strings, (s->dyn_size += 8) * sizeof(*s->dyn_strings)); + if (!new) + return -1; + s->dyn_strings = new; + } + s->dyn_strings[s->dyn_n++] = str = malloc((size_t)len + 1U); + if (!str) + return -1; + memcpy(str, buf, (size_t)len + 1U); + return add_string_n(s, str, (size_t)len); +} + + +/** + * Append the serialisation of a sentence to a string + * + * @param this The sentence + * @param s The string to append the serialisation to + * @param map `.travel_index` to reference ID map, any sentence that has + * not be been serialised will have its slot set to `SIZE_MAX`; + * set to `NULL` (could also mean that there are not duplicates) + * by `libnormalform__debug_print__` to allow printing during + * indexing and restoration + * @param nextrefp Reference to the next reference ID + * @param embed Whether the sentence may be embed as additional terms + * (1, 0 otherwise); -1 is used by `libnormalform__debug_print__` + * to globally disable embedding + * @param depth_limit Decreases by 1 for each level of recursion, when 0 is + * reached "..." will be appended to `s`, instead of the + * the serialisation of `this`; this is only used by + * `libnormalform__debug_print__`, for + * `libnormalform_to_string` it is initially set to `SIZE_MAX` + * to nullify it's effect (you cannot possibility have more + * levels of recursion than bytes in your stack memory) + * @return 0 on success, -1 on failure + */ +USE_RESULT SOME_NONNULL_INPUT(1, 2, 4) +static int +to_string(LIBNORMALFORM_SENTENCE *this, struct string_construction *s, + size_t *map, size_t *nextrefp, int embed, size_t depth_limit) +{ +#define ADD_STRING(SC, STRING) add_string_n((SC), STRING, sizeof(STRING) - 1U) +#define IF_MAY_EMBED(X) (embed < 0 ? -1 : (X)) + + LIBNORMALFORM_SENTENCE *term1 = NULL, *term2 = NULL; + int inv = 0, associative = 0; + const char *function, *identifier = NULL; + + if (!depth_limit) + return ADD_STRING(s, "..."); + + if (this->travel_count > 1 && map) { + embed = -(embed < 0); + if (map[this->travel_index] != SIZE_MAX) { + if (ADD_STRING(s, "REF(") || add_number(s, map[this->travel_index])) + return -1; + return ADD_STRING(s, ")"); + } + } + + switch (this->type) { + case TYPE_TRUE: + return ADD_STRING(s, "TRUE"); + + case TYPE_FALSE: + return ADD_STRING(s, "FALSE"); + + case TYPE_AND: + function = "AND"; + goto binary; + + case TYPE_OR: + function = "OR"; + goto binary; + + case TYPE_XOR: + function = "XOR"; + binary: + term1 = this->data.binary.l; + term2 = this->data.binary.r; + associative = 1; + break; + + case TYPE_ALL: + function = "ALL"; + goto qualifier; + + case TYPE_ANY: + function = "ANY"; + goto qualifier; + + case TYPE_NOT_ONE: + inv = 1; + /* fall through */ + + case TYPE_ONE: + function = "ONE"; + qualifier: + identifier = this->data.qualifier.domain->identifier; + term1 = this->data.qualifier.antecedent; + term2 = this->data.qualifier.predicate; + break; + + case TYPE_VARIABLE: + inv = this->data.literal.inverted; + function = "VARIABLE"; + identifier = this->data.literal.atom.variable->identifier; + break; + + case TYPE_FUNCTION: + inv = this->data.literal.inverted; + function = "FUNCTION"; + identifier = this->data.literal.atom.function->identifier; + break; + + case TYPE_TRANS: + function = "TRANSFORMATION"; + identifier = this->data.trans.function->identifier; + term1 = this->data.trans.input; + break; + + default: + abort(); + } + + if (embed == 1) + goto add_terms; + + if (inv) { + if (this->travel_count > 1 && map) { + if (ADD_STRING(s, "NOT@") || add_number(s, *nextrefp) || ADD_STRING(s, "(")) + return -1; + map[this->travel_index] = (*nextrefp)++; + } else { + if (ADD_STRING(s, "NOT(")) + return -1; + } + } + if (add_string(s, function)) + return -1; + if (!inv && this->travel_count > 1 && map) { + if (ADD_STRING(s, "@") || add_number(s, *nextrefp)) + return -1; + map[this->travel_index] = (*nextrefp)++; + } + if (ADD_STRING(s, "(")) + return -1; + + if (identifier) { + if (add_string(s, identifier)) + return -1; + if (term1 && ADD_STRING(s, ", ")) + return -1; + } +add_terms: + if (term1) { + if (to_string(term1, s, map, nextrefp, IF_MAY_EMBED(associative && term1->type == this->type), depth_limit - 1)) + return -1; + if (term2 && ADD_STRING(s, ", ")) + return -1; + } + if (term2) { + if (to_string(term2, s, map, nextrefp, IF_MAY_EMBED(associative && term2->type == this->type), depth_limit - 1)) + return -1; + } + + return embed == 1 ? 0 : inv ? ADD_STRING(s, "))") : ADD_STRING(s, ")"); + +#undef ADD_STRING +#undef IF_MAY_EMBED +} + + +#ifdef DEBUG + +void +libnormalform__debug_print__(LIBNORMALFORM_SENTENCE *this, size_t depth_limit) +{ + struct string_construction s; + char *res = NULL, *p; + size_t i, nextref = 0; + + if (!this) { + fprintf(stderr, "<debug error: libnormalform__debug_print__: %s>\n", "Null pointer input"); + return; + } + + s.strings = NULL; + s.dyn_strings = NULL; + s.n = 0; + s.size = 0; + s.length = 0; + s.dyn_n = 0; + s.dyn_size = 0; + + if (to_string(this, &s, NULL, &nextref, -1, depth_limit ? depth_limit : SIZE_MAX)) + goto out; + res = malloc(s.length + 1U); + if (!res) + goto out; + + p = res; + for (i = 0; i < s.n; i++) + p = stpcpy(p, s.strings[i]); + *p = '\0'; + +out: + while (s.dyn_n) + free(s.dyn_strings[--s.dyn_n]); + free(s.strings); + free(s.dyn_strings); + + if (res) { + fprintf(stderr, "%s\n", res); + free(res); + } else { + fprintf(stderr, "<debug error: libnormalform__debug_print__: %s>\n", strerror(errno)); + } + fflush(stderr); +} + +#endif + + +char * +(libnormalform_to_string)(LIBNORMALFORM_SENTENCE *this) +{ + struct string_construction s; + char *ret = NULL, *p; + size_t i, dupcount, *map = NULL, nextref = 0; + + dupcount = libnormalform_set_indices_and_counts__(this); + if (dupcount) { + map = malloc(dupcount * sizeof(*map)); + if (!map) + goto out; + while (dupcount--) + map[dupcount] = SIZE_MAX; + } + + s.strings = NULL; + s.dyn_strings = NULL; + s.n = 0; + s.size = 0; + s.length = 0; + s.dyn_n = 0; + s.dyn_size = 0; + + if (to_string(this, &s, map, &nextref, 0, SIZE_MAX)) + goto out; + ret = malloc(s.length + 1U); + if (!ret) + goto out; + + p = ret; + for (i = 0; i < s.n; i++) + p = stpcpy(p, s.strings[i]); + *p = '\0'; + +out: + while (s.dyn_n) + free(s.dyn_strings[--s.dyn_n]); + free(s.strings); + free(s.dyn_strings); + free(map); + libnormalform_reset_indices_and_counts__(this); + return ret; +} + + +#else + + +NONNULL_INPUT static LIBNORMALFORM_SENTENCE * +set_order(size_t order, LIBNORMALFORM_SENTENCE *x) +{ + x->hash = order; + return x; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3; + struct libnormalform_function fun1, fun2; + struct libnormalform_map dom1, dom2; + struct libnormalform_transformer trans1; + LIBNORMALFORM_SENTENCE *ref0, *ref1, *ref2, *ref3, *ref4, *a, *b, *t, *f; + LIBNORMALFORM_SENTENCE *pref0, *pref1, *ref0a, *ref0b; + char *s; + + var1.identifier = "var1"; + var2.identifier = "var2"; + var3.identifier = "var3"; + fun1.identifier = "fun1"; + fun2.identifier = "fun2"; + dom1.identifier = "dom1"; + dom2.identifier = "dom2"; + trans1.identifier = "trans1"; + + ASSUME(t = libnormalform_true()); + ASSUME(f = libnormalform_false()); + +#define REF1P "VARIABLE(var1)" + ASSUME(ref1 = libnormalform_variable(&var1)); + ASSUME(s = libnormalform_to_string(ref1)); + ASSERT(!strcmp(s, REF1P)); + free(s); + +#define REF0 "AND@0(VARIABLE@1(var1), VARIABLE(var2), VARIABLE(var3))" +#define REF0P "AND(VARIABLE(var1), VARIABLE(var2), VARIABLE(var3))" + ASSUME(ref0 = + libnormalform_and2( + set_order(1, REF(ref1)), + set_order(2, libnormalform_and2( + set_order(3, libnormalform_variable(&var2)), + set_order(4, libnormalform_variable(&var3)) + )) + ) + ); + ASSUME(s = libnormalform_to_string(ref0)); + ASSERT(!strcmp(s, REF0P)); + free(s); + + ref0a = libnormalform_ref(ref0); + +#define REF2 "NOT@2(FUNCTION(fun1))" +#define REF2P "NOT(FUNCTION(fun1))" + ASSUME(ref2 = libnormalform_not(libnormalform_function(&fun1))); + ASSUME(s = libnormalform_to_string(ref2)); + ASSERT(!strcmp(s, REF2P)); + free(s); + +#define A1 "AND("REF2", REF(1), TRANSFORMATION(trans1, FUNCTION(fun2)))" +#define A1P "AND("REF2P", "REF1P", TRANSFORMATION(trans1, FUNCTION(fun2)))" +#define A1Q "AND("REF2P", REF(1), TRANSFORMATION(trans1, FUNCTION(fun2)))" + ASSUME(a = + libnormalform_and2__( + set_order(1, libnormalform_and2__( + set_order(2, REF(ref2)), + set_order(3, REF(ref1)) + )), + set_order(4, (pref0 = libnormalform_transformation(&trans1, libnormalform_function(&fun2)))) + ) + ); + ASSUME(s = libnormalform_to_string(a)); + ASSERT(!strcmp(s, A1P)); + free(s); + + ref0b = libnormalform_ref(ref0); + +#define A2 "XOR("REF0", ALL(dom1, REF(0), "A1"))" +#define A2P "XOR("REF0", ALL(dom1, REF(0), "A1Q"))" + ASSUME(a = + libnormalform_xor2( + REF(ref0), + libnormalform_all(&dom1, REF(ref0), a) + ) + ); + ASSUME(s = libnormalform_to_string(a)); + ASSERT(!strcmp(s, A2P)); + free(s); + + libnormalform_free(ref1); + +#define REF3 "ONE@3(dom2, TRUE, TRUE)" +#define REF3P "ONE(dom2, TRUE, TRUE)" + ASSUME(ref3 = libnormalform_one(&dom2, REF(t), REF(t))); + ASSUME(s = libnormalform_to_string(ref3)); + ASSERT(!strcmp(s, REF3P)); + free(s); + +#define REF4 "NOT@4(ONE(dom2, TRUE, TRUE))" +#define REF4P "NOT(ONE(dom2, TRUE, TRUE))" + ASSUME(ref4 = libnormalform_not(REF(ref3))); + ASSUME(s = libnormalform_to_string(ref4)); + ASSERT(!strcmp(s, REF4P)); + free(s); + +#define B1P "XOR("REF3P", "REF4P")" +#define B1R "XOR(REF(3), REF(4))" + ASSUME(b = libnormalform_xor2__(set_order(1, REF(ref3)), set_order(2, REF(ref4)))); + ASSUME(s = libnormalform_to_string(b)); + ASSERT(!strcmp(s, B1P)); + free(s); + +#define A3P "OR("A2", REF(2), ANY(dom1, TRUE, TRUE), "REF3P", "REF4P")" +#define A3 A2", REF(2), ANY(dom1, TRUE, TRUE), "REF3", "REF4 + ASSUME(a = + set_order(9, libnormalform_or2( + set_order(7, libnormalform_or2( + set_order(5, libnormalform_or2( + set_order(3, libnormalform_or2( + set_order(1, a), + set_order(2, REF(ref2)) + )), + set_order(4, (pref1 = libnormalform_any(&dom1, REF(t), REF(t)))) + )), + set_order(6, REF(ref3)) + )), + set_order(8, REF(ref4)) + )) + ); + ASSUME(s = libnormalform_to_string(a)); + ASSERT(!strcmp(s, A3P)); + free(s); + +#define EXPECTED "OR("A3", "B1R")" + ASSUME(a = libnormalform_or2(set_order(1, a), set_order(2, b))); + + pref0 = libnormalform_ref(pref0); + pref1 = libnormalform_ref(pref1); + + ASSUME(s = libnormalform_to_string(a)); + ASSERT(!strcmp(s, EXPECTED)); + free(s); + + libnormalform_free(pref0); + libnormalform_free(pref1); + + libnormalform_free(a); + libnormalform_free(ref0); + libnormalform_free(ref0a); + libnormalform_free(ref0b); + libnormalform_free(ref2); + libnormalform_free(ref3); + libnormalform_free(ref4); + + ASSUME(a = libnormalform_all(&dom1, REF(t), REF(f))); + ASSUME(s = libnormalform_to_string(a)); + ASSERT(!strcmp(s, "ALL(dom1, TRUE, FALSE)")); + free(s); + libnormalform_free(a); + + libnormalform_free(t); + libnormalform_free(f); + + TEST_END; +} + + +#endif diff --git a/libnormalform_transformation.c b/libnormalform_transformation.c new file mode 100644 index 0000000..3a1218b --- /dev/null +++ b/libnormalform_transformation.c @@ -0,0 +1,419 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (Transformation function implementation) + */ +static LIBNORMALFORM_SENTENCE * +trans_inverse(LIBNORMALFORM_SENTENCE *this) +{ + return libnormalform_transformation(this->data.trans.function, this->data.trans.input->inverse(this->data.trans.input)); +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (Transformation function implementation) + */ +static int +trans_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + if (other->type != TYPE_TRANS || this->data.trans.function != other->data.trans.function) + return 0; + return this->data.trans.input->equals(this->data.trans.input, other->data.trans.input, inv_out); +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (Transformation function implementation) + */ +static int +trans_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + int r; + input = this->data.trans.function->transform(this->data.trans.function->user_data, input); + if (!input) + return -1; + r = this->data.trans.input->evaluate(this->data.trans.input, input); + if (this->data.trans.function->deallocate) + this->data.trans.function->deallocate(this->data.trans.function->user_data, input); + return r; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_transformation)(struct libnormalform_transformer *function, LIBNORMALFORM_SENTENCE *sentence) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_TRANS, + .inverse = &trans_inverse, + .equals = &trans_equals, + .evaluate = &trans_evaluate + }; + + LIBNORMALFORM_SENTENCE *ret, *left, *right; + LIBNORMALFORM_SENTENCE *(*connective)(LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); + + if (!sentence) + return NULL; + + function->builtin = LIBNORMALFORM_NOT_BUILT_IN; + + switch (sentence->type) { + case TYPE_ALL: + case TYPE_ANY: + case TYPE_ONE: + case TYPE_NOT_ONE: + libnormalform_free(sentence); + errno = EDOM; + return NULL; + + case TYPE_TRUE: + case TYPE_FALSE: + case TYPE_VARIABLE: + return sentence; + + case TYPE_FUNCTION: + case TYPE_TRANS: + ret = malloc(sizeof(*ret)); + if (!ret) { + libnormalform_free(sentence); + return NULL; + } + *ret = prototype; + ret->hash = TRANS_HASH(function, sentence); + ret->data.trans.function = function; + ret->data.trans.input = sentence; + return ret; + + case TYPE_AND: + connective = &libnormalform_and2__; + break; + case TYPE_OR: + connective = &libnormalform_or2__; + break; + case TYPE_XOR: + connective = &libnormalform_xor2__; + break; + + default: + abort(); + } + + left = LEFT(sentence), LEFT(sentence) = NULL; + right = RIGHT(sentence), RIGHT(sentence) = NULL; + libnormalform_free(sentence); + left = libnormalform_transformation(function, left); + if (!left) { + libnormalform_free(right); + return NULL; + } + right = libnormalform_transformation(function, right); + if (!right) { + libnormalform_free(left); + return NULL; + } + + return (*connective)(left, right); +} + + +#else + + +static void * +uppercase(void *user_data, void *input) +{ + char *s = strdup(input), *p; + (void) user_data; + if (s) + for (p = s; *p; p++) + *p = (char)toupper(*p); + return s; +} + + +static void * +constuppercase(void *user_data, void *input) +{ + static char ret[] = "HELLO WORLD"; + (void) user_data; + (void) input; + return ret; +} + + +static void * +einval(void *user_data, void *input) +{ + (void) user_data; + (void) input; + errno = EINVAL; + return NULL; +} + + +static void +deallocate(void *user_data, void *input) +{ + (void) user_data; + free(input); +} + + +static int +validate(void *expected, void *input) +{ + return input ? !strcmp(input, expected) : (errno = EDOM, -1); +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_transformer trans1, trans2; + struct libnormalform_function fun1, fun2; + struct libnormalform_variable var1; + struct libnormalform_map dom1; + LIBNORMALFORM_SENTENCE *a, *b, *f1, *f2; + + errno = 0; + ASSERT(!libnormalform_transformation(&trans1, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_transformation(&trans1, NULL) && errno == 1); + + /* F(⊤) = ⊤ */ + ASSUME(a = libnormalform_true()); + ASSUME(b = libnormalform_transformation(&trans1, REF(a))); + ASSERT(b->refcount == 2); + ASSERT(b->type == TYPE_TRUE); + ASSERT(b == a); + libnormalform_free(a); + libnormalform_free(b); + + /* F(⊥) = ⊥ */ + ASSUME(a = libnormalform_false()); + ASSUME(b = libnormalform_transformation(&trans1, REF(a))); + ASSERT(b->refcount == 2); + ASSERT(b->type == TYPE_FALSE); + ASSERT(b == a); + libnormalform_free(a); + libnormalform_free(b); + + /* F(a) = a */ + ASSUME(a = libnormalform_variable(&var1)); + ASSUME(b = libnormalform_transformation(&trans1, REF(a))); + ASSERT(b->refcount == 2); + ASSERT(b->type == TYPE_VARIABLE); + ASSERT(b == a); + libnormalform_free(a); + libnormalform_free(b); + + /* F(f(x)) irreducible */ + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_transformation(&trans1, REF(a))); + ASSERT(b->refcount == 1); + ASSERT(a->refcount == 2); + ASSERT(b->type == TYPE_TRANS); + ASSERT(b->data.trans.function == &trans1); + ASSERT(b->data.trans.input == a); + libnormalform_free(a); + libnormalform_free(b); + + /* F(f(x) ∧ g(x)) = F(f(x)) ∧ F(g(x)) */ + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_and2(a, b)); + ASSUME(b = libnormalform_transformation(&trans1, a)); + ASSERT(b->refcount == 1); + ASSERT(b->type == TYPE_AND); + ASSERT(LEFT(b)->type == TYPE_TRANS); + ASSERT(RIGHT(b)->type == TYPE_TRANS); + ASSERT(LEFT(b)->refcount == 1); + ASSERT(RIGHT(b)->refcount == 1); + ASSERT(LEFT(b)->data.trans.function == &trans1); + ASSERT(RIGHT(b)->data.trans.function == &trans1); + ASSERT(LEFT(b)->data.trans.input); + ASSERT(RIGHT(b)->data.trans.input); + ASSERT(LEFT(b)->data.trans.input->type == TYPE_FUNCTION); + ASSERT(RIGHT(b)->data.trans.input->type == TYPE_FUNCTION); + ASSERT(LEFT(b)->data.trans.input != RIGHT(b)->data.trans.input); + libnormalform_free(b); + + /* F(f(x) ∨ g(x)) = F(f(x)) ∨ F(g(x)) */ + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_or2(a, b)); + ASSUME(b = libnormalform_transformation(&trans1, a)); + ASSERT(b->refcount == 1); + ASSERT(b->type == TYPE_OR); + ASSERT(LEFT(b)->type == TYPE_TRANS); + ASSERT(RIGHT(b)->type == TYPE_TRANS); + ASSERT(LEFT(b)->refcount == 1); + ASSERT(RIGHT(b)->refcount == 1); + ASSERT(LEFT(b)->data.trans.function == &trans1); + ASSERT(RIGHT(b)->data.trans.function == &trans1); + ASSERT(LEFT(b)->data.trans.input); + ASSERT(RIGHT(b)->data.trans.input); + ASSERT(LEFT(b)->data.trans.input->type == TYPE_FUNCTION); + ASSERT(RIGHT(b)->data.trans.input->type == TYPE_FUNCTION); + ASSERT(LEFT(b)->data.trans.input != RIGHT(b)->data.trans.input); + libnormalform_free(b); + + /* F(f(x) ⊕ g(x)) = F(f(x)) ⊕ F(g(x)) */ + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_xor2(a, b)); + ASSUME(b = libnormalform_transformation(&trans1, a)); + ASSERT(b->refcount == 1); + ASSERT(b->type == TYPE_XOR); + ASSERT(LEFT(b)->type == TYPE_TRANS); + ASSERT(RIGHT(b)->type == TYPE_TRANS); + ASSERT(LEFT(b)->refcount == 1); + ASSERT(RIGHT(b)->refcount == 1); + ASSERT(LEFT(b)->data.trans.function == &trans1); + ASSERT(RIGHT(b)->data.trans.function == &trans1); + ASSERT(LEFT(b)->data.trans.input); + ASSERT(RIGHT(b)->data.trans.input); + ASSERT(LEFT(b)->data.trans.input->type == TYPE_FUNCTION); + ASSERT(RIGHT(b)->data.trans.input->type == TYPE_FUNCTION); + ASSERT(LEFT(b)->data.trans.input != RIGHT(b)->data.trans.input); + libnormalform_free(b); + + /* 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 */ + 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 */ + 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 */ + ASSUME(a = libnormalform_function(&fun1)); + ASSUME(b = libnormalform_function(&fun2)); + ASSUME(a = libnormalform_one(&dom1, a, b)); + ASSUME(a = libnormalform_not(a)); + errno = 0; + ASSERT(!libnormalform_transformation(&trans1, a) && errno == EDOM); + + ASSUME(a = libnormalform_function(&fun1)); + trans1.transform = &uppercase; + trans1.deallocate = &deallocate; + + fun1.evaluate = validate; + ASSUME(fun1.user_data = strdup("hello world")); + ASSERT(a->evaluate(a, (char []){"hello world"}) == 1); + ASSERT(a->evaluate(a, (char []){"HELLO WORLD"}) == 0); + ASSERT(a->evaluate(a, (char []){"x"}) == 0); + errno = 0; + ASSERT(a->evaluate(a, NULL) == -1 && errno == EDOM); + free(fun1.user_data); + + fun1.evaluate = validate; + ASSUME(fun1.user_data = strdup("HELLO WORLD")); + ASSUME(a = libnormalform_transformation(&trans1, a)); + ASSERT(a->evaluate(a, (char []){"hello world"}) == 1); + ASSERT(a->evaluate(a, (char []){"HELLO WORLD"}) == 1); + ASSERT(a->evaluate(a, (char []){"x"}) == 0); + trans1.transform = constuppercase; + trans1.deallocate = NULL; + ASSERT(a->evaluate(a, (char []){"hello world"}) == 1); + free(fun1.user_data); + + libnormalform_free(a); + + ASSUME(f1 = libnormalform_function(&fun1)); + ASSUME(f2 = libnormalform_function(&fun2)); + + ASSUME(a = libnormalform_transformation(&trans1, REF(f1))); + trans1.transform = einval; + trans1.deallocate = NULL; + errno = 0; + ASSERT(a->evaluate(a, (char []){"hello world"}) == -1 && errno == EINVAL); + + ASSERT_NOTEQUAL(a, f1); + + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_transformation(&trans1, REF(f1))); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_transformation(&trans1, libnormalform_not(REF(f1)))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_transformation(&trans1, REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_transformation(&trans2, REF(f1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_and2(REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_or2(REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_xor2(REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_all(&dom1, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_any(&dom1, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_one(&dom1, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_not(libnormalform_transformation(&trans1, REF(f1)))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + libnormalform_free(f1); + libnormalform_free(f2); + + TEST_END; +} + + +#endif diff --git a/libnormalform_true.c b/libnormalform_true.c new file mode 100644 index 0000000..e716cc5 --- /dev/null +++ b/libnormalform_true.c @@ -0,0 +1,156 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (TRUE implementation) + */ +static LIBNORMALFORM_SENTENCE * +true_inverse(LIBNORMALFORM_SENTENCE *this) +{ + (void) this; + return libnormalform_false(); +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (TRUE implementation) + */ +static int +true_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + (void) this; + if (other->type == TYPE_TRUE) { + *inv_out = 0; + return 1; + } else if (other->type == TYPE_FALSE) { + *inv_out = 1; + return 1; + } else { + return 0; + } +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (TRUE implementation) + */ +CONST static int +true_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + (void) this; + (void) input; + return 1; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_true)(void) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_TRUE, + .hash = TRUE_FALSE_HASH, + .inverse = &true_inverse, + .equals = &true_equals, + .evaluate = &true_evaluate + }; + + /* + * During normalisation, some fields may be set, + * therefore a new allocation is returned instead + * of a reference to a static allocation, so that + * converation can be done from the threads at + * the same time on different sentences, both + * including this constant. + */ + + LIBNORMALFORM_SENTENCE *ret = malloc(sizeof(*ret)); + if (ret) + *ret = prototype; + return ret; +} + + +#else + + +static int +tautology(void *user_data, void *input) +{ + (void) user_data; + (void) input; + return 1; +} + + +static int +contradiction(void *user_data, void *input) +{ + (void) user_data; + (void) input; + return 0; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2; + struct libnormalform_function fun1, fun2; + struct libnormalform_map domain; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *f1, *f2; + + var1.value = LIBNORMALFORM_FALSE; + var2.value = LIBNORMALFORM_TRUE; + fun1.evaluate = &tautology; + fun2.evaluate = &contradiction; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(f1 = libnormalform_function(&fun1)); + ASSUME(f2 = libnormalform_function(&fun2)); + + ASSUME(a = libnormalform_true()); + ASSERT(a->type == TYPE_TRUE); + ASSERT(a->refcount == 1); + ASSERT_EQUAL(a, a); + + ASSERT(a->evaluate(a, NULL) == 1); + +#define CHECK(CMP, X)\ + do {\ + ASSUME(b = (X));\ + ASSERT_##CMP(a, b);\ + libnormalform_free(b);\ + } while (0) + + CHECK(EQUAL, libnormalform_true()); + CHECK(INVEQUAL, libnormalform_false()); + CHECK(NOTEQUAL, libnormalform_and2(REF(v1), REF(v2))); + CHECK(NOTEQUAL, libnormalform_or2(REF(v1), REF(v2))); + CHECK(NOTEQUAL, libnormalform_xor2(REF(v1), REF(v2))); + CHECK(NOTEQUAL, libnormalform_all(&domain, REF(f1), REF(f2))); + CHECK(NOTEQUAL, libnormalform_any(&domain, REF(f1), REF(f2))); + CHECK(NOTEQUAL, libnormalform_one(&domain, REF(f1), REF(f2))); + CHECK(NOTEQUAL, libnormalform_not(libnormalform_one(&domain, REF(f1), REF(f2)))); + CHECK(NOTEQUAL, REF(v1)); + CHECK(NOTEQUAL, REF(f1)); + +#undef CHECK + + libnormalform_free(a); + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(f1); + libnormalform_free(f2); + + TEST_END; +} + + +#endif diff --git a/libnormalform_unique.c b/libnormalform_unique.c new file mode 100644 index 0000000..4f0b467 --- /dev/null +++ b/libnormalform_unique.c @@ -0,0 +1,234 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_unique)(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); + + +#else + + +static int +evalbool(void *user_data, void *input) +{ + int *vp = input; + (void) user_data; + return *vp; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1; + struct libnormalform_function fun1; + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *v1; + int t = 1, f = 0; + + ASSUME(v1 = libnormalform_variable(&var1)); + + errno = 0; + ASSERT(!libnormalform_unique(&dom1, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_unique(&dom1, NULL) && errno == 1); + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + /* ∃!x.⊥ = ⊥ */ + ASSUME(a = libnormalform_unique(&dom1, libnormalform_false())); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* ∃!x.φ irreducable */ + ASSUME(a = libnormalform_unique(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ONE); + libnormalform_free(a); + + /* ∃!x.⊤ irreducable */ + ASSUME(a = libnormalform_unique(&dom1, libnormalform_true())); + ASSERT(a->type == TYPE_ONE); + libnormalform_free(a); + + ASSUME(a = libnormalform_unique(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ONE); + ASSERT(a->refcount == 1); + + dom1.nmappings = 1; + + /* ∃!x∈{a}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a}.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + + /* ∃!x∈{a,b}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a,b}.⊤ = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + /* ∃!x∈∅.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈∅.⊤ = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + libnormalform_free(a); + + fun1.evaluate = &evalbool; + dom1.nmappings = 2; + + ASSUME(a = libnormalform_unique(&dom1, libnormalform_function(&fun1))); + ASSERT(a->type == TYPE_ONE); + map[0].value = NULL; + map[1].value = NULL; + + /* ∃!x∈{⊥, ⊥}.x = ⊥ */ + map[0].key = &f; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{⊥, ⊤}.x = ⊤ */ + map[0].key = &f; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃!x∈{⊤, ⊥}.x = ⊤ */ + map[0].key = &t; + map[1].key = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃!x∈{⊤, ⊤}.x = ⊥ */ + map[0].key = &t; + map[1].key = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{⊤, ⊤, ⊤}.x = ⊥ */ + map[0].key = &t; + map[1].key = &t; + map[2].key = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + libnormalform_free(a); + + ASSUME(a = libnormalform_unique(&dom1, REF(v1))); + + /* ∃!x∈X.P(x) = ∃!x∈X.(P(x) ∧ ⊤) */ + ASSUME(b = libnormalform_one(&dom1, REF(v1), libnormalform_true())); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + /* ¬∃!x∈X.P(x) = ¬∃!x∈X.P(x) */ + ASSUME(b = libnormalform_not(REF(a))); + ASSERT_INVEQUAL(a, b); + ASSERT(b->type == TYPE_NOT_ONE); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∃!x∈X.Q(x) */ + ASSUME(b = libnormalform_unique(&dom1, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∃!x∈Y.P(x)) */ + ASSUME(b = libnormalform_unique(&dom2, REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from TRUE */ + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from FALSE */ + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from variable1 */ + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from NOT variable1 */ + ASSUME(b = libnormalform_not(libnormalform_variable(&var1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from function1 */ + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from NOT function1 */ + ASSUME(b = libnormalform_not(libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from T(function1) */ + ASSUME(b = libnormalform_transformation(&trans, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∀x∈X.(P(x) → ⊤) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), libnormalform_true())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∀x∈X.(P(x) → ⊥) */ + ASSUME(b = libnormalform_all(&dom1, REF(v1), libnormalform_false())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∃x∈X.(P(x) ∧ ⊥) */ + ASSUME(b = libnormalform_any(&dom1, REF(v1), libnormalform_false())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∃x∈X.(P(x) ∧ ⊤) */ + ASSUME(b = libnormalform_any(&dom1, REF(v1), libnormalform_true())); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from AND (P, P) */ + ASSUME(b = libnormalform_and2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(X) independent from OR (P, P) */ + ASSUME(b = libnormalform_or2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from XOR (P, P) */ + ASSUME(b = libnormalform_xor2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + libnormalform_free(v1); + + TEST_END; +} + + +#endif diff --git a/libnormalform_uniquely.c b/libnormalform_uniquely.c new file mode 100644 index 0000000..53e88ba --- /dev/null +++ b/libnormalform_uniquely.c @@ -0,0 +1,224 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_uniquely)(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); + + +#else + + +static int +evalbool(void *user_data, void *input) +{ + int *vp = input; + (void) user_data; + return *vp; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1; + struct libnormalform_function fun1; + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *v1; + int t = 1, f = 0; + + ASSUME(v1 = libnormalform_variable(&var1)); + + errno = 0; + ASSERT(!libnormalform_uniquely(&dom1, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_uniquely(&dom1, NULL) && errno == 1); + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + /* ∃!x.⊥ = ⊥ */ + ASSUME(a = libnormalform_uniquely(&dom1, libnormalform_false())); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* ∃!x.φ irreducable */ + ASSUME(a = libnormalform_uniquely(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ONE); + libnormalform_free(a); + + /* ∃!x.⊤ irreducable */ + ASSUME(a = libnormalform_uniquely(&dom1, libnormalform_true())); + ASSERT(a->type == TYPE_ONE); + libnormalform_free(a); + + ASSUME(a = libnormalform_uniquely(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ONE); + ASSERT(a->refcount == 1); + + dom1.nmappings = 1; + + /* ∃!x∈{a}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a}.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + + /* ∃!x∈{a,b}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{a,b}.⊤ = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + /* ∃!x∈∅.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈∅.⊤ = ⊥ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 0); + + dom1.nmappings = 0; + + libnormalform_free(a); + + fun1.evaluate = &evalbool; + dom1.nmappings = 2; + + ASSUME(a = libnormalform_uniquely(&dom1, libnormalform_function(&fun1))); + ASSERT(a->type == TYPE_ONE); + map[0].key = NULL; + map[1].key = NULL; + + /* ∃!x∈{⊥, ⊥}.x = ⊥ */ + map[0].value = &f; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{⊥, ⊤}.x = ⊤ */ + map[0].value = &f; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃!x∈{⊤, ⊥}.x = ⊤ */ + map[0].value = &t; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∃!x∈{⊤, ⊤}.x = ⊥ */ + map[0].value = &t; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∃!x∈{⊤, ⊤, ⊤}.x = ⊥ */ + map[0].value = &t; + map[1].value = &t; + map[2].value = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + libnormalform_free(a); + + ASSUME(a = libnormalform_uniquely(&dom1, REF(v1))); + + /* ∃!x∈X.P(x) = ∃!x∈X.(⊤ ∧ P(x)) */ + ASSUME(b = libnormalform_one(&dom1, libnormalform_true(), REF(v1))); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + /* ¬∃!x∈X.P(x) = ¬∃!x∈X.P(x) */ + ASSUME(b = libnormalform_not(REF(a))); + ASSERT_INVEQUAL(a, b); + ASSERT(b->type == TYPE_NOT_ONE); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∃!x∈X.Q(x) */ + ASSUME(b = libnormalform_uniquely(&dom1, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∃!x∈Y.P(x)) */ + ASSUME(b = libnormalform_uniquely(&dom2, REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from TRUE */ + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from FALSE */ + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from variable1 */ + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from NOT variable1 */ + ASSUME(b = libnormalform_not(libnormalform_variable(&var1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from function1 */ + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from NOT function1 */ + ASSUME(b = libnormalform_not(libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from T(function1) */ + ASSUME(b = libnormalform_transformation(&trans, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∀x∈X.(⊤ → P(x)) */ + ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from ∃x∈X.(⊤ ∧ P(x)) */ + ASSUME(b = libnormalform_any(&dom1, libnormalform_true(), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from AND (P, P) */ + ASSUME(b = libnormalform_and2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(X) independent from OR (P, P) */ + ASSUME(b = libnormalform_or2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∃!x∈X.P(x) independent from XOR (P, P) */ + ASSUME(b = libnormalform_xor2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + libnormalform_free(v1); + + TEST_END; +} + + +#endif diff --git a/libnormalform_universally.c b/libnormalform_universally.c new file mode 100644 index 0000000..922437c --- /dev/null +++ b/libnormalform_universally.c @@ -0,0 +1,233 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_universally)(struct libnormalform_map *, LIBNORMALFORM_SENTENCE *); + + +#else + + +static int +evalbool(void *user_data, void *input) +{ + int *vp = input; + (void) user_data; + return *vp; +} + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1; + struct libnormalform_function fun1; + struct libnormalform_map dom1, dom2; + struct libnormalform_mapping map[3]; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *c, *v1; + int t = 1, f = 0; + + ASSUME(v1 = libnormalform_variable(&var1)); + + errno = 0; + ASSERT(!libnormalform_universally(&dom1, NULL) && errno == 0); + errno = 1; + ASSERT(!libnormalform_universally(&dom1, NULL) && errno == 1); + + dom1.mappings = map; + memset(map, 0, sizeof(map)); + + /* ∀x.⊤ = ⊤ */ + ASSUME(a = libnormalform_universally(&dom1, libnormalform_true())); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* ∀x.φ irreducable */ + ASSUME(a = libnormalform_universally(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ALL); + libnormalform_free(a); + + /* ∀x.⊥ irreducable */ + ASSUME(a = libnormalform_universally(&dom1, libnormalform_false())); + ASSERT(a->type == TYPE_ALL); + libnormalform_free(a); + + ASSUME(a = libnormalform_universally(&dom1, REF(v1))); + ASSERT(a->type == TYPE_ALL); + ASSERT(a->refcount == 1); + + dom1.nmappings = 1; + + /* ∀x∈{a}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{a}.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 2; + + /* ∀x∈{a,b}.⊥ = ⊥ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{a,b}.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + dom1.nmappings = 0; + + /* ∀x∈∅.⊥ = ⊤ */ + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈∅.⊤ = ⊤ */ + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + + libnormalform_free(a); + + fun1.evaluate = &evalbool; + dom1.nmappings = 2; + + ASSUME(a = libnormalform_universally(&dom1, libnormalform_function(&fun1))); + ASSERT(a->type == TYPE_ALL); + map[0].key = NULL; + map[1].key = NULL; + + /* ∀x∈{⊥, ⊥}.x = ⊥ */ + map[0].value = &f; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{⊥, ⊤}.x = ⊥ */ + map[0].value = &f; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{⊤, ⊥}.x = ⊥ */ + map[0].value = &t; + map[1].value = &f; + ASSERT(libnormalform_evaluate(a) == 0); + + /* ∀x∈{⊤, ⊤}.x = ⊤ */ + map[0].value = &t; + map[1].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + /* ∀x∈{⊤, ⊤, ⊤}.x = ⊤ */ + map[0].value = &t; + map[1].value = &t; + map[2].value = &t; + ASSERT(libnormalform_evaluate(a) == 1); + + libnormalform_free(a); + + ASSUME(a = libnormalform_universally(&dom1, REF(v1))); + + /* ∀x∈X.P(x) = ¬∃x∈X.(⊤ ∧ ¬P(x)) */ + ASSUME(b = libnormalform_any(&dom1, libnormalform_true(), libnormalform_not(REF(v1)))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from ∀x∈X.Q(x) */ + ASSUME(b = libnormalform_universally(&dom1, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from ∀x∈Y.P(x)) */ + ASSUME(b = libnormalform_universally(&dom2, REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) = ∀x∈X.(⊤ → P(x)) */ + ASSUME(b = libnormalform_all(&dom1, libnormalform_true(), REF(v1))); + ASSERT_EQUAL(a, b); + libnormalform_free(b); + + /* ¬∀x∈X.P(x) = ∃x∈X.(⊤ ∧ ¬P(x)) */ + ASSUME(c = libnormalform_not(REF(a))); + ASSUME(b = libnormalform_any(&dom1, libnormalform_true(), libnormalform_not(REF(v1)))); + ASSERT_EQUAL(c, b); + ASSERT(c->type == TYPE_ANY); + libnormalform_free(b); + libnormalform_free(c); + + /* ∀x∈X.P(x) independent from TRUE */ + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from FALSE */ + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from variable1 */ + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from NOT variable1 */ + ASSUME(b = libnormalform_not(libnormalform_variable(&var1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from function1 */ + ASSUME(b = libnormalform_function(&fun1)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from NOT function1 */ + ASSUME(b = libnormalform_not(libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from T(function1) */ + ASSUME(b = libnormalform_transformation(&trans, libnormalform_function(&fun1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from ∃x∈X.(⊤ ∧ P(x)) */ + ASSUME(b = libnormalform_any(&dom1, libnormalform_true(), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from ∃!x∈X.(⊤ ∧ P(x)) */ + ASSUME(b = libnormalform_one(&dom1, libnormalform_true(), REF(v1))); + ASSERT_NOTEQUAL(a, b); + + /* ∀x∈X.P(x) independent from ¬∃!x∈X.(⊤ ∧ P(x)) */ + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from AND (P, P) */ + ASSUME(b = libnormalform_and2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(X) independent from OR (P, P) */ + ASSUME(b = libnormalform_or2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + /* ∀x∈X.P(x) independent from XOR (P, P) */ + ASSUME(b = libnormalform_xor2__(REF(v1), REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + + libnormalform_free(v1); + + TEST_END; +} + + +#endif diff --git a/libnormalform_vand.c b/libnormalform_vand.c new file mode 100644 index 0000000..e550c37 --- /dev/null +++ b/libnormalform_vand.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vand)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_and.c" + +#endif diff --git a/libnormalform_vand_checked.c b/libnormalform_vand_checked.c new file mode 100644 index 0000000..437fbde --- /dev/null +++ b/libnormalform_vand_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vand_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_and.c" + +#endif diff --git a/libnormalform_variable.c b/libnormalform_variable.c new file mode 100644 index 0000000..0c8b0bf --- /dev/null +++ b/libnormalform_variable.c @@ -0,0 +1,153 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (Variable literal implementation) + */ +static LIBNORMALFORM_SENTENCE * +variable_inverse(LIBNORMALFORM_SENTENCE *this) +{ + LIBNORMALFORM_SENTENCE *ret = libnormalform_variable(this->data.literal.atom.variable); + if (ret) + ret->data.literal.inverted = this->data.literal.inverted ^ 1; + return ret; +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (Variable literal implementation) + */ +static int +variable_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + if (other->type != TYPE_VARIABLE || this->data.literal.atom.variable != other->data.literal.atom.variable) + return 0; + *inv_out = this->data.literal.inverted ^ other->data.literal.inverted; + return 1; +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (Variable literal implementation) + */ +PURE static int +variable_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + (void) input; + return (this->data.literal.atom.variable->value != LIBNORMALFORM_FALSE) ^ this->data.literal.inverted; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_variable)(struct libnormalform_variable *variable) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_VARIABLE, + .inverse = &variable_inverse, + .equals = &variable_equals, + .evaluate = &variable_evaluate + }; + + LIBNORMALFORM_SENTENCE *ret = malloc(sizeof(*ret)); + if (ret) { + *ret = prototype; + ret->hash = LITERAL_HASH(variable); + ret->data.literal.atom.variable = variable; + ret->data.literal.inverted = 0; + } + return ret; +} + + +#else + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2; + struct libnormalform_function fun1, fun2; + struct libnormalform_map domain; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *f1, *f2; + + ASSUME(a = libnormalform_variable(&var1)); + ASSERT(a->refcount == 1); + ASSERT(a->travel_index == 0); + ASSERT(a->travel_count == 0); + ASSERT(a->type == TYPE_VARIABLE); + ASSERT(a->data.literal.inverted == 0); + ASSERT(a->data.literal.atom.variable == &var1); + ASSERT_EQUAL(a, a); + var1.value = LIBNORMALFORM_TRUE; + ASSERT(libnormalform_evaluate(a) == 1); + var1.value = LIBNORMALFORM_FALSE; + ASSERT(libnormalform_evaluate(a) == 0); + + ASSUME(b = libnormalform_variable(&var1)); + ASSERT_EQUAL(a, b); + ASSUME(b = libnormalform_not(b)); + ASSERT_INVEQUAL(a, b); + ASSERT_INVEQUAL(b, a); + libnormalform_free(b); + + ASSUME(b = libnormalform_variable(&var2)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(f1 = libnormalform_function(&fun1)); + ASSUME(f2 = libnormalform_function(&fun2)); + + ASSERT_NOTEQUAL(a, f1); + + ASSUME(b = libnormalform_and2(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_or2(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_all(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_any(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + ASSUME(b = libnormalform_one(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(b); + + libnormalform_free(a); + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(f1); + libnormalform_free(f2); + + TEST_END; +} + + +#endif diff --git a/libnormalform_vif.c b/libnormalform_vif.c new file mode 100644 index 0000000..711ff4b --- /dev/null +++ b/libnormalform_vif.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vif)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_if.c" + +#endif diff --git a/libnormalform_vif_checked.c b/libnormalform_vif_checked.c new file mode 100644 index 0000000..6abe89d --- /dev/null +++ b/libnormalform_vif_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vif_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_if.c" + +#endif diff --git a/libnormalform_vimply.c b/libnormalform_vimply.c new file mode 100644 index 0000000..4ec2b3a --- /dev/null +++ b/libnormalform_vimply.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vimply)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_imply.c" + +#endif diff --git a/libnormalform_vimply_checked.c b/libnormalform_vimply_checked.c new file mode 100644 index 0000000..9cf1884 --- /dev/null +++ b/libnormalform_vimply_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vimply_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_imply.c" + +#endif diff --git a/libnormalform_vnand.c b/libnormalform_vnand.c new file mode 100644 index 0000000..61a5d83 --- /dev/null +++ b/libnormalform_vnand.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vnand)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_nand.c" + +#endif diff --git a/libnormalform_vnand_checked.c b/libnormalform_vnand_checked.c new file mode 100644 index 0000000..a884e25 --- /dev/null +++ b/libnormalform_vnand_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vnand_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_nand.c" + +#endif diff --git a/libnormalform_vnif.c b/libnormalform_vnif.c new file mode 100644 index 0000000..b76b4cb --- /dev/null +++ b/libnormalform_vnif.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vnif)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_nif.c" + +#endif diff --git a/libnormalform_vnif_checked.c b/libnormalform_vnif_checked.c new file mode 100644 index 0000000..fbd71f2 --- /dev/null +++ b/libnormalform_vnif_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vnif_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_nif.c" + +#endif diff --git a/libnormalform_vnimply.c b/libnormalform_vnimply.c new file mode 100644 index 0000000..f4f9931 --- /dev/null +++ b/libnormalform_vnimply.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vnimply)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_nimply.c" + +#endif diff --git a/libnormalform_vnimply_checked.c b/libnormalform_vnimply_checked.c new file mode 100644 index 0000000..e2ad87e --- /dev/null +++ b/libnormalform_vnimply_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vnimply_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_nimply.c" + +#endif diff --git a/libnormalform_vnor.c b/libnormalform_vnor.c new file mode 100644 index 0000000..5752706 --- /dev/null +++ b/libnormalform_vnor.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vnor)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_nor.c" + +#endif diff --git a/libnormalform_vnor_checked.c b/libnormalform_vnor_checked.c new file mode 100644 index 0000000..4c2543b --- /dev/null +++ b/libnormalform_vnor_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vnor_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_nor.c" + +#endif diff --git a/libnormalform_vor.c b/libnormalform_vor.c new file mode 100644 index 0000000..83f70bd --- /dev/null +++ b/libnormalform_vor.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vor)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_or.c" + +#endif diff --git a/libnormalform_vor_checked.c b/libnormalform_vor_checked.c new file mode 100644 index 0000000..99e218b --- /dev/null +++ b/libnormalform_vor_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vor_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_or.c" + +#endif diff --git a/libnormalform_vxnor.c b/libnormalform_vxnor.c new file mode 100644 index 0000000..7c28cb6 --- /dev/null +++ b/libnormalform_vxnor.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vxnor)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_xnor.c" + +#endif diff --git a/libnormalform_vxnor_checked.c b/libnormalform_vxnor_checked.c new file mode 100644 index 0000000..8d64bb6 --- /dev/null +++ b/libnormalform_vxnor_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vxnor_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_xnor.c" + +#endif diff --git a/libnormalform_vxor.c b/libnormalform_vxor.c new file mode 100644 index 0000000..5ea8ab9 --- /dev/null +++ b/libnormalform_vxor.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vxor)(LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_VALIST +#include "libnormalform_xor.c" + +#endif diff --git a/libnormalform_vxor_checked.c b/libnormalform_vxor_checked.c new file mode 100644 index 0000000..a9db606 --- /dev/null +++ b/libnormalform_vxor_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_vxor_checked)(size_t, LIBNORMALFORM_SENTENCE *, va_list); + + +#else + +#define USE_CHECKED_VALIST +#include "libnormalform_xor.c" + +#endif diff --git a/libnormalform_xnor.c b/libnormalform_xnor.c new file mode 100644 index 0000000..3b0bff6 --- /dev/null +++ b/libnormalform_xnor.c @@ -0,0 +1,437 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +LIBNORMALFORM_SENTENCE * +(libnormalform_xnor)(LIBNORMALFORM_SENTENCE **xs) +{ + size_t n = 0; + + while (xs[n]) + n += 1; + + /* ⨀(X ∪ {x}) = ⨀X ⊙ x = ¬(⨀X ⊕ x) */ + /* ⨀∅ = ⨀∅ ⊙ 1 = ⨀∅ ⊙ 1 ⊙ 1 = ⨀(∅ ∪ {1, 1}) = ⨀{1, 1} = 1 ⊙ 1 = 1 = ¬0 = ¬⨁∅ */ + if (n & 1U) + return libnormalform_xor(xs); + else + return libnormalform_not(libnormalform_xor(xs)); +} + + +#else + + +#define XNOR(...) LIBNORMALFORM_XNOR(__VA_ARGS__) + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!XNOR(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!XNOR(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!XNOR(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!XNOR(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!XNOR(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!XNOR(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = XNOR(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = XNOR(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL3(VALUE, V1, V2, V3)\ + do {\ + ASSUME(a = XNOR(REF(v1), REF(v2), REF(v3)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + var3.value = V3##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + ASSERT_CONST(TRUE); /* XNOR () -> TRUE */ + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* XNOR (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* XNOR (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(TRUE, F, F); /* XNOR (FALSE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, F, T); /* XNOR (FALSE, TRUE) -> FALSE */ + ASSERT_CONST(FALSE, T, F); /* XNOR (TRUE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, T, T); /* XNOR (TRUE, TRUE) -> TRUE */ + + ASSERT_EVAL2(TRUE, F, F); /* XNOR (var(FALSE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(FALSE, F, T); /* XNOR (var(FALSE), var(TRUE)) => FALSE */ + ASSERT_EVAL2(FALSE, T, F); /* XNOR (var(TRUE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(TRUE, T, T); /* XNOR (var(TRUE), var(TRUE)) => TRUE */ + +#ifndef USE_TWO + + ASSERT_CONST(FALSE, F, F, F); /* XNOR (FALSE, FALSE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, F, F, T); /* XNOR (FALSE, FALSE, TRUE) -> TRUE */ + ASSERT_CONST(TRUE, F, T, F); /* XNOR (FALSE, TRUE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, F, T, T); /* XNOR (FALSE, TRUE, TRUE) -> FALSE */ + ASSERT_CONST(TRUE, T, F, F); /* XNOR (TRUE, FALSE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, T, F, T); /* XNOR (TRUE, FALSE, TRUE) -> FALSE */ + ASSERT_CONST(FALSE, T, T, F); /* XNOR (TRUE, TRUE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, T, T, T); /* XNOR (TRUE, TRUE, TRUE) -> TRUE */ + + ASSERT_EVAL3(FALSE, F, F, F); /* XNOR (var(FALSE), var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL3(TRUE, F, F, T); /* XNOR (var(FALSE), var(FALSE), var(TRUE)) => TRUE */ + ASSERT_EVAL3(TRUE, F, T, F); /* XNOR (var(FALSE), var(TRUE), var(FALSE)) => TRUE */ + ASSERT_EVAL3(FALSE, F, T, T); /* XNOR (var(FALSE), var(TRUE), var(TRUE)) => FALSE */ + ASSERT_EVAL3(TRUE, T, F, F); /* XNOR (var(TRUE), var(FALSE), var(FALSE)) => TRUE */ + ASSERT_EVAL3(FALSE, T, F, T); /* XNOR (var(TRUE), var(FALSE), var(TRUE)) => FALSE */ + ASSERT_EVAL3(FALSE, T, T, F); /* XNOR (var(TRUE), var(TRUE), var(FALSE)) => FALSE */ + ASSERT_EVAL3(TRUE, T, T, T); /* XNOR (var(TRUE), var(TRUE), var(TRUE)) => TRUE */ + + /* XNOR (x) -> x */ + ASSUME(a = XNOR(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* XNOR (x, FALSE) -> NOT x */ + ASSUME(a = XNOR(REF(v1), F)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XNOR (FALSE, x) -> NOT x */ + ASSUME(a = XNOR(F, REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XNOR (x, TRUE) -> x */ + ASSUME(a = XNOR(REF(v1), T)); + ASSERT_EQUAL(a, v1); + libnormalform_free(a); + + /* XNOR (TRUE, x) -> x */ + ASSUME(a = XNOR(T, REF(v1))); + ASSERT_EQUAL(a, v1); + libnormalform_free(a); + +#ifndef USE_TWO + + /* XNOR (x, FALSE, FALSE) -> x */ + ASSUME(a = XNOR(REF(v1), F, F)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XNOR (x, FALSE, TRUE) -> NOT x */ + ASSUME(a = XNOR(REF(v1), F, T)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XNOR (x, TRUE, FALSE) -> NOT x */ + ASSUME(a = XNOR(REF(v1), T, F)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XNOR (x, TRUE, TRUE) -> x */ + ASSUME(a = XNOR(REF(v1), T, T)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XNOR (FALSE, x, FALSE) -> x */ + ASSUME(a = XNOR(F, REF(v1), F)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XNOR (FALSE, x, TRUE) -> NOT x */ + ASSUME(a = XNOR(F, REF(v1), T)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XNOR (TRUE, x, FALSE) -> NOT x */ + ASSUME(a = XNOR(T, REF(v1), F)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XNOR (TRUE, x, TRUE) -> x */ + ASSUME(a = XNOR(T, REF(v1), T)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XNOR (FALSE, FALSE, x) -> x */ + ASSUME(a = XNOR(F, F, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XNOR (FALSE, TRUE, x) -> NOT x */ + ASSUME(a = XNOR(F, T, REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XNOR (TRUE, FALSE, x) -> NOT x */ + ASSUME(a = XNOR(T, F, REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XNOR (TRUE, TRUE, x) -> x */ + ASSUME(a = XNOR(T, T, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + +#endif + + /* XNOR (x, x) -> TRUE */ + ASSUME(a = XNOR(REF(v1), REF(v1))); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + +#ifndef USE_TWO + + /* XNOR (x, x, FALSE) -> FALSE */ + ASSUME(a = XNOR(REF(v1), REF(v1), F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* XNOR (x, x, FALSE, FALSE) -> TRUE */ + ASSUME(a = XNOR(REF(v1), REF(v1), F, F)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* XNOR (x, x, TRUE) -> TRUE */ + ASSUME(a = XNOR(REF(v1), REF(v1), T)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + +#endif + + /* XNOR (x, NOT x) -> FALSE */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = XNOR(REF(v1), a)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + +#ifndef USE_TWO + + /* XNOR (x, NOT x, FALSE) -> TRUE */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = XNOR(REF(v1), a, F)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* XNOR (x, NOT x, FALSE, FALSE) -> FALSE */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = XNOR(REF(v1), a, F, F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* XNOR (x, NOT x, TRUE) -> FALSE */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = XNOR(REF(v1), a, T)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + +#endif + + /* XNOR (x, y) -> NOT XOR (x, y) */ + ASSUME(a = XNOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#ifndef USE_TWO + + /* XNOR (x, y, z) -> NOT XOR (NOT XOR (x, y), z) */ + ASSUME(a = XNOR(REF(v1), REF(v2), REF(v3))); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_not(b)); + ASSUME(b = libnormalform_xor2(b, REF(v3))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#endif + + /* XNOR (x, y) -> OR (AND (x, y), AND (NOT x, NOT y)) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = libnormalform_and2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_or2__(a, b)); + ASSUME(a = XNOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_OR); + ASSERT_EQUAL(a, b); + ASSERT_EQUAL(b, a); + libnormalform_free(a); + libnormalform_free(b); + + /* XNOR (x, y) -> AND (OR (x, NOT y), OR (NOT x, y)) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(a = libnormalform_or2(REF(v1), a)); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_or2(b, REF(v2))); + ASSUME(b = libnormalform_and2__(a, b)); + ASSUME(a = XNOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_AND); + ASSERT_EQUAL(a, b); + ASSERT_EQUAL(b, a); + libnormalform_free(a); + libnormalform_free(b); + + /* XNOR (x, y) -> OR (AND (x, y), AND (NOT x, NOT y)) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = libnormalform_and2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = XNOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_XOR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XNOR (x, y) -> AND (OR (x, NOT y), OR (NOT x, y)) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(a = libnormalform_or2(REF(v1), a)); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_or2(b, REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = XNOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_XOR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XNOR (x, y) -> OR (AND (NOT x, NOT y), AND (x, y)) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = libnormalform_and2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_or2__(b, a)); + ASSUME(a = XNOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_OR); + ASSERT_EQUAL(a, b); + ASSERT_EQUAL(b, a); + libnormalform_free(a); + libnormalform_free(b); + + /* XNOR (x, y) -> AND (OR (NOT x, y), OR (x, NOT y)) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(a = libnormalform_or2(REF(v1), a)); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_or2(b, REF(v2))); + ASSUME(b = libnormalform_and2__(b, a)); + ASSUME(a = XNOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_AND); + ASSERT_EQUAL(a, b); + ASSERT_EQUAL(b, a); + libnormalform_free(a); + libnormalform_free(b); + + /* XNOR (x, y) -> OR (AND (NOT x, NOT y), AND (x, y)) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = libnormalform_and2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_or2(b, a)); + ASSUME(a = XNOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_XOR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XNOR (x, y) -> AND (OR (NOT x, y), OR (x, NOT y)) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(a = libnormalform_or2(REF(v1), a)); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_or2(b, REF(v2))); + ASSUME(b = libnormalform_and2(b, a)); + ASSUME(a = XNOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_XOR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 +#undef ASSERT_EVAL3 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(ts); + libnormalform_free(fs); + + TEST_END; +} + + +#endif diff --git a/libnormalform_xnor2.c b/libnormalform_xnor2.c new file mode 100644 index 0000000..65939e7 --- /dev/null +++ b/libnormalform_xnor2.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_xnor2)(LIBNORMALFORM_SENTENCE *, LIBNORMALFORM_SENTENCE *); + + +#else + +#define USE_TWO +#include "libnormalform_xnor.c" + +#endif diff --git a/libnormalform_xnor_checked.c b/libnormalform_xnor_checked.c new file mode 100644 index 0000000..d559019 --- /dev/null +++ b/libnormalform_xnor_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_xnor_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_xnor.c" + +#endif diff --git a/libnormalform_xnorl.c b/libnormalform_xnorl.c new file mode 100644 index 0000000..7646191 --- /dev/null +++ b/libnormalform_xnorl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_xnorl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_xnor.c" + +#endif diff --git a/libnormalform_xnorl_checked.c b/libnormalform_xnorl_checked.c new file mode 100644 index 0000000..5d4eb88 --- /dev/null +++ b/libnormalform_xnorl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_xnorl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_xnor.c" + +#endif diff --git a/libnormalform_xnorl_macro_test.c b/libnormalform_xnorl_macro_test.c new file mode 100644 index 0000000..af4857f --- /dev/null +++ b/libnormalform_xnorl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_xnor.c" +#endif diff --git a/libnormalform_xor.c b/libnormalform_xor.c new file mode 100644 index 0000000..451fbf9 --- /dev/null +++ b/libnormalform_xor.c @@ -0,0 +1,710 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * Check if a sentence equivalent to a specific sentence + * or its inverse is in an array of sentences + * + * @param x The sentence to look for + * @param among The array of sentences to look in + * @param n The number of sentences in `among` + * @param index_out Output parameter for the offset, in `among`, + * of the found sentence + * @param inv_out Output parameter for equivalency of `x` and the + * sentence found in `among`; set to 0 if the two + * are equivalent or 1 if `x` is equivalent to the + * inverse of the sentence found in `among` + * @return 1 if a sentence equivalent to `x` or its inverse + * was found, 0 otherwise + */ +static int +find(LIBNORMALFORM_SENTENCE *x, LIBNORMALFORM_SENTENCE **among, size_t n, size_t *index_out, int *inv_out) +{ + for (*index_out = 0; *index_out < n; ++*index_out) + if (x->equals(x, among[*index_out], inv_out)) + return 1; + return 0; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_xor)(LIBNORMALFORM_SENTENCE **xs) +{ + LIBNORMALFORM_SENTENCE *x, *ret, **saved = xs; + size_t n = 0, i; + int inv; + + /* ⨁∅ = {⨁X ⊕ (x ⊕ x) = ⨁X ⊕ 0 = ⨁X ⇒ ⨁X = ⨁(X ∪ {x, x})} = x ⊕ x = 0 */ + ret = libnormalform_false(); + + for (; (x = *xs); xs++) { + if (x->type == TYPE_FALSE) { + /* ⨁X ⊕ 0 = ⨁X */ + libnormalform_free(x); + + } else if (x->type == TYPE_TRUE) { + /* ⨁X ⊕ 1 = ¬⨁X */ + libnormalform_free(x); + invert: + ret = libnormalform_not(ret); + + } else if (find(x, saved, n, &i, &inv)) { + /* ⨁X ⊕ x ⊕ x = ⨁X ⊕ (x ⊕ x) = ⨁X ⊕ 0 = ⨁X */ + /* ⨁X ⊕ x ⊕ ¬x = ⨁X ⊕ (x ⊕ ¬x) = ⨁X ⊕ 1 = ¬⨁X */ + libnormalform_free(saved[i]); + libnormalform_free(x); + saved[i] = saved[--n]; + if (inv) + goto invert; + + } else { + saved[n++] = x; + } + } + + saved[n] = NULL; + /* ⨁(X ∪ x) = ⨁X ⊕ x */ + for (; *saved; saved++) + ret = libnormalform_xor2(ret, *saved); + + return ret; +} + + +#else + + +#define XOR(...) LIBNORMALFORM_XOR(__VA_ARGS__) + + +int +main(void) +{ + TEST_BEGIN; + + struct libnormalform_variable var1, var2, var3, var4; + struct libnormalform_function fun1, fun2; + struct libnormalform_map domain; + struct libnormalform_transformer trans; + LIBNORMALFORM_SENTENCE *a, *b, *v1, *v2, *v3, *v4, *f1, *f2; + LIBNORMALFORM_SENTENCE *ts, *fs; + + ASSUME(v1 = libnormalform_variable(&var1)); + ASSUME(v2 = libnormalform_variable(&var2)); + ASSUME(v3 = libnormalform_variable(&var3)); + ASSUME(v4 = libnormalform_variable(&var4)); + ASSUME(f1 = libnormalform_function(&fun1)); + ASSUME(f2 = libnormalform_function(&fun2)); + ASSUME(ts = libnormalform_true()); + ASSUME(fs = libnormalform_false()); + +#ifdef USE_CHECKED_VERSION + errno = 0; + ASSERT(!XOR(REF(v1), NULL) && errno == 0); + errno = 1; + ASSERT(!XOR(REF(v1), NULL) && errno == 1); + errno = 0; + ASSERT(!XOR(NULL, REF(v1)) && errno == 0); + errno = 1; + ASSERT(!XOR(NULL, REF(v1)) && errno == 1); + errno = 0; + ASSERT(!XOR(NULL, NULL) && errno == 0); + errno = 1; + ASSERT(!XOR(NULL, NULL) && errno == 1); +#endif + +#define T REF(ts) +#define F REF(fs) +#define TV LIBNORMALFORM_TRUE +#define FV LIBNORMALFORM_FALSE + +#define ASSERT_CONST(VALUE, ...)\ + do {\ + ASSUME(a = XOR(__VA_ARGS__));\ + ASSERT(a->type == TYPE_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL2(VALUE, V1, V2)\ + do {\ + ASSUME(a = XOR(REF(v1), REF(v2)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#define ASSERT_EVAL3(VALUE, V1, V2, V3)\ + do {\ + ASSUME(a = XOR(REF(v1), REF(v2), REF(v3)));\ + ASSERT(a->type != TYPE_TRUE && a->type != TYPE_FALSE);\ + var1.value = V1##V;\ + var2.value = V2##V;\ + var3.value = V3##V;\ + ASSERT(libnormalform_evaluate(a) == (int)LIBNORMALFORM_##VALUE);\ + libnormalform_free(a);\ + } while (0) + +#ifndef USE_TWO + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + + ASSERT_CONST(FALSE); /* XOR () -> FALSE */ + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + ASSERT_CONST(TRUE, T); /* XOR (TRUE) -> TRUE */ + ASSERT_CONST(FALSE, F); /* XOR (FALSE) -> FALSE */ + +#endif + + ASSERT_CONST(FALSE, F, F); /* XOR (FALSE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, F, T); /* XOR (FALSE, TRUE) -> TRUE */ + ASSERT_CONST(TRUE, T, F); /* XOR (TRUE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, T, T); /* XOR (TRUE, TRUE) -> FALSE */ + + ASSERT_EVAL2(FALSE, F, F); /* XOR (var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL2(TRUE, F, T); /* XOR (var(FALSE), var(TRUE)) => TRUE */ + ASSERT_EVAL2(TRUE, T, F); /* XOR (var(TRUE), var(FALSE)) => TRUE */ + ASSERT_EVAL2(FALSE, T, T); /* XOR (var(TRUE), var(TRUE)) => FALSE */ + +#ifndef USE_TWO + + ASSERT_CONST(FALSE, F, F, F); /* XOR (FALSE, FALSE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, F, F, T); /* XOR (FALSE, FALSE, TRUE) -> TRUE */ + ASSERT_CONST(TRUE, F, T, F); /* XOR (FALSE, TRUE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, F, T, T); /* XOR (FALSE, TRUE, TRUE) -> FALSE */ + ASSERT_CONST(TRUE, T, F, F); /* XOR (TRUE, FALSE, FALSE) -> TRUE */ + ASSERT_CONST(FALSE, T, F, T); /* XOR (TRUE, FALSE, TRUE) -> FALSE */ + ASSERT_CONST(FALSE, T, T, F); /* XOR (TRUE, TRUE, FALSE) -> FALSE */ + ASSERT_CONST(TRUE, T, T, T); /* XOR (TRUE, TRUE, TRUE) -> TRUE */ + + ASSERT_EVAL3(FALSE, F, F, F); /* XOR (var(FALSE), var(FALSE), var(FALSE)) => FALSE */ + ASSERT_EVAL3(TRUE, F, F, T); /* XOR (var(FALSE), var(FALSE), var(TRUE)) => TRUE */ + ASSERT_EVAL3(TRUE, F, T, F); /* XOR (var(FALSE), var(TRUE), var(FALSE)) => TRUE */ + ASSERT_EVAL3(FALSE, F, T, T); /* XOR (var(FALSE), var(TRUE), var(TRUE)) => FALSE */ + ASSERT_EVAL3(TRUE, T, F, F); /* XOR (var(TRUE), var(FALSE), var(FALSE)) => TRUE */ + ASSERT_EVAL3(FALSE, T, F, T); /* XOR (var(TRUE), var(FALSE), var(TRUE)) => FALSE */ + ASSERT_EVAL3(FALSE, T, T, F); /* XOR (var(TRUE), var(TRUE), var(FALSE)) => FALSE */ + ASSERT_EVAL3(TRUE, T, T, T); /* XOR (var(TRUE), var(TRUE), var(TRUE)) => TRUE */ + + /* XOR (x) -> x */ + ASSUME(a = XOR(REF(v1))); + ASSERT(a == v1); + ASSERT(a->refcount == 2); + libnormalform_free(a); + +#endif + + /* XOR (x, TRUE) -> NOT x */ + ASSUME(a = XOR(REF(v1), T)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XOR (TRUE, x) -> NOT x */ + ASSUME(a = XOR(T, REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XOR (x, FALSE) -> x */ + ASSUME(a = XOR(REF(v1), F)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XOR (FALSE, x) -> x */ + ASSUME(a = XOR(F, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + +#ifndef USE_TWO + + /* XOR (x, FALSE, FALSE) -> x */ + ASSUME(a = XOR(REF(v1), F, F)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XOR (x, FALSE, TRUE) -> NOT x */ + ASSUME(a = XOR(REF(v1), F, T)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XOR (x, TRUE, FALSE) -> NOT x */ + ASSUME(a = XOR(REF(v1), T, F)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XOR (x, TRUE, TRUE) -> x */ + ASSUME(a = XOR(REF(v1), T, T)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XOR (FALSE, x, FALSE) -> x */ + ASSUME(a = XOR(F, REF(v1), F)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XOR (FALSE, x, TRUE) -> NOT x */ + ASSUME(a = XOR(F, REF(v1), T)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XOR (TRUE, x, FALSE) -> NOT x */ + ASSUME(a = XOR(T, REF(v1), F)); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XOR (TRUE, x, TRUE) -> x */ + ASSUME(a = XOR(T, REF(v1), T)); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XOR (FALSE, FALSE, x) -> x */ + ASSUME(a = XOR(F, F, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + + /* XOR (FALSE, TRUE, x) -> NOT x */ + ASSUME(a = XOR(F, T, REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XOR (TRUE, FALSE, x) -> NOT x */ + ASSUME(a = XOR(T, F, REF(v1))); + ASSERT_INVEQUAL(a, v1); + libnormalform_free(a); + + /* XOR (TRUE, TRUE, x) -> x */ + ASSUME(a = XOR(T, T, REF(v1))); + ASSERT_EQUAL(a, v1); + ASSERT(a == v1); + libnormalform_free(a); + +#endif + + /* XOR (x, x) -> FALSE */ + ASSUME(a = XOR(REF(v1), REF(v1))); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + +#ifndef USE_TWO + + /* XOR (x, x, FALSE) -> FALSE */ + ASSUME(a = XOR(REF(v1), REF(v1), F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* XOR (x, x, FALSE, FALSE) -> FALSE */ + ASSUME(a = XOR(REF(v1), REF(v1), F, F)); + ASSERT(a->type == TYPE_FALSE); + libnormalform_free(a); + + /* XOR (x, x, TRUE) -> TRUE */ + ASSUME(a = XOR(REF(v1), REF(v1), T)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + +#endif + + /* XOR (x, NOT x) -> TRUE */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(a = XOR(REF(v1), a)); + ASSERT(a->type == TYPE_TRUE); + libnormalform_free(a); + + /* XOR (x, y) -> XOR (x, y) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = XOR(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (y, x) -> XOR (x, y) */ + ASSUME(a = XOR(REF(v2), REF(v1))); + ASSUME(b = XOR(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#ifndef USE_TWO + + /* XOR (TRUE, x, y) -> NOT XOR (x, y) */ + ASSUME(a = XOR(T, REF(v1), REF(v2))); + ASSUME(b = XOR(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, TRUE, y) -> NOT XOR (x, y) */ + ASSUME(a = XOR(REF(v1), T, REF(v2))); + ASSUME(b = XOR(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y, TRUE) -> NOT XOR (x, y) */ + ASSUME(a = XOR(REF(v1), REF(v2), T)); + ASSUME(b = XOR(REF(v1), REF(v2))); + ASSERT_INVEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (FALSE, x, y) -> XOR (x, y) */ + ASSUME(a = XOR(F, REF(v1), REF(v2))); + ASSUME(b = XOR(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, FALSE, y) -> XOR (x, y) */ + ASSUME(a = XOR(REF(v1), F, REF(v2))); + ASSUME(b = XOR(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y, FALSE) -> XOR (x, y) */ + ASSUME(a = XOR(REF(v1), REF(v2), F)); + ASSUME(b = XOR(REF(v1), REF(v2))); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#endif + + /* NOT XOR (x, y) -> XOR (NOT x, y) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(a = libnormalform_not(a)); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_xorl(b, REF(v2), NULL)); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* NOT XOR (x, y) -> XOR (x, NOT y) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(a = libnormalform_not(a)); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_xorl(REF(v1), b, NULL)); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* NOT XOR (x, y) -> XOR (x, y, TRUE) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(a = libnormalform_not(a)); + ASSUME(b = libnormalform_xorl(REF(v1), REF(v2), T, NULL)); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from XOR (x, z) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_xor2(REF(v1), REF(v3))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from XOR (z, x) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_xor2(REF(v3), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from XOR (z, w) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_xor2(REF(v3), REF(v4))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from AND (x, y) */ + ASSUME(a = REF(v1)); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from AND (x, NOT y) */ + ASSUME(a = REF(v1)); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from AND (NOT x, y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from AND (NOT x, NOT y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from OR (x, y) */ + ASSUME(a = REF(v1)); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from OR (x, NOT y) */ + ASSUME(a = REF(v1)); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from OR (NOT x, y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = REF(v2)); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from OR (NOT x, NOT y) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from TRUE */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_true()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from FALSE */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_false()); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from variable1 */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, v1); + libnormalform_free(a); + + /* XOR (x, y) -> independence from NOT variable1 */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_not(REF(v1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from function1 */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT_NOTEQUAL(a, f1); + libnormalform_free(a); + + /* XOR (x, y) -> independence from NOT function1 */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_not(REF(f1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from T(function1) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_transformation(&trans, REF(f1))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from ALL (domain1, function1, function2) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_all(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from ANY (domain1, function1, function2) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_any(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from ONE (domain1, function1, function2) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_one(&domain, REF(f1), REF(f2))); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> independence from NOT ONE (domain1, function1, function2) */ + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSUME(b = libnormalform_one(&domain, REF(f1), REF(f2))); + ASSUME(b = libnormalform_not(b)); + ASSERT_NOTEQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> OR (AND (x, NOT y), AND (NOT x, y)) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(a = libnormalform_and2(REF(v1), a)); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_and2(b, REF(v2))); + ASSUME(b = libnormalform_or2__(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_OR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> AND (OR (x, y), OR (NOT x, NOT y)) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = libnormalform_or2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_and2__(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_AND); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> OR (AND (x, NOT y), AND (NOT x, y)) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(a = libnormalform_and2(REF(v1), a)); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_and2(b, REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_XOR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> AND (OR (x, y), OR (NOT x, NOT y)) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = libnormalform_or2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_and2(a, b)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_XOR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> OR (AND (NOT x, y), AND (x, NOT y)) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(a = libnormalform_and2(REF(v1), a)); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_and2(b, REF(v2))); + ASSUME(b = libnormalform_or2__(b, a)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_OR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> AND (OR (NOT x, NOT y), OR (x, y)) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = libnormalform_or2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_and2__(b, a)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_AND); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> OR (AND (NOT x, y), AND (x, NOT y)) */ + ASSUME(a = libnormalform_not(REF(v2))); + ASSUME(a = libnormalform_and2(REF(v1), a)); + ASSUME(b = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_and2(b, REF(v2))); + ASSUME(b = libnormalform_or2(b, a)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_XOR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + + /* XOR (x, y) -> AND (OR (NOT x, NOT y), OR (x, y)) */ + ASSUME(a = libnormalform_not(REF(v1))); + ASSUME(b = libnormalform_not(REF(v2))); + ASSUME(b = libnormalform_or2(a, b)); + ASSUME(a = libnormalform_or2(REF(v1), REF(v2))); + ASSUME(b = libnormalform_and2(b, a)); + ASSUME(a = XOR(REF(v1), REF(v2))); + ASSERT(a->type == TYPE_XOR); + ASSERT(b->type == TYPE_XOR); + ASSERT_EQUAL(a, b); + libnormalform_free(a); + libnormalform_free(b); + +#undef T +#undef F +#undef TV +#undef FV + +#undef ASSERT_CONST +#undef ASSERT_EVAL2 +#undef ASSERT_EVAL3 + + libnormalform_free(v1); + libnormalform_free(v2); + libnormalform_free(v3); + libnormalform_free(v4); + libnormalform_free(f1); + libnormalform_free(f2); + libnormalform_free(ts); + libnormalform_free(fs); + + /* cascading of evaluation failure is tested in libnormalform_function.c */ + + TEST_END; +} + + +#endif diff --git a/libnormalform_xor2.c b/libnormalform_xor2.c new file mode 100644 index 0000000..a00c575 --- /dev/null +++ b/libnormalform_xor2.c @@ -0,0 +1,63 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +LIBNORMALFORM_SENTENCE * +(libnormalform_xor2)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ + int inv; + + if (!l || !r) { + libnormalform_free(l); + libnormalform_free(r); + return NULL; + } + + if (l->equals(l, r, &inv)) { + libnormalform_free(l); + libnormalform_free(r); + + if (!inv) { + /* x ⊕ x = 0 */ + return libnormalform_false(); + + } else { + /* x ⊕ ¬x = 1 */ + return libnormalform_true(); + } + + } else if (l->type == TYPE_TRUE) { + /* 1 ⊕ x = (1 ∨ x) ∧ ¬(1 ∧ x) = 1 ∧ ¬x = ¬x */ + r = libnormalform_not(r); + return_r: + libnormalform_free(l); + return r; + + } else if (l->type == TYPE_FALSE) { + /* 0 ⊕ x = (0 ∨ x) ∧ ¬(0 ∧ x) = x ∧ ¬0 = x ∧ 1 = x */ + goto return_r; + + } else if (r->type == TYPE_TRUE) { + /* x ⊕ 1 = 1 ⊕ x = ¬x */ + l = libnormalform_not(l); + return_l: + libnormalform_free(r); + return l; + + } else if (r->type == TYPE_FALSE) { + /* x ⊕ 0 = 0 ⊕ x = x */ + goto return_l; + + } else { + return libnormalform_xor2__(l, r); + } +} + + +#else + +#define USE_TWO +#include "libnormalform_xor.c" + +#endif diff --git a/libnormalform_xor2__.c b/libnormalform_xor2__.c new file mode 100644 index 0000000..c495428 --- /dev/null +++ b/libnormalform_xor2__.c @@ -0,0 +1,174 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +/** + * See `.inverse` in `struct libnormalform_sentence` (XOR implementation) + */ +static LIBNORMALFORM_SENTENCE * +xor_inverse(LIBNORMALFORM_SENTENCE *this) +{ + /* ¬(l ⊕ r) = ¬l ⊕ r, (¬l ⊕ ¬r = l ⊕ r) */ + LIBNORMALFORM_SENTENCE *l, *r, *ret; + l = this->data.binary.l->inverse(this->data.binary.l); + if (!l) + return NULL; + r = libnormalform_ref(this->data.binary.r); + if (!r) { + libnormalform_free(l); + return NULL; + } + ret = libnormalform_xor2__(l, r); + if (ret && this->atom) { + ret->atom = this->atom; + ret->atom->refcount += 1; + } + return ret; +} + + +/** + * See `.equals` in `struct libnormalform_sentence` (XOR implementation) + */ +static int +xor_equals(LIBNORMALFORM_SENTENCE *this, LIBNORMALFORM_SENTENCE *other, int *inv_out) +{ + LIBNORMALFORM_SENTENCE *tl, *tr, *oll, *olr; + LIBNORMALFORM_SENTENCE *ol, *or, *orl, *orr; + int inv; + + if (other->type != TYPE_XOR) { + int invll, invrl, invlr, invrr; + if (this->hash != other->hash) + return 0; + if (other->type != TYPE_AND && other->type != TYPE_OR) + return 0; + /* a ⊕ b = (a ∨ b) ∧ (¬a ∨ ¬b) */ + /* ¬(a ⊕ b) = (a ∧ b) ∨ (¬a ∧ ¬b) */ + /* ¬(a ⊕ b) = (a ∨ ¬b) ∧ (¬a ∨ b) */ + /* a ⊕ b = (a ∧ ¬b) ∨ (¬a ∧ b) */ + tl = this->data.binary.l; + tr = this->data.binary.r; + ol = other->data.binary.l; + or = other->data.binary.r; + if (ol->hash != or->hash) + return 0; + if (ol->type != or->type) + return 0; + if ((ol->type ^ other->type) != (TYPE_AND ^ TYPE_OR)) + return 0; + oll = ol->data.binary.l; + olr = ol->data.binary.r; + orl = or->data.binary.l; + orr = or->data.binary.r; + if (!tl->equals(tl, oll, &invll)) { + oll = ol->data.binary.r; + olr = ol->data.binary.l; + if (!tl->equals(tl, oll, &invll)) + return 0; + } + if (!tl->equals(tl, orl, &invrl)) { + orl = or->data.binary.r; + orr = or->data.binary.l; + if (!tl->equals(tl, orl, &invrl)) + return 0; + } + if (invll == invrl) + return 0; + if (!tr->equals(tr, olr, &invlr)) + return 0; + if (!tr->equals(tr, orr, &invrr)) + return 0; + if (invlr == invrr) + return 0; + *inv_out = (invll == invlr) == (other->type == TYPE_OR); + return 1; + } + + tl = this->data.binary.l; + tr = this->data.binary.r; + ol = other->data.binary.l; + or = other->data.binary.r; + + if (tl->hash != ol->hash || tr->hash != or->hash) + return 0; + + if (!tl->equals(tl, ol, inv_out)) { + if (tl->hash == tr->hash) { + ol = other->data.binary.r; + or = other->data.binary.l; + if (!tl->equals(tl, ol, inv_out)) + return 0; + } else { + return 0; + } + } + if (!tr->equals(tr, or, &inv)) + return 0; + *inv_out ^= inv; + return 1; +} + + +/** + * See `.evaluate` in `struct libnormalform_sentence` (XOR implementation) + */ +static int +xor_evaluate(LIBNORMALFORM_SENTENCE *this, void *input) +{ + int l, r; + + l = this->data.binary.l->evaluate(this->data.binary.l, input); + if (l < 0) + return l; + + r = this->data.binary.r->evaluate(this->data.binary.r, input); + if (r < 0) + return r; + + return l ^ r; +} + + +LIBNORMALFORM_SENTENCE * +(libnormalform_xor2__)(LIBNORMALFORM_SENTENCE *l, LIBNORMALFORM_SENTENCE *r) +{ + static const struct libnormalform_sentence prototype = { + PROTOTYPE_COMMON, + .type = TYPE_XOR, + .inverse = &xor_inverse, + .equals = &xor_equals, + .evaluate = &xor_evaluate + }; + + LIBNORMALFORM_SENTENCE *ret = malloc(sizeof(*ret)); + if (!ret) { + libnormalform_free(l); + libnormalform_free(r); + return NULL; + } + *ret = prototype; + ret->hash = XOR_HASH(l, r); + if (l->hash <= r->hash) { + ret->data.binary.l = l; + ret->data.binary.r = r; + } else { + ret->data.binary.l = r; + ret->data.binary.r = l; + } + return ret; +} + + +#else + + +CONST int +main(void) +{ + return 0; /* indirectly tested */ +} + + +#endif diff --git a/libnormalform_xor_checked.c b/libnormalform_xor_checked.c new file mode 100644 index 0000000..cad6bd3 --- /dev/null +++ b/libnormalform_xor_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_xor_checked)(size_t, LIBNORMALFORM_SENTENCE **); + + +#else + +#define USE_CHECKED +#include "libnormalform_xor.c" + +#endif diff --git a/libnormalform_xorl.c b/libnormalform_xorl.c new file mode 100644 index 0000000..8a5e5c6 --- /dev/null +++ b/libnormalform_xorl.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_xorl)(LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_VARARGS +#include "libnormalform_xor.c" + +#endif diff --git a/libnormalform_xorl_checked.c b/libnormalform_xorl_checked.c new file mode 100644 index 0000000..eff35c4 --- /dev/null +++ b/libnormalform_xorl_checked.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef TEST +#include "common.h" + + +extern inline LIBNORMALFORM_SENTENCE *(libnormalform_xorl_checked)(size_t, LIBNORMALFORM_SENTENCE *, ...); + + +#else + +#define USE_CHECKED_VARARGS +#include "libnormalform_xor.c" + +#endif diff --git a/libnormalform_xorl_macro_test.c b/libnormalform_xorl_macro_test.c new file mode 100644 index 0000000..21ce6ed --- /dev/null +++ b/libnormalform_xorl_macro_test.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#ifdef TEST +#define USE_VARARGS_MACRO +#include "libnormalform_xor.c" +#endif diff --git a/man3/LIBNORMALFORM_AND.3 b/man3/LIBNORMALFORM_AND.3 new file mode 120000 index 0000000..1cc3b47 --- /dev/null +++ b/man3/LIBNORMALFORM_AND.3 @@ -0,0 +1 @@ +libnormalform_and.3
\ No newline at end of file diff --git a/man3/LIBNORMALFORM_IF.3 b/man3/LIBNORMALFORM_IF.3 new file mode 120000 index 0000000..f5174b0 --- /dev/null +++ b/man3/LIBNORMALFORM_IF.3 @@ -0,0 +1 @@ +libnormalform_if.3
\ No newline at end of file diff --git a/man3/LIBNORMALFORM_IMPLY.3 b/man3/LIBNORMALFORM_IMPLY.3 new file mode 120000 index 0000000..8956d98 --- /dev/null +++ b/man3/LIBNORMALFORM_IMPLY.3 @@ -0,0 +1 @@ +libnormalform_imply.3
\ No newline at end of file diff --git a/man3/LIBNORMALFORM_NAND.3 b/man3/LIBNORMALFORM_NAND.3 new file mode 120000 index 0000000..e25f19a --- /dev/null +++ b/man3/LIBNORMALFORM_NAND.3 @@ -0,0 +1 @@ +libnormalform_nand.3
\ No newline at end of file diff --git a/man3/LIBNORMALFORM_NIF.3 b/man3/LIBNORMALFORM_NIF.3 new file mode 120000 index 0000000..d269483 --- /dev/null +++ b/man3/LIBNORMALFORM_NIF.3 @@ -0,0 +1 @@ +libnormalform_nif.3
\ No newline at end of file diff --git a/man3/LIBNORMALFORM_NIMPLY.3 b/man3/LIBNORMALFORM_NIMPLY.3 new file mode 120000 index 0000000..47711fc --- /dev/null +++ b/man3/LIBNORMALFORM_NIMPLY.3 @@ -0,0 +1 @@ +libnormalform_nimply.3
\ No newline at end of file diff --git a/man3/LIBNORMALFORM_NOR.3 b/man3/LIBNORMALFORM_NOR.3 new file mode 120000 index 0000000..dd76dbe --- /dev/null +++ b/man3/LIBNORMALFORM_NOR.3 @@ -0,0 +1 @@ +libnormalform_nor.3
\ No newline at end of file diff --git a/man3/LIBNORMALFORM_OR.3 b/man3/LIBNORMALFORM_OR.3 new file mode 120000 index 0000000..25bea64 --- /dev/null +++ b/man3/LIBNORMALFORM_OR.3 @@ -0,0 +1 @@ +libnormalform_or.3
\ No newline at end of file diff --git a/man3/LIBNORMALFORM_SENTENCE.3 b/man3/LIBNORMALFORM_SENTENCE.3 new file mode 100644 index 0000000..8af097e --- /dev/null +++ b/man3/LIBNORMALFORM_SENTENCE.3 @@ -0,0 +1,61 @@ +.TH LIBNORMALFORM_SENTENCE 3 LIBNORMALFORM +.SH NAME +LIBNORMALFORM_SENTENCE \- Logical sentence description object + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +typedef struct libnormalform_sentence LIBNORMALFORM_SENTENCE; +.fi + +.SH DESCRIPTION +The +.B LIBNORMALFORM_SENTENCE +type is a reference counted opaque data type used to +describe logical sentences of the first order. +.PP +Instances of this type can have there references count +increased using the +.BR libnormalform_ref (3) +function and decreased (and deallocated if 0 is reached) +using the +.BR libnormalform_free (3) +function. +.PP +Because this type contains temporary memory and caches, +and not mechanisms to avoid race conditions, instances +of this type cannot be safely used by any function from +two threads at the same time. However, the function +.BR libnormalform_clone (3) +can be used to create a brand new but identical instance +which can safely be used in another thread. +.PP +Instances can be serialised, as human-readable strings, +and deserialised using the +.BR libnormalform_to_string (3) +and +.BR libnormalform_from_string (3) +functions. +.PP +Apart from the +.BR libnormalform_ref (3) +and +.BR libnormalform_clone (3) +functions, every function that return objects of this +type, will acquire the ownership of the any reference +to instances of this type passed into the function, +they will also fail without modifying +.I errno +if any of them are +.IR NULL , +but they will still acquire the ownership of the +references in this case or any other time they fail. + +.SH NOTES +The name +.B struct libnormalform_sentence +is exposed only for type safety and should not be used. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/LIBNORMALFORM_XNOR.3 b/man3/LIBNORMALFORM_XNOR.3 new file mode 120000 index 0000000..49b2b6c --- /dev/null +++ b/man3/LIBNORMALFORM_XNOR.3 @@ -0,0 +1 @@ +libnormalform_xnor.3
\ No newline at end of file diff --git a/man3/LIBNORMALFORM_XOR.3 b/man3/LIBNORMALFORM_XOR.3 new file mode 120000 index 0000000..7da4f32 --- /dev/null +++ b/man3/LIBNORMALFORM_XOR.3 @@ -0,0 +1 @@ +libnormalform_xor.3
\ No newline at end of file diff --git a/man3/enum_libnormalform_builtin_transformer.3 b/man3/enum_libnormalform_builtin_transformer.3 new file mode 120000 index 0000000..9bb1a97 --- /dev/null +++ b/man3/enum_libnormalform_builtin_transformer.3 @@ -0,0 +1 @@ +struct_libnormalform_transformer.3
\ No newline at end of file diff --git a/man3/enum_libnormalform_value.3 b/man3/enum_libnormalform_value.3 new file mode 120000 index 0000000..8d964dc --- /dev/null +++ b/man3/enum_libnormalform_value.3 @@ -0,0 +1 @@ +struct_libnormalform_variable.3
\ No newline at end of file diff --git a/man3/libnormalform_all.3 b/man3/libnormalform_all.3 new file mode 100644 index 0000000..901bba2 --- /dev/null +++ b/man3/libnormalform_all.3 @@ -0,0 +1,202 @@ +.TH LIBNORMALFORM_ALL 3 LIBNORMALFORM +.SH NAME +libnormalform_all \- Universal qualifier + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +struct libnormalform_mapping { + void *\fIkey\fP; + void *\fIvalue\fP; +}; + +struct libnormalform_map { + struct libnormalform_mapping *\fImappings\fP; + size_t \fInmappings\fP; + void *\fIuser_data\fP; + const char *\fIidentifier\fP; + struct libnormalform_map *\fIcopy_for_clone\fP; +}; + +LIBNORMALFORM_SENTENCE * +libnormalform_all(struct libnormalform_map *\fId\fP, LIBNORMALFORM_SENTENCE *\fIk\fP, LIBNORMALFORM_SENTENCE *\fIv\fP); + +LIBNORMALFORM_SENTENCE * +libnormalform_universally(struct libnormalform_map *\fId\fP, LIBNORMALFORM_SENTENCE *\fIv\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_all () +function creates a sentence that is true +when and only when the sentence +.I v +is true for the +.I .value +for every element in +.I d +for which +.I k +is also true for the +.I .key +of the element. +.PP +The +.BR libnormalform_universally () +function creates a sentence that is true +when and only when the sentence +.I v +is true for the +.I .value +of every element in +.IR d . +.PP +.I d->mappings +and +.I d->nmappings +is used only be the +.BR libnormalform_evaluate (3) +function and must be set before +.BR libnormalform_evaluate (3) +is called, but need not be set +earlier. +.I d->nmappings +shall be set to number of elements in +the qualifers domain of interest, and +.I d->mappings +shall be set to the list of elements +in the domain. Each element shall have its +.I .key +set to the value the antecedent formula +.RI ( k ) +is tested on, and +.I .value +set to the value the predicate formula +.RI ( v ) +is tested on; these fields may be +.IR NULL , +and are ultimately evaluated via +arguments passed to the +.BR libnormalform_function (3) +function but may undergo transformation +via arguments passed to the +.BR libnormalform_transformation (3) +function on the way. +.PP +The values +.I d->mappings +and +.I d->nmappings +may be set differently every time the +.BR libnormalform_evaluate (3) +is called. +.PP +See +.BR libnormalform_to_string (3) +for the purpose of +.IR d->identifier , +it need not be set before the +.BR libnormalform_to_string (3) +function is called. +.PP +See +.BR libnormalform_clone (3) +for the purpose of +.IR d->copy_for_clone , +it need not be set before the +.BR libnormalform_clone (3) +function is called. +.PP +The application can set +.I d->user_data +set freely, and can opt to leave it +unset. It is never used or reference +by the library, but it could be used +by the application to identify the +domain. +.PP +.I d +must not be +.IR NULL . +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_all () +and +.BR libnormalform_universally () +functions return an object representing +the sentence; otherwise, the functions +return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_all () +and +.BR libnormalform_universally () +functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +These functions will also fail without setting +.I errno +if +.I k +or +.I v +is +.IR NULL . + +.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_all (), +.br +.BR libnormalform_universally () +T} Thread safety MT-Safe race:\fIk\fP,\fIv\fP +T{ +.BR libnormalform_all (), +.br +.BR libnormalform_universally () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_all (), +.br +.BR libnormalform_universally () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_function (3) diff --git a/man3/libnormalform_and.3 b/man3/libnormalform_and.3 new file mode 100644 index 0000000..bb65a74 --- /dev/null +++ b/man3/libnormalform_and.3 @@ -0,0 +1,233 @@ +.TH LIBNORMALFORM_AND 3 LIBNORMALFORM +.SH NAME +libnormalform_and \- Conjunction + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_and(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_andl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vand(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_and_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_andl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vand_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_and2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_AND(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_and () +function creates a sentence that is logically +equivalent to the conjunction of the arguments, +that is, a sentence that is true when and only +when all subsentences are true. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_andl () +function is a variant of +.BR libnormalform_and () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vand () +function is a variant of +.BR libnormalform_andl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_and_checked (), +.BR libnormalform_andl_checked (), +and +.BR libnormalform_vand_checked () +functions are variants of the +.BR libnormalform_and (), +.BR libnormalform_andl (), +and +.BR libnormalform_vand () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_and2(p, q)" +is equivalent to +.IR "libnormalform_andl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_AND +macro is a wrapper for the +.BR libnormalform_and_checked () +but is more similar to +.BR libnormalform_andl (). +Unlike +.BR libnormalform_andl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_and () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +The +.BR libnormalform_and_checked (), +.BR libnormalform_andl_checked (), +and +.BR libnormalform_vand_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_and2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_and (), +.br +.BR libnormalform_and_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_andl (), +.br +.BR libnormalform_andl_checked (), +.br +.BR libnormalform_and2 (), +.br +.BR LIBNORMALFORM_AND () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vand (), +.br +.BR libnormalform_vand_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_and (), +.br +.BR libnormalform_andl (), +.br +.BR libnormalform_vand (), +.br +.BR libnormalform_and_checked (), +.br +.BR libnormalform_andl_checked (), +.br +.BR libnormalform_vand_checked (), +.br +.BR libnormalform_vand2 (), +.br +.BR LIBNORMALFORM_AND () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_and (), +.br +.BR libnormalform_andl (), +.br +.BR libnormalform_vand (), +.br +.BR libnormalform_and_checked (), +.br +.BR libnormalform_andl_checked (), +.br +.BR libnormalform_vand_checked (), +.br +.BR libnormalform_vand2 (), +.br +.BR LIBNORMALFORM_AND () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +If there are no subsentences, the resulting sentence is a tautology. +.PP +The connective is commutative and associative. It's truthtable is (0001). +.PP +The +.BR LIBNORMALFORM_AND () +macro requires ISO C23 support. +.PP +Using +.BR libnormalform_and2 (), +has greatest performance, however +.BR libnormalform_and () +is better at simplifying the sentence. +The other functions are just wrappers for the +.BR libnormalform_and () +function. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_AND () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_and2.3 b/man3/libnormalform_and2.3 new file mode 120000 index 0000000..85d5d8b --- /dev/null +++ b/man3/libnormalform_and2.3 @@ -0,0 +1 @@ +libnormalform_and_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_and_checked.3 b/man3/libnormalform_and_checked.3 new file mode 120000 index 0000000..1cc3b47 --- /dev/null +++ b/man3/libnormalform_and_checked.3 @@ -0,0 +1 @@ +libnormalform_and.3
\ No newline at end of file diff --git a/man3/libnormalform_andl.3 b/man3/libnormalform_andl.3 new file mode 120000 index 0000000..1cc3b47 --- /dev/null +++ b/man3/libnormalform_andl.3 @@ -0,0 +1 @@ +libnormalform_and.3
\ No newline at end of file diff --git a/man3/libnormalform_andl_checked.3 b/man3/libnormalform_andl_checked.3 new file mode 120000 index 0000000..85d5d8b --- /dev/null +++ b/man3/libnormalform_andl_checked.3 @@ -0,0 +1 @@ +libnormalform_and_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_any.3 b/man3/libnormalform_any.3 new file mode 100644 index 0000000..ac44b0b --- /dev/null +++ b/man3/libnormalform_any.3 @@ -0,0 +1,294 @@ +.TH LIBNORMALFORM_ANY 3 LIBNORMALFORM +.SH NAME +libnormalform_any \- Existential qualifier + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +struct libnormalform_mapping { + void *\fIkey\fP; + void *\fIvalue\fP; +}; + +struct libnormalform_map { + struct libnormalform_mapping *\fImappings\fP; + size_t \fInmappings\fP; + void *\fIuser_data\fP; + const char *\fIidentifier\fP; + struct libnormalform_map *\fIcopy_for_clone\fP; +}; + +LIBNORMALFORM_SENTENCE * +libnormalform_any(struct libnormalform_map *\fId\fP, LIBNORMALFORM_SENTENCE *\fIk\fP, LIBNORMALFORM_SENTENCE *\fIv\fP); + +LIBNORMALFORM_SENTENCE * +libnormalform_exists(struct libnormalform_map *\fId\fP, LIBNORMALFORM_SENTENCE *\fIk\fP); + +LIBNORMALFORM_SENTENCE * +libnormalform_nexists(struct libnormalform_map *\fId\fP, LIBNORMALFORM_SENTENCE *\fIk\fP); + +LIBNORMALFORM_SENTENCE * +libnormalform_existentially(struct libnormalform_map *\fId\fP, LIBNORMALFORM_SENTENCE *\fIv\fP); + +LIBNORMALFORM_SENTENCE * +libnormalform_empty(struct libnormalform_map *\fId\fP); + +LIBNORMALFORM_SENTENCE * +libnormalform_nonempty(struct libnormalform_map *\fId\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_any () +function creates a sentence that is true +when and only when the sentence +.I v +is true for the +.I .value +for at least one element in +.I d +for which +.I k +is also true for the +.I .key +of the element. +.PP +The +.BR libnormalform_exists () +function creates a sentence that is true +when and only when the sentence +.I k +is true for the +.I .key +of at least one element in +.IR d . +.PP +The +.BR libnormalform_nexists () +function creates a sentence that is true +when and only when the sentence +.I k +is false for the +.I .key +of every element in +.IR d . +.PP +The +.BR libnormalform_existentially () +function creates a sentence that is true +when and only when the sentence +.I v +is true for the +.I .value +of at least one element in +.IR d . +.PP +The +.BR libnormalform_empty () +function creates a sentence that is true +when and only when +.IR d +contains no elements. +.PP +The +.BR libnormalform_nonempty () +function creates a sentence that is true +when and only when +.IR d +contains at least one element. +.PP +.I d->mappings +and +.I d->nmappings +is used only be the +.BR libnormalform_evaluate (3) +function and must be set before +.BR libnormalform_evaluate (3) +is called, but need not be set +earlier. +.I d->nmappings +shall be set to number of elements in +the qualifers domain of interest, and +.I d->mappings +shall be set to the list of elements +in the domain. Each element shall have its +.I .key +set to the value the antecedent formula +.RI ( k ) +is tested on, and +.I .value +set to the value the predicate formula +.RI ( v ) +is tested on; these fields may be +.IR NULL , +and are ultimately evaluated via +arguments passed to the +.BR libnormalform_function (3) +function but may undergo transformation +via arguments passed to the +.BR libnormalform_transformation (3) +function on the way. +.PP +The values +.I d->mappings +and +.I d->nmappings +may be set differently every time the +.BR libnormalform_evaluate (3) +is called. +.PP +Although unused, the +.BR libnormalform_empty () +and +.BR libnormalform_nonempty () +functions require that +.I d->mappings +is properly set up (although the values +in it can all be +.IR NULL ) +before the +.BR libnormalform_evaluate (3) +function is called. +.PP +See +.BR libnormalform_to_string (3) +for the purpose of +.IR d->identifier , +it need not be set before the +.BR libnormalform_to_string (3) +function is called. +.PP +See +.BR libnormalform_clone (3) +for the purpose of +.IR d->copy_for_clone , +it need not be set before the +.BR libnormalform_clone (3) +function is called. +.PP +The application can set +.I d->user_data +set freely, and can opt to leave it +unset. It is never used or reference +by the library, but it could be used +by the application to identify the +domain. +.PP +.I d +must not be +.IR NULL . +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_any (), +.BR libnormalform_exists (), +.BR libnormalform_nexists (), +.BR libnormalform_existentially (), +.BR libnormalform_empty (), +and +.BR libnormalform_nonempty (), +functions return an object representing +the sentence; otherwise, the functions +return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_any (), +.BR libnormalform_exists (), +.BR libnormalform_nexists (), +.BR libnormalform_existentially (), +.BR libnormalform_empty (), +and +.BR libnormalform_nonempty (), +functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +These functions will also fail without setting +.I errno +if +.I k +or +.I v +is +.IR NULL . + +.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_any (), +.br +.BR libnormalform_exists (), +.br +.BR libnormalform_nexists (), +.br +.BR libnormalform_existentially () +T} Thread safety MT-Safe race:\fIk\fP,\fIv\fP +T{ +.BR libnormalform_empty (), +.br +.BR libnormalform_nonempty () +T} Thread safety MT-Safe +T{ +.BR libnormalform_any (), +.br +.BR libnormalform_exists (), +.br +.BR libnormalform_nexists (), +.br +.BR libnormalform_existentially (), +.br +.BR libnormalform_empty (), +.br +.BR libnormalform_nonempty () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_any (), +.br +.BR libnormalform_exists (), +.br +.BR libnormalform_nexists (), +.br +.BR libnormalform_existentially (), +.br +.BR libnormalform_empty (), +.br +.BR libnormalform_nonempty () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_function (3) diff --git a/man3/libnormalform_builtin_transformer.3 b/man3/libnormalform_builtin_transformer.3 new file mode 120000 index 0000000..935c5c4 --- /dev/null +++ b/man3/libnormalform_builtin_transformer.3 @@ -0,0 +1 @@ +enum_libnormalform_builtin_transformer.3
\ No newline at end of file diff --git a/man3/libnormalform_clone.3 b/man3/libnormalform_clone.3 new file mode 100644 index 0000000..0906a44 --- /dev/null +++ b/man3/libnormalform_clone.3 @@ -0,0 +1,104 @@ +.TH LIBNORMALFORM_CLONE 3 LIBNORMALFORM +.SH NAME +libnormalform_clone \- Create a deep clone + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_clone(LIBNORMALFORM_SENTENCE *\fIx\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_clone () +function creates a deep clone of +.IR x , +letting the application use the same +sentence in concurrently from two threads. +.PP +Before calling the +.BR libnormalform_clone () +function, application provided objects in +.I x +must be configured for cloning: +.I .copy_for_clone +in each +.IR "struct libnormalform_variable" , +.IR "struct libnormalform_function" , +.IR "struct libnormalform_map" , +and +.IR "struct libnormalform_transformer" , +shall either be set to the copy of the +object to use in the clone of +.IR x , +or to +.I NULL +if the object can safely be used by the +clone of +.IR x . +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_clone () +function returns a deep clone of +.IR x ; +otherwise, the function returns +.I NULL +and sets +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_clone () +function fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +The +.BR libnormalform_clone () +function also fails without setting +.I errno +if +.I x +is +.IR NULL . + +.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_clone () +T} Thread safety MT-Safe race:\fIx\fP +T{ +.BR libnormalform_clone () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_clone () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_ref (3), +.BR libnormalform_free (3) diff --git a/man3/libnormalform_empty.3 b/man3/libnormalform_empty.3 new file mode 120000 index 0000000..899f515 --- /dev/null +++ b/man3/libnormalform_empty.3 @@ -0,0 +1 @@ +libnormalform_any.3
\ No newline at end of file diff --git a/man3/libnormalform_evaluate.3 b/man3/libnormalform_evaluate.3 new file mode 100644 index 0000000..12675ee --- /dev/null +++ b/man3/libnormalform_evaluate.3 @@ -0,0 +1,78 @@ +.TH LIBNORMALFORM_EVALUATE 3 LIBNORMALFORM +.SH NAME +libnormalform_evaluate \- Determine value of a formula + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +int libnormalform_evaluate(LIBNORMALFORM_SENTENCE *\fIformula\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_evaluate () +function determines the value of +.IR formula . +.PP +Before calling the +.BR libnormalform_evaluate () +function, the application must configure application +defined parts of the formula. That is, any +.IR "struct libnormalform_variable" , +.IR "struct libnormalform_function" , +.IR "struct libnormalform_map" , +and +.IR "struct libnormalform_transformer" +must be set configuared accord to the specifications +specified for the function it was used in. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_evaluate () +function returns 1 if the +.I formula +is true for configured input, 0 if the +.I formula +is false for configured input; +otherwise, the function returns +.IR -1 . + +.SH ERRORS +The +.BR libnormalform_evaluate () +function fails if an application function fails. +The +.BR libnormalform_evaluate () +function does not modify +.IR errno , +but lets application functions modify +.IR errno . + +.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_evaluate () +T} Thread safety MT-Safe race:\fIsentence\fP +T{ +.BR libnormalform_evaluate () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_evaluate () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_existentially.3 b/man3/libnormalform_existentially.3 new file mode 120000 index 0000000..899f515 --- /dev/null +++ b/man3/libnormalform_existentially.3 @@ -0,0 +1 @@ +libnormalform_any.3
\ No newline at end of file diff --git a/man3/libnormalform_exists.3 b/man3/libnormalform_exists.3 new file mode 120000 index 0000000..899f515 --- /dev/null +++ b/man3/libnormalform_exists.3 @@ -0,0 +1 @@ +libnormalform_any.3
\ No newline at end of file diff --git a/man3/libnormalform_false.3 b/man3/libnormalform_false.3 new file mode 100644 index 0000000..83dd97a --- /dev/null +++ b/man3/libnormalform_false.3 @@ -0,0 +1,70 @@ +.TH LIBNORMALFORM_FALSE 3 LIBNORMALFORM +.SH NAME +libnormalform_false \- Contradiction + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_false(void); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_false () +function creates a contradictory sentence: +a sentence that is always false, regardless +of the its containing formula's input. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_false () +function returns an object representing +the sentence; otherwise, the function returns +.I NULL +and sets +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_false () +function fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. + +.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_false () +T} Thread safety MT-Safe +T{ +.BR libnormalform_false () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_false () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_free.3 b/man3/libnormalform_free.3 new file mode 100644 index 0000000..7846dc5 --- /dev/null +++ b/man3/libnormalform_free.3 @@ -0,0 +1,73 @@ +.TH LIBNORMALFORM_FREE 3 LIBNORMALFORM +.SH NAME +libnormalform_free \- Release a reference + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +void libnormalform_free(/* LIBNORMALFORM_SENTENCE | struct libnormalform_term */ *\fIobj\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_free () +function can operate on both the type +.I LIBNORMALFORM_SENTENCE * +and the type +.IR "struct libnormalform_term *" . +The function recursively deallocates +.IR obj , +however sence +.I LIBNORMALFORM_SENTENCE * +is reference counted, +.IR obj , +if it is a +.IR "LIBNORMALFORM_SENTENCE *" , +and any +.IR "LIBNORMALFORM_SENTENCE *" +stored in side it, will have it's reference +count decreased by one, and the object is +only deallocated (recursively) once the the +reference count reaches 0. +.PP +No action is taken if +.I obj +is +.IR NULL . + +.SH RETURN VALUE +None. + +.SH ERRORS +None. + +.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_free () +T} Thread safety MT-Safe race:\fIobj\fP +T{ +.BR libnormalform_free () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_free () +T} Async-cancel safety AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_ref (3), +.BR libnormalform_free (3) diff --git a/man3/libnormalform_from_string.3 b/man3/libnormalform_from_string.3 new file mode 100644 index 0000000..f0bfc98 --- /dev/null +++ b/man3/libnormalform_from_string.3 @@ -0,0 +1,265 @@ +.TH LIBNORMALFORM_FROM_STRING 3 LIBNORMALFORM +.SH NAME +libnormalform_from_string \- Deserialise a sentence from a string + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +struct libnormalform_representation_spec { + void *\fIuser_data\fP; + struct libnormalform_variable *(*\fIget_variable\fP)(char *, char **, void *); + struct libnormalform_function *(*\fIget_function\fP)(char *, char **, void *); + struct libnormalform_map *(*\fIget_map\fP)(char *, char **, void *); + struct libnormalform_transformer *(*\fIget_transformer\fP)(char *, char **, void *); +}; + +LIBNORMALFORM_SENTENCE *libnormalform_from_string(/* optionally const */ char *\fIs\fP, + /* at least as const as \fIs\fP */ char **\fIend_out\fP, + const struct libnormalform_representation_spec *\fIspec\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_from_string () +function parse +\fIs\fP +and returns an equivalent sentence. +.P +If +.I end_out +is +.RI non- NULL , +the position in +.I s +after the end of the representation is stored in +.IR *end_out +upon successful terminate (unspecified on failure), +otherwise the function will validate that it +only contains whitespace after the end of the +representation. +.PP +.I s +will not be modified by the +.BR libnormalform_from_string () +function, however it will let user provided +functions modify +.IR s , +therefore +.I s +may be read-only if, and only if, the user provided +functions do not modify +.IR s . +.PP +.IR spec->get_variable , +.IR spec->get_function , +.IR spec->get_map , +and +.IR spec->get_transformer , +may each be either +.I NULL +or a function pointers returns an object, +according to their return type, described in +the sentence. The functions shall return +.I NULL +on and only on failure. When these functions +are called by the +.BR libnormalform_from_string () +function, the first argument will be the beginning +of the string they shall parse — these strings will +not be properly terminated, — the second argument +will be pointer that the end (the position immediately +after the last byte) of the parsed string shall be +stored (on success), and the third argument will be +.I spec->user_data +which is only used by the application for +application-defined purposes. +.I spec->user_data +may be +.IR NULL . +.PP +Neither +.I s +nor +.I spec +may be +.IR NULL . +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_from_string () +sentence object equivalent to the sentence +described by +.IR s ; +otherwise, the function returns +.I NULL +and sets +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_from_string () +function fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.TP +.I EINVAL +The representation is invalid. +.TP +.I EINVAL +.I end_out +is +.I NULL +but there are non-whitespace characters in +.I s +after the detected end of the representation. +.TP +.I EDOM +The described sentence contains an empty clause +for a connective that does not support empty +clauses. +.TP +.I EDOM +The described sentence contains a transformation +somewhere over a qualifer. +.TP +.I ENOENT +The described sentence contains a boolean variable but +.I spec->get_variable +was +.IR NULL . +.TP +.I ENOENT +The described sentence contains a boolean function but +.I spec->get_function +was +.IR NULL . +.TP +.I ENOENT +The described sentence contains (a domain of interest +for a) qualifier but +.I spec->get_map +was +.IR NULL . +.TP +.I ENOENT +The described sentence contains a transformation +(a input transformation function) but +.I spec->get_transformer +was +.IR NULL . +.PP +The +.BR libnormalform_from_string () +function will also fail if +.IR spec->get_variable , +.IR spec->get_function , +.IR spec->get_map , +or +.IR spec->get_transformer +fails, and will retain +.I errno +as set by the function that failed, or leave +.I errno +unmodified if the failing function did not +modify +.IR errno . + +.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_from_string () +T} Thread safety MT-Safe +T{ +.BR libnormalform_from_string () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_from_string () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH EXTENDED DESCRIPTION +The representation shall be on the whitespace-insensitive form: +.PP +.RS +.nf +\fIconstant\fP ::= "\fBTRUE\fP" | "\fBFALSE\fP"; +\fIconnective1\fP ::= "\fBNOT\fP"; +\fIconnective2\fP ::= "\fBAND\fP" | "\fBOR\fP" | "\fBXOR\fP" | "\fBIF\fP" | "\fBIMPLY\fP" | + "\fBNAND\fP" | "\fBNOR\fP" | "\fBXNOR\fP" | "\fBNIF\fP" | "\fBNIMPLY\fP"; +\fIqualifier0\fP ::= "\fBEMPTY\fP" | "\fBNONEMPTY\fP" | "\fBSINGLETON\fP"; +\fIqualifier1\fP ::= "\fBEXISTS\fP" | "\fBNEXISTS\fP" | "\fBUNIQUE\fP" + "\fBEXISTENTIALLY\fP" | "\fBUNIVERSALLY\fP" | "\fBUNIQUELY\fP"; +\fIqualifier2\fP ::= "\fBALL\fP" | "\fBANY\fP" | "\fBONE\fP"; +\fIunary\fP ::= \fIconnective1\fP [\fIreference-point\fP] "\fB(\fP" \fIsentence\fP "\fB)\fP"; +\fIvariadic\fP ::= \fIconnective2\fP [\fIreference-point\fP] "\fB(\fP" [\fIsentence-list\fP] "\fB)\fP"; +\fIsentence-list\fP ::= \fIsentence\fP ["\fB,\fP" \fIsentence-list\fP]; +\fIqualified0\fP ::= \fIqualifier0\fP [\fIreference-point\fP] "\fB(\fP" \fImap\fP "\fB)\fP"; +\fIqualified1\fP ::= \fIqualifier1\fP [\fIreference-point\fP] "\fB(\fP" \fImap\fP "\fB,\fP" \fIsentence\fP "\fB)\fP"; +\fIqualified2\fP ::= \fIqualifier2\fP [\fIreference-point\fP] "\fB(\fP" \fImap\fP "\fB,\fP" \fIsentence\fP "\fB,\fP" \fIsentence\fP "\fB)\fP"; +\fIatom\fP ::= \fIatom-var\fP | \fIatom-fun\fP; +\fIatom-var\fP ::= "\fBVARIABLE\fP" [\fIreference-point\fP] "\fB(\fP" \fIvariable\fP "\fB)\fP"; +\fIatom-fun\fP ::= "\fBFUNCTION\fP" [\fIreference-point\fP] "\fB(\fP" \fIfunction\fP "\fB)\fP"; +\fItransformation\fP ::= "\fBTRANSFORMATION\fP" [\fIreference-point\fP] "\fB(\fP" \fItransformer\fP "\fB,\fP" \fIsentence\fP "\fB)\fP"; +\fIreference-point\fP ::= "\fB@\fP" \fIdecimal-number\fP; +\fIreference\fP ::= "\fBREF(\fP" \fIdecimal-number\fP "\fB)\fP"; +\fIone-to-nine\fP ::= "\fB1\fP" | "\fB2\fP" | "\fB3\fP" | "\fB4\fP" | "\fB5\fP" | "\fB6\fP" | "\fB7\fP" | "\fB8\fP" | "\fB9\fP"; +\fIzero-to-nine\fP ::= "\fB0\fP" | \fIone-to-nine\fP; +\fIdecimal-number\fP ::= "\fB0\fP" | \fIpositive-decimal\fP; +\fIpositive-decimal\fP ::= \fIone-to-nine\fP [\fIdigits\fP]; +\fIdigits\fP ::= \fIzero-to-nine\fP [\fIdigits\fP]; +\fIsentence\fP ::= \fIconstant\fP | \fIunary\fP | \fIvariadic\fP | \fIqualified0\fP | \fIqualified1\fP | + \fIqualified2\fP |\fIatom\fP | \fItransformation\fP | \fIreference\fP; +.fi +.RE +.PP +where +.I sentence +is the root, and where +.I map +is parsed by +.IR *spec->get_map , +.I variable +is parsed by +.IR *spec->get_variable , +.I function +is parsed by +.IR spec->get_function , +and +.I transformer +is parsed by +.IR *spec->get_transformer . +References must start at 0 and increment by one, +in left-to-right order, every time a reference point +is specified, and forward-references are not allowed +(this includes reference to containing sentence (whose +reference ID is lower because it is written earlier)). + +.SH NOTES +The function will not attempt to deduplicate identical +subsentences, but it will use any explicit deduplication. + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_from_string (3) diff --git a/man3/libnormalform_function.3 b/man3/libnormalform_function.3 new file mode 100644 index 0000000..d89c0e9 --- /dev/null +++ b/man3/libnormalform_function.3 @@ -0,0 +1,152 @@ +.TH LIBNORMALFORM_FUNCTION 3 LIBNORMALFORM +.SH NAME +libnormalform_function \- Function with boolean codomain + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +struct libnormalform_function { + int (*\fIevaluate\fP)(void *user_data, void *input); + void *\fIuser_data\fP; + const char *\fIidentifier\fP; + struct libnormalform_function *\fIcopy_for_clone\fP; + struct libnormalform_function *\fIrelaxation\fP; + int \fIrequires_relaxation\fP; +}; + +LIBNORMALFORM_SENTENCE *libnormalform_function(struct libnormalform_function *\fIfunction\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_function () +function creates a sentence object out +of an application-managed function with +a boolean output. +.PP +.I function->evaluate +is used only be the +.BR libnormalform_evaluate (3) +function to determine the +sentence's value for that specific +call to the +.BR libnormalform_evaluate (3) +function. +.I function->user_data is passed to +.I *function->evaluate +as its first argument, and the function +input is passed as its second argument. +The function is expected to return +.I 1 +if the function is true for the input, +.I 0 +if the function is false for the input, +and +.I -1 +on failure; on failure the function may +optionally set +.IR errno . +.I function->user_data +is only used by the application for +application-defined purposes. +.I function->user_data +may, unlike +.IR function->evalute , +be +.IR NULL ; +however +.I function->evalute +and +.I function->user_data +need not be set before the +.BR libnormalform_evaluate (3) +function is called. +.PP +See +.BR libnormalform_to_string (3) +for the purpose of +.IR function->identifier , +it need not be set before the +.BR libnormalform_to_string (3) +function is called. +.PP +See +.BR libnormalform_clone (3) +for the purpose of +.IR function->copy_for_clone , +it need not be set before the +.BR libnormalform_clone (3) +function is called. +.PP +See +.BR libnormalform_express (3) +for the purpose of +.I function->relaxation +and +.IR function->requires_relaxation , +they need not be set before any of the +.BR libnormalform_express (3), +.BR libnormalform_dnf (3), +and +.BR libnormalform_cnf (3) +functions are called. +.PP +.I function +must not be +.IR NULL . +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_function () +function returns an sentence object of +the function; otherwise, the function +returns +.I NULL +and sets +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_function () +function fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. + +.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_function () +T} Thread safety MT-Safe +T{ +.BR libnormalform_function () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_function () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_variable (3) diff --git a/man3/libnormalform_if.3 b/man3/libnormalform_if.3 new file mode 100644 index 0000000..86fde19 --- /dev/null +++ b/man3/libnormalform_if.3 @@ -0,0 +1,233 @@ +.TH LIBNORMALFORM_IF 3 LIBNORMALFORM +.SH NAME +libnormalform_if \- Converse implication + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_if(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_ifl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vif(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_if_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_ifl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vif_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_if2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_IF(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_if () +function creates a sentence that is logically +equivalent to the converse implication of the +arguments, that is, a clause that is only false +when the first term is false despite all other +terms being true. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_ifl () +function is a variant of +.BR libnormalform_if () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vif () +function is a variant of +.BR libnormalform_ifl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_if_checked (), +.BR libnormalform_ifl_checked (), +and +.BR libnormalform_vif_checked () +functions are variants of the +.BR libnormalform_if (), +.BR libnormalform_ifl (), +and +.BR libnormalform_vif () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_if2(p, q)" +is equivalent to +.IR "libnormalform_ifl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_IF +macro is a wrapper for the +.BR libnormalform_if_checked () +but is more similar to +.BR libnormalform_ifl (). +Unlike +.BR libnormalform_ifl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_if () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.TP +.I EDOM +The function was not given any subsentences. +.PP +The +.BR libnormalform_if_checked (), +.BR libnormalform_ifl_checked (), +and +.BR libnormalform_vif_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_if2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_if (), +.br +.BR libnormalform_if_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_ifl (), +.br +.BR libnormalform_ifl_checked (), +.br +.BR libnormalform_if2 (), +.br +.BR LIBNORMALFORM_IF () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vif (), +.br +.BR libnormalform_vif_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_if (), +.br +.BR libnormalform_ifl (), +.br +.BR libnormalform_vif (), +.br +.BR libnormalform_if_checked (), +.br +.BR libnormalform_ifl_checked (), +.br +.BR libnormalform_vif_checked (), +.br +.BR libnormalform_vif2 (), +.br +.BR LIBNORMALFORM_IF () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_if (), +.br +.BR libnormalform_ifl (), +.br +.BR libnormalform_vif (), +.br +.BR libnormalform_if_checked (), +.br +.BR libnormalform_ifl_checked (), +.br +.BR libnormalform_vif_checked (), +.br +.BR libnormalform_vif2 (), +.br +.BR LIBNORMALFORM_IF () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +The connective is non-commutative and left-associative. It's truthtable is (1101). +.PP +The +.BR LIBNORMALFORM_IF () +macro requires ISO C23 support. +.PP +The +.BR libnormalform_if () +is primary function, the other functions are +just wrappers for the +.BR libnormalform_if () +function. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_IF () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_if2.3 b/man3/libnormalform_if2.3 new file mode 120000 index 0000000..5bc98a1 --- /dev/null +++ b/man3/libnormalform_if2.3 @@ -0,0 +1 @@ +libnormalform_if_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_if_checked.3 b/man3/libnormalform_if_checked.3 new file mode 120000 index 0000000..f5174b0 --- /dev/null +++ b/man3/libnormalform_if_checked.3 @@ -0,0 +1 @@ +libnormalform_if.3
\ No newline at end of file diff --git a/man3/libnormalform_ifl.3 b/man3/libnormalform_ifl.3 new file mode 120000 index 0000000..f5174b0 --- /dev/null +++ b/man3/libnormalform_ifl.3 @@ -0,0 +1 @@ +libnormalform_if.3
\ No newline at end of file diff --git a/man3/libnormalform_ifl_checked.3 b/man3/libnormalform_ifl_checked.3 new file mode 120000 index 0000000..5bc98a1 --- /dev/null +++ b/man3/libnormalform_ifl_checked.3 @@ -0,0 +1 @@ +libnormalform_if_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_imply.3 b/man3/libnormalform_imply.3 new file mode 100644 index 0000000..b6e0284 --- /dev/null +++ b/man3/libnormalform_imply.3 @@ -0,0 +1,236 @@ +.TH LIBNORMALFORM_IMPLY 3 LIBNORMALFORM +.SH NAME +libnormalform_imply \- Material implication + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_imply(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_implyl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vimply(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_imply_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_implyl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vimply_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_imply2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_IMPLY(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_imply () +function creates a sentence that is logically +equivalent to the material implication of the +arguments, that is, a clause that is only false +when the last term is false despite all other +terms being true. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_implyl () +function is a variant of +.BR libnormalform_imply () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vimply () +function is a variant of +.BR libnormalform_implyl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_imply_checked (), +.BR libnormalform_implyl_checked (), +and +.BR libnormalform_vimply_checked () +functions are variants of the +.BR libnormalform_imply (), +.BR libnormalform_implyl (), +and +.BR libnormalform_vimply () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_imply2(p, q)" +is equivalent to +.IR "libnormalform_implyl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_IMPLY +macro is a wrapper for the +.BR libnormalform_imply_checked () +but is more similar to +.BR libnormalform_implyl (). +Unlike +.BR libnormalform_implyl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_imply () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +The +.BR libnormalform_imply_checked (), +.BR libnormalform_implyl_checked (), +and +.BR libnormalform_vimply_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_imply2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_imply (), +.br +.BR libnormalform_imply_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_implyl (), +.br +.BR libnormalform_implyl_checked (), +.br +.BR libnormalform_imply2 (), +.br +.BR LIBNORMALFORM_IMPLY () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vimply (), +.br +.BR libnormalform_vimply_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_imply (), +.br +.BR libnormalform_implyl (), +.br +.BR libnormalform_vimply (), +.br +.BR libnormalform_imply_checked (), +.br +.BR libnormalform_implyl_checked (), +.br +.BR libnormalform_vimply_checked (), +.br +.BR libnormalform_vimply2 (), +.br +.BR LIBNORMALFORM_IMPLY () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_imply (), +.br +.BR libnormalform_implyl (), +.br +.BR libnormalform_vimply (), +.br +.BR libnormalform_imply_checked (), +.br +.BR libnormalform_implyl_checked (), +.br +.BR libnormalform_vimply_checked (), +.br +.BR libnormalform_vimply2 (), +.br +.BR LIBNORMALFORM_IMPLY () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +If there are no subsentences, the resulting sentence is a tautology. +.PP +The connective is non-commutative and right-associative. It's truthtable is (1011). +.PP +The +.BR LIBNORMALFORM_IMPLY () +macro requires ISO C23 support. +.PP +The +.BR libnormalform_imply () +is primary function, the other functions are +just wrappers for the +.BR libnormalform_imply () +function, however +.BR libnormalform_imply () +itself uses the +.BR libnormalform_if () +function if it has subsentences. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_IMPLY () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_imply2.3 b/man3/libnormalform_imply2.3 new file mode 120000 index 0000000..d9d72bd --- /dev/null +++ b/man3/libnormalform_imply2.3 @@ -0,0 +1 @@ +libnormalform_imply_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_imply_checked.3 b/man3/libnormalform_imply_checked.3 new file mode 120000 index 0000000..8956d98 --- /dev/null +++ b/man3/libnormalform_imply_checked.3 @@ -0,0 +1 @@ +libnormalform_imply.3
\ No newline at end of file diff --git a/man3/libnormalform_implyl.3 b/man3/libnormalform_implyl.3 new file mode 120000 index 0000000..8956d98 --- /dev/null +++ b/man3/libnormalform_implyl.3 @@ -0,0 +1 @@ +libnormalform_imply.3
\ No newline at end of file diff --git a/man3/libnormalform_implyl_checked.3 b/man3/libnormalform_implyl_checked.3 new file mode 120000 index 0000000..d9d72bd --- /dev/null +++ b/man3/libnormalform_implyl_checked.3 @@ -0,0 +1 @@ +libnormalform_imply_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_map.3 b/man3/libnormalform_map.3 new file mode 120000 index 0000000..907aac0 --- /dev/null +++ b/man3/libnormalform_map.3 @@ -0,0 +1 @@ +struct_libnormalform_map.3
\ No newline at end of file diff --git a/man3/libnormalform_mapping.3 b/man3/libnormalform_mapping.3 new file mode 120000 index 0000000..7609d21 --- /dev/null +++ b/man3/libnormalform_mapping.3 @@ -0,0 +1 @@ +struct_libnormalform_mapping.3
\ No newline at end of file diff --git a/man3/libnormalform_nand.3 b/man3/libnormalform_nand.3 new file mode 100644 index 0000000..a3604ae --- /dev/null +++ b/man3/libnormalform_nand.3 @@ -0,0 +1,250 @@ +.TH LIBNORMALFORM_NAND 3 LIBNORMALFORM +.SH NAME +libnormalform_nand \- Alternative denial + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_nand(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nandl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vnand(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nand_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nandl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vnand_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nand2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_NAND(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_nand () +function creates a sentence where each subsentence +is connected with a NAND (alternative denial) operator. +In the case that exactly two subsentences are given, +this creates a clause that is true when and only when +at least one is false. There is no generally meaningful +interpretation if a NAND-clause containing more than +two terms. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_nandl () +function is a variant of +.BR libnormalform_nand () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vnand () +function is a variant of +.BR libnormalform_nandl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_nand_checked (), +.BR libnormalform_nandl_checked (), +and +.BR libnormalform_vnand_checked () +functions are variants of the +.BR libnormalform_nand (), +.BR libnormalform_nandl (), +and +.BR libnormalform_vnand () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_nand2(p, q)" +is equivalent to +.IR "libnormalform_nandl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_NAND +macro is a wrapper for the +.BR libnormalform_nand_checked () +but is more similar to +.BR libnormalform_nandl (). +Unlike +.BR libnormalform_nandl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_nand () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.TP +.I EDOM +The function was not given any subsentences. +.PP +The +.BR libnormalform_nand_checked (), +.BR libnormalform_nandl_checked (), +and +.BR libnormalform_vnand_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_nand2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_nand (), +.br +.BR libnormalform_nand_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_nandl (), +.br +.BR libnormalform_nandl_checked (), +.br +.BR libnormalform_nand2 (), +.br +.BR LIBNORMALFORM_NAND () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vnand (), +.br +.BR libnormalform_vnand_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_nand (), +.br +.BR libnormalform_nandl (), +.br +.BR libnormalform_vnand (), +.br +.BR libnormalform_nand_checked (), +.br +.BR libnormalform_nandl_checked (), +.br +.BR libnormalform_vnand_checked (), +.br +.BR libnormalform_vnand2 (), +.br +.BR LIBNORMALFORM_NAND () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_nand (), +.br +.BR libnormalform_nandl (), +.br +.BR libnormalform_vnand (), +.br +.BR libnormalform_nand_checked (), +.br +.BR libnormalform_nandl_checked (), +.br +.BR libnormalform_vnand_checked (), +.br +.BR libnormalform_vnand2 (), +.br +.BR LIBNORMALFORM_NAND () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +The connective is commutative and left-associative. It's truthtable is (1110). +.PP +The +.BR LIBNORMALFORM_NAND () +macro requires ISO C23 support. +.PP +The +.BR libnormalform_nand () +is primary function, the other functions are +just wrappers for the +.BR libnormalform_nand () +function. +.PP +These functions creates a clause where each term +is connected with the NAND connective. Unlike what +the name Alternative denial imply, this does +.I not +create a sentence that is true when at most one +subsentences is true. Nor does it unlike what the +name Negated AND imply, this does +.I not +create a sentence that is true when at least +one subsentence is false. There is unfortunately +no generally meaningful interpretation: adding +false term on the right always create a tautology, +while adding a true term on the right inverts the +original clause. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_NAND () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_nand2.3 b/man3/libnormalform_nand2.3 new file mode 120000 index 0000000..0a1c901 --- /dev/null +++ b/man3/libnormalform_nand2.3 @@ -0,0 +1 @@ +libnormalform_nand_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_nand_checked.3 b/man3/libnormalform_nand_checked.3 new file mode 120000 index 0000000..e25f19a --- /dev/null +++ b/man3/libnormalform_nand_checked.3 @@ -0,0 +1 @@ +libnormalform_nand.3
\ No newline at end of file diff --git a/man3/libnormalform_nandl.3 b/man3/libnormalform_nandl.3 new file mode 120000 index 0000000..e25f19a --- /dev/null +++ b/man3/libnormalform_nandl.3 @@ -0,0 +1 @@ +libnormalform_nand.3
\ No newline at end of file diff --git a/man3/libnormalform_nandl_checked.3 b/man3/libnormalform_nandl_checked.3 new file mode 120000 index 0000000..0a1c901 --- /dev/null +++ b/man3/libnormalform_nandl_checked.3 @@ -0,0 +1 @@ +libnormalform_nand_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_nexists.3 b/man3/libnormalform_nexists.3 new file mode 120000 index 0000000..eb792d0 --- /dev/null +++ b/man3/libnormalform_nexists.3 @@ -0,0 +1 @@ +libnormalform_exists.3
\ No newline at end of file diff --git a/man3/libnormalform_nif.3 b/man3/libnormalform_nif.3 new file mode 100644 index 0000000..174b9d4 --- /dev/null +++ b/man3/libnormalform_nif.3 @@ -0,0 +1,239 @@ +.TH LIBNORMALFORM_NIF 3 LIBNORMALFORM +.SH NAME +libnormalform_nif \- Converse abjunction + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_nif(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nifl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vnif(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nif_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nifl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vnif_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nif2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_NIF(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_nif () +function creates a sentence that is logically +equivalent to the converse abjunction of the +arguments. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_nifl () +function is a variant of +.BR libnormalform_nif () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vnif () +function is a variant of +.BR libnormalform_nifl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_nif_checked (), +.BR libnormalform_nifl_checked (), +and +.BR libnormalform_vnif_checked () +functions are variants of the +.BR libnormalform_nif (), +.BR libnormalform_nifl (), +and +.BR libnormalform_vnif () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_nif2(p, q)" +is equivalent to +.IR "libnormalform_nifl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_NIF +macro is a wrapper for the +.BR libnormalform_nif_checked () +but is more similar to +.BR libnormalform_nifl (). +Unlike +.BR libnormalform_nifl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_nif () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +The +.BR libnormalform_nif_checked (), +.BR libnormalform_nifl_checked (), +and +.BR libnormalform_vnif_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_nif2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_nif (), +.br +.BR libnormalform_nif_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_nifl (), +.br +.BR libnormalform_nifl_checked (), +.br +.BR libnormalform_nif2 (), +.br +.BR LIBNORMALFORM_NIF () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vnif (), +.br +.BR libnormalform_vnif_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_nif (), +.br +.BR libnormalform_nifl (), +.br +.BR libnormalform_vnif (), +.br +.BR libnormalform_nif_checked (), +.br +.BR libnormalform_nifl_checked (), +.br +.BR libnormalform_vnif_checked (), +.br +.BR libnormalform_vnif2 (), +.br +.BR LIBNORMALFORM_NIF () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_nif (), +.br +.BR libnormalform_nifl (), +.br +.BR libnormalform_vnif (), +.br +.BR libnormalform_nif_checked (), +.br +.BR libnormalform_nifl_checked (), +.br +.BR libnormalform_vnif_checked (), +.br +.BR libnormalform_vnif2 (), +.br +.BR LIBNORMALFORM_NIF () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +If there are no subsentences, the resulting sentence is a contradiction. +.PP +The connective is non-commutative and left-associative. It's truthtable is (0010). +.PP +The +.BR LIBNORMALFORM_NIF () +macro requires ISO C23 support. +.PP +The +.BR libnormalform_nif () +is primary function, the other functions are +just wrappers for the +.BR libnormalform_nif () +function. +.PP +These functions creates a clause where each term is +connected with the NIF connective. There is +unfortunately no generally meaningful interpretation: +adding a false term on the left does not change the +result but adding a true term on the left inverts the +result only if all terms are true, whereas adding false +term on the right creates a contradiction, while adding +a true term on the right inverts the original clause. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_NIF () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_nif2.3 b/man3/libnormalform_nif2.3 new file mode 120000 index 0000000..5949db4 --- /dev/null +++ b/man3/libnormalform_nif2.3 @@ -0,0 +1 @@ +libnormalform_nif_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_nif_checked.3 b/man3/libnormalform_nif_checked.3 new file mode 120000 index 0000000..d269483 --- /dev/null +++ b/man3/libnormalform_nif_checked.3 @@ -0,0 +1 @@ +libnormalform_nif.3
\ No newline at end of file diff --git a/man3/libnormalform_nifl.3 b/man3/libnormalform_nifl.3 new file mode 120000 index 0000000..d269483 --- /dev/null +++ b/man3/libnormalform_nifl.3 @@ -0,0 +1 @@ +libnormalform_nif.3
\ No newline at end of file diff --git a/man3/libnormalform_nifl_checked.3 b/man3/libnormalform_nifl_checked.3 new file mode 120000 index 0000000..5949db4 --- /dev/null +++ b/man3/libnormalform_nifl_checked.3 @@ -0,0 +1 @@ +libnormalform_nif_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_nimply.3 b/man3/libnormalform_nimply.3 new file mode 100644 index 0000000..ca7a0e9 --- /dev/null +++ b/man3/libnormalform_nimply.3 @@ -0,0 +1,241 @@ +.TH LIBNORMALFORM_NIMPLY 3 LIBNORMALFORM +.SH NAME +libnormalform_nimply \- Material abjunction + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_nimply(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nimplyl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vnimply(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nimply_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nimplyl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vnimply_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nimply2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_NIMPLY(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_nimply () +function creates a sentence that is logically +equivalent to the material abjunction of the +arguments. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_nimplyl () +function is a variant of +.BR libnormalform_nimply () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vnimply () +function is a variant of +.BR libnormalform_nimplyl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_nimply_checked (), +.BR libnormalform_nimplyl_checked (), +and +.BR libnormalform_vnimply_checked () +functions are variants of the +.BR libnormalform_nimply (), +.BR libnormalform_nimplyl (), +and +.BR libnormalform_vnimply () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_nimply2(p, q)" +is equivalent to +.IR "libnormalform_nimplyl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_NIMPLY +macro is a wrapper for the +.BR libnormalform_nimply_checked () +but is more similar to +.BR libnormalform_nimplyl (). +Unlike +.BR libnormalform_nimplyl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_nimply () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.TP +.I EDOM +The function was not given any subsentences. +.PP +The +.BR libnormalform_nimply_checked (), +.BR libnormalform_nimplyl_checked (), +and +.BR libnormalform_vnimply_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_nimply2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_nimply (), +.br +.BR libnormalform_nimply_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_nimplyl (), +.br +.BR libnormalform_nimplyl_checked (), +.br +.BR libnormalform_nimply2 (), +.br +.BR LIBNORMALFORM_NIMPLY () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vnimply (), +.br +.BR libnormalform_vnimply_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_nimply (), +.br +.BR libnormalform_nimplyl (), +.br +.BR libnormalform_vnimply (), +.br +.BR libnormalform_nimply_checked (), +.br +.BR libnormalform_nimplyl_checked (), +.br +.BR libnormalform_vnimply_checked (), +.br +.BR libnormalform_vnimply2 (), +.br +.BR LIBNORMALFORM_NIMPLY () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_nimply (), +.br +.BR libnormalform_nimplyl (), +.br +.BR libnormalform_vnimply (), +.br +.BR libnormalform_nimply_checked (), +.br +.BR libnormalform_nimplyl_checked (), +.br +.BR libnormalform_vnimply_checked (), +.br +.BR libnormalform_vnimply2 (), +.br +.BR LIBNORMALFORM_NIMPLY () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +The connective is non-commutative and right-associative. It's truthtable is (0100). +.PP +The +.BR LIBNORMALFORM_NIMPLY () +macro requires ISO C23 support. +.PP +The +.BR libnormalform_nimply () +is primary function, the other functions are +just wrappers for the +.BR libnormalform_nimply () +function. +.PP +These functions creates a clause where each term is +connected with the NIMPLY connective. There is +unfortunately no generally meaningful interpretation: +adding false term on the left creates a contradiction, +while adding a true term on the left inverts the +original clause, whereas adding a false term on the +right does not change the result but adding a true +term on the right inverts the result only if all terms +are true. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_NIMPLY () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_nimply2.3 b/man3/libnormalform_nimply2.3 new file mode 120000 index 0000000..adb482f --- /dev/null +++ b/man3/libnormalform_nimply2.3 @@ -0,0 +1 @@ +libnormalform_nimply_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_nimply_checked.3 b/man3/libnormalform_nimply_checked.3 new file mode 120000 index 0000000..47711fc --- /dev/null +++ b/man3/libnormalform_nimply_checked.3 @@ -0,0 +1 @@ +libnormalform_nimply.3
\ No newline at end of file diff --git a/man3/libnormalform_nimplyl.3 b/man3/libnormalform_nimplyl.3 new file mode 120000 index 0000000..47711fc --- /dev/null +++ b/man3/libnormalform_nimplyl.3 @@ -0,0 +1 @@ +libnormalform_nimply.3
\ No newline at end of file diff --git a/man3/libnormalform_nimplyl_checked.3 b/man3/libnormalform_nimplyl_checked.3 new file mode 120000 index 0000000..adb482f --- /dev/null +++ b/man3/libnormalform_nimplyl_checked.3 @@ -0,0 +1 @@ +libnormalform_nimply_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_nonempty.3 b/man3/libnormalform_nonempty.3 new file mode 120000 index 0000000..899f515 --- /dev/null +++ b/man3/libnormalform_nonempty.3 @@ -0,0 +1 @@ +libnormalform_any.3
\ No newline at end of file diff --git a/man3/libnormalform_nor.3 b/man3/libnormalform_nor.3 new file mode 100644 index 0000000..164e8c5 --- /dev/null +++ b/man3/libnormalform_nor.3 @@ -0,0 +1,245 @@ +.TH LIBNORMALFORM_NOR 3 LIBNORMALFORM +.SH NAME +libnormalform_nor \- Alternative denial + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_nor(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_norl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vnor(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nor_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_norl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vnor_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_nor2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_NOR(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_nor () +function creates a sentence where each subsentence +is connected with a NOR (alternative denial) operator. +In the case that exactly two subsentences are given, +this creates a clause that is true when and only when +at least one is false. There is no generally meaningful +interpretation if a NOR-clause containing more than +two terms. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_norl () +function is a variant of +.BR libnormalform_nor () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vnor () +function is a variant of +.BR libnormalform_norl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_nor_checked (), +.BR libnormalform_norl_checked (), +and +.BR libnormalform_vnor_checked () +functions are variants of the +.BR libnormalform_nor (), +.BR libnormalform_norl (), +and +.BR libnormalform_vnor () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_nor2(p, q)" +is equivalent to +.IR "libnormalform_norl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_NOR +macro is a wrapper for the +.BR libnormalform_nor_checked () +but is more similar to +.BR libnormalform_norl (). +Unlike +.BR libnormalform_norl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_nor () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.TP +.I EDOM +The function was not given any subsentences. +.PP +The +.BR libnormalform_nor_checked (), +.BR libnormalform_norl_checked (), +and +.BR libnormalform_vnor_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_nor2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_nor (), +.br +.BR libnormalform_nor_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_norl (), +.br +.BR libnormalform_norl_checked (), +.br +.BR libnormalform_nor2 (), +.br +.BR LIBNORMALFORM_NOR () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vnor (), +.br +.BR libnormalform_vnor_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_nor (), +.br +.BR libnormalform_norl (), +.br +.BR libnormalform_vnor (), +.br +.BR libnormalform_nor_checked (), +.br +.BR libnormalform_norl_checked (), +.br +.BR libnormalform_vnor_checked (), +.br +.BR libnormalform_vnor2 (), +.br +.BR LIBNORMALFORM_NOR () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_nor (), +.br +.BR libnormalform_norl (), +.br +.BR libnormalform_vnor (), +.br +.BR libnormalform_nor_checked (), +.br +.BR libnormalform_norl_checked (), +.br +.BR libnormalform_vnor_checked (), +.br +.BR libnormalform_vnor2 (), +.br +.BR LIBNORMALFORM_NOR () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +The connective is commutative and left-associative. It's truthtable is (1000). +.PP +The +.BR LIBNORMALFORM_NOR () +macro requires ISO C23 support. +.PP +The +.BR libnormalform_nor () +is primary function, the other functions are +just wrappers for the +.BR libnormalform_nor () +function. +.PP +These functions creates a clause where each term is +connected with the NOR connective. Unlike what the +names Joint denial and Negated OR imply, this does +.I not +create a sentence that is true when all subsentences +are false. There is unfortunately no generally +meaningful interpretation: adding true term on the +right always create a contradiction, while adding a +false term on the right inverts the original clause. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_NOR () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_nor2.3 b/man3/libnormalform_nor2.3 new file mode 120000 index 0000000..acea879 --- /dev/null +++ b/man3/libnormalform_nor2.3 @@ -0,0 +1 @@ +libnormalform_nor_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_nor_checked.3 b/man3/libnormalform_nor_checked.3 new file mode 120000 index 0000000..dd76dbe --- /dev/null +++ b/man3/libnormalform_nor_checked.3 @@ -0,0 +1 @@ +libnormalform_nor.3
\ No newline at end of file diff --git a/man3/libnormalform_norl.3 b/man3/libnormalform_norl.3 new file mode 120000 index 0000000..dd76dbe --- /dev/null +++ b/man3/libnormalform_norl.3 @@ -0,0 +1 @@ +libnormalform_nor.3
\ No newline at end of file diff --git a/man3/libnormalform_norl_checked.3 b/man3/libnormalform_norl_checked.3 new file mode 120000 index 0000000..acea879 --- /dev/null +++ b/man3/libnormalform_norl_checked.3 @@ -0,0 +1 @@ +libnormalform_nor_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_not.3 b/man3/libnormalform_not.3 new file mode 100644 index 0000000..0ebe548 --- /dev/null +++ b/man3/libnormalform_not.3 @@ -0,0 +1,92 @@ +.TH LIBNORMALFORM_NOT 3 LIBNORMALFORM +.SH NAME +libnormalform_not \- Negation + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_not(LIBNORMALFORM_SENTENCE *\fIx\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_not () +function creates a sentence whose value is +always the inverse of the value the sentence +.I x +takes. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +The +.BR libnormalform_not () +function adopt the ownership of +.IR x . +Therefore, the user shall not attempt to +deallocate +.IR x . +This holds even on failure: if the function +fails, +.I x +is deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_not () +function returns an object representing +the sentence; otherwise, the function returns +.I NULL +and sets +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_not () +function fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +The +.BR libnormalform_not () +function also fails without setting +.I errno +if +.I x +is +.IR NULL . + +.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_not () +T} Thread safety MT-Safe race:\fIx\fP +T{ +.BR libnormalform_not () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_not () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_one.3 b/man3/libnormalform_one.3 new file mode 100644 index 0000000..bf79161 --- /dev/null +++ b/man3/libnormalform_one.3 @@ -0,0 +1,253 @@ +.TH LIBNORMALFORM_ONE 3 LIBNORMALFORM +.SH NAME +libnormalform_one \- Unique existential qualifier + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +struct libnormalform_mapping { + void *\fIkey\fP; + void *\fIvalue\fP; +}; + +struct libnormalform_map { + struct libnormalform_mapping *\fImappings\fP; + size_t \fInmappings\fP; + void *\fIuser_data\fP; + const char *\fIidentifier\fP; + struct libnormalform_map *\fIcopy_for_clone\fP; +}; + +LIBNORMALFORM_SENTENCE * +libnormalform_one(struct libnormalform_map *\fId\fP, LIBNORMALFORM_SENTENCE *\fIk\fP, LIBNORMALFORM_SENTENCE *\fIv\fP); + +LIBNORMALFORM_SENTENCE * +libnormalform_unique(struct libnormalform_map *\fId\fP, LIBNORMALFORM_SENTENCE *\fIk\fP); + +LIBNORMALFORM_SENTENCE * +libnormalform_uniquely(struct libnormalform_map *\fId\fP, LIBNORMALFORM_SENTENCE *\fIv\fP); + +LIBNORMALFORM_SENTENCE * +libnormalform_singleton(struct libnormalform_map *\fId\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_one () +function creates a sentence that is true +when and only when the sentence +.I v +is true for the +.I .value +for exactly one element in +.I d +for which +.I k +is also true for the +.I .key +of the element. +.PP +The +.BR libnormalform_unique () +function creates a sentence that is true +when and only when the sentence +.I k +is true for the +.I .key +of exactly one element in +.IR d . +.PP +The +.BR libnormalform_uniquely () +function creates a sentence that is true +when and only when the sentence +.I v +is true for the +.I .value +of exactly one element in +.IR d . +.PP +The +.BR libnormalform_singleton () +function creates a sentence that is true +when and only when +.IR d +contains exactly one element. +.PP +.I d->mappings +and +.I d->nmappings +is used only be the +.BR libnormalform_evaluate (3) +function and must be set before +.BR libnormalform_evaluate (3) +is called, but need not be set +earlier. +.I d->nmappings +shall be set to number of elements in +the qualifers domain of interest, and +.I d->mappings +shall be set to the list of elements +in the domain. Each element shall have its +.I .key +set to the value the antecedent formula +.RI ( k ) +is tested on, and +.I .value +set to the value the predicate formula +.RI ( v ) +is tested on; these fields may be +.IR NULL , +and are ultimately evaluated via +arguments passed to the +.BR libnormalform_function (3) +function but may undergo transformation +via arguments passed to the +.BR libnormalform_transformation (3) +function on the way. +.PP +The values +.I d->mappings +and +.I d->nmappings +may be set differently every time the +.BR libnormalform_evaluate (3) +is called. +.PP +Although unused, the +.BR libnormalform_singleton () +function requires that +.I d->mappings +is properly set up (although the values +in it can all be +.IR NULL ) +before the +.BR libnormalform_evaluate (3) +function is called. +.PP +See +.BR libnormalform_to_string (3) +for the purpose of +.IR d->identifier , +it need not be set before the +.BR libnormalform_to_string (3) +function is called. +.PP +See +.BR libnormalform_clone (3) +for the purpose of +.IR d->copy_for_clone , +it need not be set before the +.BR libnormalform_clone (3) +function is called. +.PP +The application can set +.I d->user_data +set freely, and can opt to leave it +unset. It is never used or reference +by the library, but it could be used +by the application to identify the +domain. +.PP +.I d +must not be +.IR NULL . +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_one (), +.BR libnormalform_unique (), +.BR libnormalform_uniquely (), +and +.BR libnormalform_singleton () +functions return an object representing +the sentence; otherwise, the functions +return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_one (), +.BR libnormalform_unique (), +.BR libnormalform_uniquely (), +and +.BR libnormalform_singleton () +functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +These functions will also fail without setting +.I errno +if +.I k +or +.I v +is +.IR NULL . + +.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_one (), +.br +.BR libnormalform_unique (), +.br +.BR libnormalform_uniquely () +T} Thread safety MT-Safe race:\fIk\fP,\fIv\fP +T{ +.BR libnormalform_singleton () +T} Thread safety MT-Safe +T{ +.BR libnormalform_one (), +.br +.BR libnormalform_unique (), +.br +.BR libnormalform_uniquely (), +.br +.BR libnormalform_singleton () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_one (), +.br +.BR libnormalform_unique (), +.br +.BR libnormalform_uniquely (), +.br +.BR libnormalform_singleton () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_function (3) diff --git a/man3/libnormalform_or.3 b/man3/libnormalform_or.3 new file mode 100644 index 0000000..9d57ee8 --- /dev/null +++ b/man3/libnormalform_or.3 @@ -0,0 +1,233 @@ +.TH LIBNORMALFORM_OR 3 LIBNORMALFORM +.SH NAME +libnormalform_or \- Inclusive disjunction + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_or(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_orl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vor(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_or_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_orl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vor_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_or2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_OR(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_or () +function creates a sentence that is logically +equivalent to the inclusive disjunction of the +arguments, that is, a sentence that is true when +and only when at least one subsentence is true. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_orl () +function is a variant of +.BR libnormalform_or () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vor () +function is a variant of +.BR libnormalform_orl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_or_checked (), +.BR libnormalform_orl_checked (), +and +.BR libnormalform_vor_checked () +functions are variants of the +.BR libnormalform_or (), +.BR libnormalform_orl (), +and +.BR libnormalform_vor () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_or2(p, q)" +is equivalent to +.IR "libnormalform_orl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_OR +macro is a wrapper for the +.BR libnormalform_or_checked () +but is more similar to +.BR libnormalform_orl (). +Unlike +.BR libnormalform_orl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_or () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +The +.BR libnormalform_or_checked (), +.BR libnormalform_orl_checked (), +and +.BR libnormalform_vor_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_or2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_or (), +.br +.BR libnormalform_or_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_orl (), +.br +.BR libnormalform_orl_checked (), +.br +.BR libnormalform_or2 (), +.br +.BR LIBNORMALFORM_OR () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vor (), +.br +.BR libnormalform_vor_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_or (), +.br +.BR libnormalform_orl (), +.br +.BR libnormalform_vor (), +.br +.BR libnormalform_or_checked (), +.br +.BR libnormalform_orl_checked (), +.br +.BR libnormalform_vor_checked (), +.br +.BR libnormalform_vor2 (), +.br +.BR LIBNORMALFORM_OR () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_or (), +.br +.BR libnormalform_orl (), +.br +.BR libnormalform_vor (), +.br +.BR libnormalform_or_checked (), +.br +.BR libnormalform_orl_checked (), +.br +.BR libnormalform_vor_checked (), +.br +.BR libnormalform_vor2 (), +.br +.BR LIBNORMALFORM_OR () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +If there are no subsentences, the resulting sentence is a contradiction. +.PP +The connective is commutative and associative. It's truthtable is (0111). +.PP +The +.BR LIBNORMALFORM_OR () +macro requires ISO C23 support. +.PP +Using +.BR libnormalform_or2 (), +has greatest performance, however +.BR libnormalform_or () +is better at simplifying the sentence. +The other functions are just wrappers for the +.BR libnormalform_or () +function. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_OR () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_or2.3 b/man3/libnormalform_or2.3 new file mode 120000 index 0000000..3d2cdc4 --- /dev/null +++ b/man3/libnormalform_or2.3 @@ -0,0 +1 @@ +libnormalform_or_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_or_checked.3 b/man3/libnormalform_or_checked.3 new file mode 120000 index 0000000..25bea64 --- /dev/null +++ b/man3/libnormalform_or_checked.3 @@ -0,0 +1 @@ +libnormalform_or.3
\ No newline at end of file diff --git a/man3/libnormalform_orl.3 b/man3/libnormalform_orl.3 new file mode 120000 index 0000000..25bea64 --- /dev/null +++ b/man3/libnormalform_orl.3 @@ -0,0 +1 @@ +libnormalform_or.3
\ No newline at end of file diff --git a/man3/libnormalform_orl_checked.3 b/man3/libnormalform_orl_checked.3 new file mode 120000 index 0000000..3d2cdc4 --- /dev/null +++ b/man3/libnormalform_orl_checked.3 @@ -0,0 +1 @@ +libnormalform_or_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_ref.3 b/man3/libnormalform_ref.3 new file mode 100644 index 0000000..5592e48 --- /dev/null +++ b/man3/libnormalform_ref.3 @@ -0,0 +1,111 @@ +.TH LIBNORMALFORM_REF 3 LIBNORMALFORM +.SH NAME +libnormalform_ref \- Acquire new reference + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_ref(LIBNORMALFORM_SENTENCE *\fIx\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_ref () +function increases the reference count of +.I x +by one and returns +.I x +(it is advisable to treat the return pointer +as unique but that it must be used in the +same thread as the input pointer), +letting the application input the sentence +object to multiple sentences or multiple +times in the same sentence. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_ref () +function returns +.IR x ; +otherwise, the function returns +.I NULL +and sets +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_ref () +function fails if: +.TP +.I ENOMEM +The reference count of +.I x +was already maximised (at +.IR SIZE_MAX ); +this would imply that something is wrong in the +application, and it might as well abort. +.PP +The +.BR libnormalform_ref () +function also fails without setting +.I errno +if +.I x +is +.IR NULL . + +.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_ref () +T} Thread safety MT-Safe race:\fIx\fP +T{ +.BR libnormalform_ref () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_ref () +T} Async-cancel safety AC-Unsafe heap +.TE + +.SH APPLICATION USAGE +The +.B LIBNORMALFORM_SENTENCE +type is not thread-safe. If you want to use an +instance of it from two or more threads concurrently, +you need to use the +.BR libnormalform_clone (3) +function to create a deep clone of the instance. +Additionally applications shall assume that any +.B LIBNORMALFORM_SENTENCE +constructed using an instance of this type contains +that instance and cannot be used concurrently, from +another thread, with that instance or any other +instance created with it, appart from instances +created with the +.BR libnormalform_clone (3) +function. + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_clone (3), +.BR libnormalform_free (3) diff --git a/man3/libnormalform_representation_spec.3 b/man3/libnormalform_representation_spec.3 new file mode 120000 index 0000000..a996c52 --- /dev/null +++ b/man3/libnormalform_representation_spec.3 @@ -0,0 +1 @@ +struct_libnormalform_representation_spec.3
\ No newline at end of file diff --git a/man3/libnormalform_sentence.3 b/man3/libnormalform_sentence.3 new file mode 120000 index 0000000..3f5af8c --- /dev/null +++ b/man3/libnormalform_sentence.3 @@ -0,0 +1 @@ +struct_libnormalform_sentence.3
\ No newline at end of file diff --git a/man3/libnormalform_singleton.3 b/man3/libnormalform_singleton.3 new file mode 120000 index 0000000..5e3df78 --- /dev/null +++ b/man3/libnormalform_singleton.3 @@ -0,0 +1 @@ +libnormalform_one.3
\ No newline at end of file diff --git a/man3/libnormalform_to_string.3 b/man3/libnormalform_to_string.3 new file mode 100644 index 0000000..7008990 --- /dev/null +++ b/man3/libnormalform_to_string.3 @@ -0,0 +1,105 @@ +.TH LIBNORMALFORM_TO_STRING 3 LIBNORMALFORM +.SH NAME +libnormalform_to_string \- Serialise into a string + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +char *libnormalform_to_string(LIBNORMALFORM_SENTENCE *\fIx\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_to_string () +function creates a string representation if +.I x +designed for machine parsing, but can also be +read by sufficiently literate humans. +.PP +Before calling the +.BR libnormalform_to_string () +function, application provided objects in +.I x +must be configured for serialisation: +.I .identifier +in each +.IR "struct libnormalform_variable" , +.IR "struct libnormalform_function" , +.IR "struct libnormalform_map" , +and +.IR "struct libnormalform_transformer" , +shall either be set to the string representation, +of the object, that can be parsed by the application +without it being terminated by a null byte. +.PP +The returned pointer shall either be +deallocated with the +.BR free (3) +function. +.PP +.I x +must not be +.IR NULL . + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_to_string () +function returns a null-byte terminated +non-empty, ASCII string representation of +.IR x ; +otherwise, the function returns +.I NULL +and sets +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_to_string () +function fails if: +.TP +.I ENOMEM +Insufficient memory was available to +serialise the sentence object. + +.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_to_string () +T} Thread safety MT-Safe race:\fIx\fP +T{ +.BR libnormalform_to_string () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_to_string () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +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 +normal form, except with XOR allowed, but not necessarily +to any canonical form) during construction, so the +.BR libnormalform_to_string () +function will not necessarily reproduce the sentence +as it was specified when it was constructed. + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_from_string (3) diff --git a/man3/libnormalform_transformation.3 b/man3/libnormalform_transformation.3 new file mode 100644 index 0000000..7bb129e --- /dev/null +++ b/man3/libnormalform_transformation.3 @@ -0,0 +1,245 @@ +.TH LIBNORMALFORM_TRANSFORMATION 3 LIBNORMALFORM +.SH PROLOGUE +This document describes the function +.BR libnormalform_transformation , +refer to +.BR struct_libnormalform_transformation (3) +for the type +.IR "struct libnormalform_transformation" . + +.SH NAME +libnormalform_transformation \- Function morphism + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +enum libnormalform_builtin_transformer { + \fILIBNORMALFORM_NOT_BUILT_IN\fP = 0, + \fILIBNORMALFORM_DOMAIN_VIEW\fP, + \fILIBNORMALFORM_IMAGE_VIEW\fP +}; + +struct libnormalform_transformer { + void *(*\fItransform\fP)(void *user_data, void *input); + void (*\fIdeallocate\fP)(void *user_data, void *output); + void *\fIuser_data\fP; + const char *\fIidentifier\fP; + struct libnormalform_transformer *\fIcopy_for_clone\fP; + int \fIrequires_elimination\fP; + enum libnormalform_builtin_transformer \fIbuiltin\fP; +}; + +LIBNORMALFORM_SENTENCE *libnormalform_transformation(struct libnormalform_transformer *\fIfunction\fP, + LIBNORMALFORM_SENTENCE *\fIsentence\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_transformation () +function creates a sentence output +the transformation with the application-defined +.I function +over a +.IR sentence. +The pointer +.I function +is application-managed, and must exists while +the returned sentece exists. +.PP +.I function->transform +is used only be the +.BR libnormalform_evaluate (3) +function to modify input to other +functions (both +.I struct libnormalform_function +and +.IR "struct libnormalform_transformer" ). +.I function->user_data is passed to +.I *function->transform +as its first argument, and the function +input is passed as its second argument. +The function is expected to return a +.RI non- NULL +pointer upon successful completion and +.I NULL +on failure; on failure the function may +optionally set +.IR errno . +If +.I function->deallocate +is +.IR non -NULL , +but unless +.I *function->transform +returns +.IR NULL , +the library will call +.I *function->deallocate +when the pointer returned by +.I *function->transform +is no longer needed. +.I function->user_data is passed to +.I *function->deallocate +as its first argument, and the returned +pointer is passed as its second argument. +.I function->user_data +is only used by the application for +application-defined purposes. +.I function->user_data +and +.I function->deallocate +may, unlike +.IR function->transform , +be +.IR NULL ; +however +.IR function->transform , +.IR function->deallocate , +and +.I function->user_data +need not be set before the +.BR libnormalform_evaluate (3) +function is called. +.PP +See +.BR libnormalform_to_string (3) +for the purpose of +.IR function->identifier , +it need not be set before the +.BR libnormalform_to_string (3) +function is called. +.PP +See +.BR libnormalform_clone (3) +for the purpose of +.IR function->copy_for_clone , +it need not be set before the +.BR libnormalform_clone (3) +function is called. +.PP +See +.BR libnormalform_express (3) +for the purpose of +.IR function->requires_elimination , +it need not be set before any of the +.BR libnormalform_express (3), +.BR libnormalform_dnf (3), +and +.BR libnormalform_cnf (3) +functions are called. +.PP +See +.BR libnormalform_express (3) +for the purpose of +.IR function->builtin , +the +.BR libnormalform_transformation () +function always sets it to +.IR LIBNORMALFORM_NOT_BUILT_IN . +.PP +.I function +must not be +.IR NULL . +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +The +.BR libnormalform_transformation () +function adopt the ownership of +.IR sentence . +Therefore, the user shall not attempt to +deallocate +.IR sentence . +This holds even on failure: if the function +fails, +.I sentence +is deallocated. +.PP +.I *function->transform +may be called multiple types for the same +input even if it is only specified in a sentence +once, therefore calling it multiple times must +not have undesirable side effects. The function +shall return equivalent output given equivalent +input. Additionally for any functions F, G, +T(F # G) must be equivalent to T(F) # T(G) for +any operator #, where T is +.IR *function->transform . +Transformations over constants and variables +are removed as these do not take any input. +Transformations over qualifiers are illegal. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_transformation () +function returns an sentence object of +the transformation; otherwise, the function +returns +.I NULL +and sets +.I errno +to indicate the error. +.PP +The +.BR libnormalform_transformation () +function also fails without setting +.I errno +if +.I sentence +is +.IR NULL . + +.SH ERRORS +The +.BR libnormalform_transformation () +function fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.TP +.I EDOM +.I sentence +contains a qualifier. + +.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_transformation () +T} Thread safety MT-Safe race:\fIsentence\fP +T{ +.BR libnormalform_transformation () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_transformation () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +Sentences created with the +.BR libnormalform_empty (), +.BR libnormalform_nonempty (), +and +.BR libnormalform_singleton () +functions count as qualifiers and +cannot be used in a transformation. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_transformer.3 b/man3/libnormalform_transformer.3 new file mode 120000 index 0000000..9bb1a97 --- /dev/null +++ b/man3/libnormalform_transformer.3 @@ -0,0 +1 @@ +struct_libnormalform_transformer.3
\ No newline at end of file diff --git a/man3/libnormalform_true.3 b/man3/libnormalform_true.3 new file mode 100644 index 0000000..a5da907 --- /dev/null +++ b/man3/libnormalform_true.3 @@ -0,0 +1,70 @@ +.TH LIBNORMALFORM_TRUE 3 LIBNORMALFORM +.SH NAME +libnormalform_true \- Tautology + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_true(void); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_true () +function creates a tautological sentence: +a sentence that is always true, regardless +of the its containing formula's input. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_true () +function returns an object representing +the sentence; otherwise, the function returns +.I NULL +and sets +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_true () +function fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. + +.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_true () +T} Thread safety MT-Safe +T{ +.BR libnormalform_true () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_true () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_unique.3 b/man3/libnormalform_unique.3 new file mode 120000 index 0000000..5e3df78 --- /dev/null +++ b/man3/libnormalform_unique.3 @@ -0,0 +1 @@ +libnormalform_one.3
\ No newline at end of file diff --git a/man3/libnormalform_uniquely.3 b/man3/libnormalform_uniquely.3 new file mode 120000 index 0000000..5e3df78 --- /dev/null +++ b/man3/libnormalform_uniquely.3 @@ -0,0 +1 @@ +libnormalform_one.3
\ No newline at end of file diff --git a/man3/libnormalform_universally.3 b/man3/libnormalform_universally.3 new file mode 120000 index 0000000..e479a8c --- /dev/null +++ b/man3/libnormalform_universally.3 @@ -0,0 +1 @@ +libnormalform_all.3
\ No newline at end of file diff --git a/man3/libnormalform_value.3 b/man3/libnormalform_value.3 new file mode 120000 index 0000000..9602858 --- /dev/null +++ b/man3/libnormalform_value.3 @@ -0,0 +1 @@ +enum_libnormalform_value.3
\ No newline at end of file diff --git a/man3/libnormalform_vand.3 b/man3/libnormalform_vand.3 new file mode 120000 index 0000000..32c5aa9 --- /dev/null +++ b/man3/libnormalform_vand.3 @@ -0,0 +1 @@ +libnormalform_andl.3
\ No newline at end of file diff --git a/man3/libnormalform_vand_checked.3 b/man3/libnormalform_vand_checked.3 new file mode 120000 index 0000000..b4cbf43 --- /dev/null +++ b/man3/libnormalform_vand_checked.3 @@ -0,0 +1 @@ +libnormalform_andl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_variable.3 b/man3/libnormalform_variable.3 new file mode 100644 index 0000000..ad30a37 --- /dev/null +++ b/man3/libnormalform_variable.3 @@ -0,0 +1,127 @@ +.TH LIBNORMALFORM_VARIABLE 3 LIBNORMALFORM +.SH NAME +libnormalform_variable \- Boolean variable + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +enum libnormalform_value { + \fILIBNORMALFORM_FALSE\fP = 0, + \fILIBNORMALFORM_TRUE\fP = 1 +}; + +struct libnormalform_variable { + enum libnormalform_value \fIvalue\fP; + void *\fIuser_data\fP; + const char *\fIidentifier; + struct libnormalform_variable *\fIcopy_for_clone\fP; +}; + +LIBNORMALFORM_SENTENCE *libnormalform_variable(struct libnormalform_variable *\fIvariable\fP); +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_variable () +function creates a sentence object out +of an application-managed variable. +.PP +.I variable->value +is used only be the +.BR libnormalform_evaluate (3) +function and must be set to either +.I LIBNORMALFORM_FALSE +or +.I LIBNORMALFORM_TRUE +before the +.BR libnormalform_evaluate (3) +function is called, to let the +function know what value the +variable has for that specific +call to the +.BR libnormalform_evaluate (3) +function. +.PP +See +.BR libnormalform_to_string (3) +for the purpose of +.IR variable->identifier , +it need not be set before the +.BR libnormalform_to_string (3) +function is called. +.PP +See +.BR libnormalform_clone (3) +for the purpose of +.IR variable->copy_for_clone , +it need not be set before the +.BR libnormalform_clone (3) +function is called. +.PP +The application can set +.I variable->user_data +set freely, and can opt to leave it +unset. It is never used or reference +by the library, but it could be used +by the application to identify the +variable. +.PP +.I variable +must not be +.IR NULL . +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_variable () +function returns an sentence object of +the variable; otherwise, the function +returns +.I NULL +and sets +.I errno +to indicate the error. + +.SH ERRORS +The +.BR libnormalform_variable () +function fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. + +.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_variable () +T} Thread safety MT-Safe +T{ +.BR libnormalform_variable () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_variable () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH SEE ALSO +.BR libnormalform (7), +.BR libnormalform_function (3) diff --git a/man3/libnormalform_vif.3 b/man3/libnormalform_vif.3 new file mode 120000 index 0000000..8c9a9cd --- /dev/null +++ b/man3/libnormalform_vif.3 @@ -0,0 +1 @@ +libnormalform_ifl.3
\ No newline at end of file diff --git a/man3/libnormalform_vif_checked.3 b/man3/libnormalform_vif_checked.3 new file mode 120000 index 0000000..6b5aa3c --- /dev/null +++ b/man3/libnormalform_vif_checked.3 @@ -0,0 +1 @@ +libnormalform_ifl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_vimply.3 b/man3/libnormalform_vimply.3 new file mode 120000 index 0000000..b973027 --- /dev/null +++ b/man3/libnormalform_vimply.3 @@ -0,0 +1 @@ +libnormalform_implyl.3
\ No newline at end of file diff --git a/man3/libnormalform_vimply_checked.3 b/man3/libnormalform_vimply_checked.3 new file mode 120000 index 0000000..777fe30 --- /dev/null +++ b/man3/libnormalform_vimply_checked.3 @@ -0,0 +1 @@ +libnormalform_implyl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_vnand.3 b/man3/libnormalform_vnand.3 new file mode 120000 index 0000000..a6f43be --- /dev/null +++ b/man3/libnormalform_vnand.3 @@ -0,0 +1 @@ +libnormalform_nandl.3
\ No newline at end of file diff --git a/man3/libnormalform_vnand_checked.3 b/man3/libnormalform_vnand_checked.3 new file mode 120000 index 0000000..c7005ad --- /dev/null +++ b/man3/libnormalform_vnand_checked.3 @@ -0,0 +1 @@ +libnormalform_nandl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_vnif.3 b/man3/libnormalform_vnif.3 new file mode 120000 index 0000000..fecbc72 --- /dev/null +++ b/man3/libnormalform_vnif.3 @@ -0,0 +1 @@ +libnormalform_nifl.3
\ No newline at end of file diff --git a/man3/libnormalform_vnif_checked.3 b/man3/libnormalform_vnif_checked.3 new file mode 120000 index 0000000..52f1181 --- /dev/null +++ b/man3/libnormalform_vnif_checked.3 @@ -0,0 +1 @@ +libnormalform_nifl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_vnimply.3 b/man3/libnormalform_vnimply.3 new file mode 120000 index 0000000..c4fb4a5 --- /dev/null +++ b/man3/libnormalform_vnimply.3 @@ -0,0 +1 @@ +libnormalform_nimplyl.3
\ No newline at end of file diff --git a/man3/libnormalform_vnimply_checked.3 b/man3/libnormalform_vnimply_checked.3 new file mode 120000 index 0000000..f768f8c --- /dev/null +++ b/man3/libnormalform_vnimply_checked.3 @@ -0,0 +1 @@ +libnormalform_nimplyl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_vnor.3 b/man3/libnormalform_vnor.3 new file mode 120000 index 0000000..7247725 --- /dev/null +++ b/man3/libnormalform_vnor.3 @@ -0,0 +1 @@ +libnormalform_norl.3
\ No newline at end of file diff --git a/man3/libnormalform_vnor_checked.3 b/man3/libnormalform_vnor_checked.3 new file mode 120000 index 0000000..b11849c --- /dev/null +++ b/man3/libnormalform_vnor_checked.3 @@ -0,0 +1 @@ +libnormalform_norl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_vor.3 b/man3/libnormalform_vor.3 new file mode 120000 index 0000000..27a0be5 --- /dev/null +++ b/man3/libnormalform_vor.3 @@ -0,0 +1 @@ +libnormalform_orl.3
\ No newline at end of file diff --git a/man3/libnormalform_vor_checked.3 b/man3/libnormalform_vor_checked.3 new file mode 120000 index 0000000..da75167 --- /dev/null +++ b/man3/libnormalform_vor_checked.3 @@ -0,0 +1 @@ +libnormalform_orl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_vxnor.3 b/man3/libnormalform_vxnor.3 new file mode 120000 index 0000000..6f2ae2d --- /dev/null +++ b/man3/libnormalform_vxnor.3 @@ -0,0 +1 @@ +libnormalform_xnorl.3
\ No newline at end of file diff --git a/man3/libnormalform_vxnor_checked.3 b/man3/libnormalform_vxnor_checked.3 new file mode 120000 index 0000000..2f6251f --- /dev/null +++ b/man3/libnormalform_vxnor_checked.3 @@ -0,0 +1 @@ +libnormalform_xnorl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_vxor.3 b/man3/libnormalform_vxor.3 new file mode 120000 index 0000000..08ad00c --- /dev/null +++ b/man3/libnormalform_vxor.3 @@ -0,0 +1 @@ +libnormalform_xorl.3
\ No newline at end of file diff --git a/man3/libnormalform_vxor_checked.3 b/man3/libnormalform_vxor_checked.3 new file mode 120000 index 0000000..89c3126 --- /dev/null +++ b/man3/libnormalform_vxor_checked.3 @@ -0,0 +1 @@ +libnormalform_xorl_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_xnor.3 b/man3/libnormalform_xnor.3 new file mode 100644 index 0000000..56a3cab --- /dev/null +++ b/man3/libnormalform_xnor.3 @@ -0,0 +1,245 @@ +.TH LIBNORMALFORM_XNOR 3 LIBNORMALFORM +.SH NAME +libnormalform_xnor \- Logical equivalence + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_xnor(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_xnorl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vxnor(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_xnor_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_xnorl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vxnor_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_xnor2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_XNOR(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_xnor () +function creates a sentence that is logically +equivalent to the logical equivalecne of the +arguments, that is, a sentence that is true +exactly when the parity of the subsentences +equal to whether the number of subsentences is odd. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_xnorl () +function is a variant of +.BR libnormalform_xnor () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vxnor () +function is a variant of +.BR libnormalform_xnorl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_xnor_checked (), +.BR libnormalform_xnorl_checked (), +and +.BR libnormalform_vxnor_checked () +functions are variants of the +.BR libnormalform_xnor (), +.BR libnormalform_xnorl (), +and +.BR libnormalform_vxnor () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_xnor2(p, q)" +is equivalent to +.IR "libnormalform_xnorl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_XNOR +macro is a wrapper for the +.BR libnormalform_xnor_checked () +but is more similar to +.BR libnormalform_xnorl (). +Unlike +.BR libnormalform_xnorl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_xnor () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +The +.BR libnormalform_xnor_checked (), +.BR libnormalform_xnorl_checked (), +and +.BR libnormalform_vxnor_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_xnor2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_xnor (), +.br +.BR libnormalform_xnor_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_xnorl (), +.br +.BR libnormalform_xnorl_checked (), +.br +.BR libnormalform_xnor2 (), +.br +.BR LIBNORMALFORM_XNOR () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vxnor (), +.br +.BR libnormalform_vxnor_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_xnor (), +.br +.BR libnormalform_xnorl (), +.br +.BR libnormalform_vxnor (), +.br +.BR libnormalform_xnor_checked (), +.br +.BR libnormalform_xnorl_checked (), +.br +.BR libnormalform_vxnor_checked (), +.br +.BR libnormalform_vxnor2 (), +.br +.BR LIBNORMALFORM_XNOR () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_xnor (), +.br +.BR libnormalform_xnorl (), +.br +.BR libnormalform_vxnor (), +.br +.BR libnormalform_xnor_checked (), +.br +.BR libnormalform_xnorl_checked (), +.br +.BR libnormalform_vxnor_checked (), +.br +.BR libnormalform_vxnor2 (), +.br +.BR LIBNORMALFORM_XNOR () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +If there are no subsentences, the resulting sentence is a tautology. +.PP +The connective is commutative and associative. It's truthtable is (1001). +.PP +The +.BR LIBNORMALFORM_XNOR () +macro requires ISO C23 support. +.PP +Using +.BR libnormalform_xnor2 (), +has greatest performance, however +.BR libnormalform_xnor () +is better at simplifying the sentence. +The other functions are just wrappers for the +.BR libnormalform_xnor () +function. +.PP +These functions creates a clause where each term +is connected with the XNOR connective. Unlike what +the name Logical equivalence, this does +.I not +create a sentence that is true when all subsentences +have the same value (this is only the case when there +are 2 or 0 subsentences). Rather, when the number +of terms is odd, it is equivalent to the parity (XOR) +of the terms, but when the number of terms is odd, it +is the inverse of the parity. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_XNOR () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_xnor2.3 b/man3/libnormalform_xnor2.3 new file mode 120000 index 0000000..2c62e7a --- /dev/null +++ b/man3/libnormalform_xnor2.3 @@ -0,0 +1 @@ +libnormalform_xnor_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_xnor_checked.3 b/man3/libnormalform_xnor_checked.3 new file mode 120000 index 0000000..49b2b6c --- /dev/null +++ b/man3/libnormalform_xnor_checked.3 @@ -0,0 +1 @@ +libnormalform_xnor.3
\ No newline at end of file diff --git a/man3/libnormalform_xnorl.3 b/man3/libnormalform_xnorl.3 new file mode 120000 index 0000000..49b2b6c --- /dev/null +++ b/man3/libnormalform_xnorl.3 @@ -0,0 +1 @@ +libnormalform_xnor.3
\ No newline at end of file diff --git a/man3/libnormalform_xnorl_checked.3 b/man3/libnormalform_xnorl_checked.3 new file mode 120000 index 0000000..2c62e7a --- /dev/null +++ b/man3/libnormalform_xnorl_checked.3 @@ -0,0 +1 @@ +libnormalform_xnor_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_xor.3 b/man3/libnormalform_xor.3 new file mode 100644 index 0000000..70e9584 --- /dev/null +++ b/man3/libnormalform_xor.3 @@ -0,0 +1,242 @@ +.TH LIBNORMALFORM_XOR 3 LIBNORMALFORM +.SH NAME +libnormalform_xor \- Exclusive disjunction + +.SH SYNOPSIS +.nf +#include <libnormalform.h> + +LIBNORMALFORM_SENTENCE *libnormalform_xor(LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_xorl(LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vxor(LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_xor_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE **\fIxs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_xorl_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, ... /*, NULL */); +LIBNORMALFORM_SENTENCE *libnormalform_vxor_checked(size_t \fIn\fP, LIBNORMALFORM_SENTENCE *\fIa\fP, va_list \fPargs\fP); +LIBNORMALFORM_SENTENCE *libnormalform_xor2(LIBNORMALFORM_SENTENCE *\fIp\fP, LIBNORMALFORM_SENTENCE *\fIq\fP); +#define LIBNORMALFORM_XOR(...) /* ... */ +.fi +.PP +Link with +.IR -lnormalform . + +.SH DESCRIPTION +The +.BR libnormalform_xor () +function creates a sentence that is logically +equivalent to the exclusive disjunction of the +arguments, that is, a sentence that is true when +and only when an odd number of subsentence is true. +.PP +The value of the +.I xs +parameter shall be a null-pointer terminated list +of subsentences. +.PP +The +.BR libnormalform_xorl () +function is a variant of +.BR libnormalform_xor () +which uses variadic arguments instead of an array. +.PP +The +.BR libnormalform_vxor () +function is a variant of +.BR libnormalform_xorl () +which takes an +.I va_list +in place of variadic arguments. +.PP +The +.BR libnormalform_xor_checked (), +.BR libnormalform_xorl_checked (), +and +.BR libnormalform_vxor_checked () +functions are variants of the +.BR libnormalform_xor (), +.BR libnormalform_xorl (), +and +.BR libnormalform_vxor () +functions respectively which assumes +that it is given +.I n +subsentances, and checks that all are +.RI non- NULL . +However, these functions still require +the list of subsentences to be terminated +using a null-pointer. +.PP +.I "libnormalform_xor2(p, q)" +is equivalent to +.IR "libnormalform_xorl_checked(2, p, q, NULL)" . +.PP +The +.BR LIBNORMALFORM_XOR +macro is a wrapper for the +.BR libnormalform_xor_checked () +but is more similar to +.BR libnormalform_xorl (). +Unlike +.BR libnormalform_xorl (), +the argument list +.I must not +be terminated by a null-pointer, instead +the macro automatically as the null-pointer. +The macro will count the number of arguments +it is given, therefore it will fail it the +argument list is has an extra null-pointer. +.PP +The returned pointer shall either be +deallocated with the +.BR libnormalform_free (3) +function or be relinquished by being +used as part of another sentence. +.PP +These functions adopt the ownership of any +.I LIBNORMALFORM_SENTENCE * +passed into it. Therefore, the user shall +not attempt to deallocate input sentences. +This holds even on failure: if the function +fails, input sentences are deallocated. + +.SH RETURN VALUE +Upon successful completion, the +.BR libnormalform_xor () +function and its variants return an object +representing the sentence; otherwise, the +functions return +.I NULL +and set +.I errno +to indicate the error. + +.SH ERRORS +These functions fails if: +.TP +.I ENOMEM +Insufficient memory was available to +create the sentence object. +.PP +The +.BR libnormalform_xor_checked (), +.BR libnormalform_xorl_checked (), +and +.BR libnormalform_vxor_checked () +functions will also fail without setting +.I errno +if any of the first +.I n +.IR "LIBNORMALFORM_SENTENCE *" 's +are +.IR NULL . +Likewise, the +.BR libnormalform_xor2 () +function will fail without setting +.I errno +if +.I p +or +.I q +is +.IR NULL . + +.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_xor (), +.br +.BR libnormalform_xor_checked () +T} Thread safety MT-Safe race:\fIxs\fP and elements in \fIxs\fP +T{ +.BR libnormalform_xorl (), +.br +.BR libnormalform_xorl_checked (), +.br +.BR libnormalform_xor2 (), +.br +.BR LIBNORMALFORM_XOR () +T} Thread safety MT-Safe race:parameters +T{ +.BR libnormalform_vxor (), +.br +.BR libnormalform_vxor_checked () +T} Thread safety MT-Safe race:parameters and arguments in \fIargs\fP +T{ +.BR libnormalform_xor (), +.br +.BR libnormalform_xorl (), +.br +.BR libnormalform_vxor (), +.br +.BR libnormalform_xor_checked (), +.br +.BR libnormalform_xorl_checked (), +.br +.BR libnormalform_vxor_checked (), +.br +.BR libnormalform_vxor2 (), +.br +.BR LIBNORMALFORM_XOR () +T} Async-signal safety AS-Unsafe heap +T{ +.BR libnormalform_xor (), +.br +.BR libnormalform_xorl (), +.br +.BR libnormalform_vxor (), +.br +.BR libnormalform_xor_checked (), +.br +.BR libnormalform_xorl_checked (), +.br +.BR libnormalform_vxor_checked (), +.br +.BR libnormalform_vxor2 (), +.br +.BR LIBNORMALFORM_XOR () +T} Async-cancel safety AC-Safe mem, AC-Unsafe heap +.TE + +.SH NOTES +If there are no subsentences, the resulting sentence is a contradiction. +.PP +The connective is commutative and associative. It's truthtable is (0110). +.PP +The +.BR LIBNORMALFORM_XOR () +macro requires ISO C23 support. +.PP +Using +.BR libnormalform_xor2 (), +has greatest performance, however +.BR libnormalform_xor () +is better at simplifying the sentence. +The other functions are just wrappers for the +.BR libnormalform_xor () +function. +.PP +These functions creates a clause where each term +is connected with the XOR connective. Unlike what +the name Exclusive disjunction imply, this does +.I not +create a sentence that is true when exactly one +subsentence is true. Rather this creates the parity +of the terms: the sentece is when an odd number of +subsentences are true. +.PP +Side-effects in the arguments of the macro +.BR LIBNORMALFORM_XOR () +are guaranteed to be applied exactly once. +The side-effects in each macro are applied +in no particular order. + +.SH SEE ALSO +.BR libnormalform (7) diff --git a/man3/libnormalform_xor2.3 b/man3/libnormalform_xor2.3 new file mode 120000 index 0000000..cd0eb39 --- /dev/null +++ b/man3/libnormalform_xor2.3 @@ -0,0 +1 @@ +libnormalform_xor_checked.3
\ No newline at end of file diff --git a/man3/libnormalform_xor_checked.3 b/man3/libnormalform_xor_checked.3 new file mode 120000 index 0000000..7da4f32 --- /dev/null +++ b/man3/libnormalform_xor_checked.3 @@ -0,0 +1 @@ +libnormalform_xor.3
\ No newline at end of file diff --git a/man3/libnormalform_xorl.3 b/man3/libnormalform_xorl.3 new file mode 120000 index 0000000..7da4f32 --- /dev/null +++ b/man3/libnormalform_xorl.3 @@ -0,0 +1 @@ +libnormalform_xor.3
\ No newline at end of file diff --git a/man3/libnormalform_xorl_checked.3 b/man3/libnormalform_xorl_checked.3 new file mode 120000 index 0000000..cd0eb39 --- /dev/null +++ b/man3/libnormalform_xorl_checked.3 @@ -0,0 +1 @@ +libnormalform_xor_checked.3
\ No newline at end of file diff --git a/man3/struct_libnormalform_function.3 b/man3/struct_libnormalform_function.3 new file mode 120000 index 0000000..57241ab --- /dev/null +++ b/man3/struct_libnormalform_function.3 @@ -0,0 +1 @@ +libnormalform_function.3
\ No newline at end of file diff --git a/man3/struct_libnormalform_map.3 b/man3/struct_libnormalform_map.3 new file mode 120000 index 0000000..899f515 --- /dev/null +++ b/man3/struct_libnormalform_map.3 @@ -0,0 +1 @@ +libnormalform_any.3
\ No newline at end of file diff --git a/man3/struct_libnormalform_mapping.3 b/man3/struct_libnormalform_mapping.3 new file mode 120000 index 0000000..907aac0 --- /dev/null +++ b/man3/struct_libnormalform_mapping.3 @@ -0,0 +1 @@ +struct_libnormalform_map.3
\ No newline at end of file diff --git a/man3/struct_libnormalform_representation_spec.3 b/man3/struct_libnormalform_representation_spec.3 new file mode 120000 index 0000000..19a7285 --- /dev/null +++ b/man3/struct_libnormalform_representation_spec.3 @@ -0,0 +1 @@ +libnormalform_from_string.3
\ No newline at end of file diff --git a/man3/struct_libnormalform_sentence.3 b/man3/struct_libnormalform_sentence.3 new file mode 120000 index 0000000..8d3cfbb --- /dev/null +++ b/man3/struct_libnormalform_sentence.3 @@ -0,0 +1 @@ +LIBNORMALFORM_SENTENCE.3
\ No newline at end of file diff --git a/man3/struct_libnormalform_transformer.3 b/man3/struct_libnormalform_transformer.3 new file mode 120000 index 0000000..86848de --- /dev/null +++ b/man3/struct_libnormalform_transformer.3 @@ -0,0 +1 @@ +libnormalform_transformation.3
\ No newline at end of file diff --git a/man3/struct_libnormalform_variable.3 b/man3/struct_libnormalform_variable.3 new file mode 120000 index 0000000..3bf0ee7 --- /dev/null +++ b/man3/struct_libnormalform_variable.3 @@ -0,0 +1 @@ +libnormalform_variable.3
\ No newline at end of file diff --git a/memcheck.c b/memcheck.c new file mode 100644 index 0000000..b119fad --- /dev/null +++ b/memcheck.c @@ -0,0 +1,638 @@ +/* See LICENSE file for copyright and license details. */ +#include "memcheck.h" + +#include <sys/syscall.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#define UNW_LOCAL_ONLY +#include <libunwind.h> +#include <elfutils/libdwfl.h> + + +enum alloctype { + ALLOCTYPE_REALLOC, + ALLOCTYPE_MMAP +}; + +#ifdef PRINT_BACKTRACES +struct backtrace { + size_t alloc_size; + size_t n; + uintptr_t rips[]; +}; +#endif + +struct allocinfo { + void *real; + void *ptr; + size_t real_size; + enum alloctype type; + int flags; + int dont_track; +#ifdef PRINT_BACKTRACES + struct backtrace *backtrace; +#endif +}; + + +static struct allocinfo *allocs = NULL; +static size_t nallocs = 0; +static size_t allocs_size = 0; +static size_t alloc_fail_exclusion = 0; +static size_t memleak_exclusion = 1; +static size_t alloc_fail_in = 0; +static int alloc_fail_all = 0; +static int have_custom_malloc = 0; + + +static void * +memcheck_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off) +{ +#ifdef SYS_mmap2 + unsigned long pgoff = (unsigned long)(off / 4096); + long int r = syscall(SYS_mmap2, addr, len, (unsigned long)prot, (unsigned long)flags, fd, pgoff); +#else + long int r = syscall(SYS_mmap, addr, len, (unsigned long)prot, (unsigned long)flags, fd, off); +#endif + return (void *)r; +} + + +static void * +memcheck_mremap(void *old, size_t old_size, size_t new_size, int flags, void *new_address) +{ + long int r = syscall(SYS_mremap, old, old_size, new_size, flags, new_address); + return (void *)r; +} + + +static int +memcheck_munmap(void *ptr, size_t n) +{ + return (int)syscall(SYS_munmap, ptr, n); +} + + +static void * +memcheck_mmap_ram(size_t n) +{ + return memcheck_mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +} + + +#ifdef PRINT_BACKTRACES +static struct backtrace * +memcheck_create_backtrace(void) +{ + struct backtrace *ret = NULL, *new; + unw_word_t rip; + unw_cursor_t cursor; + unw_context_t context; + size_t n = 0, size = 0; + int saved_errno = errno; + + memleak_exclusion += 1; + + if (unw_getcontext(&context)) + goto fail; + if (unw_init_local(&cursor, &context)) + goto fail; + + n = 8; + size = offsetof(struct backtrace, rips) + n * sizeof(*ret->rips); + ret = memcheck_mmap_ram(size); + if (!ret) + goto fail; + ret->alloc_size = size; + + ret->n = 0; + while (unw_step(&cursor) > 0) { + if (unw_get_reg(&cursor, UNW_REG_IP, &rip)) + goto fail; + if (ret->n == n) { + n += 8; + size = offsetof(struct backtrace, rips) + n * sizeof(*ret->rips); + new = memcheck_mmap_ram(size); + if (!new) + goto fail; + new->alloc_size = size; + new->n = ret->n; + memcpy(new->rips, ret->rips, n * sizeof(*ret->rips)); + memcheck_munmap(ret, ret->alloc_size); + ret = new; + } + ret->rips[ret->n++] = (uintptr_t)rip; + } + + errno = saved_errno; + memleak_exclusion -= 1; + return ret; + +fail: + if (ret) + memcheck_munmap(ret, ret->alloc_size); + errno = saved_errno; + memleak_exclusion -= 1; + return NULL; +} +#endif + + +#ifdef PRINT_BACKTRACES +static void +memcheck_print_backtrace(struct backtrace *backtrace, const char *indent) +{ + int saved_errno = errno, lineno; + char *debuginfo_path = NULL; + Dwarf_Addr ip; + Dwfl_Callbacks callbacks; + Dwfl *dwfl = NULL; + Dwfl_Line *line = NULL; + Dwfl_Module *module = NULL; + const char *filename = NULL; + const char *funcname = NULL; + size_t i; + + if (!backtrace) + return; + + memleak_exclusion += 1; + + memset(&callbacks, 0, sizeof(callbacks)); + + callbacks.find_elf = dwfl_linux_proc_find_elf; + callbacks.find_debuginfo = dwfl_standard_find_debuginfo; + callbacks.debuginfo_path = &debuginfo_path; + + if (dwfl) { + if (dwfl_linux_proc_report(dwfl, getpid()) || + dwfl_report_end(dwfl, NULL, NULL)) { + perror(""); + dwfl_end(dwfl); + dwfl = NULL; + } + } + + for (i = 0; i < backtrace->n; i++) { + ip = (Dwarf_Addr)backtrace->rips[i]; + + if (dwfl) { + module = dwfl_addrmodule(dwfl, ip); + funcname = module ? dwfl_module_addrname(module, ip) : NULL; + line = dwfl_getsrc(dwfl, ip); + if (line) + filename = dwfl_lineinfo(line, &(Dwarf_Addr){0}, &lineno, NULL, NULL, NULL); + } + + dprintf(STDERR_FILENO, "%s%s 0x%016"PRIxPTR": %s", + indent, !i ? "at" : "by", + (uintptr_t)ip, + funcname ? funcname : "???"); + if (line) + dprintf(STDERR_FILENO, " (%s:%i)\n", filename, lineno); + else + dprintf(STDERR_FILENO, "\n"); + } + + if (dwfl) + dwfl_end(dwfl); + + errno = saved_errno; + memleak_exclusion -= 1; +} +#endif + + +#if defined(__GNUC__) +__attribute__((__format__(__gnu_printf__, 1, 2))) +#endif +static void +memcheck_errorf(const char *fmt, ...) +{ + va_list ap; + memleak_exclusion = 1; + va_start(ap, fmt); + vdprintf(STDERR_FILENO, fmt, ap); + dprintf(STDERR_FILENO, ", aborting...\n"); + va_end(ap); +#ifdef PRINT_BACKTRACES + memcheck_print_backtrace(memcheck_create_backtrace(), "\t"); +#endif + abort(); +} + + +#if defined(__GNUC__) +__attribute__((__pure__)) +#endif +static struct allocinfo * +memcheck_getalloc(void *ptr) +{ + size_t i; + for (i = 0; i < nallocs; i++) + if (allocs[i].ptr == ptr) + return &allocs[i]; + return NULL; +} + + +static int +memcheck_fake_fail(void) +{ + have_custom_malloc = 1; + + if (!alloc_fail_exclusion) { + if (alloc_fail_all || (alloc_fail_in && !--alloc_fail_in)) { + alloc_fail_all = 1; + errno = ENOMEM; + return 1; + } + } + + return 0; +} + + +static void +memcheck_putalloc(struct allocinfo *info) +{ + if (nallocs == allocs_size) { + size_t old_allocs_size = allocs_size; + void *new; + if (allocs_size > SIZE_MAX / sizeof(*allocs) - 64) + memcheck_errorf("size of memory bookkeeping will exceed SIZE_MAX"); + allocs_size += 64; + new = memcheck_mmap_ram(allocs_size * sizeof(*allocs)); + if (!new) + memcheck_errorf("failed to allocate enought memory for bookkeeping"); + if (allocs) { + memcpy(new, allocs, nallocs * sizeof(*allocs)); + memcheck_munmap(allocs, old_allocs_size * sizeof(*allocs)); + } + allocs = new; + } + allocs[nallocs++] = *info; +} + + +static int +memcheck_free(void *ptr, size_t size, enum alloctype type) +{ + int ret; + struct allocinfo *alloc; + alloc = memcheck_getalloc(ptr); + if (!alloc) + memcheck_errorf("attempting to deallocate unknown pointer %p", ptr); + if (alloc->type != type) + memcheck_errorf("attempting to deallocating with incompatible function"); + if (type == ALLOCTYPE_MMAP && size != alloc->real_size) + memcheck_errorf("attempting to munmap(2) pointer %p with wrong size", ptr); + ret = memcheck_munmap(alloc->real, alloc->real_size); +#ifdef PRINT_BACKTRACES + if (alloc->backtrace) + memcheck_munmap(alloc->backtrace, alloc->backtrace->alloc_size); +#endif + *alloc = allocs[--nallocs]; + if (!nallocs) { + memcheck_munmap(allocs, allocs_size * sizeof(*allocs)); + allocs = NULL; + nallocs = 0; + allocs_size = 0; + } + return ret; +} + + +static void * +memcheck_realloc(void *old, size_t n, size_t m, size_t alignment, int initbyte) +{ + struct allocinfo info; + uintptr_t addr; + void *ret; + + if (memcheck_fake_fail()) + return NULL; + + if (!n || !m) + memcheck_errorf("attempting to allocate with zero size which is implementation-defined behaviour"); + if (n > SIZE_MAX / m) + memcheck_errorf("attempting allocate more than SIZE_MAX"); + + if (!alignment) + alignment = _Alignof(max_align_t); + + n *= m; + if (n > SIZE_MAX - (alignment - 1U)) + memcheck_errorf("attempting allocate more than SIZE_MAX after alignment padding"); + +#ifdef PRINT_BACKTRACES + info.backtrace = memleak_exclusion ? NULL : memcheck_create_backtrace(); +#endif + info.dont_track = memleak_exclusion > 0; + info.flags = 0; + info.type = ALLOCTYPE_REALLOC; + info.real_size = n + (alignment - 1U); + info.real = memcheck_mmap_ram(info.real_size); + if (!info.real) + memcheck_errorf("failed to allocate enough memory"); + memset(info.real, initbyte, info.real_size); + + addr = (uintptr_t)info.real; + if (addr % alignment) + addr += alignment - addr % alignment; + ret = info.ptr = (void *)addr; + + memcheck_putalloc(&info); + + if (old) { + struct allocinfo *oldinfo; + oldinfo = memcheck_getalloc(old); + if (!oldinfo) + memcheck_errorf("attempting to reallocate unknown pointer %p", old); + memcpy(info.real, oldinfo->real, info.real_size < oldinfo->real_size ? info.real_size : oldinfo->real_size); + memcheck_free(old, 0, ALLOCTYPE_REALLOC); + } + + return ret; +} + + + +/* Implementation of checked functions */ + + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +#endif + + +void * +mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off) +{ + struct allocinfo info; + void *ret; + + if (off % 4096) + memcheck_errorf("attempting to mmap(2) with offset %ju which is not a multiple of 4096", (uintmax_t)off); + +#ifdef PRINT_BACKTRACES + info.backtrace = memleak_exclusion ? NULL : memcheck_create_backtrace(); +#endif + info.dont_track = memleak_exclusion > 0; + info.flags = flags; + info.type = ALLOCTYPE_MMAP; + info.real_size = len; + info.real = memcheck_mmap(addr, len, prot, flags, fd, off); + if (!info.real) + memcheck_errorf("failed to mmap(2)"); + ret = info.ptr = info.real; + + memcheck_putalloc(&info); + return ret; +} + + +int +munmap(void *ptr, size_t n) +{ + return memcheck_free(ptr, n, ALLOCTYPE_MMAP); +} + + +void * +mremap(void *old, size_t old_size, size_t new_size, int flags, ...) +{ + struct allocinfo *oldinfo; + void *new_address = NULL; + void *ret; + + if (memcheck_fake_fail()) + return NULL; + + oldinfo = memcheck_getalloc(old); + if (!oldinfo) + memcheck_errorf("attempting to mremap(2) unknown pointer %p", old); + if (oldinfo->type != ALLOCTYPE_MMAP) + memcheck_errorf("attempting to mremap(2) pointer %p that was not allocated with mmap(2) or mremap(2)", old); + + if (flags & MREMAP_FIXED) { + va_list ap; + va_start(ap, flags); + new_address = va_arg(ap, void *); + va_end(ap); + } + + ret = memcheck_mremap(old, old_size, new_size, flags, new_address); + if (!ret) + return NULL; + + if (ret == old || !(flags & MREMAP_DONTUNMAP)) { + oldinfo->ptr = oldinfo->real = ret; + oldinfo->real_size = new_size; + } else { + struct allocinfo info; + info = *oldinfo; + info.ptr = info.real = ret; + info.real_size = new_size; + memcheck_putalloc(&info); + } + + return ret; +} + + +void +free(void *ptr) +{ + if (!ptr) + return; + memcheck_free(ptr, 0, ALLOCTYPE_REALLOC); +} + + +void * +malloc(size_t n) +{ + return memcheck_realloc(NULL, 1, n, 0, 'x'); +} + + +void * +calloc(size_t n, size_t m) +{ + return memcheck_realloc(NULL, n, m, 0, 0); +} + + +void * +realloc(void *old, size_t n) +{ + return memcheck_realloc(old, 1, n, 0, 'x'); +} + + +void * +reallocarray(void *old, size_t n, size_t m) +{ + return memcheck_realloc(old, n, m, 0, 'x'); +} + + +void * +memdup(const void *s, size_t n) +{ + char *r = memcheck_realloc(NULL, 1, n, 0, 'x'); + if (r) + memcpy(r, s, n); + return r; +} + + +char * +strdup(const char *s) +{ + size_t n = strlen(s) + 1U; + char *r = memcheck_realloc(NULL, 1, n, 0, 'x'); + if (r) + memcpy(r, s, n); + return r; +} + + +char * +strndup(const char *s, size_t max) +{ + size_t n = strnlen(s, max) + 1U; + char *r = memcheck_realloc(NULL, 1, n, 0, 'x'); + if (n > max) + n = max; + if (r) + memcpy(r, s, n); + return r; +} + + +int +posix_memalign(void **ret_out, size_t align, size_t n) +{ + int saved_errno, r; + saved_errno = errno, errno = 0; + if (!align || (align & (align - 1U)) || align % sizeof(void *)) + memcheck_errorf("invalid alignment for posix_memalign: %zu", align); + *ret_out = memcheck_realloc(NULL, 1, n, align, 'x'); + return r = errno, errno = saved_errno, r; +} + + +void * +memalign(size_t align, size_t n) +{ + if (!align) + memcheck_errorf("invalid alignment for memalign: %zu", align); + return memcheck_realloc(NULL, 1, n, align, 'x'); +} + + +void * +aligned_alloc(size_t align, size_t n) +{ + if (!align || (align & (align - 1U))) + memcheck_errorf("invalid alignment for aligned_alloc: %zu", align); + return memcheck_realloc(NULL, 1, n, align, 'x'); +} + + +void * +valloc(size_t n) +{ + return memcheck_realloc(NULL, 1, n, 4096U, 'x'); +} + + +void * +pvalloc(size_t n) +{ + if (n % 4096U) { + n |= 4096U - 1U; + if (n == SIZE_MAX) + memcheck_errorf("attempting to allocate beyond SIZE_MAX"); + n += 1U; + } + return memcheck_realloc(NULL, 1, n, 4096U, 'x'); +} + + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + + +/* Test functions */ + + +int +memcheck_have_custom_malloc(void) +{ + static void *volatile test_have_custom_malloc_ptr = NULL; + int saved_errno = errno; + alloc_fail_exclusion++; + memleak_exclusion++; + test_have_custom_malloc_ptr = malloc(1); + free(test_have_custom_malloc_ptr); + memleak_exclusion--; + alloc_fail_exclusion--; + errno = saved_errno; + return have_custom_malloc; +} + +size_t +memcheck_alloc_fail_in(size_t n) +{ + size_t ret = alloc_fail_in; + alloc_fail_in = n; + alloc_fail_all = 0; + return ret; +} + + +int +memcheck_check_memleaks(void) +{ + struct allocinfo *leak; + size_t i, count = 0; + memleak_exclusion = 1; + for (i = 0; i < nallocs; i++) { + leak = &allocs[i]; + if (leak->dont_track) + continue; + count += 1; + } + if (!count) + return 1; + dprintf(STDERR_FILENO, "%zu %s!\n", count, count == 1 ? "memory leak" : "memory leaks"); +#ifdef PRINT_BACKTRACES + for (i = 0; i < nallocs; i++) { + leak = &allocs[i]; + if (leak->dont_track) + continue; + memcheck_print_backtrace(leak->backtrace, "\t"); + } +#endif + return 0; +} + +void memcheck_alloc_fail_exclusion_begin(void) { alloc_fail_exclusion++; } +void memcheck_alloc_fail_exclusion_end(void) { alloc_fail_exclusion--; } +void memcheck_exclusion_begin(void) { memleak_exclusion++; } +void memcheck_exclusion_end(void) { memleak_exclusion--; } +void memcheck_begin(void) { memleak_exclusion = 0; } diff --git a/memcheck.h b/memcheck.h new file mode 100644 index 0000000..66625df --- /dev/null +++ b/memcheck.h @@ -0,0 +1,11 @@ +/* See LICENSE file for copyright and license details. */ +#include <stddef.h> + +int memcheck_have_custom_malloc(void); +size_t memcheck_alloc_fail_in(size_t); +int memcheck_check_memleaks(void); +void memcheck_alloc_fail_exclusion_begin(void); +void memcheck_alloc_fail_exclusion_end(void); +void memcheck_exclusion_begin(void); +void memcheck_exclusion_end(void); +void memcheck_begin(void); diff --git a/mk/linux.mk b/mk/linux.mk new file mode 100644 index 0000000..ad58f69 --- /dev/null +++ b/mk/linux.mk @@ -0,0 +1,6 @@ +LIBEXT = so +LIBFLAGS = -shared -Wl,-soname,lib$(LIB_NAME).$(LIBEXT).$(LIB_MAJOR) +LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR) +LIBMINOREXT = $(LIBEXT).$(LIB_VERSION) + +FIX_INSTALL_NAME = : diff --git a/mk/macos.mk b/mk/macos.mk new file mode 100644 index 0000000..f7c4977 --- /dev/null +++ b/mk/macos.mk @@ -0,0 +1,6 @@ +LIBEXT = dylib +LIBFLAGS = -dynamiclib -Wl,-compatibility_version,$(LIB_MAJOR) -Wl,-current_version,$(LIB_VERSION) +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = install_name_tool -id "$(PREFIX)/lib/libnormalform.$(LIBMAJOREXT)" diff --git a/mk/windows.mk b/mk/windows.mk new file mode 100644 index 0000000..ed5ec8d --- /dev/null +++ b/mk/windows.mk @@ -0,0 +1,6 @@ +LIBEXT = dll +LIBFLAGS = -shared +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = : |
