From 4294ec0ed06ee34920c9edaeebaeb8b65c720791 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Fri, 19 Jul 2024 01:29:42 +0200 Subject: First commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 16 + LICENSE | 15 + Makefile | 251 ++ README | 38 + common.h | 632 ++++ config.mk | 19 + libnormalform.7 | 94 + libnormalform.h | 4213 +++++++++++++++++++++++ libnormalform_all.c | 452 +++ libnormalform_and.c | 578 ++++ libnormalform_and2.c | 82 + libnormalform_and2__.c | 119 + libnormalform_and_checked.c | 14 + libnormalform_andl.c | 14 + libnormalform_andl_checked.c | 14 + libnormalform_andl_macro_test.c | 5 + libnormalform_any.c | 451 +++ libnormalform_clone.c | 558 +++ libnormalform_empty.c | 69 + libnormalform_evaluate.c | 44 + libnormalform_existentially.c | 235 ++ libnormalform_exists.c | 243 ++ libnormalform_express.c | 849 +++++ libnormalform_false.c | 155 + libnormalform_free.c | 123 + libnormalform_from_string.c | 918 +++++ libnormalform_function.c | 287 ++ libnormalform_if.c | 249 ++ libnormalform_if2.c | 14 + libnormalform_if_checked.c | 14 + libnormalform_ifl.c | 14 + libnormalform_ifl_checked.c | 14 + libnormalform_ifl_macro_test.c | 5 + libnormalform_imply.c | 166 + libnormalform_imply2.c | 14 + libnormalform_imply_checked.c | 14 + libnormalform_implyl.c | 14 + libnormalform_implyl_checked.c | 14 + libnormalform_implyl_macro_test.c | 5 + libnormalform_nand.c | 235 ++ libnormalform_nand2.c | 14 + libnormalform_nand_checked.c | 14 + libnormalform_nandl.c | 14 + libnormalform_nandl_checked.c | 14 + libnormalform_nandl_macro_test.c | 5 + libnormalform_nexists.c | 243 ++ libnormalform_nif.c | 228 ++ libnormalform_nif2.c | 14 + libnormalform_nif_checked.c | 14 + libnormalform_nifl.c | 14 + libnormalform_nifl_checked.c | 14 + libnormalform_nifl_macro_test.c | 5 + libnormalform_nimply.c | 172 + libnormalform_nimply2.c | 14 + libnormalform_nimply_checked.c | 14 + libnormalform_nimplyl.c | 14 + libnormalform_nimplyl_checked.c | 14 + libnormalform_nimplyl_macro_test.c | 5 + libnormalform_nonempty.c | 69 + libnormalform_nor.c | 235 ++ libnormalform_nor2.c | 14 + libnormalform_nor_checked.c | 14 + libnormalform_norl.c | 14 + libnormalform_norl_checked.c | 14 + libnormalform_norl_macro_test.c | 5 + libnormalform_not.c | 115 + libnormalform_one.c | 449 +++ libnormalform_or.c | 562 +++ libnormalform_or2.c | 82 + libnormalform_or2__.c | 119 + libnormalform_or_checked.c | 14 + libnormalform_orl.c | 14 + libnormalform_orl_checked.c | 14 + libnormalform_orl_macro_test.c | 5 + libnormalform_ref.c | 57 + libnormalform_reset_indices_and_counts__.c | 44 + libnormalform_set_indices_and_counts__.c | 56 + libnormalform_singleton.c | 71 + libnormalform_to_string.c | 519 +++ libnormalform_transformation.c | 419 +++ libnormalform_true.c | 156 + libnormalform_unique.c | 234 ++ libnormalform_uniquely.c | 224 ++ libnormalform_universally.c | 233 ++ libnormalform_vand.c | 14 + libnormalform_vand_checked.c | 14 + libnormalform_variable.c | 153 + libnormalform_vif.c | 14 + libnormalform_vif_checked.c | 14 + libnormalform_vimply.c | 14 + libnormalform_vimply_checked.c | 14 + libnormalform_vnand.c | 14 + libnormalform_vnand_checked.c | 14 + libnormalform_vnif.c | 14 + libnormalform_vnif_checked.c | 14 + libnormalform_vnimply.c | 14 + libnormalform_vnimply_checked.c | 14 + libnormalform_vnor.c | 14 + libnormalform_vnor_checked.c | 14 + libnormalform_vor.c | 14 + libnormalform_vor_checked.c | 14 + libnormalform_vxnor.c | 14 + libnormalform_vxnor_checked.c | 14 + libnormalform_vxor.c | 14 + libnormalform_vxor_checked.c | 14 + libnormalform_xnor.c | 437 +++ libnormalform_xnor2.c | 14 + libnormalform_xnor_checked.c | 14 + libnormalform_xnorl.c | 14 + libnormalform_xnorl_checked.c | 14 + libnormalform_xnorl_macro_test.c | 5 + libnormalform_xor.c | 710 ++++ libnormalform_xor2.c | 63 + libnormalform_xor2__.c | 174 + libnormalform_xor_checked.c | 14 + libnormalform_xorl.c | 14 + libnormalform_xorl_checked.c | 14 + libnormalform_xorl_macro_test.c | 5 + man3/LIBNORMALFORM_AND.3 | 1 + man3/LIBNORMALFORM_IF.3 | 1 + man3/LIBNORMALFORM_IMPLY.3 | 1 + man3/LIBNORMALFORM_NAND.3 | 1 + man3/LIBNORMALFORM_NIF.3 | 1 + man3/LIBNORMALFORM_NIMPLY.3 | 1 + man3/LIBNORMALFORM_NOR.3 | 1 + man3/LIBNORMALFORM_OR.3 | 1 + man3/LIBNORMALFORM_SENTENCE.3 | 61 + man3/LIBNORMALFORM_XNOR.3 | 1 + man3/LIBNORMALFORM_XOR.3 | 1 + man3/enum_libnormalform_builtin_transformer.3 | 1 + man3/enum_libnormalform_value.3 | 1 + man3/libnormalform_all.3 | 202 ++ man3/libnormalform_and.3 | 233 ++ man3/libnormalform_and2.3 | 1 + man3/libnormalform_and_checked.3 | 1 + man3/libnormalform_andl.3 | 1 + man3/libnormalform_andl_checked.3 | 1 + man3/libnormalform_any.3 | 294 ++ man3/libnormalform_builtin_transformer.3 | 1 + man3/libnormalform_clone.3 | 104 + man3/libnormalform_empty.3 | 1 + man3/libnormalform_evaluate.3 | 78 + man3/libnormalform_existentially.3 | 1 + man3/libnormalform_exists.3 | 1 + man3/libnormalform_false.3 | 70 + man3/libnormalform_free.3 | 73 + man3/libnormalform_from_string.3 | 265 ++ man3/libnormalform_function.3 | 152 + man3/libnormalform_if.3 | 233 ++ man3/libnormalform_if2.3 | 1 + man3/libnormalform_if_checked.3 | 1 + man3/libnormalform_ifl.3 | 1 + man3/libnormalform_ifl_checked.3 | 1 + man3/libnormalform_imply.3 | 236 ++ man3/libnormalform_imply2.3 | 1 + man3/libnormalform_imply_checked.3 | 1 + man3/libnormalform_implyl.3 | 1 + man3/libnormalform_implyl_checked.3 | 1 + man3/libnormalform_map.3 | 1 + man3/libnormalform_mapping.3 | 1 + man3/libnormalform_nand.3 | 250 ++ man3/libnormalform_nand2.3 | 1 + man3/libnormalform_nand_checked.3 | 1 + man3/libnormalform_nandl.3 | 1 + man3/libnormalform_nandl_checked.3 | 1 + man3/libnormalform_nexists.3 | 1 + man3/libnormalform_nif.3 | 239 ++ man3/libnormalform_nif2.3 | 1 + man3/libnormalform_nif_checked.3 | 1 + man3/libnormalform_nifl.3 | 1 + man3/libnormalform_nifl_checked.3 | 1 + man3/libnormalform_nimply.3 | 241 ++ man3/libnormalform_nimply2.3 | 1 + man3/libnormalform_nimply_checked.3 | 1 + man3/libnormalform_nimplyl.3 | 1 + man3/libnormalform_nimplyl_checked.3 | 1 + man3/libnormalform_nonempty.3 | 1 + man3/libnormalform_nor.3 | 245 ++ man3/libnormalform_nor2.3 | 1 + man3/libnormalform_nor_checked.3 | 1 + man3/libnormalform_norl.3 | 1 + man3/libnormalform_norl_checked.3 | 1 + man3/libnormalform_not.3 | 92 + man3/libnormalform_one.3 | 253 ++ man3/libnormalform_or.3 | 233 ++ man3/libnormalform_or2.3 | 1 + man3/libnormalform_or_checked.3 | 1 + man3/libnormalform_orl.3 | 1 + man3/libnormalform_orl_checked.3 | 1 + man3/libnormalform_ref.3 | 111 + man3/libnormalform_representation_spec.3 | 1 + man3/libnormalform_sentence.3 | 1 + man3/libnormalform_singleton.3 | 1 + man3/libnormalform_to_string.3 | 105 + man3/libnormalform_transformation.3 | 245 ++ man3/libnormalform_transformer.3 | 1 + man3/libnormalform_true.3 | 70 + man3/libnormalform_unique.3 | 1 + man3/libnormalform_uniquely.3 | 1 + man3/libnormalform_universally.3 | 1 + man3/libnormalform_value.3 | 1 + man3/libnormalform_vand.3 | 1 + man3/libnormalform_vand_checked.3 | 1 + man3/libnormalform_variable.3 | 127 + man3/libnormalform_vif.3 | 1 + man3/libnormalform_vif_checked.3 | 1 + man3/libnormalform_vimply.3 | 1 + man3/libnormalform_vimply_checked.3 | 1 + man3/libnormalform_vnand.3 | 1 + man3/libnormalform_vnand_checked.3 | 1 + man3/libnormalform_vnif.3 | 1 + man3/libnormalform_vnif_checked.3 | 1 + man3/libnormalform_vnimply.3 | 1 + man3/libnormalform_vnimply_checked.3 | 1 + man3/libnormalform_vnor.3 | 1 + man3/libnormalform_vnor_checked.3 | 1 + man3/libnormalform_vor.3 | 1 + man3/libnormalform_vor_checked.3 | 1 + man3/libnormalform_vxnor.3 | 1 + man3/libnormalform_vxnor_checked.3 | 1 + man3/libnormalform_vxor.3 | 1 + man3/libnormalform_vxor_checked.3 | 1 + man3/libnormalform_xnor.3 | 245 ++ man3/libnormalform_xnor2.3 | 1 + man3/libnormalform_xnor_checked.3 | 1 + man3/libnormalform_xnorl.3 | 1 + man3/libnormalform_xnorl_checked.3 | 1 + man3/libnormalform_xor.3 | 242 ++ man3/libnormalform_xor2.3 | 1 + man3/libnormalform_xor_checked.3 | 1 + man3/libnormalform_xorl.3 | 1 + man3/libnormalform_xorl_checked.3 | 1 + man3/struct_libnormalform_function.3 | 1 + man3/struct_libnormalform_map.3 | 1 + man3/struct_libnormalform_mapping.3 | 1 + man3/struct_libnormalform_representation_spec.3 | 1 + man3/struct_libnormalform_sentence.3 | 1 + man3/struct_libnormalform_transformer.3 | 1 + man3/struct_libnormalform_variable.3 | 1 + memcheck.c | 638 ++++ memcheck.h | 11 + mk/linux.mk | 6 + mk/macos.mk | 6 + mk/windows.mk | 6 + 244 files changed, 23224 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 common.h create mode 100644 config.mk create mode 100644 libnormalform.7 create mode 100644 libnormalform.h create mode 100644 libnormalform_all.c create mode 100644 libnormalform_and.c create mode 100644 libnormalform_and2.c create mode 100644 libnormalform_and2__.c create mode 100644 libnormalform_and_checked.c create mode 100644 libnormalform_andl.c create mode 100644 libnormalform_andl_checked.c create mode 100644 libnormalform_andl_macro_test.c create mode 100644 libnormalform_any.c create mode 100644 libnormalform_clone.c create mode 100644 libnormalform_empty.c create mode 100644 libnormalform_evaluate.c create mode 100644 libnormalform_existentially.c create mode 100644 libnormalform_exists.c create mode 100644 libnormalform_express.c create mode 100644 libnormalform_false.c create mode 100644 libnormalform_free.c create mode 100644 libnormalform_from_string.c create mode 100644 libnormalform_function.c create mode 100644 libnormalform_if.c create mode 100644 libnormalform_if2.c create mode 100644 libnormalform_if_checked.c create mode 100644 libnormalform_ifl.c create mode 100644 libnormalform_ifl_checked.c create mode 100644 libnormalform_ifl_macro_test.c create mode 100644 libnormalform_imply.c create mode 100644 libnormalform_imply2.c create mode 100644 libnormalform_imply_checked.c create mode 100644 libnormalform_implyl.c create mode 100644 libnormalform_implyl_checked.c create mode 100644 libnormalform_implyl_macro_test.c create mode 100644 libnormalform_nand.c create mode 100644 libnormalform_nand2.c create mode 100644 libnormalform_nand_checked.c create mode 100644 libnormalform_nandl.c create mode 100644 libnormalform_nandl_checked.c create mode 100644 libnormalform_nandl_macro_test.c create mode 100644 libnormalform_nexists.c create mode 100644 libnormalform_nif.c create mode 100644 libnormalform_nif2.c create mode 100644 libnormalform_nif_checked.c create mode 100644 libnormalform_nifl.c create mode 100644 libnormalform_nifl_checked.c create mode 100644 libnormalform_nifl_macro_test.c create mode 100644 libnormalform_nimply.c create mode 100644 libnormalform_nimply2.c create mode 100644 libnormalform_nimply_checked.c create mode 100644 libnormalform_nimplyl.c create mode 100644 libnormalform_nimplyl_checked.c create mode 100644 libnormalform_nimplyl_macro_test.c create mode 100644 libnormalform_nonempty.c create mode 100644 libnormalform_nor.c create mode 100644 libnormalform_nor2.c create mode 100644 libnormalform_nor_checked.c create mode 100644 libnormalform_norl.c create mode 100644 libnormalform_norl_checked.c create mode 100644 libnormalform_norl_macro_test.c create mode 100644 libnormalform_not.c create mode 100644 libnormalform_one.c create mode 100644 libnormalform_or.c create mode 100644 libnormalform_or2.c create mode 100644 libnormalform_or2__.c create mode 100644 libnormalform_or_checked.c create mode 100644 libnormalform_orl.c create mode 100644 libnormalform_orl_checked.c create mode 100644 libnormalform_orl_macro_test.c create mode 100644 libnormalform_ref.c create mode 100644 libnormalform_reset_indices_and_counts__.c create mode 100644 libnormalform_set_indices_and_counts__.c create mode 100644 libnormalform_singleton.c create mode 100644 libnormalform_to_string.c create mode 100644 libnormalform_transformation.c create mode 100644 libnormalform_true.c create mode 100644 libnormalform_unique.c create mode 100644 libnormalform_uniquely.c create mode 100644 libnormalform_universally.c create mode 100644 libnormalform_vand.c create mode 100644 libnormalform_vand_checked.c create mode 100644 libnormalform_variable.c create mode 100644 libnormalform_vif.c create mode 100644 libnormalform_vif_checked.c create mode 100644 libnormalform_vimply.c create mode 100644 libnormalform_vimply_checked.c create mode 100644 libnormalform_vnand.c create mode 100644 libnormalform_vnand_checked.c create mode 100644 libnormalform_vnif.c create mode 100644 libnormalform_vnif_checked.c create mode 100644 libnormalform_vnimply.c create mode 100644 libnormalform_vnimply_checked.c create mode 100644 libnormalform_vnor.c create mode 100644 libnormalform_vnor_checked.c create mode 100644 libnormalform_vor.c create mode 100644 libnormalform_vor_checked.c create mode 100644 libnormalform_vxnor.c create mode 100644 libnormalform_vxnor_checked.c create mode 100644 libnormalform_vxor.c create mode 100644 libnormalform_vxor_checked.c create mode 100644 libnormalform_xnor.c create mode 100644 libnormalform_xnor2.c create mode 100644 libnormalform_xnor_checked.c create mode 100644 libnormalform_xnorl.c create mode 100644 libnormalform_xnorl_checked.c create mode 100644 libnormalform_xnorl_macro_test.c create mode 100644 libnormalform_xor.c create mode 100644 libnormalform_xor2.c create mode 100644 libnormalform_xor2__.c create mode 100644 libnormalform_xor_checked.c create mode 100644 libnormalform_xorl.c create mode 100644 libnormalform_xorl_checked.c create mode 100644 libnormalform_xorl_macro_test.c create mode 120000 man3/LIBNORMALFORM_AND.3 create mode 120000 man3/LIBNORMALFORM_IF.3 create mode 120000 man3/LIBNORMALFORM_IMPLY.3 create mode 120000 man3/LIBNORMALFORM_NAND.3 create mode 120000 man3/LIBNORMALFORM_NIF.3 create mode 120000 man3/LIBNORMALFORM_NIMPLY.3 create mode 120000 man3/LIBNORMALFORM_NOR.3 create mode 120000 man3/LIBNORMALFORM_OR.3 create mode 100644 man3/LIBNORMALFORM_SENTENCE.3 create mode 120000 man3/LIBNORMALFORM_XNOR.3 create mode 120000 man3/LIBNORMALFORM_XOR.3 create mode 120000 man3/enum_libnormalform_builtin_transformer.3 create mode 120000 man3/enum_libnormalform_value.3 create mode 100644 man3/libnormalform_all.3 create mode 100644 man3/libnormalform_and.3 create mode 120000 man3/libnormalform_and2.3 create mode 120000 man3/libnormalform_and_checked.3 create mode 120000 man3/libnormalform_andl.3 create mode 120000 man3/libnormalform_andl_checked.3 create mode 100644 man3/libnormalform_any.3 create mode 120000 man3/libnormalform_builtin_transformer.3 create mode 100644 man3/libnormalform_clone.3 create mode 120000 man3/libnormalform_empty.3 create mode 100644 man3/libnormalform_evaluate.3 create mode 120000 man3/libnormalform_existentially.3 create mode 120000 man3/libnormalform_exists.3 create mode 100644 man3/libnormalform_false.3 create mode 100644 man3/libnormalform_free.3 create mode 100644 man3/libnormalform_from_string.3 create mode 100644 man3/libnormalform_function.3 create mode 100644 man3/libnormalform_if.3 create mode 120000 man3/libnormalform_if2.3 create mode 120000 man3/libnormalform_if_checked.3 create mode 120000 man3/libnormalform_ifl.3 create mode 120000 man3/libnormalform_ifl_checked.3 create mode 100644 man3/libnormalform_imply.3 create mode 120000 man3/libnormalform_imply2.3 create mode 120000 man3/libnormalform_imply_checked.3 create mode 120000 man3/libnormalform_implyl.3 create mode 120000 man3/libnormalform_implyl_checked.3 create mode 120000 man3/libnormalform_map.3 create mode 120000 man3/libnormalform_mapping.3 create mode 100644 man3/libnormalform_nand.3 create mode 120000 man3/libnormalform_nand2.3 create mode 120000 man3/libnormalform_nand_checked.3 create mode 120000 man3/libnormalform_nandl.3 create mode 120000 man3/libnormalform_nandl_checked.3 create mode 120000 man3/libnormalform_nexists.3 create mode 100644 man3/libnormalform_nif.3 create mode 120000 man3/libnormalform_nif2.3 create mode 120000 man3/libnormalform_nif_checked.3 create mode 120000 man3/libnormalform_nifl.3 create mode 120000 man3/libnormalform_nifl_checked.3 create mode 100644 man3/libnormalform_nimply.3 create mode 120000 man3/libnormalform_nimply2.3 create mode 120000 man3/libnormalform_nimply_checked.3 create mode 120000 man3/libnormalform_nimplyl.3 create mode 120000 man3/libnormalform_nimplyl_checked.3 create mode 120000 man3/libnormalform_nonempty.3 create mode 100644 man3/libnormalform_nor.3 create mode 120000 man3/libnormalform_nor2.3 create mode 120000 man3/libnormalform_nor_checked.3 create mode 120000 man3/libnormalform_norl.3 create mode 120000 man3/libnormalform_norl_checked.3 create mode 100644 man3/libnormalform_not.3 create mode 100644 man3/libnormalform_one.3 create mode 100644 man3/libnormalform_or.3 create mode 120000 man3/libnormalform_or2.3 create mode 120000 man3/libnormalform_or_checked.3 create mode 120000 man3/libnormalform_orl.3 create mode 120000 man3/libnormalform_orl_checked.3 create mode 100644 man3/libnormalform_ref.3 create mode 120000 man3/libnormalform_representation_spec.3 create mode 120000 man3/libnormalform_sentence.3 create mode 120000 man3/libnormalform_singleton.3 create mode 100644 man3/libnormalform_to_string.3 create mode 100644 man3/libnormalform_transformation.3 create mode 120000 man3/libnormalform_transformer.3 create mode 100644 man3/libnormalform_true.3 create mode 120000 man3/libnormalform_unique.3 create mode 120000 man3/libnormalform_uniquely.3 create mode 120000 man3/libnormalform_universally.3 create mode 120000 man3/libnormalform_value.3 create mode 120000 man3/libnormalform_vand.3 create mode 120000 man3/libnormalform_vand_checked.3 create mode 100644 man3/libnormalform_variable.3 create mode 120000 man3/libnormalform_vif.3 create mode 120000 man3/libnormalform_vif_checked.3 create mode 120000 man3/libnormalform_vimply.3 create mode 120000 man3/libnormalform_vimply_checked.3 create mode 120000 man3/libnormalform_vnand.3 create mode 120000 man3/libnormalform_vnand_checked.3 create mode 120000 man3/libnormalform_vnif.3 create mode 120000 man3/libnormalform_vnif_checked.3 create mode 120000 man3/libnormalform_vnimply.3 create mode 120000 man3/libnormalform_vnimply_checked.3 create mode 120000 man3/libnormalform_vnor.3 create mode 120000 man3/libnormalform_vnor_checked.3 create mode 120000 man3/libnormalform_vor.3 create mode 120000 man3/libnormalform_vor_checked.3 create mode 120000 man3/libnormalform_vxnor.3 create mode 120000 man3/libnormalform_vxnor_checked.3 create mode 120000 man3/libnormalform_vxor.3 create mode 120000 man3/libnormalform_vxor_checked.3 create mode 100644 man3/libnormalform_xnor.3 create mode 120000 man3/libnormalform_xnor2.3 create mode 120000 man3/libnormalform_xnor_checked.3 create mode 120000 man3/libnormalform_xnorl.3 create mode 120000 man3/libnormalform_xnorl_checked.3 create mode 100644 man3/libnormalform_xor.3 create mode 120000 man3/libnormalform_xor2.3 create mode 120000 man3/libnormalform_xor_checked.3 create mode 120000 man3/libnormalform_xorl.3 create mode 120000 man3/libnormalform_xorl_checked.3 create mode 120000 man3/struct_libnormalform_function.3 create mode 120000 man3/struct_libnormalform_map.3 create mode 120000 man3/struct_libnormalform_mapping.3 create mode 120000 man3/struct_libnormalform_representation_spec.3 create mode 120000 man3/struct_libnormalform_sentence.3 create mode 120000 man3/struct_libnormalform_transformer.3 create mode 120000 man3/struct_libnormalform_variable.3 create mode 100644 memcheck.c create mode 100644 memcheck.h create mode 100644 mk/linux.mk create mode 100644 mk/macos.mk create mode 100644 mk/windows.mk 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 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fccd785 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +© 2024 Mattias Andrée + +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 diff --git a/README b/README new file mode 100644 index 0000000..37019cc --- /dev/null +++ b/README @@ -0,0 +1,38 @@ +NAME + libnormalform - First-order logic sentence canonicalisation library + +SYNOPSIS + #include + + 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 +#include +#include +#include +#include +#include + + +#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 +#include + +#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_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 +#include +#include + + +#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 "↚", "⇍", "!<-", "<-~", "" + * + * @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 = LIBNORMALFORM_DOMAIN_VIEW + }; + static struct libnormalform_transformer image_view = { + .transform = &image_view_transform, + .deallocate = NULL, + .user_data = NULL, + .identifier = "", + .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, "\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, "\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 + +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 + +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_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 + +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_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 + +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_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 + +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 + +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 + +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_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_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_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_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_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_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_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 + +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_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_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 + +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 + +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_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 + +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_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_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UNW_LOCAL_ONLY +#include +#include + + +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 + +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 = : -- cgit v1.3.1