* [PATCH v4 01/12] kconfig: Add picosat.h
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 02/12] kconfig: Add picosat.c (1/3) Ole Schuerks
` (10 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
PicoSAT (https://fmv.jku.at/picosat/) is the SAT solver used in this
project. This commit contains the header file for PicoSAT. We use
release 965.
Signed-off-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/picosat.h | 658 ++++++++++++++++++++++++++++++++++++++
1 file changed, 658 insertions(+)
create mode 100644 scripts/kconfig/picosat.h
diff --git a/scripts/kconfig/picosat.h b/scripts/kconfig/picosat.h
new file mode 100644
index 000000000000..93930180912e
--- /dev/null
+++ b/scripts/kconfig/picosat.h
@@ -0,0 +1,658 @@
+/****************************************************************************
+Copyright (c) 2006 - 2015, Armin Biere, Johannes Kepler University.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+****************************************************************************/
+
+#ifndef picosat_h_INCLUDED
+#define picosat_h_INCLUDED
+
+/*------------------------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+
+/*------------------------------------------------------------------------*/
+/* The following macros allows for users to distiguish between different
+ * versions of the API. The first 'PICOSAT_REENTRANT_API' is defined for
+ * the new reentrant API which allows to generate multiple instances of
+ * PicoSAT in one process. The second 'PICOSAT_API_VERSION' defines the
+ * (smallest) version of PicoSAT to which this API conforms.
+ */
+#define PICOSAT_REENTRANT_API
+#define PICOSAT_API_VERSION 953 /* API version */
+
+/*------------------------------------------------------------------------*/
+/* These are the return values for 'picosat_sat' as for instance
+ * standardized by the output format of the SAT competition.
+ */
+#define PICOSAT_UNKNOWN 0
+#define PICOSAT_SATISFIABLE 10
+#define PICOSAT_UNSATISFIABLE 20
+
+/*------------------------------------------------------------------------*/
+
+typedef struct PicoSAT PicoSAT;
+
+/*------------------------------------------------------------------------*/
+
+const char *picosat_version (void);
+const char *picosat_config (void);
+const char *picosat_copyright (void);
+
+/*------------------------------------------------------------------------*/
+/* You can make PicoSAT use an external memory manager instead of the one
+ * provided by LIBC. But then you need to call these three function before
+ * 'picosat_init'. The memory manager functions here all have an additional
+ * first argument which is a pointer to the memory manager, but otherwise
+ * are supposed to work as their LIBC counter parts 'malloc', 'realloc' and
+ * 'free'. As exception the 'resize' and 'delete' function have as third
+ * argument the number of bytes of the block given as second argument.
+ */
+
+typedef void * (*picosat_malloc)(void *, size_t);
+typedef void * (*picosat_realloc)(void*, void *, size_t, size_t);
+typedef void (*picosat_free)(void*, void*, size_t);
+
+/*------------------------------------------------------------------------*/
+
+PicoSAT * picosat_init (void); /* constructor */
+
+PicoSAT * picosat_minit (void * state,
+ picosat_malloc,
+ picosat_realloc,
+ picosat_free);
+
+void picosat_reset (PicoSAT *); /* destructor */
+
+/*------------------------------------------------------------------------*/
+/* The following five functions are essentially parameters to 'init', and
+ * thus should be called right after 'picosat_init' before doing anything
+ * else. You should not call any of them after adding a literal.
+ */
+
+/* Set output file, default is 'stdout'.
+ */
+void picosat_set_output (PicoSAT *, FILE *);
+
+/* Measure all time spent in all calls in the solver. By default only the
+ * time spent in 'picosat_sat' is measured. Enabling this function might
+ * for instance triple the time needed to add large CNFs, since every call
+ * to 'picosat_add' will trigger a call to 'getrusage'.
+ */
+void picosat_measure_all_calls (PicoSAT *);
+
+/* Set the prefix used for printing verbose messages and statistics.
+ * Default is "c ".
+ */
+void picosat_set_prefix (PicoSAT *, const char *);
+
+/* Set verbosity level. A verbosity level of 1 and above prints more and
+ * more detailed progress reports on the output file, set by
+ * 'picosat_set_output'. Verbose messages are prefixed with the string set
+ * by 'picosat_set_prefix'.
+ */
+void picosat_set_verbosity (PicoSAT *, int new_verbosity_level);
+
+/* Disable/Enable all pre-processing, currently only failed literal probing.
+ *
+ * new_plain_value != 0 only 'plain' solving, so no preprocessing
+ * new_plain_value == 0 allow preprocessing
+ */
+void picosat_set_plain (PicoSAT *, int new_plain_value);
+
+/* Set default initial phase:
+ *
+ * 0 = false
+ * 1 = true
+ * 2 = Jeroslow-Wang (default)
+ * 3 = random initial phase
+ *
+ * After a variable has been assigned the first time, it will always
+ * be assigned the previous value if it is picked as decision variable.
+ * The initial assignment can be chosen with this function.
+ */
+void picosat_set_global_default_phase (PicoSAT *, int);
+
+/* Set next/initial phase of a particular variable if picked as decision
+ * variable. Second argument 'phase' has the following meaning:
+ *
+ * negative = next value if picked as decision variable is false
+ *
+ * positive = next value if picked as decision variable is true
+ *
+ * 0 = use global default phase as next value and
+ * assume 'lit' was never assigned
+ *
+ * Again if 'lit' is assigned afterwards through a forced assignment,
+ * then this forced assignment is the next phase if this variable is
+ * used as decision variable.
+ */
+void picosat_set_default_phase_lit (PicoSAT *, int lit, int phase);
+
+/* You can reset all phases by the following function.
+ */
+void picosat_reset_phases (PicoSAT *);
+
+/* Scores can be erased as well. Note, however, that even after erasing
+ * scores and phases, learned clauses are kept. In addition head tail
+ * pointers for literals are not moved either. So expect a difference
+ * between calling the solver in incremental mode or with a fresh copy of
+ * the CNF.
+ */
+void picosat_reset_scores (PicoSAT *);
+
+/* Reset assignment if in SAT state and then remove the given percentage of
+ * less active (large) learned clauses. If you specify 100% all large
+ * learned clauses are removed.
+ */
+void picosat_remove_learned (PicoSAT *, unsigned percentage);
+
+/* Set some variables to be more important than others. These variables are
+ * always used as decisions before other variables are used. Dually there
+ * is a set of variables that is used last. The default is
+ * to mark all variables as being indifferent only.
+ */
+void picosat_set_more_important_lit (PicoSAT *, int lit);
+void picosat_set_less_important_lit (PicoSAT *, int lit);
+
+/* Allows to print to internal 'out' file from client.
+ */
+void picosat_message (PicoSAT *, int verbosity_level, const char * fmt, ...);
+
+/* Set a seed for the random number generator. The random number generator
+ * is currently just used for generating random decisions. In our
+ * experiments having random decisions did not really help on industrial
+ * examples, but was rather helpful to randomize the solver in order to
+ * do proper benchmarking of different internal parameter sets.
+ */
+void picosat_set_seed (PicoSAT *, unsigned random_number_generator_seed);
+
+/* If you ever want to extract cores or proof traces with the current
+ * instance of PicoSAT initialized with 'picosat_init', then make sure to
+ * call 'picosat_enable_trace_generation' right after 'picosat_init'. This
+ * is not necessary if you only use 'picosat_set_incremental_rup_file'.
+ *
+ * NOTE, trace generation code is not necessarily included, e.g. if you
+ * configure PicoSAT with full optimzation as './configure.sh -O' or with
+
+ * you do not get any results by trying to generate traces.
+ *
+ * The return value is non-zero if code for generating traces is included
+ * and it is zero if traces can not be generated.
+ */
+int picosat_enable_trace_generation (PicoSAT *);
+
+/* You can dump proof traces in RUP format incrementally even without
+ * keeping the proof trace in memory. The advantage is a reduction of
+ * memory usage, but the dumped clauses do not necessarily belong to the
+ * clausal core. Beside the file the additional parameters denotes the
+ * maximal number of variables and the number of original clauses.
+ */
+void picosat_set_incremental_rup_file (PicoSAT *, FILE * file, int m, int n);
+
+/* Save original clauses for 'picosat_deref_partial'. See comments to that
+ * function further down.
+ */
+void picosat_save_original_clauses (PicoSAT *);
+
+/* Add a call back which is checked regularly to notify the SAT solver
+ * to terminate earlier. This is useful for setting external time limits
+ * or terminate early in say a portfolio style parallel SAT solver.
+ */
+void picosat_set_interrupt (PicoSAT *,
+ void * external_state,
+ int (*interrupted)(void * external_state));
+
+/*------------------------------------------------------------------------*/
+/* This function returns the next available unused variable index and
+ * allocates a variable for it even though this variable does not occur as
+ * assumption, nor in a clause or any other constraints. In future calls to
+ * 'picosat_sat', 'picosat_deref' and particularly for 'picosat_changed',
+ * this variable is treated as if it had been used.
+ */
+int picosat_inc_max_var (PicoSAT *);
+
+/*------------------------------------------------------------------------*/
+/* Push and pop semantics for PicoSAT. 'picosat_push' opens up a new
+ * context. All clauses added in this context are attached to it and
+ * discarded when the context is closed with 'picosat_pop'. It is also
+ * possible to nest contexts.
+ *
+ * The current implementation uses a new internal variable for each context.
+ * However, the indices for these internal variables are shared with
+ * ordinary external variables. This means that after any call to
+ * 'picosat_push', new variable indices should be obtained with
+ * 'picosat_inc_max_var' and not just by incrementing the largest variable
+ * index used so far.
+ *
+ * The return value is the index of the literal that assumes this context.
+ * This literal can only be used for 'picosat_failed_context' otherwise
+ * it will lead to an API usage error.
+ */
+int picosat_push (PicoSAT *);
+
+/* This is as 'picosat_failed_assumption', but only for internal variables
+ * generated by 'picosat_push'.
+ */
+int picosat_failed_context (PicoSAT *, int lit);
+
+/* Returns the literal that assumes the current context or zero if the
+ * outer context has been reached.
+ */
+int picosat_context (PicoSAT *);
+
+/* Closes the current context and recycles the literal generated for
+ * assuming this context. The return value is the literal for the new
+ * outer context or zero if the outer most context has been reached.
+ */
+int picosat_pop (PicoSAT *);
+
+/* Force immmediate removal of all satisfied clauses and clauses that are
+ * added or generated in closed contexts. This function is called
+ * internally if enough units are learned or after a certain number of
+ * contexts have been closed. This number is fixed at compile time
+ * and defined as MAXCILS in 'picosat.c'.
+ *
+ * Note that learned clauses which only involve outer contexts are kept.
+ */
+void picosat_simplify (PicoSAT *);
+
+/*------------------------------------------------------------------------*/
+/* If you know a good estimate on how many variables you are going to use
+ * then calling this function before adding literals will result in less
+ * resizing of the variable table. But this is just a minor optimization.
+ * Beside exactly allocating enough variables it has the same effect as
+ * calling 'picosat_inc_max_var'.
+ */
+void picosat_adjust (PicoSAT *, int max_idx);
+
+/*------------------------------------------------------------------------*/
+/* Statistics.
+ */
+int picosat_variables (PicoSAT *); /* p cnf <m> n */
+int picosat_added_original_clauses (PicoSAT *); /* p cnf m <n> */
+size_t picosat_max_bytes_allocated (PicoSAT *);
+double picosat_time_stamp (void); /* ... in process */
+void picosat_stats (PicoSAT *); /* > output file */
+unsigned long long picosat_propagations (PicoSAT *); /* #propagations */
+unsigned long long picosat_decisions (PicoSAT *); /* #decisions */
+unsigned long long picosat_visits (PicoSAT *); /* #visits */
+
+/* The time spent in calls to the library or in 'picosat_sat' respectively.
+ * The former is returned if, right after initialization
+ * 'picosat_measure_all_calls' is called.
+ */
+double picosat_seconds (PicoSAT *);
+
+/*------------------------------------------------------------------------*/
+/* Add a literal of the next clause. A zero terminates the clause. The
+ * solver is incremental. Adding a new literal will reset the previous
+ * assignment. The return value is the original clause index to which
+ * this literal respectively the trailing zero belong starting at 0.
+ */
+int picosat_add (PicoSAT *, int lit);
+
+/* As the previous function, but allows to add a full clause at once with an
+ * at compiled time known size. The list of argument literals has to be
+ * terminated with a zero literal. Literals beyond the first zero literal
+ * are discarded.
+ */
+int picosat_add_arg (PicoSAT *, ...);
+
+/* As the previous function but with an at compile time unknown size.
+ */
+int picosat_add_lits (PicoSAT *, int * lits);
+
+/* Print the CNF to the given file in DIMACS format.
+ */
+void picosat_print (PicoSAT *, FILE *);
+
+/* You can add arbitrary many assumptions before the next 'picosat_sat'
+ * call. This is similar to the using assumptions in MiniSAT, except that
+ * for PicoSAT you do not have to collect all your assumptions in a vector
+ * yourself. In PicoSAT you can add one after the other, to be used in the
+ * next call to 'picosat_sat'.
+ *
+ * These assumptions can be interpreted as adding unit clauses with those
+ * assumptions as literals. However these assumption clauses are only valid
+ * for exactly the next call to 'picosat_sat', and will be removed
+ * afterwards, e.g. in following future calls to 'picosat_sat' after the
+ * next 'picosat_sat' call, unless they are assumed again trough
+ * 'picosat_assume'.
+ *
+ * More precisely, assumptions actually remain valid even after the next
+ * call to 'picosat_sat' has returned. Valid means they remain 'assumed'
+ * internally until a call to 'picosat_add', 'picosat_assume', or a second
+ * 'picosat_sat', following the first 'picosat_sat'. The reason for keeping
+ * them valid is to allow 'picosat_failed_assumption' to return correct
+ * values.
+ *
+ * Example:
+ *
+ * picosat_assume (1); // assume unit clause '1 0'
+ * picosat_assume (-2); // additionally assume clause '-2 0'
+ * res = picosat_sat (1000); // assumes 1 and -2 to hold
+ * // 1000 decisions max.
+ *
+ * if (res == PICOSAT_UNSATISFIABLE)
+ * {
+ * if (picosat_failed_assumption (1))
+ * // unit clause '1 0' was necessary to derive UNSAT
+ *
+ * if (picosat_failed_assumption (-2))
+ * // unit clause '-2 0' was necessary to derive UNSAT
+ *
+ * // at least one but also both could be necessary
+ *
+ * picosat_assume (17); // previous assumptions are removed
+ * // now assume unit clause '17 0' for
+ * // the next call to 'picosat_sat'
+ *
+ * // adding a new clause, actually the first literal of
+ * // a clause would also make the assumptions used in the previous
+ * // call to 'picosat_sat' invalid.
+ *
+ * // The first two assumptions above are not assumed anymore. Only
+ * // the assumptions, since the last call to 'picosat_sat' returned
+ * // are assumed, e.g. the unit clause '17 0'.
+ *
+ * res = picosat_sat (-1);
+ * }
+ * else if (res == PICOSAT_SATISFIABLE)
+ * {
+ * // now the assignment is valid and we can call 'picosat_deref'
+ *
+ * assert (picosat_deref (1) == 1));
+ * assert (picosat_deref (-2) == 1));
+ *
+ * val = picosat_deref (15);
+ *
+ * // previous two assumptions are still valid
+ *
+ * // would become invalid if 'picosat_add' or 'picosat_assume' is
+ * // called here, but we immediately call 'picosat_sat'. Now when
+ * // entering 'picosat_sat' the solver knows that the previous call
+ * // returned SAT and it can safely reset the previous assumptions
+ *
+ * res = picosat_sat (-1);
+ * }
+ * else
+ * {
+ * assert (res == PICOSAT_UNKNOWN);
+ *
+ * // assumptions valid, but assignment invalid
+ * // except for top level assigned literals which
+ * // necessarily need to have this value if the formula is SAT
+ *
+ * // as above the solver nows that the previous call returned UNKWOWN
+ * // and will before doing anything else reset assumptions
+ *
+ * picosat_sat (-1);
+ * }
+ */
+void picosat_assume (PicoSAT *, int lit);
+
+/*------------------------------------------------------------------------*/
+/* This is an experimental feature for handling 'all different constraints'
+ * (ADC). Currently only one global ADC can be handled. The bit-width of
+ * all the bit-vectors entered in this ADC (stored in 'all different
+ * objects' or ADOs) has to be identical.
+ *
+ * TODO: also handle top level assigned literals here.
+ */
+void picosat_add_ado_lit (PicoSAT *, int);
+
+/*------------------------------------------------------------------------*/
+/* Call the main SAT routine. A negative decision limit sets no limit on
+ * the number of decisions. The return values are as above, e.g.
+ * 'PICOSAT_UNSATISFIABLE', 'PICOSAT_SATISFIABLE', or 'PICOSAT_UNKNOWN'.
+ */
+int picosat_sat (PicoSAT *, int decision_limit);
+
+/* As alternative to a decision limit you can use the number of propagations
+ * as limit. This is more linearly related to execution time. This has to
+ * be called after 'picosat_init' and before 'picosat_sat'.
+ */
+void picosat_set_propagation_limit (PicoSAT *, unsigned long long limit);
+
+/* Return last result of calling 'picosat_sat' or '0' if not called.
+ */
+int picosat_res (PicoSAT *);
+
+/* After 'picosat_sat' was called and returned 'PICOSAT_SATISFIABLE', then
+ * the satisfying assignment can be obtained by 'dereferencing' literals.
+ * The value of the literal is return as '1' for 'true', '-1' for 'false'
+ * and '0' for an unknown value.
+ */
+int picosat_deref (PicoSAT *, int lit);
+
+/* Same as before but just returns true resp. false if the literals is
+ * forced to this assignment at the top level. This function does not
+ * require that 'picosat_sat' was called and also does not internally reset
+ * incremental usage.
+ */
+int picosat_deref_toplevel (PicoSAT *, int lit);
+
+/* After 'picosat_sat' was called and returned 'PICOSAT_SATISFIABLE' a
+ * partial satisfying assignment can be obtained as well. It satisfies all
+ * original clauses. The value of the literal is return as '1' for 'true',
+ * '-1' for 'false' and '0' for an unknown value. In order to make this
+ * work all original clauses have to be saved internally, which has to be
+ * enabled by 'picosat_save_original_clauses' right after initialization.
+ */
+int picosat_deref_partial (PicoSAT *, int lit);
+
+/* Returns non zero if the CNF is unsatisfiable because an empty clause was
+ * added or derived.
+ */
+int picosat_inconsistent (PicoSAT *);
+
+/* Returns non zero if the literal is a failed assumption, which is defined
+ * as an assumption used to derive unsatisfiability. This is as accurate as
+ * generating core literals, but still of course is an overapproximation of
+ * the set of assumptions really necessary. The technique does not need
+ * clausal core generation nor tracing to be enabled and thus can be much
+ * more effective. The function can only be called as long the current
+ * assumptions are valid. See 'picosat_assume' for more details.
+ */
+int picosat_failed_assumption (PicoSAT *, int lit);
+
+/* Returns a zero terminated list of failed assumption in the last call to
+ * 'picosat_sat'. The pointer is valid until the next call to
+ * 'picosat_sat' or 'picosat_failed_assumptions'. It only makes sense if the
+ * last call to 'picosat_sat' returned 'PICOSAT_UNSATISFIABLE'.
+ */
+const int * picosat_failed_assumptions (PicoSAT *);
+
+/* Returns a zero terminated minimized list of failed assumption for the last
+ * call to 'picosat_sat'. The pointer is valid until the next call to this
+ * function or 'picosat_sat' or 'picosat_mus_assumptions'. It only makes sense
+ * if the last call to 'picosat_sat' returned 'PICOSAT_UNSATISFIABLE'.
+ *
+ * The call back function is called for all successful simplification
+ * attempts. The first argument of the call back function is the state
+ * given as first argument to 'picosat_mus_assumptions'. The second
+ * argument to the call back function is the new reduced list of failed
+ * assumptions.
+ *
+ * This function will call 'picosat_assume' and 'picosat_sat' internally but
+ * before returning reestablish a proper UNSAT state, e.g.
+ * 'picosat_failed_assumption' will work afterwards as expected.
+ *
+ * The last argument if non zero fixes assumptions. In particular, if an
+ * assumption can not be removed it is permanently assigned true, otherwise
+ * if it turns out to be redundant it is permanently assumed to be false.
+ */
+const int * picosat_mus_assumptions (PicoSAT *, void *,
+ void(*)(void*,const int*),int);
+
+/* Compute one maximal subset of satisfiable assumptions. You need to set
+ * the assumptions, call 'picosat_sat' and check for 'picosat_inconsistent',
+ * before calling this function. The result is a zero terminated array of
+ * assumptions that consistently can be asserted at the same time. Before
+ * returing the library 'reassumes' all assumptions.
+ *
+ * It could be beneficial to set the default phase of assumptions
+ * to true (positive). This can speed up the computation.
+ */
+const int * picosat_maximal_satisfiable_subset_of_assumptions (PicoSAT *);
+
+/* This function assumes that you have set up all assumptions with
+ * 'picosat_assume'. Then it calls 'picosat_sat' internally unless the
+ * formula is already inconsistent without assumptions, i.e. it contains
+ * the empty clause. After that it extracts a maximal satisfiable subset of
+ * assumptions.
+ *
+ * The result is a zero terminated maximal subset of consistent assumptions
+ * or a zero pointer if the formula contains the empty clause and thus no
+ * more maximal consistent subsets of assumptions can be extracted. In the
+ * first case, before returning, a blocking clause is added, that rules out
+ * the result for the next call.
+ *
+ * NOTE: adding the blocking clause changes the CNF.
+ *
+ * So the following idiom
+ *
+ * const int * mss;
+ * picosat_assume (a1);
+ * picosat_assume (a2);
+ * picosat_assume (a3);
+ * picosat_assume (a4);
+ * while ((mss = picosat_next_maximal_satisfiable_subset_of_assumptions ()))
+ * process_mss (mss);
+ *
+ * can be used to iterate over all maximal consistent subsets of
+ * the set of assumptions {a1,a2,a3,a4}.
+ *
+ * It could be beneficial to set the default phase of assumptions
+ * to true (positive). This might speed up the computation.
+ */
+const int *
+picosat_next_maximal_satisfiable_subset_of_assumptions (PicoSAT *);
+
+/* Similarly we can iterate over all minimal correcting assumption sets.
+ * See the CAMUS literature [M. Liffiton, K. Sakallah JAR 2008].
+ *
+ * The result contains each assumed literal only once, even if it
+ * was assumed multiple times (in contrast to the maximal consistent
+ * subset functions above).
+ *
+ * It could be beneficial to set the default phase of assumptions
+ * to true (positive). This might speed up the computation.
+ */
+const int *
+picosat_next_minimal_correcting_subset_of_assumptions (PicoSAT *);
+
+/* Compute the union of all minmal correcting sets, which is called
+ * the 'high level union of all minimal unsatisfiable subset sets'
+ * or 'HUMUS' in our papers.
+ *
+ * It uses 'picosat_next_minimal_correcting_subset_of_assumptions' and
+ * the same notes and advices apply. In particular, this implies that
+ * after calling the function once, the current CNF becomes inconsistent,
+ * and PicoSAT has to be reset. So even this function internally uses
+ * PicoSAT incrementally, it can not be used incrementally itself at this
+ * point.
+ *
+ * The 'callback' can be used for progress logging and is called after
+ * each extracted minimal correcting set if non zero. The 'nhumus'
+ * parameter of 'callback' denotes the number of assumptions found to be
+ * part of the HUMUS sofar.
+ */
+const int *
+picosat_humus (PicoSAT *,
+ void (*callback)(void * state, int nmcs, int nhumus),
+ void * state);
+
+/*------------------------------------------------------------------------*/
+/* Assume that a previous call to 'picosat_sat' in incremental usage,
+ * returned 'SATISFIABLE'. Then a couple of clauses and optionally new
+ * variables were added (a new variable is a variable that has an index
+ * larger then the maximum variable added so far). The next call to
+ * 'picosat_sat' also returns 'SATISFIABLE'. If this function
+ * 'picosat_changed' returns '0', then the assignment to the old variables
+ * is guaranteed to not have changed. Otherwise it might have changed.
+ *
+ * The return value to this function is only valid until new clauses are
+ * added through 'picosat_add', an assumption is made through
+ * 'picosat_assume', or again 'picosat_sat' is called. This is the same
+ * assumption as for 'picosat_deref'.
+ *
+ * TODO currently this function might also return a non zero value even if
+ * the old assignment did not change, because it only checks whether the
+ * assignment of at least one old variable was flipped at least once during
+ * the search. In principle it should be possible to be exact in the other
+ * direction as well by using a counter of variables that have an odd number
+ * of flips. But this is not implemented yet.
+ */
+int picosat_changed (PicoSAT *);
+
+/*------------------------------------------------------------------------*/
+/* The following six functions internally extract the variable and clausal
+ * core and thus require trace generation to be enabled with
+ * 'picosat_enable_trace_generation' right after calling 'picosat_init'.
+ *
+ * TODO: using these functions in incremental mode with failed assumptions
+ * has only been tested for 'picosat_corelit' thoroughly. The others
+ * probably only work in non-incremental mode or without using
+ * 'picosat_assume'.
+ */
+
+/* This function determines whether the i'th added original clause is in the
+ * core. The 'i' is the return value of 'picosat_add', which starts at zero
+ * and is incremented by one after a original clause is added (that is after
+ * 'picosat_add (0)'). For the index 'i' the following has to hold:
+ *
+ * 0 <= i < picosat_added_original_clauses ()
+ */
+int picosat_coreclause (PicoSAT *, int i);
+
+/* This function gives access to the variable core, which is made up of the
+ * variables that were resolved in deriving the empty clause.
+ */
+int picosat_corelit (PicoSAT *, int lit);
+
+/* Write the clauses that were used in deriving the empty clause to a file
+ * in DIMACS format.
+ */
+void picosat_write_clausal_core (PicoSAT *, FILE * core_file);
+
+/* Write a proof trace in TraceCheck format to a file.
+ */
+void picosat_write_compact_trace (PicoSAT *, FILE * trace_file);
+void picosat_write_extended_trace (PicoSAT *, FILE * trace_file);
+
+/* Write a RUP trace to a file. This trace file contains only the learned
+ * core clauses while this is not necessarily the case for the RUP file
+ * obtained with 'picosat_set_incremental_rup_file'.
+ */
+void picosat_write_rup_trace (PicoSAT *, FILE * trace_file);
+
+/*------------------------------------------------------------------------*/
+/* Keeping the proof trace around is not necessary if an over-approximation
+ * of the core is enough. A literal is 'used' if it was involved in a
+ * resolution to derive a learned clause. The core literals are necessarily
+ * a subset of the 'used' literals.
+ */
+
+int picosat_usedlit (PicoSAT *, int lit);
+/*------------------------------------------------------------------------*/
+#endif
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v4 02/12] kconfig: Add picosat.c (1/3)
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 01/12] kconfig: Add picosat.h Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-08-12 8:41 ` Masahiro Yamada
2024-07-10 6:52 ` [PATCH v4 03/12] kconfig: Add picosat.c (2/3) Ole Schuerks
` (9 subsequent siblings)
11 siblings, 1 reply; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
PicoSAT is the SAT solver used in this project. picosat.c is the actual
SAT solver. Since the file is too big for a single patch, it needs to be
split up. This patch contains the first part of the file.
Signed-off-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/picosat.c | 3000 +++++++++++++++++++++++++++++++++++++
1 file changed, 3000 insertions(+)
create mode 100644 scripts/kconfig/picosat.c
diff --git a/scripts/kconfig/picosat.c b/scripts/kconfig/picosat.c
new file mode 100644
index 000000000000..872a38b335c4
--- /dev/null
+++ b/scripts/kconfig/picosat.c
@@ -0,0 +1,3000 @@
+/****************************************************************************
+Copyright (c) 2006 - 2015, Armin Biere, Johannes Kepler University.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include "picosat.h"
+
+/* By default code for 'all different constraints' is disabled, since 'NADC'
+ * is defined.
+ */
+#define NADC
+
+/* By default we enable failed literals, since 'NFL' is undefined.
+ *
+#define NFL
+ */
+
+/* By default we 'detach satisfied (large) clauses', e.g. NDSC undefined.
+ *
+#define NDSC
+ */
+
+/* Do not use luby restart schedule instead of inner/outer.
+ *
+#define NLUBY
+ */
+
+/* Enabling this define, will use gnuplot to visualize how the scores evolve.
+ *
+#define VISCORES
+ */
+#ifdef VISCORES
+// #define WRITEGIF /* ... to generate a video */
+#endif
+
+#ifdef VISCORES
+#ifndef WRITEGIF
+#include <unistd.h> /* for 'usleep' */
+#endif
+#endif
+
+#ifdef RCODE
+#include <R.h>
+#endif
+
+#define MINRESTART 100 /* minimum restart interval */
+#define MAXRESTART 1000000 /* maximum restart interval */
+#define RDECIDE 1000 /* interval of random decisions */
+#define FRESTART 110 /* restart increase factor in percent */
+#define FREDUCE 110 /* reduce increase factor in percent */
+#define FREDADJ 121 /* reduce increase adjustment factor */
+#define MAXCILS 10 /* maximal number of unrecycled internals */
+#define FFLIPPED 10000 /* flipped reduce factor */
+#define FFLIPPEDPREC 10000000/* flipped reduce factor precision */
+#define INTERRUPTLIM 1024 /* check interrupt after that many decisions */
+
+#ifndef TRACE
+#define NO_BINARY_CLAUSES /* store binary clauses more compactly */
+#endif
+
+/* For debugging purposes you may want to define 'LOGGING', which actually
+ * can be enforced by using './configure.sh --log'.
+ */
+#ifdef LOGGING
+#define LOG(code) do { code; } while (0)
+#else
+#define LOG(code) do { } while (0)
+#endif
+#define NOLOG(code) do { } while (0) /* log exception */
+#define ONLYLOG(code) do { code; } while (0) /* force logging */
+
+#define FALSE ((Val)-1)
+#define UNDEF ((Val)0)
+#define TRUE ((Val)1)
+
+#define COMPACT_TRACECHECK_TRACE_FMT 0
+#define EXTENDED_TRACECHECK_TRACE_FMT 1
+#define RUP_TRACE_FMT 2
+
+#define NEWN(p,n) do { (p) = new (ps, sizeof (*(p)) * (n)); } while (0)
+#define CLRN(p,n) do { memset ((p), 0, sizeof (*(p)) * (n)); } while (0)
+#define CLR(p) CLRN(p,1)
+#define DELETEN(p,n) \
+ do { delete (ps, p, sizeof (*(p)) * (n)); (p) = 0; } while (0)
+
+#define RESIZEN(p,old_num,new_num) \
+ do { \
+ size_t old_size = sizeof (*(p)) * (old_num); \
+ size_t new_size = sizeof (*(p)) * (new_num); \
+ (p) = resize (ps, (p), old_size, new_size) ; \
+ } while (0)
+
+#define ENLARGE(start,head,end) \
+ do { \
+ unsigned old_num = (ptrdiff_t)((end) - (start)); \
+ size_t new_num = old_num ? (2 * old_num) : 1; \
+ unsigned count = (head) - (start); \
+ assert ((start) <= (end)); \
+ RESIZEN((start),old_num,new_num); \
+ (head) = (start) + count; \
+ (end) = (start) + new_num; \
+ } while (0)
+
+#define NOTLIT(l) (ps->lits + (1 ^ ((l) - ps->lits)))
+
+#define LIT2IDX(l) ((ptrdiff_t)((l) - ps->lits) / 2)
+#define LIT2IMPLS(l) (ps->impls + (ptrdiff_t)((l) - ps->lits))
+#define LIT2INT(l) ((int)(LIT2SGN(l) * LIT2IDX(l)))
+#define LIT2SGN(l) (((ptrdiff_t)((l) - ps->lits) & 1) ? -1 : 1)
+#define LIT2VAR(l) (ps->vars + LIT2IDX(l))
+#define LIT2HTPS(l) (ps->htps + (ptrdiff_t)((l) - ps->lits))
+#define LIT2JWH(l) (ps->jwh + ((l) - ps->lits))
+
+#ifndef NDSC
+#define LIT2DHTPS(l) (ps->dhtps + (ptrdiff_t)((l) - ps->lits))
+#endif
+
+#ifdef NO_BINARY_CLAUSES
+typedef uintptr_t Wrd;
+#define ISLITREASON(C) (1&(Wrd)C)
+#define LIT2REASON(L) \
+ (assert (L->val==TRUE), ((Cls*)(1 + (2*(L - ps->lits)))))
+#define REASON2LIT(C) ((Lit*)(ps->lits + ((Wrd)C)/2))
+#endif
+
+#define ENDOFCLS(c) ((void*)((Lit**)(c)->lits + (c)->size))
+
+#define SOC ((ps->oclauses == ps->ohead) ? ps->lclauses : ps->oclauses)
+#define EOC (ps->lhead)
+#define NXC(p) (((p) + 1 == ps->ohead) ? ps->lclauses : (p) + 1)
+
+#define OIDX2IDX(idx) (2 * ((idx) + 1))
+#define LIDX2IDX(idx) (2 * (idx) + 1)
+
+#define ISLIDX(idx) ((idx)&1)
+
+#define IDX2OIDX(idx) (assert(!ISLIDX(idx)), (idx)/2 - 1)
+#define IDX2LIDX(idx) (assert(ISLIDX(idx)), (idx)/2)
+
+#define EXPORTIDX(idx) \
+ ((ISLIDX(idx) ? (IDX2LIDX (idx) + (ps->ohead - ps->oclauses)) : IDX2OIDX(idx)) + 1)
+
+#define IDX2CLS(i) \
+ (assert(i), (ISLIDX(i) ? ps->lclauses : ps->oclauses)[(i)/2 - !ISLIDX(i)])
+
+#define IDX2ZHN(i) (assert(i), (ISLIDX(i) ? ps->zhains[(i)/2] : 0))
+
+#define CLS2TRD(c) (((Trd*)(c)) - 1)
+#define CLS2IDX(c) ((((Trd*)(c)) - 1)->idx)
+
+#define CLS2ACT(c) \
+ ((Act*)((assert((c)->learned)),assert((c)->size>2),ENDOFCLS(c)))
+
+#define VAR2LIT(v) (ps->lits + 2 * ((v) - ps->vars))
+#define VAR2RNK(v) (ps->rnks + ((v) - ps->vars))
+
+#define RNK2LIT(r) (ps->lits + 2 * ((r) - ps->rnks))
+#define RNK2VAR(r) (ps->vars + ((r) - ps->rnks))
+
+#define BLK_FILL_BYTES 8
+#define SIZE_OF_BLK (sizeof (Blk) - BLK_FILL_BYTES)
+
+#define PTR2BLK(void_ptr) \
+ ((void_ptr) ? (Blk*)(((char*)(void_ptr)) - SIZE_OF_BLK) : 0)
+
+#define AVERAGE(a,b) ((b) ? (((double)a) / (double)(b)) : 0.0)
+#define PERCENT(a,b) (100.0 * AVERAGE(a,b))
+
+#ifndef RCODE
+#define ABORT(msg) \
+ do { \
+ fputs ("*** picosat: " msg "\n", stderr); \
+ abort (); \
+ } while (0)
+#else
+#define ABORT(msg) \
+ do { \
+ Rf_error (msg); \
+ } while (0)
+#endif
+
+#define ABORTIF(cond,msg) \
+ do { \
+ if (!(cond)) break; \
+ ABORT (msg); \
+ } while (0)
+
+#define ZEROFLT (0x00000000u)
+#define EPSFLT (0x00000001u)
+#define INFFLT (0xffffffffu)
+
+#define FLTCARRY (1u << 25)
+#define FLTMSB (1u << 24)
+#define FLTMAXMANTISSA (FLTMSB - 1)
+
+#define FLTMANTISSA(d) ((d) & FLTMAXMANTISSA)
+#define FLTEXPONENT(d) ((int)((d) >> 24) - 128)
+
+#define FLTMINEXPONENT (-128)
+#define FLTMAXEXPONENT (127)
+
+#define CMPSWAPFLT(a,b) \
+ do { \
+ Flt tmp; \
+ if (((a) < (b))) \
+ { \
+ tmp = (a); \
+ (a) = (b); \
+ (b) = tmp; \
+ } \
+ } while (0)
+
+#define UNPACKFLT(u,m,e) \
+ do { \
+ (m) = FLTMANTISSA(u); \
+ (e) = FLTEXPONENT(u); \
+ (m) |= FLTMSB; \
+ } while (0)
+
+#define INSERTION_SORT_LIMIT 10
+
+#define SORTING_SWAP(T,p,q) \
+do { \
+ T tmp = *(q); \
+ *(q) = *(p); \
+ *(p) = tmp; \
+} while (0)
+
+#define SORTING_CMP_SWAP(T,cmp,p,q) \
+do { \
+ if ((cmp) (ps, *(p), *(q)) > 0) \
+ SORTING_SWAP (T, p, q); \
+} while(0)
+
+#define QUICKSORT_PARTITION(T,cmp,a,l,r) \
+do { \
+ T pivot; \
+ int j; \
+ i = (l) - 1; /* result in 'i' */ \
+ j = (r); \
+ pivot = (a)[j]; \
+ for (;;) \
+ { \
+ while ((cmp) (ps, (a)[++i], pivot) < 0) \
+ ; \
+ while ((cmp) (ps, pivot, (a)[--j]) < 0) \
+ if (j == (l)) \
+ break; \
+ if (i >= j) \
+ break; \
+ SORTING_SWAP (T, (a) + i, (a) + j); \
+ } \
+ SORTING_SWAP (T, (a) + i, (a) + (r)); \
+} while(0)
+
+#define QUICKSORT(T,cmp,a,n) \
+do { \
+ int l = 0, r = (n) - 1, m, ll, rr, i; \
+ assert (ps->ihead == ps->indices); \
+ if (r - l <= INSERTION_SORT_LIMIT) \
+ break; \
+ for (;;) \
+ { \
+ m = (l + r) / 2; \
+ SORTING_SWAP (T, (a) + m, (a) + r - 1); \
+ SORTING_CMP_SWAP (T, cmp, (a) + l, (a) + r - 1); \
+ SORTING_CMP_SWAP (T, cmp, (a) + l, (a) + r); \
+ SORTING_CMP_SWAP (T, cmp, (a) + r - 1, (a) + r); \
+ QUICKSORT_PARTITION (T, cmp, (a), l + 1, r - 1); \
+ if (i - l < r - i) \
+ { \
+ ll = i + 1; \
+ rr = r; \
+ r = i - 1; \
+ } \
+ else \
+ { \
+ ll = l; \
+ rr = i - 1; \
+ l = i + 1; \
+ } \
+ if (r - l > INSERTION_SORT_LIMIT) \
+ { \
+ assert (rr - ll > INSERTION_SORT_LIMIT); \
+ if (ps->ihead == ps->eoi) \
+ ENLARGE (ps->indices, ps->ihead, ps->eoi); \
+ *ps->ihead++ = ll; \
+ if (ps->ihead == ps->eoi) \
+ ENLARGE (ps->indices, ps->ihead, ps->eoi); \
+ *ps->ihead++ = rr; \
+ } \
+ else if (rr - ll > INSERTION_SORT_LIMIT) \
+ { \
+ l = ll; \
+ r = rr; \
+ } \
+ else if (ps->ihead > ps->indices) \
+ { \
+ r = *--ps->ihead; \
+ l = *--ps->ihead; \
+ } \
+ else \
+ break; \
+ } \
+} while (0)
+
+#define INSERTION_SORT(T,cmp,a,n) \
+do { \
+ T pivot; \
+ int l = 0, r = (n) - 1, i, j; \
+ for (i = r; i > l; i--) \
+ SORTING_CMP_SWAP (T, cmp, (a) + i - 1, (a) + i); \
+ for (i = l + 2; i <= r; i++) \
+ { \
+ j = i; \
+ pivot = (a)[i]; \
+ while ((cmp) (ps, pivot, (a)[j - 1]) < 0) \
+ { \
+ (a)[j] = (a)[j - 1]; \
+ j--; \
+ } \
+ (a)[j] = pivot; \
+ } \
+} while (0)
+
+#ifdef NDEBUG
+#define CHECK_SORTED(cmp,a,n) do { } while(0)
+#else
+#define CHECK_SORTED(cmp,a,n) \
+do { \
+ int i; \
+ for (i = 0; i < (n) - 1; i++) \
+ assert ((cmp) (ps, (a)[i], (a)[i + 1]) <= 0); \
+} while(0)
+#endif
+
+#define SORT(T,cmp,a,n) \
+do { \
+ T * aa = (a); \
+ int nn = (n); \
+ QUICKSORT (T, cmp, aa, nn); \
+ INSERTION_SORT (T, cmp, aa, nn); \
+ assert (ps->ihead == ps->indices); \
+ CHECK_SORTED (cmp, aa, nn); \
+} while (0)
+
+#define WRDSZ (sizeof (long) * 8)
+
+#ifdef RCODE
+#define fprintf(...) do { } while (0)
+#define vfprintf(...) do { } while (0)
+#define fputs(...) do { } while (0)
+#define fputc(...) do { } while (0)
+#endif
+
+typedef unsigned Flt; /* 32 bit deterministic soft float */
+typedef Flt Act; /* clause and variable activity */
+typedef struct Blk Blk; /* allocated memory block */
+typedef struct Cls Cls; /* clause */
+typedef struct Lit Lit; /* literal */
+typedef struct Rnk Rnk; /* variable to score mapping */
+typedef signed char Val; /* TRUE, UNDEF, FALSE */
+typedef struct Var Var; /* variable */
+#ifdef TRACE
+typedef struct Trd Trd; /* trace data for clauses */
+typedef struct Zhn Zhn; /* compressed chain (=zain) data */
+typedef unsigned char Znt; /* compressed antecedent data */
+#endif
+
+#ifdef NO_BINARY_CLAUSES
+typedef struct Ltk Ltk;
+
+struct Ltk
+{
+ Lit ** start;
+ unsigned count : WRDSZ == 32 ? 27 : 32;
+ unsigned ldsize : WRDSZ == 32 ? 5 : 32;
+};
+#endif
+
+struct Lit
+{
+ Val val;
+};
+
+struct Var
+{
+ unsigned mark : 1; /*bit 1*/
+ unsigned resolved : 1; /*bit 2*/
+ unsigned phase : 1; /*bit 3*/
+ unsigned assigned : 1; /*bit 4*/
+ unsigned used : 1; /*bit 5*/
+ unsigned failed : 1; /*bit 6*/
+ unsigned internal : 1; /*bit 7*/
+ unsigned usedefphase : 1; /*bit 8*/
+ unsigned defphase : 1; /*bit 9*/
+ unsigned msspos : 1; /*bit 10*/
+ unsigned mssneg : 1; /*bit 11*/
+ unsigned humuspos : 1; /*bit 12*/
+ unsigned humusneg : 1; /*bit 13*/
+ unsigned partial : 1; /*bit 14*/
+#ifdef TRACE
+ unsigned core : 1; /*bit 15*/
+#endif
+ unsigned level;
+ Cls *reason;
+#ifndef NADC
+ Lit ** inado;
+ Lit ** ado;
+ Lit *** adotabpos;
+#endif
+};
+
+struct Rnk
+{
+ Act score;
+ unsigned pos : 30; /* 0 iff not on heap */
+ unsigned moreimportant : 1;
+ unsigned lessimportant : 1;
+};
+
+struct Cls
+{
+ unsigned size;
+
+ unsigned collect:1; /* bit 1 */
+ unsigned learned:1; /* bit 2 */
+ unsigned locked:1; /* bit 3 */
+ unsigned used:1; /* bit 4 */
+#ifndef NDEBUG
+ unsigned connected:1; /* bit 5 */
+#endif
+#ifdef TRACE
+ unsigned collected:1; /* bit 6 */
+ unsigned core:1; /* bit 7 */
+#endif
+
+#define LDMAXGLUE 25 /* 32 - 7 */
+#define MAXGLUE ((1<<LDMAXGLUE)-1)
+
+ unsigned glue:LDMAXGLUE;
+
+ Cls *next[2];
+ Lit *lits[2];
+};
+
+#ifdef TRACE
+struct Zhn
+{
+ unsigned ref:31;
+ unsigned core:1;
+ Znt * liz;
+ Znt znt[0];
+};
+
+struct Trd
+{
+ unsigned idx;
+ Cls cls[0];
+};
+#endif
+
+struct Blk
+{
+#ifndef NDEBUG
+ union
+ {
+ size_t size; /* this is what we really use */
+ void *as_two_ptrs[2]; /* 2 * sizeof (void*) alignment of data */
+ }
+ header;
+#endif
+ char data[BLK_FILL_BYTES];
+};
+
+enum State
+{
+ RESET = 0,
+ READY = 1,
+ SAT = 2,
+ UNSAT = 3,
+ UNKNOWN = 4,
+};
+
+enum Phase
+{
+ POSPHASE,
+ NEGPHASE,
+ JWLPHASE,
+ RNDPHASE,
+};
+
+struct PicoSAT
+{
+ enum State state;
+ enum Phase defaultphase;
+ int last_sat_call_result;
+
+ FILE *out;
+ char * prefix;
+ int verbosity;
+ int plain;
+ unsigned LEVEL;
+ unsigned max_var;
+ unsigned size_vars;
+
+ Lit *lits;
+ Var *vars;
+ Rnk *rnks;
+ Flt *jwh;
+ Cls **htps;
+#ifndef NDSC
+ Cls **dhtps;
+#endif
+#ifdef NO_BINARY_CLAUSES
+ Ltk *impls;
+ Cls impl, cimpl;
+ int implvalid, cimplvalid;
+#else
+ Cls **impls;
+#endif
+ Lit **trail, **thead, **eot, **ttail, ** ttail2;
+#ifndef NADC
+ Lit **ttailado;
+#endif
+ unsigned adecidelevel;
+ Lit **als, **alshead, **alstail, **eoals;
+ Lit **CLS, **clshead, **eocls;
+ int *rils, *rilshead, *eorils;
+ int *cils, *cilshead, *eocils;
+ int *fals, *falshead, *eofals;
+ int *mass, szmass;
+ int *mssass, szmssass;
+ int *mcsass, nmcsass, szmcsass;
+ int *humus, szhumus;
+ Lit *failed_assumption;
+ int extracted_all_failed_assumptions;
+ Rnk **heap, **hhead, **eoh;
+ Cls **oclauses, **ohead, **eoo; /* original clauses */
+ Cls **lclauses, **lhead, ** EOL; /* learned clauses */
+ int * soclauses, * sohead, * eoso; /* saved original clauses */
+ int saveorig;
+ int partial;
+#ifdef TRACE
+ int trace;
+ Zhn **zhains, **zhead, **eoz;
+ int ocore;
+#endif
+ FILE * rup;
+ int rupstarted;
+ int rupvariables;
+ int rupclauses;
+ Cls *mtcls;
+ Cls *conflict;
+ Lit **added, **ahead, **eoa;
+ Var **marked, **mhead, **eom;
+ Var **dfs, **dhead, **eod;
+ Cls **resolved, **rhead, **eor;
+ unsigned char *levels, *levelshead, *eolevels;
+ unsigned *dused, *dusedhead, *eodused;
+ unsigned char *buffer, *bhead, *eob;
+ Act vinc, lscore, ilvinc, ifvinc;
+#ifdef VISCORES
+ Act fvinc, nvinc;
+#endif
+ Act cinc, lcinc, ilcinc, fcinc;
+ unsigned srng;
+ size_t current_bytes;
+ size_t max_bytes;
+ size_t recycled;
+ double seconds, flseconds;
+ double entered;
+ unsigned nentered;
+ int measurealltimeinlib;
+ char *rline[2];
+ int szrline, RCOUNT;
+ double levelsum;
+ unsigned iterations;
+ int reports;
+ int lastrheader;
+ unsigned calls;
+ unsigned decisions;
+ unsigned restarts;
+ unsigned simps;
+ unsigned fsimplify;
+ unsigned isimplify;
+ unsigned reductions;
+ unsigned lreduce;
+ unsigned lreduceadjustcnt;
+ unsigned lreduceadjustinc;
+ unsigned lastreduceconflicts;
+ unsigned llocked; /* locked large learned clauses */
+ unsigned lrestart;
+#ifdef NLUBY
+ unsigned drestart;
+ unsigned ddrestart;
+#else
+ unsigned lubycnt;
+ unsigned lubymaxdelta;
+ int waslubymaxdelta;
+#endif
+ unsigned long long lsimplify;
+ unsigned long long propagations;
+ unsigned long long lpropagations;
+ unsigned fixed; /* top level assignments */
+#ifndef NFL
+ unsigned failedlits;
+ unsigned ifailedlits;
+ unsigned efailedlits;
+ unsigned flcalls;
+#ifdef STATS
+ unsigned flrounds;
+ unsigned long long flprops;
+ unsigned long long floopsed, fltried, flskipped;
+#endif
+ unsigned long long fllimit;
+ int simplifying;
+ Lit ** saved;
+ unsigned saved_size;
+#endif
+ unsigned conflicts;
+ unsigned contexts;
+ unsigned internals;
+ unsigned noclauses; /* current number large original clauses */
+ unsigned nlclauses; /* current number large learned clauses */
+ unsigned olits; /* current literals in large original clauses */
+ unsigned llits; /* current literals in large learned clauses */
+ unsigned oadded; /* added original clauses */
+ unsigned ladded; /* added learned clauses */
+ unsigned loadded; /* added original large clauses */
+ unsigned lladded; /* added learned large clauses */
+ unsigned addedclauses; /* oadded + ladded */
+ unsigned vused; /* used variables */
+ unsigned llitsadded; /* added learned literals */
+ unsigned long long visits;
+#ifdef STATS
+ unsigned loused; /* used large original clauses */
+ unsigned llused; /* used large learned clauses */
+ unsigned long long bvisits;
+ unsigned long long tvisits;
+ unsigned long long lvisits;
+ unsigned long long othertrue;
+ unsigned long long othertrue2;
+ unsigned long long othertruel;
+ unsigned long long othertrue2u;
+ unsigned long long othertruelu;
+ unsigned long long ltraversals;
+ unsigned long long traversals;
+#ifdef TRACE
+ unsigned long long antecedents;
+#endif
+ unsigned uips;
+ unsigned znts;
+ unsigned assumptions;
+ unsigned rdecisions;
+ unsigned sdecisions;
+ size_t srecycled;
+ size_t rrecycled;
+ unsigned long long derefs;
+#endif
+ unsigned minimizedllits;
+ unsigned nonminimizedllits;
+#ifndef NADC
+ Lit *** ados, *** hados, *** eados;
+ Lit *** adotab;
+ unsigned nadotab;
+ unsigned szadotab;
+ Cls * adoconflict;
+ unsigned adoconflicts;
+ unsigned adoconflictlimit;
+ int addingtoado;
+ int adodisabled;
+#endif
+ unsigned long long flips;
+#ifdef STATS
+ unsigned long long FORCED;
+ unsigned long long assignments;
+ unsigned inclreduces;
+ unsigned staticphasedecisions;
+ unsigned skippedrestarts;
+#endif
+ int * indices, * ihead, *eoi;
+ unsigned sdflips;
+
+ unsigned long long saved_flips;
+ unsigned saved_max_var;
+ unsigned min_flipped;
+
+ void * emgr;
+ picosat_malloc enew;
+ picosat_realloc eresize;
+ picosat_free edelete;
+
+ struct {
+ void * state;
+ int (*function) (void *);
+ } interrupt;
+
+#ifdef VISCORES
+ FILE * fviscores;
+#endif
+};
+
+typedef PicoSAT PS;
+
+static Flt
+packflt (unsigned m, int e)
+{
+ Flt res;
+ assert (m < FLTMSB);
+ assert (FLTMINEXPONENT <= e);
+ assert (e <= FLTMAXEXPONENT);
+ res = m | ((unsigned)(e + 128) << 24);
+ return res;
+}
+
+static Flt
+base2flt (unsigned m, int e)
+{
+ if (!m)
+ return ZEROFLT;
+
+ if (m < FLTMSB)
+ {
+ do
+ {
+ if (e <= FLTMINEXPONENT)
+ return EPSFLT;
+
+ e--;
+ m <<= 1;
+
+ }
+ while (m < FLTMSB);
+ }
+ else
+ {
+ while (m >= FLTCARRY)
+ {
+ if (e >= FLTMAXEXPONENT)
+ return INFFLT;
+
+ e++;
+ m >>= 1;
+ }
+ }
+
+ m &= ~FLTMSB;
+ return packflt (m, e);
+}
+
+static Flt
+addflt (Flt a, Flt b)
+{
+ unsigned ma, mb, delta;
+ int ea, eb;
+
+ CMPSWAPFLT (a, b);
+ if (!b)
+ return a;
+
+ UNPACKFLT (a, ma, ea);
+ UNPACKFLT (b, mb, eb);
+
+ assert (ea >= eb);
+ delta = ea - eb;
+ if (delta < 32) mb >>= delta; else mb = 0;
+ if (!mb)
+ return a;
+
+ ma += mb;
+ if (ma & FLTCARRY)
+ {
+ if (ea == FLTMAXEXPONENT)
+ return INFFLT;
+
+ ea++;
+ ma >>= 1;
+ }
+
+ assert (ma < FLTCARRY);
+ ma &= FLTMAXMANTISSA;
+
+ return packflt (ma, ea);
+}
+
+static Flt
+mulflt (Flt a, Flt b)
+{
+ unsigned ma, mb;
+ unsigned long long accu;
+ int ea, eb;
+
+ CMPSWAPFLT (a, b);
+ if (!b)
+ return ZEROFLT;
+
+ UNPACKFLT (a, ma, ea);
+ UNPACKFLT (b, mb, eb);
+
+ ea += eb;
+ ea += 24;
+ if (ea > FLTMAXEXPONENT)
+ return INFFLT;
+
+ if (ea < FLTMINEXPONENT)
+ return EPSFLT;
+
+ accu = ma;
+ accu *= mb;
+ accu >>= 24;
+
+ if (accu >= FLTCARRY)
+ {
+ if (ea == FLTMAXEXPONENT)
+ return INFFLT;
+
+ ea++;
+ accu >>= 1;
+
+ if (accu >= FLTCARRY)
+ return INFFLT;
+ }
+
+ assert (accu < FLTCARRY);
+ assert (accu & FLTMSB);
+
+ ma = accu;
+ ma &= ~FLTMSB;
+
+ return packflt (ma, ea);
+}
+
+static Flt
+ascii2flt (const char *str)
+{
+ Flt ten = base2flt (10, 0);
+ Flt onetenth = base2flt (26843546, -28);
+ Flt res = ZEROFLT, tmp, base;
+ const char *p = str;
+ int ch;
+
+ ch = *p++;
+
+ if (ch != '.')
+ {
+ if (!isdigit (ch))
+ return INFFLT; /* better abort ? */
+
+ res = base2flt (ch - '0', 0);
+
+ while ((ch = *p++))
+ {
+ if (ch == '.')
+ break;
+
+ if (!isdigit (ch))
+ return INFFLT; /* better abort? */
+
+ res = mulflt (res, ten);
+ tmp = base2flt (ch - '0', 0);
+ res = addflt (res, tmp);
+ }
+ }
+
+ if (ch == '.')
+ {
+ ch = *p++;
+ if (!isdigit (ch))
+ return INFFLT; /* better abort ? */
+
+ base = onetenth;
+ tmp = mulflt (base2flt (ch - '0', 0), base);
+ res = addflt (res, tmp);
+
+ while ((ch = *p++))
+ {
+ if (!isdigit (ch))
+ return INFFLT; /* better abort? */
+
+ base = mulflt (base, onetenth);
+ tmp = mulflt (base2flt (ch - '0', 0), base);
+ res = addflt (res, tmp);
+ }
+ }
+
+ return res;
+}
+
+#if defined(VISCORES)
+
+static double
+flt2double (Flt f)
+{
+ double res;
+ unsigned m;
+ int e, i;
+
+ UNPACKFLT (f, m, e);
+ res = m;
+
+ if (e < 0)
+ {
+ for (i = e; i < 0; i++)
+ res *= 0.5;
+ }
+ else
+ {
+ for (i = 0; i < e; i++)
+ res *= 2.0;
+ }
+
+ return res;
+}
+
+#endif
+
+static int
+log2flt (Flt a)
+{
+ return FLTEXPONENT (a) + 24;
+}
+
+static int
+cmpflt (Flt a, Flt b)
+{
+ if (a < b)
+ return -1;
+
+ if (a > b)
+ return 1;
+
+ return 0;
+}
+
+static void *
+new (PS * ps, size_t size)
+{
+ size_t bytes;
+ Blk *b;
+
+ if (!size)
+ return 0;
+
+ bytes = size + SIZE_OF_BLK;
+
+ if (ps->enew)
+ b = ps->enew (ps->emgr, bytes);
+ else
+ b = malloc (bytes);
+
+ ABORTIF (!b, "out of memory in 'new'");
+#ifndef NDEBUG
+ b->header.size = size;
+#endif
+ ps->current_bytes += size;
+ if (ps->current_bytes > ps->max_bytes)
+ ps->max_bytes = ps->current_bytes;
+ return b->data;
+}
+
+static void
+delete (PS * ps, void *void_ptr, size_t size)
+{
+ size_t bytes;
+ Blk *b;
+
+ if (!void_ptr)
+ {
+ assert (!size);
+ return;
+ }
+
+ assert (size);
+ b = PTR2BLK (void_ptr);
+
+ assert (size <= ps->current_bytes);
+ ps->current_bytes -= size;
+
+ assert (b->header.size == size);
+
+ bytes = size + SIZE_OF_BLK;
+ if (ps->edelete)
+ ps->edelete (ps->emgr, b, bytes);
+ else
+ free (b);
+}
+
+static void *
+resize (PS * ps, void *void_ptr, size_t old_size, size_t new_size)
+{
+ size_t old_bytes, new_bytes;
+ Blk *b;
+
+ b = PTR2BLK (void_ptr);
+
+ assert (old_size <= ps->current_bytes);
+ ps->current_bytes -= old_size;
+
+ if ((old_bytes = old_size))
+ {
+ assert (old_size && b && b->header.size == old_size);
+ old_bytes += SIZE_OF_BLK;
+ }
+ else
+ assert (!b);
+
+ if ((new_bytes = new_size))
+ new_bytes += SIZE_OF_BLK;
+
+ if (ps->eresize)
+ b = ps->eresize (ps->emgr, b, old_bytes, new_bytes);
+ else
+ b = realloc (b, new_bytes);
+
+ if (!new_size)
+ {
+ assert (!b);
+ return 0;
+ }
+
+ ABORTIF (!b, "out of memory in 'resize'");
+#ifndef NDEBUG
+ b->header.size = new_size;
+#endif
+
+ ps->current_bytes += new_size;
+ if (ps->current_bytes > ps->max_bytes)
+ ps->max_bytes = ps->current_bytes;
+
+ return b->data;
+}
+
+static unsigned
+int2unsigned (int l)
+{
+ return (l < 0) ? 1 + 2 * -l : 2 * l;
+}
+
+static Lit *
+int2lit (PS * ps, int l)
+{
+ return ps->lits + int2unsigned (l);
+}
+
+static Lit **
+end_of_lits (Cls * c)
+{
+ return (Lit**)c->lits + c->size;
+}
+
+#if !defined(NDEBUG) || defined(LOGGING)
+
+static void
+dumplits (PS * ps, Lit ** l, Lit ** end)
+{
+ int first;
+ Lit ** p;
+
+ if (l == end)
+ {
+ /* empty clause */
+ }
+ else if (l + 1 == end)
+ {
+ fprintf (ps->out, "%d ", LIT2INT (l[0]));
+ }
+ else
+ {
+ assert (l + 2 <= end);
+ first = (abs (LIT2INT (l[0])) > abs (LIT2INT (l[1])));
+ fprintf (ps->out, "%d ", LIT2INT (l[first]));
+ fprintf (ps->out, "%d ", LIT2INT (l[!first]));
+ for (p = l + 2; p < end; p++)
+ fprintf (ps->out, "%d ", LIT2INT (*p));
+ }
+
+ fputc ('0', ps->out);
+}
+
+static void
+dumpcls (PS * ps, Cls * c)
+{
+ Lit **end;
+
+ if (c)
+ {
+ end = end_of_lits (c);
+ dumplits (ps, c->lits, end);
+#ifdef TRACE
+ if (ps->trace)
+ fprintf (ps->out, " clause(%u)", CLS2IDX (c));
+#endif
+ }
+ else
+ fputs ("DECISION", ps->out);
+}
+
+static void
+dumpclsnl (PS * ps, Cls * c)
+{
+ dumpcls (ps, c);
+ fputc ('\n', ps->out);
+}
+
+void
+dumpcnf (PS * ps)
+{
+ Cls **p, *c;
+
+ for (p = SOC; p != EOC; p = NXC (p))
+ {
+ c = *p;
+
+ if (!c)
+ continue;
+
+#ifdef TRACE
+ if (c->collected)
+ continue;
+#endif
+
+ dumpclsnl (ps, *p);
+ }
+}
+
+#endif
+
+static void
+delete_prefix (PS * ps)
+{
+ if (!ps->prefix)
+ return;
+
+ delete (ps, ps->prefix, strlen (ps->prefix) + 1);
+ ps->prefix = 0;
+}
+
+static void
+new_prefix (PS * ps, const char * str)
+{
+ delete_prefix (ps);
+ assert (str);
+ ps->prefix = new (ps, strlen (str) + 1);
+ strcpy (ps->prefix, str);
+}
+
+static PS *
+init (void * pmgr,
+ picosat_malloc pnew, picosat_realloc presize, picosat_free pdelete)
+{
+ PS * ps;
+
+#if 0
+ int count = 3 - !pnew - !presize - !pdelete;
+
+ ABORTIF (count && !pnew, "API usage: missing 'picosat_set_new'");
+ ABORTIF (count && !presize, "API usage: missing 'picosat_set_resize'");
+ ABORTIF (count && !pdelete, "API usage: missing 'picosat_set_delete'");
+#endif
+
+ ps = pnew ? pnew (pmgr, sizeof *ps) : malloc (sizeof *ps);
+ ABORTIF (!ps, "failed to allocate memory for PicoSAT manager");
+ memset (ps, 0, sizeof *ps);
+
+ ps->emgr = pmgr;
+ ps->enew = pnew;
+ ps->eresize = presize;
+ ps->edelete = pdelete;
+
+ ps->size_vars = 1;
+ ps->state = RESET;
+ ps->defaultphase = JWLPHASE;
+#ifdef TRACE
+ ps->ocore = -1;
+#endif
+ ps->lastrheader = -2;
+#ifndef NADC
+ ps->adoconflictlimit = UINT_MAX;
+#endif
+ ps->min_flipped = UINT_MAX;
+
+ NEWN (ps->lits, 2 * ps->size_vars);
+ NEWN (ps->jwh, 2 * ps->size_vars);
+ NEWN (ps->htps, 2 * ps->size_vars);
+#ifndef NDSC
+ NEWN (ps->dhtps, 2 * ps->size_vars);
+#endif
+ NEWN (ps->impls, 2 * ps->size_vars);
+ NEWN (ps->vars, ps->size_vars);
+ NEWN (ps->rnks, ps->size_vars);
+
+ /* because '0' pos denotes not on heap
+ */
+ ENLARGE (ps->heap, ps->hhead, ps->eoh);
+ ps->hhead = ps->heap + 1;
+
+ ps->vinc = base2flt (1, 0); /* initial var activity */
+ ps->ifvinc = ascii2flt ("1.05"); /* var score rescore factor */
+#ifdef VISCORES
+ ps->fvinc = ascii2flt ("0.9523809"); /* 1/f = 1/1.05 */
+ ps->nvinc = ascii2flt ("0.0476191"); /* 1 - 1/f = 1 - 1/1.05 */
+#endif
+ ps->lscore = base2flt (1, 90); /* var activity rescore limit */
+ ps->ilvinc = base2flt (1, -90); /* inverse of 'lscore' */
+
+ ps->cinc = base2flt (1, 0); /* initial clause activity */
+ ps->fcinc = ascii2flt ("1.001"); /* cls activity rescore factor */
+ ps->lcinc = base2flt (1, 90); /* cls activity rescore limit */
+ ps->ilcinc = base2flt (1, -90); /* inverse of 'ilcinc' */
+
+ ps->lreduceadjustcnt = ps->lreduceadjustinc = 100;
+ ps->lpropagations = ~0ull;
+
+#ifndef RCODE
+ ps->out = stdout;
+#else
+ ps->out = 0;
+#endif
+ new_prefix (ps, "c ");
+ ps->verbosity = 0;
+ ps->plain = 0;
+
+#ifdef NO_BINARY_CLAUSES
+ memset (&ps->impl, 0, sizeof (ps->impl));
+ ps->impl.size = 2;
+
+ memset (&ps->cimpl, 0, sizeof (ps->impl));
+ ps->cimpl.size = 2;
+#endif
+
+#ifdef VISCORES
+ ps->fviscores = popen (
+ "/usr/bin/gnuplot -background black"
+ " -xrm 'gnuplot*textColor:white'"
+ " -xrm 'gnuplot*borderColor:white'"
+ " -xrm 'gnuplot*axisColor:white'"
+ , "w");
+ fprintf (ps->fviscores, "unset key\n");
+ // fprintf (ps->fviscores, "set log y\n");
+ fflush (ps->fviscores);
+ system ("rm -rf /tmp/picosat-viscores");
+ system ("mkdir /tmp/picosat-viscores");
+ system ("mkdir /tmp/picosat-viscores/data");
+#ifdef WRITEGIF
+ system ("mkdir /tmp/picosat-viscores/gif");
+ fprintf (ps->fviscores,
+ "set terminal gif giant animate opt size 1024,768 x000000 xffffff"
+ "\n");
+
+ fprintf (ps->fviscores,
+ "set output \"/tmp/picosat-viscores/gif/animated.gif\"\n");
+#endif
+#endif
+ ps->defaultphase = JWLPHASE;
+ ps->state = READY;
+ ps->last_sat_call_result = 0;
+
+ return ps;
+}
+
+static size_t
+bytes_clause (PS * ps, unsigned size, unsigned learned)
+{
+ size_t res;
+
+ res = sizeof (Cls);
+ res += size * sizeof (Lit *);
+ res -= 2 * sizeof (Lit *);
+
+ if (learned && size > 2)
+ res += sizeof (Act); /* add activity */
+
+#ifdef TRACE
+ if (ps->trace)
+ res += sizeof (Trd); /* add trace data */
+#else
+ (void) ps;
+#endif
+
+ return res;
+}
+
+static Cls *
+new_clause (PS * ps, unsigned size, unsigned learned)
+{
+ size_t bytes;
+ void * tmp;
+#ifdef TRACE
+ Trd *trd;
+#endif
+ Cls *res;
+
+ bytes = bytes_clause (ps, size, learned);
+ tmp = new (ps, bytes);
+
+#ifdef TRACE
+ if (ps->trace)
+ {
+ trd = tmp;
+
+ if (learned)
+ trd->idx = LIDX2IDX (ps->lhead - ps->lclauses);
+ else
+ trd->idx = OIDX2IDX (ps->ohead - ps->oclauses);
+
+ res = trd->cls;
+ }
+ else
+#endif
+ res = tmp;
+
+ res->size = size;
+ res->learned = learned;
+
+ res->collect = 0;
+#ifndef NDEBUG
+ res->connected = 0;
+#endif
+ res->locked = 0;
+ res->used = 0;
+#ifdef TRACE
+ res->core = 0;
+ res->collected = 0;
+#endif
+
+ if (learned && size > 2)
+ {
+ Act * p = CLS2ACT (res);
+ *p = ps->cinc;
+ }
+
+ return res;
+}
+
+static void
+delete_clause (PS * ps, Cls * c)
+{
+ size_t bytes;
+#ifdef TRACE
+ Trd *trd;
+#endif
+
+ bytes = bytes_clause (ps, c->size, c->learned);
+
+#ifdef TRACE
+ if (ps->trace)
+ {
+ trd = CLS2TRD (c);
+ delete (ps, trd, bytes);
+ }
+ else
+#endif
+ delete (ps, c, bytes);
+}
+
+static void
+delete_clauses (PS * ps)
+{
+ Cls **p;
+ for (p = SOC; p != EOC; p = NXC (p))
+ if (*p)
+ delete_clause (ps, *p);
+
+ DELETEN (ps->oclauses, ps->eoo - ps->oclauses);
+ DELETEN (ps->lclauses, ps->EOL - ps->lclauses);
+
+ ps->ohead = ps->eoo = ps->lhead = ps->EOL = 0;
+}
+
+#ifdef TRACE
+
+static void
+delete_zhain (PS * ps, Zhn * zhain)
+{
+ const Znt *p, *znt;
+
+ assert (zhain);
+
+ znt = zhain->znt;
+ for (p = znt; *p; p++)
+ ;
+
+ delete (ps, zhain, sizeof (Zhn) + (p - znt) + 1);
+}
+
+static void
+delete_zhains (PS * ps)
+{
+ Zhn **p, *z;
+ for (p = ps->zhains; p < ps->zhead; p++)
+ if ((z = *p))
+ delete_zhain (ps, z);
+
+ DELETEN (ps->zhains, ps->eoz - ps->zhains);
+ ps->eoz = ps->zhead = 0;
+}
+
+#endif
+
+#ifdef NO_BINARY_CLAUSES
+static void
+lrelease (PS * ps, Ltk * stk)
+{
+ if (stk->start)
+ DELETEN (stk->start, (1 << (stk->ldsize)));
+ memset (stk, 0, sizeof (*stk));
+}
+#endif
+
+#ifndef NADC
+
+static unsigned
+llength (Lit ** a)
+{
+ Lit ** p;
+ for (p = a; *p; p++)
+ ;
+ return p - a;
+}
+
+static void
+resetadoconflict (PS * ps)
+{
+ assert (ps->adoconflict);
+ delete_clause (ps, ps->adoconflict);
+ ps->adoconflict = 0;
+}
+
+static void
+reset_ados (PS * ps)
+{
+ Lit *** p;
+
+ for (p = ps->ados; p < ps->hados; p++)
+ DELETEN (*p, llength (*p) + 1);
+
+ DELETEN (ps->ados, ps->eados - ps->ados);
+ ps->hados = ps->eados = 0;
+
+ DELETEN (ps->adotab, ps->szadotab);
+ ps->szadotab = ps->nadotab = 0;
+
+ if (ps->adoconflict)
+ resetadoconflict (ps);
+
+ ps->adoconflicts = 0;
+ ps->adoconflictlimit = UINT_MAX;
+ ps->adodisabled = 0;
+}
+
+#endif
+
+static void
+reset (PS * ps)
+{
+ ABORTIF (!ps ||
+ ps->state == RESET, "API usage: reset without initialization");
+
+ delete_clauses (ps);
+#ifdef TRACE
+ delete_zhains (ps);
+#endif
+#ifdef NO_BINARY_CLAUSES
+ {
+ unsigned i;
+ for (i = 2; i <= 2 * ps->max_var + 1; i++)
+ lrelease (ps, ps->impls + i);
+ }
+#endif
+#ifndef NADC
+ reset_ados (ps);
+#endif
+#ifndef NFL
+ DELETEN (ps->saved, ps->saved_size);
+#endif
+ DELETEN (ps->htps, 2 * ps->size_vars);
+#ifndef NDSC
+ DELETEN (ps->dhtps, 2 * ps->size_vars);
+#endif
+ DELETEN (ps->impls, 2 * ps->size_vars);
+ DELETEN (ps->lits, 2 * ps->size_vars);
+ DELETEN (ps->jwh, 2 * ps->size_vars);
+ DELETEN (ps->vars, ps->size_vars);
+ DELETEN (ps->rnks, ps->size_vars);
+ DELETEN (ps->trail, ps->eot - ps->trail);
+ DELETEN (ps->heap, ps->eoh - ps->heap);
+ DELETEN (ps->als, ps->eoals - ps->als);
+ DELETEN (ps->CLS, ps->eocls - ps->CLS);
+ DELETEN (ps->rils, ps->eorils - ps->rils);
+ DELETEN (ps->cils, ps->eocils - ps->cils);
+ DELETEN (ps->fals, ps->eofals - ps->fals);
+ DELETEN (ps->mass, ps->szmass);
+ DELETEN (ps->mssass, ps->szmssass);
+ DELETEN (ps->mcsass, ps->szmcsass);
+ DELETEN (ps->humus, ps->szhumus);
+ DELETEN (ps->added, ps->eoa - ps->added);
+ DELETEN (ps->marked, ps->eom - ps->marked);
+ DELETEN (ps->dfs, ps->eod - ps->dfs);
+ DELETEN (ps->resolved, ps->eor - ps->resolved);
+ DELETEN (ps->levels, ps->eolevels - ps->levels);
+ DELETEN (ps->dused, ps->eodused - ps->dused);
+ DELETEN (ps->buffer, ps->eob - ps->buffer);
+ DELETEN (ps->indices, ps->eoi - ps->indices);
+ DELETEN (ps->soclauses, ps->eoso - ps->soclauses);
+ delete_prefix (ps);
+ delete (ps, ps->rline[0], ps->szrline);
+ delete (ps, ps->rline[1], ps->szrline);
+ assert (getenv ("LEAK") || !ps->current_bytes); /* found leak if failing */
+#ifdef VISCORES
+ pclose (ps->fviscores);
+#endif
+ if (ps->edelete)
+ ps->edelete (ps->emgr, ps, sizeof *ps);
+ else
+ free (ps);
+}
+
+inline static void
+tpush (PS * ps, Lit * lit)
+{
+ assert (ps->lits < lit && lit <= ps->lits + 2* ps->max_var + 1);
+ if (ps->thead == ps->eot)
+ {
+ unsigned ttail2count = ps->ttail2 - ps->trail;
+ unsigned ttailcount = ps->ttail - ps->trail;
+#ifndef NADC
+ unsigned ttailadocount = ps->ttailado - ps->trail;
+#endif
+ ENLARGE (ps->trail, ps->thead, ps->eot);
+ ps->ttail = ps->trail + ttailcount;
+ ps->ttail2 = ps->trail + ttail2count;
+#ifndef NADC
+ ps->ttailado = ps->trail + ttailadocount;
+#endif
+ }
+
+ *ps->thead++ = lit;
+}
+
+static void
+assign_reason (PS * ps, Var * v, Cls * reason)
+{
+#if defined(NO_BINARY_CLAUSES) && !defined(NDEBUG)
+ assert (reason != &ps->impl);
+#else
+ (void) ps;
+#endif
+ v->reason = reason;
+}
+
+static void
+assign_phase (PS * ps, Lit * lit)
+{
+ unsigned new_phase, idx;
+ Var * v = LIT2VAR (lit);
+
+#ifndef NFL
+ /* In 'simplifying' mode we only need to keep 'min_flipped' up to date if
+ * we force assignments on the top level. The other assignments will be
+ * undone and thus we can keep the old saved value of the phase.
+ */
+ if (!ps->LEVEL || !ps->simplifying)
+#endif
+ {
+ new_phase = (LIT2SGN (lit) > 0);
+
+ if (v->assigned)
+ {
+ ps->sdflips -= ps->sdflips/FFLIPPED;
+
+ if (new_phase != v->phase)
+ {
+ assert (FFLIPPEDPREC >= FFLIPPED);
+ ps->sdflips += FFLIPPEDPREC / FFLIPPED;
+ ps->flips++;
+
+ idx = LIT2IDX (lit);
+ if (idx < ps->min_flipped)
+ ps->min_flipped = idx;
+
+ NOLOG (fprintf (ps->out,
+ "%sflipped %d\n",
+ ps->prefix, LIT2INT (lit)));
+ }
+ }
+
+ v->phase = new_phase;
+ v->assigned = 1;
+ }
+
+ lit->val = TRUE;
+ NOTLIT (lit)->val = FALSE;
+}
+
+inline static void
+assign (PS * ps, Lit * lit, Cls * reason)
+{
+ Var * v = LIT2VAR (lit);
+ assert (lit->val == UNDEF);
+#ifdef STATS
+ ps->assignments++;
+#endif
+ v->level = ps->LEVEL;
+ assign_phase (ps, lit);
+ assign_reason (ps, v, reason);
+ tpush (ps, lit);
+}
+
+inline static int
+cmp_added (PS * ps, Lit * k, Lit * l)
+{
+ Val a = k->val, b = l->val;
+ Var *u, *v;
+ int res;
+
+ if (a == UNDEF && b != UNDEF)
+ return -1;
+
+ if (a != UNDEF && b == UNDEF)
+ return 1;
+
+ u = LIT2VAR (k);
+ v = LIT2VAR (l);
+
+ if (a != UNDEF)
+ {
+ assert (b != UNDEF);
+ res = v->level - u->level;
+ if (res)
+ return res; /* larger level first */
+ }
+
+ res = cmpflt (VAR2RNK (u)->score, VAR2RNK (v)->score);
+ if (res)
+ return res; /* smaller activity first */
+
+ return u - v; /* smaller index first */
+}
+
+static void
+sorttwolits (Lit ** v)
+{
+ Lit * a = v[0], * b = v[1];
+
+ assert (a != b);
+
+ if (a < b)
+ return;
+
+ v[0] = b;
+ v[1] = a;
+}
+
+inline static void
+sortlits (PS * ps, Lit ** v, unsigned size)
+{
+ if (size == 2)
+ sorttwolits (v); /* same order with and with out 'NO_BINARY_CLAUSES' */
+ else
+ SORT (Lit *, cmp_added, v, size);
+}
+
+#ifdef NO_BINARY_CLAUSES
+static Cls *
+setimpl (PS * ps, Lit * a, Lit * b)
+{
+ assert (!ps->implvalid);
+ assert (ps->impl.size == 2);
+
+ ps->impl.lits[0] = a;
+ ps->impl.lits[1] = b;
+
+ sorttwolits (ps->impl.lits);
+ ps->implvalid = 1;
+
+ return &ps->impl;
+}
+
+static void
+resetimpl (PS * ps)
+{
+ ps->implvalid = 0;
+}
+
+static Cls *
+setcimpl (PS * ps, Lit * a, Lit * b)
+{
+ assert (!ps->cimplvalid);
+ assert (ps->cimpl.size == 2);
+
+ ps->cimpl.lits[0] = a;
+ ps->cimpl.lits[1] = b;
+
+ sorttwolits (ps->cimpl.lits);
+ ps->cimplvalid = 1;
+
+ return &ps->cimpl;
+}
+
+static void
+resetcimpl (PS * ps)
+{
+ assert (ps->cimplvalid);
+ ps->cimplvalid = 0;
+}
+
+#endif
+
+static int
+cmp_ptr (PS * ps, void *l, void *k)
+{
+ (void) ps;
+ return ((char*)l) - (char*)k; /* arbitrarily already reverse */
+}
+
+static int
+cmp_rnk (Rnk * r, Rnk * s)
+{
+ if (!r->moreimportant && s->moreimportant)
+ return -1;
+
+ if (r->moreimportant && !s->moreimportant)
+ return 1;
+
+ if (!r->lessimportant && s->lessimportant)
+ return 1;
+
+ if (r->lessimportant && !s->lessimportant)
+ return -1;
+
+ if (r->score < s->score)
+ return -1;
+
+ if (r->score > s->score)
+ return 1;
+
+ return -cmp_ptr (0, r, s);
+}
+
+static void
+hup (PS * ps, Rnk * v)
+{
+ int upos, vpos;
+ Rnk *u;
+
+#ifndef NFL
+ assert (!ps->simplifying);
+#endif
+
+ vpos = v->pos;
+
+ assert (0 < vpos);
+ assert (vpos < ps->hhead - ps->heap);
+ assert (ps->heap[vpos] == v);
+
+ while (vpos > 1)
+ {
+ upos = vpos / 2;
+
+ u = ps->heap[upos];
+
+ if (cmp_rnk (u, v) > 0)
+ break;
+
+ ps->heap[vpos] = u;
+ u->pos = vpos;
+
+ vpos = upos;
+ }
+
+ ps->heap[vpos] = v;
+ v->pos = vpos;
+}
+
+static Cls *add_simplified_clause (PS *, int);
+
+inline static void
+add_antecedent (PS * ps, Cls * c)
+{
+ assert (c);
+
+#ifdef NO_BINARY_CLAUSES
+ if (ISLITREASON (c))
+ return;
+
+ if (c == &ps->impl)
+ return;
+#elif defined(STATS) && defined(TRACE)
+ ps->antecedents++;
+#endif
+ if (ps->rhead == ps->eor)
+ ENLARGE (ps->resolved, ps->rhead, ps->eor);
+
+ assert (ps->rhead < ps->eor);
+ *ps->rhead++ = c;
+}
+
+#ifdef TRACE
+
+#ifdef NO_BINARY_CLAUSES
+#error "can not combine TRACE and NO_BINARY_CLAUSES"
+#endif
+
+#endif /* TRACE */
+
+static void
+add_lit (PS * ps, Lit * lit)
+{
+ assert (lit);
+
+ if (ps->ahead == ps->eoa)
+ ENLARGE (ps->added, ps->ahead, ps->eoa);
+
+ *ps->ahead++ = lit;
+}
+
+static void
+push_var_as_marked (PS * ps, Var * v)
+{
+ if (ps->mhead == ps->eom)
+ ENLARGE (ps->marked, ps->mhead, ps->eom);
+
+ *ps->mhead++ = v;
+}
+
+static void
+mark_var (PS * ps, Var * v)
+{
+ assert (!v->mark);
+ v->mark = 1;
+ push_var_as_marked (ps, v);
+}
+
+#ifdef NO_BINARY_CLAUSES
+
+static Cls *
+impl2reason (PS * ps, Lit * lit)
+{
+ Lit * other;
+ Cls * res;
+ other = ps->impl.lits[0];
+ if (lit == other)
+ other = ps->impl.lits[1];
+ assert (other->val == FALSE);
+ res = LIT2REASON (NOTLIT (other));
+ resetimpl (ps);
+ return res;
+}
+
+#endif
+
+/* Whenever we have a top level derived unit we really should derive a unit
+ * clause otherwise the resolutions in 'add_simplified_clause' become
+ * incorrect.
+ */
+static Cls *
+resolve_top_level_unit (PS * ps, Lit * lit, Cls * reason)
+{
+ unsigned count_resolved;
+ Lit **p, **eol, *other;
+ Var *u, *v;
+
+ assert (ps->rhead == ps->resolved);
+ assert (ps->ahead == ps->added);
+
+ add_lit (ps, lit);
+ add_antecedent (ps, reason);
+ count_resolved = 1;
+ v = LIT2VAR (lit);
+
+ eol = end_of_lits (reason);
+ for (p = reason->lits; p < eol; p++)
+ {
+ other = *p;
+ u = LIT2VAR (other);
+ if (u == v)
+ continue;
+
+ add_antecedent (ps, u->reason);
+ count_resolved++;
+ }
+
+ /* Some of the literals could be assumptions. If at least one
+ * variable is not an assumption, we should resolve.
+ */
+ if (count_resolved >= 2)
+ {
+#ifdef NO_BINARY_CLAUSES
+ if (reason == &ps->impl)
+ resetimpl (ps);
+#endif
+ reason = add_simplified_clause (ps, 1);
+#ifdef NO_BINARY_CLAUSES
+ if (reason->size == 2)
+ {
+ assert (reason == &ps->impl);
+ reason = impl2reason (ps, lit);
+ }
+#endif
+ assign_reason (ps, v, reason);
+ }
+ else
+ {
+ ps->ahead = ps->added;
+ ps->rhead = ps->resolved;
+ }
+
+ return reason;
+}
+
+static void
+fixvar (PS * ps, Var * v)
+{
+ Rnk * r;
+
+ assert (VAR2LIT (v) != UNDEF);
+ assert (!v->level);
+
+ ps->fixed++;
+
+ r = VAR2RNK (v);
+ r->score = INFFLT;
+
+#ifndef NFL
+ if (ps->simplifying)
+ return;
+#endif
+
+ if (!r->pos)
+ return;
+
+ hup (ps, r);
+}
+
+static void
+use_var (PS * ps, Var * v)
+{
+ if (v->used)
+ return;
+
+ v->used = 1;
+ ps->vused++;
+}
+
+static void
+assign_forced (PS * ps, Lit * lit, Cls * reason)
+{
+ Var *v;
+
+ assert (reason);
+ assert (lit->val == UNDEF);
+
+#ifdef STATS
+ ps->FORCED++;
+#endif
+ assign (ps, lit, reason);
+
+#ifdef NO_BINARY_CLAUSES
+ assert (reason != &ps->impl);
+ if (ISLITREASON (reason))
+ {
+ reason = setimpl (ps, lit, NOTLIT (REASON2LIT (reason)));
+ assert (reason);
+ }
+#endif
+ LOG ( fprintf (ps->out,
+ "%sassign %d at level %d by ",
+ ps->prefix, LIT2INT (lit), ps->LEVEL);
+ dumpclsnl (ps, reason));
+
+ v = LIT2VAR (lit);
+ if (!ps->LEVEL)
+ use_var (ps, v);
+
+ if (!ps->LEVEL && reason->size > 1)
+ {
+ reason = resolve_top_level_unit (ps, lit, reason);
+ assert (reason);
+ }
+
+#ifdef NO_BINARY_CLAUSES
+ if (ISLITREASON (reason) || reason == &ps->impl)
+ {
+ /* DO NOTHING */
+ }
+ else
+#endif
+ {
+ assert (!reason->locked);
+ reason->locked = 1;
+ if (reason->learned && reason->size > 2)
+ ps->llocked++;
+ }
+
+#ifdef NO_BINARY_CLAUSES
+ if (reason == &ps->impl)
+ resetimpl (ps);
+#endif
+
+ if (!ps->LEVEL)
+ fixvar (ps, v);
+}
+
+#ifdef NO_BINARY_CLAUSES
+
+static void
+lpush (PS * ps, Lit * lit, Cls * c)
+{
+ int pos = (c->lits[0] == lit);
+ Ltk * s = LIT2IMPLS (lit);
+ unsigned oldsize, newsize;
+
+ assert (c->size == 2);
+
+ if (!s->start)
+ {
+ assert (!s->count);
+ assert (!s->ldsize);
+ NEWN (s->start, 1);
+ }
+ else
+ {
+ oldsize = (1 << (s->ldsize));
+ assert (s->count <= oldsize);
+ if (s->count == oldsize)
+ {
+ newsize = 2 * oldsize;
+ RESIZEN (s->start, oldsize, newsize);
+ s->ldsize++;
+ }
+ }
+
+ s->start[s->count++] = c->lits[pos];
+}
+
+#endif
+
+static void
+connect_head_tail (PS * ps, Lit * lit, Cls * c)
+{
+ Cls ** s;
+ assert (c->size >= 1);
+ if (c->size == 2)
+ {
+#ifdef NO_BINARY_CLAUSES
+ lpush (ps, lit, c);
+ return;
+#else
+ s = LIT2IMPLS (lit);
+#endif
+ }
+ else
+ s = LIT2HTPS (lit);
+
+ if (c->lits[0] != lit)
+ {
+ assert (c->size >= 2);
+ assert (c->lits[1] == lit);
+ c->next[1] = *s;
+ }
+ else
+ c->next[0] = *s;
+
+ *s = c;
+}
+
+#ifdef TRACE
+static void
+zpush (PS * ps, Zhn * zhain)
+{
+ assert (ps->trace);
+
+ if (ps->zhead == ps->eoz)
+ ENLARGE (ps->zhains, ps->zhead, ps->eoz);
+
+ *ps->zhead++ = zhain;
+}
+
+static int
+cmp_resolved (PS * ps, Cls * c, Cls * d)
+{
+#ifndef NDEBUG
+ assert (ps->trace);
+#else
+ (void) ps;
+#endif
+ return CLS2IDX (c) - CLS2IDX (d);
+}
+
+static void
+bpushc (PS * ps, unsigned char ch)
+{
+ if (ps->bhead == ps->eob)
+ ENLARGE (ps->buffer, ps->bhead, ps->eob);
+
+ *ps->bhead++ = ch;
+}
+
+static void
+bpushu (PS * ps, unsigned u)
+{
+ while (u & ~0x7f)
+ {
+ bpushc (ps, u | 0x80);
+ u >>= 7;
+ }
+
+ bpushc (ps, u);
+}
+
+static void
+bpushd (PS * ps, unsigned prev, unsigned this)
+{
+ unsigned delta;
+ assert (prev < this);
+ delta = this - prev;
+ bpushu (ps, delta);
+}
+
+static void
+add_zhain (PS * ps)
+{
+ unsigned prev, this, count, rcount;
+ Cls **p, *c;
+ Zhn *res;
+
+ assert (ps->trace);
+ assert (ps->bhead == ps->buffer);
+ assert (ps->rhead > ps->resolved);
+
+ rcount = ps->rhead - ps->resolved;
+ SORT (Cls *, cmp_resolved, ps->resolved, rcount);
+
+ prev = 0;
+ for (p = ps->resolved; p < ps->rhead; p++)
+ {
+ c = *p;
+ this = CLS2TRD (c)->idx;
+ bpushd (ps, prev, this);
+ prev = this;
+ }
+ bpushc (ps, 0);
+
+ count = ps->bhead - ps->buffer;
+
+ res = new (ps, sizeof (Zhn) + count);
+ res->core = 0;
+ res->ref = 0;
+ memcpy (res->znt, ps->buffer, count);
+
+ ps->bhead = ps->buffer;
+#ifdef STATS
+ ps->znts += count - 1;
+#endif
+ zpush (ps, res);
+}
+
+#endif
+
+static void
+add_resolved (PS * ps, int learned)
+{
+#if defined(STATS) || defined(TRACE)
+ Cls **p, *c;
+
+ for (p = ps->resolved; p < ps->rhead; p++)
+ {
+ c = *p;
+ if (c->used)
+ continue;
+
+ c->used = 1;
+
+ if (c->size <= 2)
+ continue;
+
+#ifdef STATS
+ if (c->learned)
+ ps->llused++;
+ else
+ ps->loused++;
+#endif
+ }
+#endif
+
+#ifdef TRACE
+ if (learned && ps->trace)
+ add_zhain (ps);
+#else
+ (void) learned;
+#endif
+ ps->rhead = ps->resolved;
+}
+
+static void
+incjwh (PS * ps, Cls * c)
+{
+ Lit **p, *lit, ** eol;
+ Flt * f, inc, sum;
+ unsigned size = 0;
+ Var * v;
+ Val val;
+
+ eol = end_of_lits (c);
+
+ for (p = c->lits; p < eol; p++)
+ {
+ lit = *p;
+ val = lit->val;
+
+ if (val && ps->LEVEL > 0)
+ {
+ v = LIT2VAR (lit);
+ if (v->level > 0)
+ val = UNDEF;
+ }
+
+ if (val == TRUE)
+ return;
+
+ if (val != FALSE)
+ size++;
+ }
+
+ inc = base2flt (1, -size);
+
+ for (p = c->lits; p < eol; p++)
+ {
+ lit = *p;
+ f = LIT2JWH (lit);
+ sum = addflt (*f, inc);
+ *f = sum;
+ }
+}
+
+static void
+write_rup_header (PS * ps, FILE * file)
+{
+ char line[80];
+ int i;
+
+ sprintf (line, "%%RUPD32 %u %u", ps->rupvariables, ps->rupclauses);
+
+ fputs (line, file);
+ for (i = 255 - strlen (line); i >= 0; i--)
+ fputc (' ', file);
+
+ fputc ('\n', file);
+ fflush (file);
+}
+
+static Cls *
+add_simplified_clause (PS * ps, int learned)
+{
+ unsigned num_true, num_undef, num_false, size, count_resolved;
+ Lit **p, **q, *lit, ** end;
+ unsigned litlevel, glue;
+ Cls *res, * reason;
+ int reentered;
+ Val val;
+ Var *v;
+#if !defined(NDEBUG) && defined(TRACE)
+ unsigned idx;
+#endif
+
+ reentered = 0;
+
+REENTER:
+
+ size = ps->ahead - ps->added;
+
+ add_resolved (ps, learned);
+
+ if (learned)
+ {
+ ps->ladded++;
+ ps->llitsadded += size;
+ if (size > 2)
+ {
+ ps->lladded++;
+ ps->nlclauses++;
+ ps->llits += size;
+ }
+ }
+ else
+ {
+ ps->oadded++;
+ if (size > 2)
+ {
+ ps->loadded++;
+ ps->noclauses++;
+ ps->olits += size;
+ }
+ }
+
+ ps->addedclauses++;
+ assert (ps->addedclauses == ps->ladded + ps->oadded);
+
+#ifdef NO_BINARY_CLAUSES
+ if (size == 2)
+ res = setimpl (ps, ps->added[0], ps->added[1]);
+ else
+#endif
+ {
+ sortlits (ps, ps->added, size);
+
+ if (learned)
+ {
+ if (ps->lhead == ps->EOL)
+ {
+ ENLARGE (ps->lclauses, ps->lhead, ps->EOL);
+
+ /* A very difficult to find bug, which only occurs if the
+ * learned clauses stack is immediately allocated before the
+ * original clauses stack without padding. In this case, we
+ * have 'SOC == EOC', which terminates all loops using the
+ * idiom 'for (p = SOC; p != EOC; p = NXC(p))' immediately.
+ * Unfortunately this occurred in 'fix_clause_lits' after
+ * using a recent version of the memory allocator of 'Google'
+ * perftools in the context of one large benchmark for
+ * our SMT solver 'Boolector'.
+ */
+ if (ps->EOL == ps->oclauses)
+ ENLARGE (ps->lclauses, ps->lhead, ps->EOL);
+ }
+
+#if !defined(NDEBUG) && defined(TRACE)
+ idx = LIDX2IDX (ps->lhead - ps->lclauses);
+#endif
+ }
+ else
+ {
+ if (ps->ohead == ps->eoo)
+ {
+ ENLARGE (ps->oclauses, ps->ohead, ps->eoo);
+ if (ps->EOL == ps->oclauses)
+ ENLARGE (ps->oclauses, ps->ohead, ps->eoo); /* ditto */
+ }
+
+#if !defined(NDEBUG) && defined(TRACE)
+ idx = OIDX2IDX (ps->ohead - ps->oclauses);
+#endif
+ }
+
+ assert (ps->EOL != ps->oclauses); /* ditto */
+
+ res = new_clause (ps, size, learned);
+
+ glue = 0;
+
+ if (learned)
+ {
+ assert (ps->dusedhead == ps->dused);
+
+ for (p = ps->added; p < ps->ahead; p++)
+ {
+ lit = *p;
+ if (lit->val)
+ {
+ litlevel = LIT2VAR (lit)->level;
+ assert (litlevel <= ps->LEVEL);
+ while (ps->levels + litlevel >= ps->levelshead)
+ {
+ if (ps->levelshead >= ps->eolevels)
+ ENLARGE (ps->levels, ps->levelshead, ps->eolevels);
+ assert (ps->levelshead < ps->eolevels);
+ *ps->levelshead++ = 0;
+ }
+ if (!ps->levels[litlevel])
+ {
+ if (ps->dusedhead >= ps->eodused)
+ ENLARGE (ps->dused, ps->dusedhead, ps->eodused);
+ assert (ps->dusedhead < ps->eodused);
+ *ps->dusedhead++ = litlevel;
+ ps->levels[litlevel] = 1;
+ glue++;
+ }
+ }
+ else
+ glue++;
+ }
+
+ while (ps->dusedhead > ps->dused)
+ {
+ litlevel = *--ps->dusedhead;
+ assert (ps->levels + litlevel < ps->levelshead);
+ assert (ps->levels[litlevel]);
+ ps->levels[litlevel] = 0;
+ }
+ }
+
+ assert (glue <= MAXGLUE);
+ res->glue = glue;
+
+#if !defined(NDEBUG) && defined(TRACE)
+ if (ps->trace)
+ assert (CLS2IDX (res) == idx);
+#endif
+ if (learned)
+ *ps->lhead++ = res;
+ else
+ *ps->ohead++ = res;
+
+#if !defined(NDEBUG) && defined(TRACE)
+ if (ps->trace && learned)
+ assert (ps->zhead - ps->zhains == ps->lhead - ps->lclauses);
+#endif
+ assert (ps->lhead != ps->oclauses); /* ditto */
+ }
+
+ if (learned && ps->rup)
+ {
+ if (!ps->rupstarted)
+ {
+ write_rup_header (ps, ps->rup);
+ ps->rupstarted = 1;
+ }
+ }
+
+ num_true = num_undef = num_false = 0;
+
+ q = res->lits;
+ for (p = ps->added; p < ps->ahead; p++)
+ {
+ lit = *p;
+ *q++ = lit;
+
+ if (learned && ps->rup)
+ fprintf (ps->rup, "%d ", LIT2INT (lit));
+
+ val = lit->val;
+
+ num_true += (val == TRUE);
+ num_undef += (val == UNDEF);
+ num_false += (val == FALSE);
+ }
+ assert (num_false + num_true + num_undef == size);
+
+ if (learned && ps->rup)
+ fputs ("0\n", ps->rup);
+
+ ps->ahead = ps->added; /* reset */
+
+ if (!reentered) // TODO merge
+ if (size > 0)
+ {
+ assert (size <= 2 || !reentered); // TODO remove
+ connect_head_tail (ps, res->lits[0], res);
+ if (size > 1)
+ connect_head_tail (ps, res->lits[1], res);
+ }
+
+ if (size == 0)
+ {
+ if (!ps->mtcls)
+ ps->mtcls = res;
+ }
+
+#ifdef NO_BINARY_CLAUSES
+ if (size != 2)
+#endif
+#ifndef NDEBUG
+ res->connected = 1;
+#endif
+
+ LOG ( fprintf (ps->out, "%s%s ", ps->prefix, learned ? "learned" : "original");
+ dumpclsnl (ps, res));
+
+ /* Shrink clause by resolving it against top level assignments.
+ */
+ if (!ps->LEVEL && num_false > 0)
+ {
+ assert (ps->ahead == ps->added);
+ assert (ps->rhead == ps->resolved);
+
+ count_resolved = 1;
+ add_antecedent (ps, res);
+
+ end = end_of_lits (res);
+ for (p = res->lits; p < end; p++)
+ {
+ lit = *p;
+ v = LIT2VAR (lit);
+ use_var (ps, v);
+
+ if (lit->val == FALSE)
+ {
+ add_antecedent (ps, v->reason);
+ count_resolved++;
+ }
+ else
+ add_lit (ps, lit);
+ }
+
+ assert (count_resolved >= 2);
+
+ learned = 1;
+#ifdef NO_BINARY_CLAUSES
+ if (res == &ps->impl)
+ resetimpl (ps);
+#endif
+ reentered = 1;
+ goto REENTER; /* and return simplified clause */
+ }
+
+ if (!num_true && num_undef == 1) /* unit clause */
+ {
+ lit = 0;
+ for (p = res->lits; p < res->lits + size; p++)
+ {
+ if ((*p)->val == UNDEF)
+ lit = *p;
+
+ v = LIT2VAR (*p);
+ use_var (ps, v);
+ }
+ assert (lit);
+
+ reason = res;
+#ifdef NO_BINARY_CLAUSES
+ if (size == 2)
+ {
+ Lit * other = res->lits[0];
+ if (other == lit)
+ other = res->lits[1];
+
+ assert (other->val == FALSE);
+ reason = LIT2REASON (NOTLIT (other));
+ }
+#endif
+ assign_forced (ps, lit, reason);
+ num_true++;
+ }
+
+ if (num_false == size && !ps->conflict)
+ {
+#ifdef NO_BINARY_CLAUSES
+ if (res == &ps->impl)
+ ps->conflict = setcimpl (ps, res->lits[0], res->lits[1]);
+ else
+#endif
+ ps->conflict = res;
+ }
+
+ if (!learned && !num_true && num_undef)
+ incjwh (ps, res);
+
+#ifdef NO_BINARY_CLAUSES
+ if (res == &ps->impl)
+ resetimpl (ps);
+#endif
+ return res;
+}
+
+static int
+trivial_clause (PS * ps)
+{
+ Lit **p, **q, *prev;
+ Var *v;
+
+ SORT (Lit *, cmp_ptr, ps->added, ps->ahead - ps->added);
+
+ prev = 0;
+ q = ps->added;
+ for (p = q; p < ps->ahead; p++)
+ {
+ Lit *this = *p;
+
+ v = LIT2VAR (this);
+
+ if (prev == this) /* skip repeated literals */
+ continue;
+
+ /* Top level satisfied ?
+ */
+ if (this->val == TRUE && !v->level)
+ return 1;
+
+ if (prev == NOTLIT (this))/* found pair of dual literals */
+ return 1;
+
+ *q++ = prev = this;
+ }
+
+ ps->ahead = q; /* shrink */
+
+ return 0;
+}
+
+static void
+simplify_and_add_original_clause (PS * ps)
+{
+#ifdef NO_BINARY_CLAUSES
+ Cls * c;
+#endif
+ if (trivial_clause (ps))
+ {
+ ps->ahead = ps->added;
+
+ if (ps->ohead == ps->eoo)
+ ENLARGE (ps->oclauses, ps->ohead, ps->eoo);
+
+ *ps->ohead++ = 0;
+
+ ps->addedclauses++;
+ ps->oadded++;
+ }
+ else
+ {
+ if (ps->CLS != ps->clshead)
+ add_lit (ps, NOTLIT (ps->clshead[-1]));
+
+#ifdef NO_BINARY_CLAUSES
+ c =
+#endif
+ add_simplified_clause (ps, 0);
+#ifdef NO_BINARY_CLAUSES
+ if (c == &ps->impl) assert (!ps->implvalid);
+#endif
+ }
+}
+
+#ifndef NADC
+
+static void
+add_ado (PS * ps)
+{
+ unsigned len = ps->ahead - ps->added;
+ Lit ** ado, ** p, ** q, *lit;
+ Var * v, * u;
+
+#ifdef TRACE
+ assert (!ps->trace);
+#endif
+
+ ABORTIF (ps->ados < ps->hados && llength (ps->ados[0]) != len,
+ "internal: non matching all different constraint object lengths");
+
+ if (ps->hados == ps->eados)
+ ENLARGE (ps->ados, ps->hados, ps->eados);
+
+ NEWN (ado, len + 1);
+ *ps->hados++ = ado;
+
+ p = ps->added;
+ q = ado;
+ u = 0;
+ while (p < ps->ahead)
+ {
+ lit = *p++;
+ v = LIT2VAR (lit);
+ ABORTIF (v->inado,
+ "internal: variable in multiple all different objects");
+ v->inado = ado;
+ if (!u && !lit->val)
+ u = v;
+ *q++ = lit;
+ }
+
+ assert (q == ado + len);
+ *q++ = 0;
+
+ /* TODO simply do a conflict test as in propado */
+
+ ABORTIF (!u,
+ "internal: "
+ "adding fully instantiated all different object not implemented yet");
+
+ assert (u);
+ assert (u->inado == ado);
+ assert (!u->ado);
+ u->ado = ado;
+
+ ps->ahead = ps->added;
+}
+
+#endif
+
+static void
+hdown (PS * ps, Rnk * r)
+{
+ unsigned end, rpos, cpos, opos;
+ Rnk *child, *other;
+
+ assert (r->pos > 0);
+ assert (ps->heap[r->pos] == r);
+
+ end = ps->hhead - ps->heap;
+ rpos = r->pos;
+
+ for (;;)
+ {
+ cpos = 2 * rpos;
+ if (cpos >= end)
+ break;
+
+ opos = cpos + 1;
+ child = ps->heap[cpos];
+
+ if (cmp_rnk (r, child) < 0)
+ {
+ if (opos < end)
+ {
+ other = ps->heap[opos];
+
+ if (cmp_rnk (child, other) < 0)
+ {
+ child = other;
+ cpos = opos;
+ }
+ }
+ }
+ else if (opos < end)
+ {
+ child = ps->heap[opos];
+
+ if (cmp_rnk (r, child) >= 0)
+ break;
+
+ cpos = opos;
+ }
+ else
+ break;
+
+ ps->heap[rpos] = child;
+ child->pos = rpos;
+ rpos = cpos;
+ }
+
+ r->pos = rpos;
+ ps->heap[rpos] = r;
+}
+
+static Rnk *
+htop (PS * ps)
+{
+ assert (ps->hhead > ps->heap + 1);
+ return ps->heap[1];
+}
+
+static Rnk *
+hpop (PS * ps)
+{
+ Rnk *res, *last;
+ unsigned end;
+
+ assert (ps->hhead > ps->heap + 1);
+
+ res = ps->heap[1];
+ res->pos = 0;
+
+ end = --ps->hhead - ps->heap;
+ if (end == 1)
+ return res;
+
+ last = ps->heap[end];
+
+ ps->heap[last->pos = 1] = last;
+ hdown (ps, last);
+
+ return res;
+}
+
+inline static void
+hpush (PS * ps, Rnk * r)
+{
+ assert (!r->pos);
+
+ if (ps->hhead == ps->eoh)
+ ENLARGE (ps->heap, ps->hhead, ps->eoh);
+
+ r->pos = ps->hhead++ - ps->heap;
+ ps->heap[r->pos] = r;
+ hup (ps, r);
+}
+
+static void
+fix_trail_lits (PS * ps, long delta)
+{
+ Lit **p;
+ for (p = ps->trail; p < ps->thead; p++)
+ *p += delta;
+}
+
+#ifdef NO_BINARY_CLAUSES
+static void
+fix_impl_lits (PS * ps, long delta)
+{
+ Ltk * s;
+ Lit ** p;
+
+ for (s = ps->impls + 2; s <= ps->impls + 2 * ps->max_var + 1; s++)
+ for (p = s->start; p < s->start + s->count; p++)
+ *p += delta;
+}
+#endif
+
+static void
+fix_clause_lits (PS * ps, long delta)
+{
+ Cls **p, *clause;
+ Lit **q, *lit, **eol;
+
+ for (p = SOC; p != EOC; p = NXC (p))
+ {
+ clause = *p;
+ if (!clause)
+ continue;
+
+ q = clause->lits;
+ eol = end_of_lits (clause);
+ while (q < eol)
+ {
+ assert (q - clause->lits <= (int) clause->size);
+ lit = *q;
+ lit += delta;
+ *q++ = lit;
+ }
+ }
+}
+
+static void
+fix_added_lits (PS * ps, long delta)
+{
+ Lit **p;
+ for (p = ps->added; p < ps->ahead; p++)
+ *p += delta;
+}
+
+static void
+fix_assumed_lits (PS * ps, long delta)
+{
+ Lit **p;
+ for (p = ps->als; p < ps->alshead; p++)
+ *p += delta;
+}
+
+static void
+fix_cls_lits (PS * ps, long delta)
+{
+ Lit **p;
+ for (p = ps->CLS; p < ps->clshead; p++)
+ *p += delta;
+}
+
+static void
+fix_heap_rnks (PS * ps, long delta)
+{
+ Rnk **p;
+
+ for (p = ps->heap + 1; p < ps->hhead; p++)
+ *p += delta;
+}
+
+#ifndef NADC
+
+static void
+fix_ado (long delta, Lit ** ado)
+{
+ Lit ** p;
+ for (p = ado; *p; p++)
+ *p += delta;
+}
+
+static void
+fix_ados (PS * ps, long delta)
+{
+ Lit *** p;
+
+ for (p = ps->ados; p < ps->hados; p++)
+ fix_ado (delta, *p);
+}
+
+#endif
+
+static void
+enlarge (PS * ps, unsigned new_size_vars)
+{
+ long rnks_delta, lits_delta;
+ Lit *old_lits = ps->lits;
+ Rnk *old_rnks = ps->rnks;
+
+ RESIZEN (ps->lits, 2 * ps->size_vars, 2 * new_size_vars);
+ RESIZEN (ps->jwh, 2 * ps->size_vars, 2 * new_size_vars);
+ RESIZEN (ps->htps, 2 * ps->size_vars, 2 * new_size_vars);
+#ifndef NDSC
+ RESIZEN (ps->dhtps, 2 * ps->size_vars, 2 * new_size_vars);
+#endif
+ RESIZEN (ps->impls, 2 * ps->size_vars, 2 * new_size_vars);
+ RESIZEN (ps->vars, ps->size_vars, new_size_vars);
+ RESIZEN (ps->rnks, ps->size_vars, new_size_vars);
+
+ if ((lits_delta = ps->lits - old_lits))
+ {
+ fix_trail_lits (ps, lits_delta);
+ fix_clause_lits (ps, lits_delta);
+ fix_added_lits (ps, lits_delta);
+ fix_assumed_lits (ps, lits_delta);
+ fix_cls_lits (ps, lits_delta);
+#ifdef NO_BINARY_CLAUSES
+ fix_impl_lits (ps, lits_delta);
+#endif
+#ifndef NADC
+ fix_ados (ps, lits_delta);
+#endif
+ }
+
+ if ((rnks_delta = ps->rnks - old_rnks))
+ {
+ fix_heap_rnks (ps, rnks_delta);
+ }
+
+ assert (ps->mhead == ps->marked);
+
+ ps->size_vars = new_size_vars;
+}
+
+static void
+unassign (PS * ps, Lit * lit)
+{
+ Cls *reason;
+ Var *v;
+ Rnk *r;
+
+ assert (lit->val == TRUE);
+
+ LOG ( fprintf (ps->out, "%sunassign %d\n", ps->prefix, LIT2INT (lit)));
+
+ v = LIT2VAR (lit);
+ reason = v->reason;
+
+#ifdef NO_BINARY_CLAUSES
+ assert (reason != &ps->impl);
+ if (ISLITREASON (reason))
+ {
+ /* DO NOTHING */
+ }
+ else
+#endif
+ if (reason)
+ {
+ assert (reason->locked);
+ reason->locked = 0;
+ if (reason->learned && reason->size > 2)
+ {
+ assert (ps->llocked > 0);
+ ps->llocked--;
+ }
+ }
+
+ lit->val = UNDEF;
+ NOTLIT (lit)->val = UNDEF;
+
+ r = VAR2RNK (v);
+ if (!r->pos)
+ hpush (ps, r);
+
+#ifndef NDSC
+ {
+ Cls * p, * next, ** q;
+
+ q = LIT2DHTPS (lit);
+ p = *q;
+ *q = 0;
+
+ while (p)
+ {
+ Lit * other = p->lits[0];
+
+ if (other == lit)
+ {
+ other = p->lits[1];
+ q = p->next + 1;
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH v4 02/12] kconfig: Add picosat.c (1/3)
2024-07-10 6:52 ` [PATCH v4 02/12] kconfig: Add picosat.c (1/3) Ole Schuerks
@ 2024-08-12 8:41 ` Masahiro Yamada
2024-08-16 10:20 ` Ole Schuerks
0 siblings, 1 reply; 25+ messages in thread
From: Masahiro Yamada @ 2024-08-12 8:41 UTC (permalink / raw)
To: Ole Schuerks
Cc: linux-kbuild, jude.gyimah, thorsten.berger, deltaone,
jan.sollmann, mcgrof, linux-kernel
On Wed, Jul 10, 2024 at 3:54 PM Ole Schuerks <ole0811sch@gmail.com> wrote:
>
> PicoSAT is the SAT solver used in this project. picosat.c is the actual
> SAT solver. Since the file is too big for a single patch, it needs to be
> split up. This patch contains the first part of the file.
>
> Signed-off-by: Patrick Franz <deltaone@debian.org>
> Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
> Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
> Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
> ---
> scripts/kconfig/picosat.c | 3000 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 3000 insertions(+)
> create mode 100644 scripts/kconfig/picosat.c
I usually tend to avoid adding huge files like this.
Is this for avoiding any portability issues across distributions?
Debian:
https://packages.debian.org/search?keywords=picosat
Fedora:
https://packages.fedoraproject.org/pkgs/picosat/picosat/
--
Best Regards
Masahiro Yamada
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v4 02/12] kconfig: Add picosat.c (1/3)
2024-08-12 8:41 ` Masahiro Yamada
@ 2024-08-16 10:20 ` Ole Schuerks
2024-08-19 22:04 ` Luis Chamberlain
0 siblings, 1 reply; 25+ messages in thread
From: Ole Schuerks @ 2024-08-16 10:20 UTC (permalink / raw)
To: masahiroy
Cc: deltaone, jan.sollmann, jude.gyimah, linux-kbuild, linux-kernel,
mcgrof, ole0811sch, thorsten.berger
On 8/12/24 10:41, Masahiro Yamada wrote:
> On Wed, Jul 10, 2024 at 3:54 PM Ole Schuerks <ole0811sch@gmail.com> wrote:
>>
>> PicoSAT is the SAT solver used in this project. picosat.c is the actual
>> SAT solver. Since the file is too big for a single patch, it needs to be
>> split up. This patch contains the first part of the file.
>>
>> Signed-off-by: Patrick Franz <deltaone@debian.org>
>> Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
>> Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
>> Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
>> ---
>> scripts/kconfig/picosat.c | 3000 +++++++++++++++++++++++++++++++++++++
>> 1 file changed, 3000 insertions(+)
>> create mode 100644 scripts/kconfig/picosat.c
>
>
> I usually tend to avoid adding huge files like this.
>
>
> Is this for avoiding any portability issues across distributions?
>
>
>
> Debian:
> https://packages.debian.org/search?keywords=picosat
>
>
> Fedora:
> https://packages.fedoraproject.org/pkgs/picosat/picosat/
>
Thank you for the feedback. If we didn't respond to any feedback
take that as that an acknowledgement and that we'll implement the changes
in v5.
I think that with these packages (and if we additionally provide a
repository with a script to compile and install PicoSAT as a library for
the other distros) this should be portable enough. We will remove the
PicoSAT files in v5.
I assume that it should still be possible to use xconfig without PicoSAT
though. What's the best way of letting the user know that they need to
install PicoSAT if they want to use the conflict resolver? My idea would
be to notify the user via the GUI when they try to use the interface of
the conflict resolver without having PicoSAT installed. Do you see any
issues with that/do you prefer some alternative approach?
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v4 02/12] kconfig: Add picosat.c (1/3)
2024-08-16 10:20 ` Ole Schuerks
@ 2024-08-19 22:04 ` Luis Chamberlain
2024-08-29 21:23 ` Ole Schuerks
0 siblings, 1 reply; 25+ messages in thread
From: Luis Chamberlain @ 2024-08-19 22:04 UTC (permalink / raw)
To: Ole Schuerks
Cc: masahiroy, deltaone, jan.sollmann, jude.gyimah, linux-kbuild,
linux-kernel, thorsten.berger
On Fri, Aug 16, 2024 at 12:20:01PM +0200, Ole Schuerks wrote:
> What's the best way of letting the user know that they need to
> install PicoSAT if they want to use the conflict resolver?
> My idea would
> be to notify the user via the GUI when they try to use the interface of
> the conflict resolver without having PicoSAT installed. Do you see any
> issues with that/do you prefer some alternative approach?
Conflicts don't happen often and we already have a printf which happens when
one does, my recommendation would be that we simply opt-in for the
resolver if the user has the requirements installed. Otherwise we only
inform the user to install it if a conflict comes up. Documentation can
also be enhanced to describe this functionality / support.
Luis
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v4 02/12] kconfig: Add picosat.c (1/3)
2024-08-19 22:04 ` Luis Chamberlain
@ 2024-08-29 21:23 ` Ole Schuerks
2024-09-05 8:55 ` Luis Chamberlain
0 siblings, 1 reply; 25+ messages in thread
From: Ole Schuerks @ 2024-08-29 21:23 UTC (permalink / raw)
To: mcgrof
Cc: deltaone, jan.sollmann, jude.gyimah, linux-kbuild, linux-kernel,
masahiroy, ole0811sch, thorsten.berger
On 8/20/24 00:04, Luis Chamberlain wrote:
> On Fri, Aug 16, 2024 at 12:20:01PM +0200, Ole Schuerks wrote:
>> What's the best way of letting the user know that they need to
>> install PicoSAT if they want to use the conflict resolver?
>> My idea would
>> be to notify the user via the GUI when they try to use the interface of
>> the conflict resolver without having PicoSAT installed. Do you see any
>> issues with that/do you prefer some alternative approach?
>
> Conflicts don't happen often and we already have a printf which happens when
> one does, my recommendation would be that we simply opt-in for the
> resolver if the user has the requirements installed. Otherwise we only
> inform the user to install it if a conflict comes up. Documentation can
> also be enhanced to describe this functionality / support.
>
> Luis
There's perhaps a misunderstanding here. I think you are talking about the
rare scenario where a symbol is selected despite the dependencies not being
met (where the printf tells you that). But ConfigFix isn't only useful in
this case. We believe that the most common use will be to enable or disable
symbols with missing dependencies that prevent directly setting the
symbols' values via the GUI.
For example, when one symbol depends on a second symbol, and the second
symbol is set to N, then the first symbol cannot directly be set to M or Y
(assuming it isn't already selected by some symbol). One case of such a
constellation is DEBUG_MISC, which depends on DEBUG_KERNEL. ConfigFix can
identify that DEBUG_KERNEL must be set to Y in order to set DEBUG_MISC to
Y. Conflicts can also occur when trying to lower the value of a symbol: If
a symbol is selected by a second symbol, and the second symbol is set to Y,
then the first symbol can't directly be set to N or M. One such case is
EXPERT, which selects DEBUG_KERNEL.
So, the conflict resolution is useful when users want to quickly enable
some grayed out symbols. If one has to install some external package first,
then that might diminish the usefulness. While there are extreme cases
where it can take hours to manually identify all the dependencies, first
having to build PicoSAT might take longer than manually resolving the
conflict. Many users might then never install PicoSAT to try out the
conflict resolver, even if they would benefit from it.
So the question is whether using PicoSAT as an external library is worth
the portability issues and effort, and whether it wouldn't make more sense
to directly include the PicoSAT source file.
Otherwise, if we go with not including the PicoSAT source, then one could
inform users about the missing package in the GUI, like this:
When PicoSAT is installed:
https://drive.google.com/file/d/1asBfLp1qfOq94a69ZLz2bf3VsUv4IYwL/view?usp=sharing
When PicoSAT is not installed:
https://drive.google.com/file/d/1ytUppyFPtH_G8Gr22X0JAf5wIne-FiJD/view?usp=sharing
Let us know what you think. Include PicoSAT directly as a source or not,
and then inform the user via the GUI?
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v4 02/12] kconfig: Add picosat.c (1/3)
2024-08-29 21:23 ` Ole Schuerks
@ 2024-09-05 8:55 ` Luis Chamberlain
2024-09-20 9:07 ` Ole Schuerks
0 siblings, 1 reply; 25+ messages in thread
From: Luis Chamberlain @ 2024-09-05 8:55 UTC (permalink / raw)
To: Ole Schuerks
Cc: deltaone, jan.sollmann, jude.gyimah, linux-kbuild, linux-kernel,
masahiroy, thorsten.berger
On Thu, Aug 29, 2024 at 11:23:52PM +0200, Ole Schuerks wrote:
> If one has to install some external package first,
> then that might diminish the usefulness. While there are extreme cases
> where it can take hours to manually identify all the dependencies, first
> having to build PicoSAT might take longer than manually resolving the
> conflict. Many users might then never install PicoSAT to try out the
> conflict resolver, even if they would benefit from it.
That's a package dependency problem, ie, a distro thing to consider
which packages users should have installed. But isn't the bigger issue
the fact that you want some C library not the picosat binary tool? Or
would it suffice to just have picosat as a binary installed? I see at
least debian has python3 bindings now too python3-pycosat. So what type
of picosat integration really is best for the task at hand?
> So the question is whether using PicoSAT as an external library is worth
> the portability issues and effort, and whether it wouldn't make more sense
> to directly include the PicoSAT source file.
The pros of an external library are less burden on maintenance, and
otherwise we'd be forking PicoSAT, but as I mentioned, I don't see a c
library but instead just the picosat binary. An alternative is to use PicoSAT as
a git subtree inside Linux on a dedicated directory, this way PicoSAT
can evolve and we can update it when we need to. Note a git subtree is
not the same thing as a git submodule, those are terrible.
> Otherwise, if we go with not including the PicoSAT source, then one could
> inform users about the missing package in the GUI, like this:
> When PicoSAT is installed:
> https://drive.google.com/file/d/1asBfLp1qfOq94a69ZLz2bf3VsUv4IYwL/view?usp=sharing
> When PicoSAT is not installed:
> https://drive.google.com/file/d/1ytUppyFPtH_G8Gr22X0JAf5wIne-FiJD/view?usp=sharing
>
> Let us know what you think. Include PicoSAT directly as a source or not,
> and then inform the user via the GUI?
Do you need the picosat binary or the actual c code for helpers /
library? I don't think we have anything in Linux yet using git
subtrees, but I don't see why we wouldn't for generic tooling and
this might be a good example use case.
Luis
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v4 02/12] kconfig: Add picosat.c (1/3)
2024-09-05 8:55 ` Luis Chamberlain
@ 2024-09-20 9:07 ` Ole Schuerks
0 siblings, 0 replies; 25+ messages in thread
From: Ole Schuerks @ 2024-09-20 9:07 UTC (permalink / raw)
To: mcgrof
Cc: deltaone, jan.sollmann, jude.gyimah, linux-kbuild, linux-kernel,
masahiroy, ole0811sch, thorsten.berger
On 9/5/24 10:55, Luis Chamberlain wrote:
> On Thu, Aug 29, 2024 at 11:23:52PM +0200, Ole Schuerks wrote:
>> If one has to install some external package first,
>> then that might diminish the usefulness. While there are extreme cases
>> where it can take hours to manually identify all the dependencies, first
>> having to build PicoSAT might take longer than manually resolving the
>> conflict. Many users might then never install PicoSAT to try out the
>> conflict resolver, even if they would benefit from it.
>
> That's a package dependency problem, ie, a distro thing to consider
> which packages users should have installed. But isn't the bigger issue
> the fact that you want some C library not the picosat binary tool? Or
> would it suffice to just have picosat as a binary installed? I see at
> least debian has python3 bindings now too python3-pycosat. So what type
> of picosat integration really is best for the task at hand?
>
>> So the question is whether using PicoSAT as an external library is worth
>> the portability issues and effort, and whether it wouldn't make more sense
>> to directly include the PicoSAT source file.
>
> The pros of an external library are less burden on maintenance, and
> otherwise we'd be forking PicoSAT, but as I mentioned, I don't see a c
> library but instead just the picosat binary. An alternative is to use PicoSAT as
> a git subtree inside Linux on a dedicated directory, this way PicoSAT
> can evolve and we can update it when we need to. Note a git subtree is
> not the same thing as a git submodule, those are terrible.
>
>> Otherwise, if we go with not including the PicoSAT source, then one could
>> inform users about the missing package in the GUI, like this:
>> When PicoSAT is installed:
>> https://drive.google.com/file/d/1asBfLp1qfOq94a69ZLz2bf3VsUv4IYwL/view?usp=sharing
>> When PicoSAT is not installed:
>> https://drive.google.com/file/d/1ytUppyFPtH_G8Gr22X0JAf5wIne-FiJD/view?usp=sharing
>>
>> Let us know what you think. Include PicoSAT directly as a source or not,
>> and then inform the user via the GUI?
>
> Do you need the picosat binary or the actual c code for helpers /
> library? I don't think we have anything in Linux yet using git
> subtrees, but I don't see why we wouldn't for generic tooling and
> this might be a good example use case.
>
> Luis
The packages mentioned in
https://lore.kernel.org/all/20240710065255.10338-1-ole0811sch@gmail.com/T/#m34fdf309ecd545d72d898655d8c1a2653d1cdb81
include the necessary libraries. The Python bindings aren't useful for our
purposes, unfortunately, since many important features are missing, in
particular the tracing of which assumptions failed. Using PicoSAT as a
library seems to be the best solution.
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v4 03/12] kconfig: Add picosat.c (2/3)
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 01/12] kconfig: Add picosat.h Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 02/12] kconfig: Add picosat.c (1/3) Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 04/12] kconfig: Add picosat.c (3/3) Ole Schuerks
` (8 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
The second part of picosat.c
Signed-off-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/picosat.c | 3000 +++++++++++++++++++++++++++++++++++++
1 file changed, 3000 insertions(+)
diff --git a/scripts/kconfig/picosat.c b/scripts/kconfig/picosat.c
index 872a38b335c4..c183dffb89a3 100644
--- a/scripts/kconfig/picosat.c
+++ b/scripts/kconfig/picosat.c
@@ -2998,3 +2998,3003 @@ unassign (PS * ps, Lit * lit)
{
other = p->lits[1];
q = p->next + 1;
+ }
+ else
+ {
+ assert (p->lits[1] == lit);
+ q = p->next;
+ }
+
+ next = *q;
+ *q = *LIT2HTPS (other);
+ *LIT2HTPS (other) = p;
+ p = next;
+ }
+ }
+#endif
+
+#ifndef NADC
+ if (v->adotabpos)
+ {
+ assert (ps->nadotab);
+ assert (*v->adotabpos == v->ado);
+
+ *v->adotabpos = 0;
+ v->adotabpos = 0;
+
+ ps->nadotab--;
+ }
+#endif
+}
+
+static Cls *
+var2reason (PS * ps, Var * var)
+{
+ Cls * res = var->reason;
+#ifdef NO_BINARY_CLAUSES
+ Lit * this, * other;
+ if (ISLITREASON (res))
+ {
+ this = VAR2LIT (var);
+ if (this->val == FALSE)
+ this = NOTLIT (this);
+
+ other = REASON2LIT (res);
+ assert (other->val == TRUE);
+ assert (this->val == TRUE);
+ res = setimpl (ps, NOTLIT (other), this);
+ }
+#else
+ (void) ps;
+#endif
+ return res;
+}
+
+static void
+mark_clause_to_be_collected (Cls * c)
+{
+ assert (!c->collect);
+ c->collect = 1;
+}
+
+static void
+undo (PS * ps, unsigned new_level)
+{
+ Lit *lit;
+ Var *v;
+
+ while (ps->thead > ps->trail)
+ {
+ lit = *--ps->thead;
+ v = LIT2VAR (lit);
+ if (v->level == new_level)
+ {
+ ps->thead++; /* fix pre decrement */
+ break;
+ }
+
+ unassign (ps, lit);
+ }
+
+ ps->LEVEL = new_level;
+ ps->ttail = ps->thead;
+ ps->ttail2 = ps->thead;
+#ifndef NADC
+ ps->ttailado = ps->thead;
+#endif
+
+#ifdef NO_BINARY_CLAUSES
+ if (ps->conflict == &ps->cimpl)
+ resetcimpl (ps);
+#endif
+#ifndef NADC
+ if (ps->conflict && ps->conflict == ps->adoconflict)
+ resetadoconflict (ps);
+#endif
+ ps->conflict = ps->mtcls;
+ if (ps->LEVEL < ps->adecidelevel)
+ {
+ assert (ps->als < ps->alshead);
+ ps->adecidelevel = 0;
+ ps->alstail = ps->als;
+ }
+ LOG ( fprintf (ps->out, "%sback to level %u\n", ps->prefix, ps->LEVEL));
+}
+
+#ifndef NDEBUG
+
+static int
+clause_satisfied (Cls * c)
+{
+ Lit **p, **eol, *lit;
+
+ eol = end_of_lits (c);
+ for (p = c->lits; p < eol; p++)
+ {
+ lit = *p;
+ if (lit->val == TRUE)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+original_clauses_satisfied (PS * ps)
+{
+ Cls **p, *c;
+
+ for (p = ps->oclauses; p < ps->ohead; p++)
+ {
+ c = *p;
+
+ if (!c)
+ continue;
+
+ if (c->learned)
+ continue;
+
+ assert (clause_satisfied (c));
+ }
+}
+
+static void
+assumptions_satisfied (PS * ps)
+{
+ Lit *lit, ** p;
+
+ for (p = ps->als; p < ps->alshead; p++)
+ {
+ lit = *p;
+ assert (lit->val == TRUE);
+ }
+}
+
+#endif
+
+static void
+sflush (PS * ps)
+{
+ double now = picosat_time_stamp ();
+ double delta = now - ps->entered;
+ delta = (delta < 0) ? 0 : delta;
+ ps->seconds += delta;
+ ps->entered = now;
+}
+
+static double
+mb (PS * ps)
+{
+ return ps->current_bytes / (double) (1 << 20);
+}
+
+static double
+avglevel (PS * ps)
+{
+ return ps->decisions ? ps->levelsum / ps->decisions : 0.0;
+}
+
+static void
+rheader (PS * ps)
+{
+ assert (ps->lastrheader <= ps->reports);
+
+ if (ps->lastrheader == ps->reports)
+ return;
+
+ ps->lastrheader = ps->reports;
+
+ fprintf (ps->out, "%s\n", ps->prefix);
+ fprintf (ps->out, "%s %s\n", ps->prefix, ps->rline[0]);
+ fprintf (ps->out, "%s %s\n", ps->prefix, ps->rline[1]);
+ fprintf (ps->out, "%s\n", ps->prefix);
+}
+
+static unsigned
+dynamic_flips_per_assignment_per_mille (PS * ps)
+{
+ assert (FFLIPPEDPREC >= 1000);
+ return ps->sdflips / (FFLIPPEDPREC / 1000);
+}
+
+#ifdef NLUBY
+
+static int
+high_agility (PS * ps)
+{
+ return dynamic_flips_per_assignment_per_mille (ps) >= 200;
+}
+
+static int
+very_high_agility (PS * ps)
+{
+ return dynamic_flips_per_assignment_per_mille (ps) >= 250;
+}
+
+#else
+
+static int
+medium_agility (PS * ps)
+{
+ return dynamic_flips_per_assignment_per_mille (ps) >= 230;
+}
+
+#endif
+
+static void
+relemdata (PS * ps)
+{
+ char *p;
+ int x;
+
+ if (ps->reports < 0)
+ {
+ /* strip trailing white space
+ */
+ for (x = 0; x <= 1; x++)
+ {
+ p = ps->rline[x] + strlen (ps->rline[x]);
+ while (p-- > ps->rline[x])
+ {
+ if (*p != ' ')
+ break;
+
+ *p = 0;
+ }
+ }
+
+ rheader (ps);
+ }
+ else
+ fputc ('\n', ps->out);
+
+ ps->RCOUNT = 0;
+}
+
+static void
+relemhead (PS * ps, const char * name, int fp, double val)
+{
+ int x, y, len, size;
+ const char *fmt;
+ unsigned tmp, e;
+
+ if (ps->reports < 0)
+ {
+ x = ps->RCOUNT & 1;
+ y = (ps->RCOUNT / 2) * 12 + x * 6;
+
+ if (ps->RCOUNT == 1)
+ sprintf (ps->rline[1], "%6s", "");
+
+ len = strlen (name);
+ while (ps->szrline <= len + y + 1)
+ {
+ size = ps->szrline ? 2 * ps->szrline : 128;
+ ps->rline[0] = resize (ps, ps->rline[0], ps->szrline, size);
+ ps->rline[1] = resize (ps, ps->rline[1], ps->szrline, size);
+ ps->szrline = size;
+ }
+
+ fmt = (len <= 6) ? "%6s%10s" : "%-10s%4s";
+ sprintf (ps->rline[x] + y, fmt, name, "");
+ }
+ else if (val < 0)
+ {
+ assert (fp);
+
+ if (val > -100 && (tmp = val * 10.0 - 0.5) > -1000.0)
+ {
+ fprintf (ps->out, "-%4.1f ", -tmp / 10.0);
+ }
+ else
+ {
+ tmp = -val / 10.0 + 0.5;
+ e = 1;
+ while (tmp >= 100)
+ {
+ tmp /= 10;
+ e++;
+ }
+
+ fprintf (ps->out, "-%2ue%u ", tmp, e);
+ }
+ }
+ else
+ {
+ if (fp && val < 1000 && (tmp = val * 10.0 + 0.5) < 10000)
+ {
+ fprintf (ps->out, "%5.1f ", tmp / 10.0);
+ }
+ else if (!fp && (tmp = val) < 100000)
+ {
+ fprintf (ps->out, "%5u ", tmp);
+ }
+ else
+ {
+ tmp = val / 10.0 + 0.5;
+ e = 1;
+
+ while (tmp >= 1000)
+ {
+ tmp /= 10;
+ e++;
+ }
+
+ fprintf (ps->out, "%3ue%u ", tmp, e);
+ }
+ }
+
+ ps->RCOUNT++;
+}
+
+inline static void
+relem (PS * ps, const char *name, int fp, double val)
+{
+ if (name)
+ relemhead (ps, name, fp, val);
+ else
+ relemdata (ps);
+}
+
+static unsigned
+reduce_limit_on_lclauses (PS * ps)
+{
+ unsigned res = ps->lreduce;
+ res += ps->llocked;
+ return res;
+}
+
+static void
+report (PS * ps, int replevel, char type)
+{
+ int rounds;
+
+#ifdef RCODE
+ (void) type;
+#endif
+
+ if (ps->verbosity < replevel)
+ return;
+
+ sflush (ps);
+
+ if (!ps->reports)
+ ps->reports = -1;
+
+ for (rounds = (ps->reports < 0) ? 2 : 1; rounds; rounds--)
+ {
+ if (ps->reports >= 0)
+ fprintf (ps->out, "%s%c ", ps->prefix, type);
+
+ relem (ps, "seconds", 1, ps->seconds);
+ relem (ps, "level", 1, avglevel (ps));
+ assert (ps->fixed <= ps->max_var);
+ relem (ps, "variables", 0, ps->max_var - ps->fixed);
+ relem (ps, "used", 1, PERCENT (ps->vused, ps->max_var));
+ relem (ps, "original", 0, ps->noclauses);
+ relem (ps, "conflicts", 0, ps->conflicts);
+ // relem (ps, "decisions", 0, ps->decisions);
+ // relem (ps, "conf/dec", 1, PERCENT(ps->conflicts,ps->decisions));
+ // relem (ps, "limit", 0, reduce_limit_on_lclauses (ps));
+ relem (ps, "learned", 0, ps->nlclauses);
+ // relem (ps, "limit", 1, PERCENT (ps->nlclauses, reduce_limit_on_lclauses (ps)));
+ relem (ps, "limit", 0, ps->lreduce);
+#ifdef STATS
+ relem (ps, "learning", 1, PERCENT (ps->llused, ps->lladded));
+#endif
+ relem (ps, "agility", 1, dynamic_flips_per_assignment_per_mille (ps) / 10.0);
+ // relem (ps, "original", 0, ps->noclauses);
+ relem (ps, "MB", 1, mb (ps));
+ // relem (ps, "lladded", 0, ps->lladded);
+ // relem (ps, "llused", 0, ps->llused);
+
+ relem (ps, 0, 0, 0);
+
+ ps->reports++;
+ }
+
+ /* Adapt this to the number of rows in your terminal.
+ */
+ #define ROWS 25
+
+ if (ps->reports % (ROWS - 3) == (ROWS - 4))
+ rheader (ps);
+
+ fflush (ps->out);
+}
+
+static int
+bcp_queue_is_empty (PS * ps)
+{
+ if (ps->ttail != ps->thead)
+ return 0;
+
+ if (ps->ttail2 != ps->thead)
+ return 0;
+
+#ifndef NADC
+ if (ps->ttailado != ps->thead)
+ return 0;
+#endif
+
+ return 1;
+}
+
+static int
+satisfied (PS * ps)
+{
+ assert (!ps->mtcls);
+ assert (!ps->failed_assumption);
+ if (ps->alstail < ps->alshead)
+ return 0;
+ assert (!ps->conflict);
+ assert (bcp_queue_is_empty (ps));
+ return ps->thead == ps->trail + ps->max_var; /* all assigned */
+}
+
+static void
+vrescore (PS * ps)
+{
+ Rnk *p, *eor = ps->rnks + ps->max_var;
+ for (p = ps->rnks + 1; p <= eor; p++)
+ if (p->score != INFFLT)
+ p->score = mulflt (p->score, ps->ilvinc);
+ ps->vinc = mulflt (ps->vinc, ps->ilvinc);;
+#ifdef VISCORES
+ ps->nvinc = mulflt (ps->nvinc, ps->lscore);;
+#endif
+}
+
+static void
+inc_score (PS * ps, Var * v)
+{
+ Flt score;
+ Rnk *r;
+
+#ifndef NFL
+ if (ps->simplifying)
+ return;
+#endif
+
+ if (!v->level)
+ return;
+
+ if (v->internal)
+ return;
+
+ r = VAR2RNK (v);
+ score = r->score;
+
+ assert (score != INFFLT);
+
+ score = addflt (score, ps->vinc);
+ assert (score < INFFLT);
+ r->score = score;
+ if (r->pos > 0)
+ hup (ps, r);
+
+ if (score > ps->lscore)
+ vrescore (ps);
+}
+
+static void
+inc_activity (PS * ps, Cls * c)
+{
+ Act *p;
+
+ if (!c->learned)
+ return;
+
+ if (c->size <= 2)
+ return;
+
+ p = CLS2ACT (c);
+ *p = addflt (*p, ps->cinc);
+}
+
+static unsigned
+hashlevel (unsigned l)
+{
+ return 1u << (l & 31);
+}
+
+static void
+push (PS * ps, Var * v)
+{
+ if (ps->dhead == ps->eod)
+ ENLARGE (ps->dfs, ps->dhead, ps->eod);
+
+ *ps->dhead++ = v;
+}
+
+static Var *
+pop (PS * ps)
+{
+ assert (ps->dfs < ps->dhead);
+ return *--ps->dhead;
+}
+
+static void
+analyze (PS * ps)
+{
+ unsigned open, minlevel, siglevels, l, old, i, orig;
+ Lit *this, *other, **p, **q, **eol;
+ Var *v, *u, **m, *start, *uip;
+ Cls *c;
+
+ assert (ps->conflict);
+
+ assert (ps->ahead == ps->added);
+ assert (ps->mhead == ps->marked);
+ assert (ps->rhead == ps->resolved);
+
+ /* First, search for First UIP variable and mark all resolved variables.
+ * At the same time determine the minimum decision level involved.
+ * Increase activities of resolved variables.
+ */
+ q = ps->thead;
+ open = 0;
+ minlevel = ps->LEVEL;
+ siglevels = 0;
+ uip = 0;
+
+ c = ps->conflict;
+
+ for (;;)
+ {
+ add_antecedent (ps, c);
+ inc_activity (ps, c);
+ eol = end_of_lits (c);
+ for (p = c->lits; p < eol; p++)
+ {
+ other = *p;
+
+ if (other->val == TRUE)
+ continue;
+
+ assert (other->val == FALSE);
+
+ u = LIT2VAR (other);
+ if (u->mark)
+ continue;
+
+ u->mark = 1;
+ inc_score (ps, u);
+ use_var (ps, u);
+
+ if (u->level == ps->LEVEL)
+ {
+ open++;
+ }
+ else
+ {
+ push_var_as_marked (ps, u);
+
+ if (u->level)
+ {
+ /* The statistics counter 'nonminimizedllits' sums up the
+ * number of literals that would be added if only the
+ * 'first UIP' scheme for learned clauses would be used
+ * and no clause minimization.
+ */
+ ps->nonminimizedllits++;
+
+ if (u->level < minlevel)
+ minlevel = u->level;
+
+ siglevels |= hashlevel (u->level);
+ }
+ else
+ {
+ assert (!u->level);
+ assert (u->reason);
+ }
+ }
+ }
+
+ do
+ {
+ if (q == ps->trail)
+ {
+ uip = 0;
+ goto DONE_FIRST_UIP;
+ }
+
+ this = *--q;
+ uip = LIT2VAR (this);
+ }
+ while (!uip->mark);
+
+ uip->mark = 0;
+
+ c = var2reason (ps, uip);
+#ifdef NO_BINARY_CLAUSES
+ if (c == &ps->impl)
+ resetimpl (ps);
+#endif
+ open--;
+ if ((!open && ps->LEVEL) || !c)
+ break;
+
+ assert (c);
+ }
+
+DONE_FIRST_UIP:
+
+ if (uip)
+ {
+ assert (ps->LEVEL);
+ this = VAR2LIT (uip);
+ this += (this->val == TRUE);
+ ps->nonminimizedllits++;
+ ps->minimizedllits++;
+ add_lit (ps, this);
+#ifdef STATS
+ if (uip->reason)
+ ps->uips++;
+#endif
+ }
+ else
+ assert (!ps->LEVEL);
+
+ /* Second, try to mark more intermediate variables, with the goal to
+ * minimize the conflict clause. This is a DFS from already marked
+ * variables backward through the implication graph. It tries to reach
+ * other marked variables. If the search reaches an unmarked decision
+ * variable or a variable assigned below the minimum level of variables in
+ * the first uip learned clause or a level on which no variable has been
+ * marked, then the variable from which the DFS is started is not
+ * redundant. Otherwise the start variable is redundant and will
+ * eventually be removed from the learned clause in step 4. We initially
+ * implemented BFS, but then profiling revelead that this step is a bottle
+ * neck for certain incremental applications. After switching to DFS this
+ * hot spot went away.
+ */
+ orig = ps->mhead - ps->marked;
+ for (i = 0; i < orig; i++)
+ {
+ start = ps->marked[i];
+
+ assert (start->mark);
+ assert (start != uip);
+ assert (start->level < ps->LEVEL);
+
+ if (!start->reason)
+ continue;
+
+ old = ps->mhead - ps->marked;
+ assert (ps->dhead == ps->dfs);
+ push (ps, start);
+
+ while (ps->dhead > ps->dfs)
+ {
+ u = pop (ps);
+ assert (u->mark);
+
+ c = var2reason (ps, u);
+#ifdef NO_BINARY_CLAUSES
+ if (c == &ps->impl)
+ resetimpl (ps);
+#endif
+ if (!c ||
+ ((l = u->level) &&
+ (l < minlevel || ((hashlevel (l) & ~siglevels)))))
+ {
+ while (ps->mhead > ps->marked + old) /* reset all marked */
+ (*--ps->mhead)->mark = 0;
+
+ ps->dhead = ps->dfs; /* and DFS stack */
+ break;
+ }
+
+ eol = end_of_lits (c);
+ for (p = c->lits; p < eol; p++)
+ {
+ v = LIT2VAR (*p);
+ if (v->mark)
+ continue;
+
+ mark_var (ps, v);
+ push (ps, v);
+ }
+ }
+ }
+
+ for (m = ps->marked; m < ps->mhead; m++)
+ {
+ v = *m;
+
+ assert (v->mark);
+ assert (!v->resolved);
+
+ use_var (ps, v);
+
+ c = var2reason (ps, v);
+ if (!c)
+ continue;
+
+#ifdef NO_BINARY_CLAUSES
+ if (c == &ps->impl)
+ resetimpl (ps);
+#endif
+ eol = end_of_lits (c);
+ for (p = c->lits; p < eol; p++)
+ {
+ other = *p;
+
+ u = LIT2VAR (other);
+ if (!u->level)
+ continue;
+
+ if (!u->mark) /* 'MARKTEST' */
+ break;
+ }
+
+ if (p != eol)
+ continue;
+
+ add_antecedent (ps, c);
+ v->resolved = 1;
+ }
+
+ for (m = ps->marked; m < ps->mhead; m++)
+ {
+ v = *m;
+
+ assert (v->mark);
+ v->mark = 0;
+
+ if (v->resolved)
+ {
+ v->resolved = 0;
+ continue;
+ }
+
+ this = VAR2LIT (v);
+ if (this->val == TRUE)
+ this++; /* actually NOTLIT */
+
+ add_lit (ps, this);
+ ps->minimizedllits++;
+ }
+
+ assert (ps->ahead <= ps->eoa);
+ assert (ps->rhead <= ps->eor);
+
+ ps->mhead = ps->marked;
+}
+
+static void
+fanalyze (PS * ps)
+{
+ Lit ** eol, ** p, * lit;
+ Cls * c, * reason;
+ Var * v, * u;
+ int next;
+
+#ifndef RCODE
+ double start = picosat_time_stamp ();
+#endif
+
+ assert (ps->failed_assumption);
+ assert (ps->failed_assumption->val == FALSE);
+
+ v = LIT2VAR (ps->failed_assumption);
+ reason = var2reason (ps, v);
+ if (!reason) return;
+#ifdef NO_BINARY_CLAUSES
+ if (reason == &ps->impl)
+ resetimpl (ps);
+#endif
+
+ eol = end_of_lits (reason);
+ for (p = reason->lits; p != eol; p++)
+ {
+ lit = *p;
+ u = LIT2VAR (lit);
+ if (u == v) continue;
+ if (u->reason) break;
+ }
+ if (p == eol) return;
+
+ assert (ps->ahead == ps->added);
+ assert (ps->mhead == ps->marked);
+ assert (ps->rhead == ps->resolved);
+
+ next = 0;
+ mark_var (ps, v);
+ add_lit (ps, NOTLIT (ps->failed_assumption));
+
+ do
+ {
+ v = ps->marked[next++];
+ use_var (ps, v);
+ if (v->reason)
+ {
+ reason = var2reason (ps, v);
+#ifdef NO_BINARY_CLAUSES
+ if (reason == &ps->impl)
+ resetimpl (ps);
+#endif
+ add_antecedent (ps, reason);
+ eol = end_of_lits (reason);
+ for (p = reason->lits; p != eol; p++)
+ {
+ lit = *p;
+ u = LIT2VAR (lit);
+ if (u == v) continue;
+ if (u->mark) continue;
+ mark_var (ps, u);
+ }
+ }
+ else
+ {
+ lit = VAR2LIT (v);
+ if (lit->val == TRUE) lit = NOTLIT (lit);
+ add_lit (ps, lit);
+ }
+ }
+ while (ps->marked + next < ps->mhead);
+
+ c = add_simplified_clause (ps, 1);
+ v = LIT2VAR (ps->failed_assumption);
+ reason = v->reason;
+#ifdef NO_BINARY_CLAUSES
+ if (!ISLITREASON (reason))
+#endif
+ {
+ assert (reason->locked);
+ reason->locked = 0;
+ if (reason->learned && reason->size > 2)
+ {
+ assert (ps->llocked > 0);
+ ps->llocked--;
+ }
+ }
+
+#ifdef NO_BINARY_CLAUSES
+ if (c == &ps->impl)
+ {
+ c = impl2reason (ps, NOTLIT (ps->failed_assumption));
+ }
+ else
+#endif
+ {
+ assert (c->learned);
+ assert (!c->locked);
+ c->locked = 1;
+ if (c->size > 2)
+ {
+ ps->llocked++;
+ assert (ps->llocked > 0);
+ }
+ }
+
+ v->reason = c;
+
+ while (ps->mhead > ps->marked)
+ (*--ps->mhead)->mark = 0;
+
+ if (ps->verbosity)
+ fprintf (ps->out, "%sfanalyze took %.1f seconds\n",
+ ps->prefix, picosat_time_stamp () - start);
+}
+
+/* Propagate assignment of 'this' to 'FALSE' by visiting all binary clauses in
+ * which 'this' occurs.
+ */
+inline static void
+prop2 (PS * ps, Lit * this)
+{
+#ifdef NO_BINARY_CLAUSES
+ Lit ** l, ** start;
+ Ltk * lstk;
+#else
+ Cls * c, ** p;
+ Cls * next;
+#endif
+ Lit * other;
+ Val tmp;
+
+ assert (this->val == FALSE);
+
+#ifdef NO_BINARY_CLAUSES
+ lstk = LIT2IMPLS (this);
+ start = lstk->start;
+ l = start + lstk->count;
+ while (l != start)
+ {
+ /* The counter 'visits' is the number of clauses that are
+ * visited during propagations of assignments.
+ */
+ ps->visits++;
+#ifdef STATS
+ ps->bvisits++;
+#endif
+ other = *--l;
+ tmp = other->val;
+
+ if (tmp == TRUE)
+ {
+#ifdef STATS
+ ps->othertrue++;
+ ps->othertrue2++;
+ if (LIT2VAR (other)->level < ps->LEVEL)
+ ps->othertrue2u++;
+#endif
+ continue;
+ }
+
+ if (tmp != FALSE)
+ {
+ assign_forced (ps, other, LIT2REASON (NOTLIT(this)));
+ continue;
+ }
+
+ if (ps->conflict == &ps->cimpl)
+ resetcimpl (ps);
+ ps->conflict = setcimpl (ps, this, other);
+ }
+#else
+ /* Traverse all binary clauses with 'this'. Head/Tail pointers for binary
+ * clauses do not have to be modified here.
+ */
+ p = LIT2IMPLS (this);
+ for (c = *p; c; c = next)
+ {
+ ps->visits++;
+#ifdef STATS
+ ps->bvisits++;
+#endif
+ assert (!c->collect);
+#ifdef TRACE
+ assert (!c->collected);
+#endif
+ assert (c->size == 2);
+
+ other = c->lits[0];
+ if (other == this)
+ {
+ next = c->next[0];
+ other = c->lits[1];
+ }
+ else
+ next = c->next[1];
+
+ tmp = other->val;
+
+ if (tmp == TRUE)
+ {
+#ifdef STATS
+ ps->othertrue++;
+ ps->othertrue2++;
+ if (LIT2VAR (other)->level < ps->LEVEL)
+ ps->othertrue2u++;
+#endif
+ continue;
+ }
+
+ if (tmp == FALSE)
+ ps->conflict = c;
+ else
+ assign_forced (ps, other, c); /* unit clause */
+ }
+#endif /* !defined(NO_BINARY_CLAUSES) */
+}
+
+#ifndef NDSC
+static int
+should_disconnect_head_tail (PS * ps, Lit * lit)
+{
+ unsigned litlevel;
+ Var * v;
+
+ assert (lit->val == TRUE);
+
+ v = LIT2VAR (lit);
+ litlevel = v->level;
+
+ if (!litlevel)
+ return 1;
+
+#ifndef NFL
+ if (ps->simplifying)
+ return 0;
+#endif
+
+ return litlevel < ps->LEVEL;
+}
+#endif
+
+inline static void
+propl (PS * ps, Lit * this)
+{
+ Lit **l, *other, *prev, *new_lit, **eol;
+ Cls *next, **htp_ptr, **new_htp_ptr;
+ Cls *c;
+#ifdef STATS
+ unsigned size;
+#endif
+
+ htp_ptr = LIT2HTPS (this);
+ assert (this->val == FALSE);
+
+ /* Traverse all non binary clauses with 'this'. Head/Tail pointers are
+ * updated as well.
+ */
+ for (c = *htp_ptr; c; c = next)
+ {
+ ps->visits++;
+#ifdef STATS
+ size = c->size;
+ assert (size >= 3);
+ ps->traversals++; /* other is dereferenced at least */
+
+ if (size == 3)
+ ps->tvisits++;
+ else if (size >= 4)
+ {
+ ps->lvisits++;
+ ps->ltraversals++;
+ }
+#endif
+#ifdef TRACE
+ assert (!c->collected);
+#endif
+ assert (c->size > 0);
+
+ other = c->lits[0];
+ if (other != this)
+ {
+ assert (c->size != 1);
+ c->lits[0] = this;
+ c->lits[1] = other;
+ next = c->next[1];
+ c->next[1] = c->next[0];
+ c->next[0] = next;
+ }
+ else if (c->size == 1) /* With assumptions we need to
+ * traverse unit clauses as well.
+ */
+ {
+ assert (!ps->conflict);
+ ps->conflict = c;
+ break;
+ }
+ else
+ {
+ assert (other == this && c->size > 1);
+ other = c->lits[1];
+ next = c->next[0];
+ }
+ assert (other == c->lits[1]);
+ assert (this == c->lits[0]);
+ assert (next == c->next[0]);
+ assert (!c->collect);
+
+ if (other->val == TRUE)
+ {
+#ifdef STATS
+ ps->othertrue++;
+ ps->othertruel++;
+#endif
+#ifndef NDSC
+ if (should_disconnect_head_tail (ps, other))
+ {
+ new_htp_ptr = LIT2DHTPS (other);
+ c->next[0] = *new_htp_ptr;
+ *new_htp_ptr = c;
+#ifdef STATS
+ ps->othertruelu++;
+#endif
+ *htp_ptr = next;
+ continue;
+ }
+#endif
+ htp_ptr = c->next;
+ continue;
+ }
+
+ l = c->lits + 1;
+ eol = (Lit**) c->lits + c->size;
+ prev = this;
+
+ while (++l != eol)
+ {
+#ifdef STATS
+ if (size >= 3)
+ {
+ ps->traversals++;
+ if (size > 3)
+ ps->ltraversals++;
+ }
+#endif
+ new_lit = *l;
+ *l = prev;
+ prev = new_lit;
+ if (new_lit->val != FALSE) break;
+ }
+
+ if (l == eol)
+ {
+ while (l > c->lits + 2)
+ {
+ new_lit = *--l;
+ *l = prev;
+ prev = new_lit;
+ }
+ assert (c->lits[0] == this);
+
+ assert (other == c->lits[1]);
+ if (other->val == FALSE) /* found conflict */
+ {
+ assert (!ps->conflict);
+ ps->conflict = c;
+ return;
+ }
+
+ assign_forced (ps, other, c); /* unit clause */
+ htp_ptr = c->next;
+ }
+ else
+ {
+ assert (new_lit->val == TRUE || new_lit->val == UNDEF);
+ c->lits[0] = new_lit;
+ // *l = this;
+ new_htp_ptr = LIT2HTPS (new_lit);
+ c->next[0] = *new_htp_ptr;
+ *new_htp_ptr = c;
+ *htp_ptr = next;
+ }
+ }
+}
+
+#ifndef NADC
+
+static unsigned primes[] = { 996293, 330643, 753947, 500873 };
+
+#define PRIMES ((sizeof primes)/sizeof *primes)
+
+static unsigned
+hash_ado (PS * ps, Lit ** ado, unsigned salt)
+{
+ unsigned i, res, tmp;
+ Lit ** p, * lit;
+
+ assert (salt < PRIMES);
+
+ i = salt;
+ res = 0;
+
+ for (p = ado; (lit = *p); p++)
+ {
+ assert (lit->val);
+
+ tmp = res >> 31;
+ res <<= 1;
+
+ if (lit->val > 0)
+ res |= 1;
+
+ assert (i < PRIMES);
+ res *= primes[i++];
+ if (i == PRIMES)
+ i = 0;
+
+ res += tmp;
+ }
+
+ return res & (ps->szadotab - 1);
+}
+
+static unsigned
+cmp_ado (Lit ** a, Lit ** b)
+{
+ Lit ** p, ** q, * l, * k;
+ int res;
+
+ for (p = a, q = b; (l = *p); p++, q++)
+ {
+ k = *q;
+ assert (k);
+ if ((res = (l->val - k->val)))
+ return res;
+ }
+
+ assert (!*q);
+
+ return 0;
+}
+
+static Lit ***
+find_ado (PS * ps, Lit ** ado)
+{
+ Lit *** res, ** other;
+ unsigned pos, delta;
+
+ pos = hash_ado (ps, ado, 0);
+ assert (pos < ps->szadotab);
+ res = ps->adotab + pos;
+
+ other = *res;
+ if (!other || !cmp_ado (other, ado))
+ return res;
+
+ delta = hash_ado (ps, ado, 1);
+ if (!(delta & 1))
+ delta++;
+
+ assert (delta & 1);
+ assert (delta < ps->szadotab);
+
+ for (;;)
+ {
+ pos += delta;
+ if (pos >= ps->szadotab)
+ pos -= ps->szadotab;
+
+ assert (pos < ps->szadotab);
+ res = ps->adotab + pos;
+ other = *res;
+ if (!other || !cmp_ado (other, ado))
+ return res;
+ }
+}
+
+static void
+enlarge_adotab (PS * ps)
+{
+ /* TODO make this generic */
+
+ ABORTIF (ps->szadotab,
+ "internal: all different objects table needs larger initial size");
+ assert (!ps->nadotab);
+ ps->szadotab = 10000;
+ NEWN (ps->adotab, ps->szadotab);
+ CLRN (ps->adotab, ps->szadotab);
+}
+
+static int
+propado (PS * ps, Var * v)
+{
+ Lit ** p, ** q, *** adotabpos, **ado, * lit;
+ Var * u;
+
+ if (ps->LEVEL && ps->adodisabled)
+ return 1;
+
+ assert (!ps->conflict);
+ assert (!ps->adoconflict);
+ assert (VAR2LIT (v)->val != UNDEF);
+ assert (!v->adotabpos);
+
+ if (!v->ado)
+ return 1;
+
+ assert (v->inado);
+
+ for (p = v->ado; (lit = *p); p++)
+ if (lit->val == UNDEF)
+ {
+ u = LIT2VAR (lit);
+ assert (!u->ado);
+ u->ado = v->ado;
+ v->ado = 0;
+
+ return 1;
+ }
+
+ if (4 * ps->nadotab >= 3 * ps->szadotab) /* at least 75% filled */
+ enlarge_adotab (ps);
+
+ adotabpos = find_ado (ps, v->ado);
+ ado = *adotabpos;
+
+ if (!ado)
+ {
+ ps->nadotab++;
+ v->adotabpos = adotabpos;
+ *adotabpos = v->ado;
+ return 1;
+ }
+
+ assert (ado != v->ado);
+
+ ps->adoconflict = new_clause (ps, 2 * llength (ado), 1);
+ q = ps->adoconflict->lits;
+
+ for (p = ado; (lit = *p); p++)
+ *q++ = lit->val == FALSE ? lit : NOTLIT (lit);
+
+ for (p = v->ado; (lit = *p); p++)
+ *q++ = lit->val == FALSE ? lit : NOTLIT (lit);
+
+ assert (q == ENDOFCLS (ps->adoconflict));
+ ps->conflict = ps->adoconflict;
+ ps->adoconflicts++;
+ return 0;
+}
+
+#endif
+
+static void
+bcp (PS * ps)
+{
+ int props = 0;
+ assert (!ps->conflict);
+
+ if (ps->mtcls)
+ return;
+
+ for (;;)
+ {
+ if (ps->ttail2 < ps->thead) /* prioritize implications */
+ {
+ props++;
+ prop2 (ps, NOTLIT (*ps->ttail2++));
+ }
+ else if (ps->ttail < ps->thead) /* unit clauses or clauses with length > 2 */
+ {
+ if (ps->conflict) break;
+ propl (ps, NOTLIT (*ps->ttail++));
+ if (ps->conflict) break;
+ }
+#ifndef NADC
+ else if (ps->ttailado < ps->thead)
+ {
+ if (ps->conflict) break;
+ propado (ps, LIT2VAR (*ps->ttailado++));
+ if (ps->conflict) break;
+ }
+#endif
+ else
+ break; /* all assignments propagated, so break */
+ }
+
+ ps->propagations += props;
+}
+
+static unsigned
+drive (PS * ps)
+{
+ unsigned res, vlevel;
+ Lit **p;
+ Var *v;
+
+ res = 0;
+ for (p = ps->added; p < ps->ahead; p++)
+ {
+ v = LIT2VAR (*p);
+ vlevel = v->level;
+ assert (vlevel <= ps->LEVEL);
+ if (vlevel < ps->LEVEL && vlevel > res)
+ res = vlevel;
+ }
+
+ return res;
+}
+
+#ifdef VISCORES
+
+static void
+viscores (PS * ps)
+{
+ Rnk *p, *eor = ps->rnks + ps->max_var;
+ char name[100], cmd[200];
+ FILE * data;
+ Flt s;
+ int i;
+
+ for (p = ps->rnks + 1; p <= ps->eor; p++)
+ {
+ s = p->score;
+ if (s == INFFLT)
+ continue;
+ s = mulflt (s, ps->nvinc);
+ assert (flt2double (s) <= 1.0);
+ }
+
+ sprintf (name, "/tmp/picosat-viscores/data/%08u", ps->conflicts);
+ sprintf (cmd, "sort -n|nl>%s", name);
+
+ data = popen (cmd, "w");
+ for (p = ps->rnks + 1; p <= ps->eor; p++)
+ {
+ s = p->score;
+ if (s == INFFLT)
+ continue;
+ s = mulflt (s, ps->nvinc);
+ fprintf (data, "%lf %d\n", 100.0 * flt2double (s), (int)(p - ps->rnks));
+ }
+ fflush (data);
+ pclose (data);
+
+ for (i = 0; i < 8; i++)
+ {
+ sprintf (cmd, "awk '$3%%8==%d' %s>%s.%d", i, name, name, i);
+ system (cmd);
+ }
+
+ fprintf (ps->fviscores, "set title \"%u\"\n", ps->conflicts);
+ fprintf (ps->fviscores, "plot [0:%u] 0, 100 * (1 - 1/1.1), 100", ps->max_var);
+
+ for (i = 0; i < 8; i++)
+ fprintf (ps->fviscores,
+ ", \"%s.%d\" using 1:2:3 with labels tc lt %d",
+ name, i, i + 1);
+
+ fputc ('\n', ps->fviscores);
+ fflush (ps->fviscores);
+#ifndef WRITEGIF
+ usleep (50000); /* refresh rate of 20 Hz */
+#endif
+}
+
+#endif
+
+static void
+crescore (PS * ps)
+{
+ Cls **p, *c;
+ Act *a;
+ Flt factor;
+ int l = log2flt (ps->cinc);
+ assert (l > 0);
+ factor = base2flt (1, -l);
+
+ for (p = ps->lclauses; p != ps->lhead; p++)
+ {
+ c = *p;
+
+ if (!c)
+ continue;
+
+#ifdef TRACE
+ if (c->collected)
+ continue;
+#endif
+ assert (c->learned);
+
+ if (c->size <= 2)
+ continue;
+
+ a = CLS2ACT (c);
+ *a = mulflt (*a, factor);
+ }
+
+ ps->cinc = mulflt (ps->cinc, factor);
+}
+
+static void
+inc_vinc (PS * ps)
+{
+#ifdef VISCORES
+ ps->nvinc = mulflt (ps->nvinc, ps->fvinc);
+#endif
+ ps->vinc = mulflt (ps->vinc, ps->ifvinc);
+}
+
+inline static void
+inc_max_var (PS * ps)
+{
+ Lit *lit;
+ Rnk *r;
+ Var *v;
+
+ assert (ps->max_var < ps->size_vars);
+
+ if (ps->max_var + 1 == ps->size_vars)
+ enlarge (ps, ps->size_vars + 2*(ps->size_vars + 3) / 4); /* +25% */
+
+ ps->max_var++; /* new index of variable */
+ assert (ps->max_var); /* no unsigned overflow */
+
+ assert (ps->max_var < ps->size_vars);
+
+ lit = ps->lits + 2 * ps->max_var;
+ lit[0].val = lit[1].val = UNDEF;
+
+ memset (ps->htps + 2 * ps->max_var, 0, 2 * sizeof *ps->htps);
+#ifndef NDSC
+ memset (ps->dhtps + 2 * ps->max_var, 0, 2 * sizeof *ps->dhtps);
+#endif
+ memset (ps->impls + 2 * ps->max_var, 0, 2 * sizeof *ps->impls);
+ memset (ps->jwh + 2 * ps->max_var, 0, 2 * sizeof *ps->jwh);
+
+ v = ps->vars + ps->max_var; /* initialize variable components */
+ CLR (v);
+
+ r = ps->rnks + ps->max_var; /* initialize rank */
+ CLR (r);
+
+ hpush (ps, r);
+}
+
+static void
+force (PS * ps, Cls * c)
+{
+ Lit ** p, ** eol, * lit, * forced;
+ Cls * reason;
+
+ forced = 0;
+ reason = c;
+
+ eol = end_of_lits (c);
+ for (p = c->lits; p < eol; p++)
+ {
+ lit = *p;
+ if (lit->val == UNDEF)
+ {
+ assert (!forced);
+ forced = lit;
+#ifdef NO_BINARY_CLAUSES
+ if (c == &ps->impl)
+ reason = LIT2REASON (NOTLIT (p[p == c->lits ? 1 : -1]));
+#endif
+ }
+ else
+ assert (lit->val == FALSE);
+ }
+
+#ifdef NO_BINARY_CLAUSES
+ if (c == &ps->impl)
+ resetimpl (ps);
+#endif
+ if (!forced)
+ return;
+
+ assign_forced (ps, forced, reason);
+}
+
+static void
+inc_lreduce (PS * ps)
+{
+#ifdef STATS
+ ps->inclreduces++;
+#endif
+ ps->lreduce *= FREDUCE;
+ ps->lreduce /= 100;
+ report (ps, 1, '+');
+}
+
+static void
+backtrack (PS * ps)
+{
+ unsigned new_level;
+ Cls * c;
+
+ ps->conflicts++;
+ LOG ( fprintf (ps->out, "%sconflict ", ps->prefix); dumpclsnl (ps, ps->conflict));
+
+ analyze (ps);
+ new_level = drive (ps);
+ // TODO: why not? assert (new_level != 1 || (ps->ahead - ps->added) == 2);
+ c = add_simplified_clause (ps, 1);
+ undo (ps, new_level);
+ force (ps, c);
+
+ if (
+#ifndef NFL
+ !ps->simplifying &&
+#endif
+ !--ps->lreduceadjustcnt)
+ {
+ /* With FREDUCE==110 and FREDADJ=121 we stir 'lreduce' to be
+ * proportional to 'sqrt(conflicts)'. In earlier version we actually
+ * used 'FREDADJ=150', which results in 'lreduce' to approximate
+ * 'conflicts^(log(1.1)/log(1.5))' which is close to the fourth root
+ * of 'conflicts', since log(1.1)/log(1.5)=0.235 (as observed by
+ * Donald Knuth). The square root is the same we get by a Glucose
+ * style increase, which simply adds a constant at every reduction.
+ * This would be way simpler to implement but for now we keep the more
+ * complicated code using the adjust increments and counters.
+ */
+ ps->lreduceadjustinc *= FREDADJ; ps->lreduceadjustinc /= 100; ps->lreduceadjustcnt
+ = ps->lreduceadjustinc;
+ inc_lreduce (ps);
+ }
+
+ if (ps->verbosity >= 4 && !(ps->conflicts % 1000))
+ report (ps, 4, 'C');
+}
+
+static void
+inc_cinc (PS * ps)
+{
+ ps->cinc = mulflt (ps->cinc, ps->fcinc);
+ if (ps->lcinc < ps->cinc)
+ crescore (ps);
+}
+
+static void
+incincs (PS * ps)
+{
+ inc_vinc (ps);
+ inc_cinc (ps);
+#ifdef VISCORES
+ viscores (ps);
+#endif
+}
+
+static void
+disconnect_clause (PS * ps, Cls * c)
+{
+ assert (c->connected);
+
+ if (c->size > 2)
+ {
+ if (c->learned)
+ {
+ assert (ps->nlclauses > 0);
+ ps->nlclauses--;
+
+ assert (ps->llits >= c->size);
+ ps->llits -= c->size;
+ }
+ else
+ {
+ assert (ps->noclauses > 0);
+ ps->noclauses--;
+
+ assert (ps->olits >= c->size);
+ ps->olits -= c->size;
+ }
+ }
+
+#ifndef NDEBUG
+ c->connected = 0;
+#endif
+}
+
+static int
+clause_is_toplevel_satisfied (PS * ps, Cls * c)
+{
+ Lit *lit, **p, **eol = end_of_lits (c);
+ Var *v;
+
+ for (p = c->lits; p < eol; p++)
+ {
+ lit = *p;
+ if (lit->val == TRUE)
+ {
+ v = LIT2VAR (lit);
+ if (!v->level)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+collect_clause (PS * ps, Cls * c)
+{
+ assert (c->collect);
+ c->collect = 0;
+
+#ifdef TRACE
+ assert (!c->collected);
+ c->collected = 1;
+#endif
+ disconnect_clause (ps, c);
+
+#ifdef TRACE
+ if (ps->trace && (!c->learned || c->used))
+ return 0;
+#endif
+ delete_clause (ps, c);
+
+ return 1;
+}
+
+static size_t
+collect_clauses (PS * ps)
+{
+ Cls *c, **p, **q, * next;
+ Lit * lit, * eol;
+ size_t res;
+ int i;
+
+ res = ps->current_bytes;
+
+ eol = ps->lits + 2 * ps->max_var + 1;
+ for (lit = ps->lits + 2; lit <= eol; lit++)
+ {
+ for (i = 0; i <= 1; i++)
+ {
+ if (i)
+ {
+#ifdef NO_BINARY_CLAUSES
+ Ltk * lstk = LIT2IMPLS (lit);
+ Lit ** r, ** s;
+ r = lstk->start;
+ if (lit->val != TRUE || LIT2VAR (lit)->level)
+ for (s = r; s < lstk->start + lstk->count; s++)
+ {
+ Lit * other = *s;
+ Var *v = LIT2VAR (other);
+ if (v->level ||
+ other->val != TRUE)
+ *r++ = other;
+ }
+ lstk->count = r - lstk->start;
+ continue;
+#else
+ p = LIT2IMPLS (lit);
+#endif
+ }
+ else
+ p = LIT2HTPS (lit);
+
+ for (c = *p; c; c = next)
+ {
+ q = c->next;
+ if (c->lits[0] != lit)
+ q++;
+
+ next = *q;
+ if (c->collect)
+ *p = next;
+ else
+ p = q;
+ }
+ }
+ }
+
+#ifndef NDSC
+ for (lit = ps->lits + 2; lit <= eol; lit++)
+ {
+ p = LIT2DHTPS (lit);
+ while ((c = *p))
+ {
+ Lit * other = c->lits[0];
+ if (other == lit)
+ {
+ q = c->next + 1;
+ }
+ else
+ {
+ assert (c->lits[1] == lit);
+ q = c->next;
+ }
+
+ if (c->collect)
+ *p = *q;
+ else
+ p = q;
+ }
+ }
+#endif
+
+ for (p = SOC; p != EOC; p = NXC (p))
+ {
+ c = *p;
+
+ if (!c)
+ continue;
+
+ if (!c->collect)
+ continue;
+
+ if (collect_clause (ps, c))
+ *p = 0;
+ }
+
+#ifdef TRACE
+ if (!ps->trace)
+#endif
+ {
+ q = ps->oclauses;
+ for (p = q; p < ps->ohead; p++)
+ if ((c = *p))
+ *q++ = c;
+ ps->ohead = q;
+
+ q = ps->lclauses;
+ for (p = q; p < ps->lhead; p++)
+ if ((c = *p))
+ *q++ = c;
+ ps->lhead = q;
+ }
+
+ assert (ps->current_bytes <= res);
+ res -= ps->current_bytes;
+ ps->recycled += res;
+
+ LOG ( fprintf (ps->out, "%scollected %ld bytes\n", ps->prefix, (long)res));
+
+ return res;
+}
+
+static int
+need_to_reduce (PS * ps)
+{
+ return ps->nlclauses >= reduce_limit_on_lclauses (ps);
+}
+
+#ifdef NLUBY
+
+static void
+inc_drestart (PS * ps)
+{
+ ps->drestart *= FRESTART;
+ ps->drestart /= 100;
+
+ if (ps->drestart >= MAXRESTART)
+ ps->drestart = MAXRESTART;
+}
+
+static void
+inc_ddrestart (PS * ps)
+{
+ ps->ddrestart *= FRESTART;
+ ps->ddrestart /= 100;
+
+ if (ps->ddrestart >= MAXRESTART)
+ ps->ddrestart = MAXRESTART;
+}
+
+#else
+
+static unsigned
+luby (unsigned i)
+{
+ unsigned k;
+ for (k = 1; k < 32; k++)
+ if (i == (1u << k) - 1)
+ return 1u << (k - 1);
+
+ for (k = 1;; k++)
+ if ((1u << (k - 1)) <= i && i < (1u << k) - 1)
+ return luby (i - (1u << (k-1)) + 1);
+}
+
+#endif
+
+#ifndef NLUBY
+static void
+inc_lrestart (PS * ps, int skip)
+{
+ unsigned delta;
+
+ delta = 100 * luby (++ps->lubycnt);
+ ps->lrestart = ps->conflicts + delta;
+
+ if (ps->waslubymaxdelta)
+ report (ps, 1, skip ? 'N' : 'R');
+ else
+ report (ps, 2, skip ? 'n' : 'r');
+
+ if (delta > ps->lubymaxdelta)
+ {
+ ps->lubymaxdelta = delta;
+ ps->waslubymaxdelta = 1;
+ }
+ else
+ ps->waslubymaxdelta = 0;
+}
+#endif
+
+static void
+init_restart (PS * ps)
+{
+#ifdef NLUBY
+ /* TODO: why is it better in incremental usage to have smaller initial
+ * outer restart interval?
+ */
+ ps->ddrestart = ps->calls > 1 ? MINRESTART : 1000;
+ ps->drestart = MINRESTART;
+ ps->lrestart = ps->conflicts + ps->drestart;
+#else
+ ps->lubycnt = 0;
+ ps->lubymaxdelta = 0;
+ ps->waslubymaxdelta = 0;
+ inc_lrestart (ps, 0);
+#endif
+}
+
+static void
+restart (PS * ps)
+{
+ int skip;
+#ifdef NLUBY
+ char kind;
+ int outer;
+
+ inc_drestart (ps);
+ outer = (ps->drestart >= ps->ddrestart);
+
+ if (outer)
+ skip = very_high_agility (ps);
+ else
+ skip = high_agility (ps);
+#else
+ skip = medium_agility (ps);
+#endif
+
+#ifdef STATS
+ if (skip)
+ ps->skippedrestarts++;
+#endif
+
+ assert (ps->conflicts >= ps->lrestart);
+
+ if (!skip)
+ {
+ ps->restarts++;
+ assert (ps->LEVEL > 1);
+ LOG ( fprintf (ps->out, "%srestart %u\n", ps->prefix, ps->restarts));
+ undo (ps, 0);
+ }
+
+#ifdef NLUBY
+ if (outer)
+ {
+ kind = skip ? 'N' : 'R';
+ inc_ddrestart (ps);
+ ps->drestart = MINRESTART;
+ }
+ else if (skip)
+ {
+ kind = 'n';
+ }
+ else
+ {
+ kind = 'r';
+ }
+
+ assert (ps->drestart <= MAXRESTART);
+ ps->lrestart = ps->conflicts + ps->drestart;
+ assert (ps->lrestart > ps->conflicts);
+
+ report (outer ? 1 : 2, kind);
+#else
+ inc_lrestart (ps, skip);
+#endif
+}
+
+inline static void
+assign_decision (PS * ps, Lit * lit)
+{
+ assert (!ps->conflict);
+
+ ps->LEVEL++;
+
+ LOG ( fprintf (ps->out, "%snew level %u\n", ps->prefix, ps->LEVEL));
+ LOG ( fprintf (ps->out,
+ "%sassign %d at level %d <= DECISION\n",
+ ps->prefix, LIT2INT (lit), ps->LEVEL));
+
+ assign (ps, lit, 0);
+}
+
+#ifndef NFL
+
+static int
+lit_has_binary_clauses (PS * ps, Lit * lit)
+{
+#ifdef NO_BINARY_CLAUSES
+ Ltk* lstk = LIT2IMPLS (lit);
+ return lstk->count != 0;
+#else
+ return *LIT2IMPLS (lit) != 0;
+#endif
+}
+
+static void
+flbcp (PS * ps)
+{
+#ifdef STATS
+ unsigned long long propagaions_before_bcp = ps->propagations;
+#endif
+ bcp (ps);
+#ifdef STATS
+ ps->flprops += ps->propagations - propagaions_before_bcp;
+#endif
+}
+
+inline static int
+cmp_inverse_rnk (PS * ps, Rnk * a, Rnk * b)
+{
+ (void) ps;
+ return -cmp_rnk (a, b);
+}
+
+inline static Flt
+rnk2jwh (PS * ps, Rnk * r)
+{
+ Flt res, sum, pjwh, njwh;
+ Lit * plit, * nlit;
+
+ plit = RNK2LIT (r);
+ nlit = plit + 1;
+
+ pjwh = *LIT2JWH (plit);
+ njwh = *LIT2JWH (nlit);
+
+ res = mulflt (pjwh, njwh);
+
+ sum = addflt (pjwh, njwh);
+ sum = mulflt (sum, base2flt (1, -10));
+ res = addflt (res, sum);
+
+ return res;
+}
+
+static int
+cmp_inverse_jwh_rnk (PS * ps, Rnk * r, Rnk * s)
+{
+ Flt a = rnk2jwh (ps, r);
+ Flt b = rnk2jwh (ps, s);
+ int res = cmpflt (a, b);
+
+ if (res)
+ return -res;
+
+ return cmp_inverse_rnk (ps, r, s);
+}
+
+static void
+faillits (PS * ps)
+{
+ unsigned i, j, old_trail_count, common, saved_count;
+ unsigned new_saved_size, oldladded = ps->ladded;
+ unsigned long long limit, delta;
+ Lit * lit, * other, * pivot;
+ Rnk * r, ** p, ** q;
+ int new_trail_count;
+ double started;
+
+ if (ps->plain)
+ return;
+
+ if (ps->heap + 1 >= ps->hhead)
+ return;
+
+ if (ps->propagations < ps->fllimit)
+ return;
+
+ sflush (ps);
+ started = ps->seconds;
+
+ ps->flcalls++;
+#ifdef STATSA
+ ps->flrounds++;
+#endif
+ delta = ps->propagations/10;
+ if (delta >= 100*1000*1000) delta = 100*1000*1000;
+ else if (delta <= 100*1000) delta = 100*1000;
+
+ limit = ps->propagations + delta;
+ ps->fllimit = ps->propagations;
+
+ assert (!ps->LEVEL);
+ assert (ps->simplifying);
+
+ if (ps->flcalls <= 1)
+ SORT (Rnk *, cmp_inverse_jwh_rnk, ps->heap + 1, ps->hhead - (ps->heap + 1));
+ else
+ SORT (Rnk *, cmp_inverse_rnk, ps->heap + 1, ps->hhead - (ps->heap + 1));
+
+ i = 1; /* NOTE: heap starts at position '1' */
+
+ while (ps->propagations < limit)
+ {
+ if (ps->heap + i == ps->hhead)
+ {
+ if (ps->ladded == oldladded)
+ break;
+
+ i = 1;
+#ifdef STATS
+ ps->flrounds++;
+#endif
+ oldladded = ps->ladded;
+ }
+
+ assert (ps->heap + i < ps->hhead);
+
+ r = ps->heap[i++];
+ lit = RNK2LIT (r);
+
+ if (lit->val)
+ continue;
+
+ if (!lit_has_binary_clauses (ps, NOTLIT (lit)))
+ {
+#ifdef STATS
+ ps->flskipped++;
+#endif
+ continue;
+ }
+
+#ifdef STATS
+ ps->fltried++;
+#endif
+ LOG ( fprintf (ps->out, "%strying %d as failed literal\n",
+ ps->prefix, LIT2INT (lit)));
+
+ assign_decision (ps, lit);
+ old_trail_count = ps->thead - ps->trail;
+ flbcp (ps);
+
+ if (ps->conflict)
+ {
+EXPLICITLY_FAILED_LITERAL:
+ LOG ( fprintf (ps->out, "%sfound explicitly failed literal %d\n",
+ ps->prefix, LIT2INT (lit)));
+
+ ps->failedlits++;
+ ps->efailedlits++;
+
+ backtrack (ps);
+ flbcp (ps);
+
+ if (!ps->conflict)
+ continue;
+
+CONTRADICTION:
+ assert (!ps->LEVEL);
+ backtrack (ps);
+ assert (ps->mtcls);
+
+ goto RETURN;
+ }
+
+ if (ps->propagations >= limit)
+ {
+ undo (ps, 0);
+ break;
+ }
+
+ lit = NOTLIT (lit);
+
+ if (!lit_has_binary_clauses (ps, NOTLIT (lit)))
+ {
+#ifdef STATS
+ ps->flskipped++;
+#endif
+ undo (ps, 0);
+ continue;
+ }
+
+#ifdef STATS
+ ps->fltried++;
+#endif
+ LOG ( fprintf (ps->out, "%strying %d as failed literals\n",
+ ps->prefix, LIT2INT (lit)));
+
+ new_trail_count = ps->thead - ps->trail;
+ saved_count = new_trail_count - old_trail_count;
+
+ if (saved_count > ps->saved_size)
+ {
+ new_saved_size = ps->saved_size ? 2 * ps->saved_size : 1;
+ while (saved_count > new_saved_size)
+ new_saved_size *= 2;
+
+ RESIZEN (ps->saved, ps->saved_size, new_saved_size);
+ ps->saved_size = new_saved_size;
+ }
+
+ for (j = 0; j < saved_count; j++)
+ ps->saved[j] = ps->trail[old_trail_count + j];
+
+ undo (ps, 0);
+
+ assign_decision (ps, lit);
+ flbcp (ps);
+
+ if (ps->conflict)
+ goto EXPLICITLY_FAILED_LITERAL;
+
+ pivot = (ps->thead - ps->trail <= new_trail_count) ? lit : NOTLIT (lit);
+
+ common = 0;
+ for (j = 0; j < saved_count; j++)
+ if ((other = ps->saved[j])->val == TRUE)
+ ps->saved[common++] = other;
+
+ undo (ps, 0);
+
+ LOG (if (common)
+ fprintf (ps->out,
+ "%sfound %d literals implied by %d and %d\n",
+ ps->prefix, common,
+ LIT2INT (NOTLIT (lit)), LIT2INT (lit)));
+
+#if 1 // set to zero to disable 'lifting'
+ for (j = 0;
+ j < common
+ /* TODO: For some Velev benchmarks, extracting the common implicit
+ * failed literals took quite some time. This needs to be fixed by
+ * a dedicated analyzer. Up to then we bound the number of
+ * propagations in this loop as well.
+ */
+ && ps->propagations < limit + delta
+ ; j++)
+ {
+ other = ps->saved[j];
+
+ if (other->val == TRUE)
+ continue;
+
+ assert (!other->val);
+
+ LOG ( fprintf (ps->out,
+ "%sforcing %d as forced implicitly failed literal\n",
+ ps->prefix, LIT2INT (other)));
+
+ assert (pivot != NOTLIT (other));
+ assert (pivot != other);
+
+ assign_decision (ps, NOTLIT (other));
+ flbcp (ps);
+
+ assert (ps->LEVEL == 1);
+
+ if (ps->conflict)
+ {
+ backtrack (ps);
+ assert (!ps->LEVEL);
+ }
+ else
+ {
+ assign_decision (ps, pivot);
+ flbcp (ps);
+
+ backtrack (ps);
+
+ if (ps->LEVEL)
+ {
+ assert (ps->LEVEL == 1);
+
+ flbcp (ps);
+
+ if (ps->conflict)
+ {
+ backtrack (ps);
+ assert (!ps->LEVEL);
+ }
+ else
+ {
+ assign_decision (ps, NOTLIT (pivot));
+ flbcp (ps);
+ backtrack (ps);
+
+ if (ps->LEVEL)
+ {
+ assert (ps->LEVEL == 1);
+ flbcp (ps);
+
+ if (!ps->conflict)
+ {
+#ifdef STATS
+ ps->floopsed++;
+#endif
+ undo (ps, 0);
+ continue;
+ }
+
+ backtrack (ps);
+ }
+
+ assert (!ps->LEVEL);
+ }
+
+ assert (!ps->LEVEL);
+ }
+ }
+ assert (!ps->LEVEL);
+ flbcp (ps);
+
+ ps->failedlits++;
+ ps->ifailedlits++;
+
+ if (ps->conflict)
+ goto CONTRADICTION;
+ }
+#endif
+ }
+
+ ps->fllimit += 9 * (ps->propagations - ps->fllimit); /* 10% for failed literals */
+
+RETURN:
+
+ /* First flush top level assigned literals. Those are prohibited from
+ * being pushed up the heap during 'faillits' since 'simplifying' is set.
+ */
+ assert (ps->heap < ps->hhead);
+ for (p = q = ps->heap + 1; p < ps->hhead; p++)
+ {
+ r = *p;
+ lit = RNK2LIT (r);
+ if (lit->val)
+ r->pos = 0;
+ else
+ *q++ = r;
+ }
+
+ /* Then resort with respect to EVSIDS score and fix positions.
+ */
+ SORT (Rnk *, cmp_inverse_rnk, ps->heap + 1, ps->hhead - (ps->heap + 1));
+ for (p = ps->heap + 1; p < ps->hhead; p++)
+ (*p)->pos = p - ps->heap;
+
+ sflush (ps);
+ ps->flseconds += ps->seconds - started;
+}
+
+#endif
+
+static void
+simplify (PS * ps, int forced)
+{
+ Lit * lit, * notlit, ** t;
+ unsigned collect, delta;
+#ifdef STATS
+ size_t bytes_collected;
+#endif
+ int * q, ilit;
+ Cls **p, *c;
+ Var * v;
+
+#ifndef NDEDBUG
+ (void) forced;
+#endif
+
+ assert (!ps->mtcls);
+ assert (!satisfied (ps));
+ assert (forced || ps->lsimplify <= ps->propagations);
+ assert (forced || ps->fsimplify <= ps->fixed);
+
+ if (ps->LEVEL)
+ undo (ps, 0);
+#ifndef NFL
+ ps->simplifying = 1;
+ faillits (ps);
+ ps->simplifying = 0;
+
+ if (ps->mtcls)
+ return;
+#endif
+
+ if (ps->cils != ps->cilshead)
+ {
+ assert (ps->ttail == ps->thead);
+ assert (ps->ttail2 == ps->thead);
+ ps->ttail = ps->trail;
+ for (t = ps->trail; t < ps->thead; t++)
+ {
+ lit = *t;
+ v = LIT2VAR (lit);
+ if (v->internal)
+ {
+ assert (LIT2INT (lit) < 0);
+ assert (lit->val == TRUE);
+ unassign (ps, lit);
+ }
+ else
+ *ps->ttail++ = lit;
+ }
+ ps->ttail2 = ps->thead = ps->ttail;
+
+ for (q = ps->cils; q != ps->cilshead; q++)
+ {
+ ilit = *q;
+ assert (0 < ilit && ilit <= (int) ps->max_var);
+ v = ps->vars + ilit;
+ assert (v->internal);
+ v->level = 0;
+ v->reason = 0;
+ lit = int2lit (ps, -ilit);
+ assert (lit->val == UNDEF);
+ lit->val = TRUE;
+ notlit = NOTLIT (lit);
+ assert (notlit->val == UNDEF);
+ notlit->val = FALSE;
+ }
+ }
+
+ collect = 0;
+ for (p = SOC; p != EOC; p = NXC (p))
+ {
+ c = *p;
+ if (!c)
+ continue;
+
+#ifdef TRACE
+ if (c->collected)
+ continue;
+#endif
+
+ if (c->locked)
+ continue;
+
+ assert (!c->collect);
+ if (clause_is_toplevel_satisfied (ps, c))
+ {
+ mark_clause_to_be_collected (c);
+ collect++;
+ }
+ }
+
+ LOG ( fprintf (ps->out, "%scollecting %d clauses\n", ps->prefix, collect));
+#ifdef STATS
+ bytes_collected =
+#endif
+ collect_clauses (ps);
+#ifdef STATS
+ ps->srecycled += bytes_collected;
+#endif
+
+ if (ps->cils != ps->cilshead)
+ {
+ for (q = ps->cils; q != ps->cilshead; q++)
+ {
+ ilit = *q;
+ assert (0 < ilit && ilit <= (int) ps->max_var);
+ assert (ps->vars[ilit].internal);
+ if (ps->rilshead == ps->eorils)
+ ENLARGE (ps->rils, ps->rilshead, ps->eorils);
+ *ps->rilshead++ = ilit;
+ lit = int2lit (ps, -ilit);
+ assert (lit->val == TRUE);
+ lit->val = UNDEF;
+ notlit = NOTLIT (lit);
+ assert (notlit->val == FALSE);
+ notlit->val = UNDEF;
+ }
+ ps->cilshead = ps->cils;
+ }
+
+ delta = 10 * (ps->olits + ps->llits) + 100000;
+ if (delta > 2000000)
+ delta = 2000000;
+ ps->lsimplify = ps->propagations + delta;
+ ps->fsimplify = ps->fixed;
+ ps->simps++;
+
+ report (ps, 1, 's');
+}
+
+static void
+iteration (PS * ps)
+{
+ assert (!ps->LEVEL);
+ assert (bcp_queue_is_empty (ps));
+ assert (ps->isimplify < ps->fixed);
+
+ ps->iterations++;
+ report (ps, 2, 'i');
+#ifdef NLUBY
+ ps->drestart = MINRESTART;
+ ps->lrestart = ps->conflicts + ps->drestart;
+#else
+ init_restart (ps);
+#endif
+ ps->isimplify = ps->fixed;
+}
+
+static int
+cmp_glue_activity_size (PS * ps, Cls * c, Cls * d)
+{
+ Act a, b, * p, * q;
+
+ (void) ps;
+
+ assert (c->learned);
+ assert (d->learned);
+
+ if (c->glue < d->glue) // smaller glue preferred
+ return 1;
+
+ if (c->glue > d->glue)
+ return -1;
+
+ p = CLS2ACT (c);
+ q = CLS2ACT (d);
+ a = *p;
+ b = *q;
+
+ if (a < b) // then higher activity
+ return -1;
+
+ if (b < a)
+ return 1;
+
+ if (c->size < d->size) // then smaller size
+ return 1;
+
+ if (c->size > d->size)
+ return -1;
+
+ return 0;
+}
+
+static void
+reduce (PS * ps, unsigned percentage)
+{
+ unsigned redcount, lcollect, collect, target;
+#ifdef STATS
+ size_t bytes_collected;
+#endif
+ Cls **p, *c;
+
+ assert (ps->rhead == ps->resolved);
+
+ ps->lastreduceconflicts = ps->conflicts;
+
+ assert (percentage <= 100);
+ LOG ( fprintf (ps->out,
+ "%sreducing %u%% learned clauses\n",
+ ps->prefix, percentage));
+
+ while (ps->nlclauses - ps->llocked > (unsigned)(ps->eor - ps->resolved))
+ ENLARGE (ps->resolved, ps->rhead, ps->eor);
+
+ collect = 0;
+ lcollect = 0;
+
+ for (p = ((ps->fsimplify < ps->fixed) ? SOC : ps->lclauses); p != EOC; p = NXC (p))
+ {
+ c = *p;
+ if (!c)
+ continue;
+
+#ifdef TRACE
+ if (c->collected)
+ continue;
+#endif
+
+ if (c->locked)
+ continue;
+
+ assert (!c->collect);
+ if (ps->fsimplify < ps->fixed && clause_is_toplevel_satisfied (ps, c))
+ {
+ mark_clause_to_be_collected (c);
+ collect++;
+
+ if (c->learned && c->size > 2)
+ lcollect++;
+
+ continue;
+ }
+
+ if (!c->learned)
+ continue;
+
+ if (c->size <= 2)
+ continue;
+
+ assert (ps->rhead < ps->eor);
+ *ps->rhead++ = c;
+ }
+ assert (ps->rhead <= ps->eor);
+
+ ps->fsimplify = ps->fixed;
+
+ redcount = ps->rhead - ps->resolved;
+ SORT (Cls *, cmp_glue_activity_size, ps->resolved, redcount);
+
+ assert (ps->nlclauses >= lcollect);
+ target = ps->nlclauses - lcollect + 1;
+
+ target = (percentage * target + 99) / 100;
+
+ if (target >= redcount)
+ target = redcount;
+
+ ps->rhead = ps->resolved + target;
+ while (ps->rhead > ps->resolved)
+ {
+ c = *--ps->rhead;
+ mark_clause_to_be_collected (c);
+
+ collect++;
+ if (c->learned && c->size > 2) /* just for consistency */
+ lcollect++;
+ }
+
+ if (collect)
+ {
+ ps->reductions++;
+#ifdef STATS
+ bytes_collected =
+#endif
+ collect_clauses (ps);
+#ifdef STATS
+ ps->rrecycled += bytes_collected;
+#endif
+ report (ps, 2, '-');
+ }
+
+ if (!lcollect)
+ inc_lreduce (ps); /* avoid dead lock */
+
+ assert (ps->rhead == ps->resolved);
+}
+
+static void
+init_reduce (PS * ps)
+{
+ // lreduce = loadded / 2;
+ ps->lreduce = 1000;
+
+ if (ps->lreduce < 100)
+ ps->lreduce = 100;
+
+ if (ps->verbosity)
+ fprintf (ps->out,
+ "%s\n%sinitial reduction limit %u clauses\n%s\n",
+ ps->prefix, ps->prefix, ps->lreduce, ps->prefix);
+}
+
+static unsigned
+rng (PS * ps)
+{
+ unsigned res = ps->srng;
+ ps->srng *= 1664525u;
+ ps->srng += 1013904223u;
+ NOLOG ( fprintf (ps->out, "%srng () = %u\n", ps->prefix, res));
+ return res;
+}
+
+static unsigned
+rrng (PS * ps, unsigned low, unsigned high)
+{
+ unsigned long long tmp;
+ unsigned res, elements;
+ assert (low <= high);
+ elements = high - low + 1;
+ tmp = rng (ps);
+ tmp *= elements;
+ tmp >>= 32;
+ tmp += low;
+ res = tmp;
+ NOLOG ( fprintf (ps->out, "%srrng (ps, %u, %u) = %u\n", ps->prefix, low, high, res));
+ assert (low <= res);
+ assert (res <= high);
+ return res;
+}
+
+static Lit *
+decide_phase (PS * ps, Lit * lit)
+{
+ Lit * not_lit = NOTLIT (lit);
+ Var *v = LIT2VAR (lit);
+
+ assert (LIT2SGN (lit) > 0);
+ if (v->usedefphase)
+ {
+ if (v->defphase)
+ {
+ /* assign to TRUE */
+ }
+ else
+ {
+ /* assign to FALSE */
+ lit = not_lit;
+ }
+ }
+ else if (!v->assigned)
+ {
+#ifdef STATS
+ ps->staticphasedecisions++;
+#endif
+ if (ps->defaultphase == POSPHASE)
+ {
+ /* assign to TRUE */
+ }
+ else if (ps->defaultphase == NEGPHASE)
+ {
+ /* assign to FALSE */
+ lit = not_lit;
+ }
+ else if (ps->defaultphase == RNDPHASE)
+ {
+ /* randomly assign default phase */
+ if (rrng (ps, 1, 2) != 2)
+ lit = not_lit;
+ }
+ else if (*LIT2JWH(lit) <= *LIT2JWH (not_lit))
+ {
+ /* assign to FALSE (Jeroslow-Wang says there are more short
+ * clauses with negative occurence of this variable, so satisfy
+ * those, to minimize BCP)
+ */
+ lit = not_lit;
+ }
+ else
+ {
+ /* assign to TRUE (... but strictly more positive occurrences) */
+ }
+ }
+ else
+ {
+ /* repeat last phase: phase saving heuristic */
+
+ if (v->phase)
+ {
+ /* assign to TRUE (last phase was TRUE as well) */
+ }
+ else
+ {
+ /* assign to FALSE (last phase was FALSE as well) */
+ lit = not_lit;
+ }
+ }
+
+ return lit;
+}
+
+static unsigned
+gcd (unsigned a, unsigned b)
+{
+ unsigned tmp;
+
+ assert (a);
+ assert (b);
+
+ if (a < b)
+ {
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ while (b)
+ {
+ assert (a >= b);
+ tmp = b;
+ b = a % b;
+ a = tmp;
+ }
+
+ return a;
+}
+
+static Lit *
+rdecide (PS * ps)
+{
+ unsigned idx, delta, spread;
+ Lit * res;
+
+ spread = RDECIDE;
+ if (rrng (ps, 1, spread) != 2)
+ return 0;
+
+ assert (1 <= ps->max_var);
+ idx = rrng (ps, 1, ps->max_var);
+ res = int2lit (ps, idx);
+
+ if (res->val != UNDEF)
+ {
+ delta = rrng (ps, 1, ps->max_var);
+ while (gcd (delta, ps->max_var) != 1)
+ delta--;
+
+ assert (1 <= delta);
+ assert (delta <= ps->max_var);
+
+ do {
+ idx += delta;
+ if (idx > ps->max_var)
+ idx -= ps->max_var;
+ res = int2lit (ps, idx);
+ } while (res->val != UNDEF);
+ }
+
+#ifdef STATS
+ ps->rdecisions++;
+#endif
+ res = decide_phase (ps, res);
+ LOG ( fprintf (ps->out, "%srdecide %d\n", ps->prefix, LIT2INT (res)));
+
+ return res;
+}
+
+static Lit *
+sdecide (PS * ps)
+{
+ Lit *res;
+ Rnk *r;
+
+ for (;;)
+ {
+ r = htop (ps);
+ res = RNK2LIT (r);
+ if (res->val == UNDEF) break;
+ (void) hpop (ps);
+ NOLOG ( fprintf (ps->out,
+ "%shpop %u %u %u\n",
+ ps->prefix, r - ps->rnks,
+ FLTMANTISSA(r->score),
+ FLTEXPONENT(r->score)));
+ }
+
+#ifdef STATS
+ ps->sdecisions++;
+#endif
+ res = decide_phase (ps, res);
+
+ LOG ( fprintf (ps->out, "%ssdecide %d\n", ps->prefix, LIT2INT (res)));
+
+ return res;
+}
+
+static Lit *
+adecide (PS * ps)
+{
+ Lit *lit;
+ Var * v;
+
+ assert (ps->als < ps->alshead);
+ assert (!ps->failed_assumption);
+
+ while (ps->alstail < ps->alshead)
+ {
+ lit = *ps->alstail++;
+
+ if (lit->val == FALSE)
+ {
+ ps->failed_assumption = lit;
+ v = LIT2VAR (lit);
+
+ use_var (ps, v);
+
+ LOG ( fprintf (ps->out, "%sfirst failed assumption %d\n",
+ ps->prefix, LIT2INT (ps->failed_assumption)));
+ fanalyze (ps);
+ return 0;
+ }
+
+ if (lit->val == TRUE)
+ {
+ v = LIT2VAR (lit);
+ if (v->level > ps->adecidelevel)
+ ps->adecidelevel = v->level;
+ continue;
+ }
+
+#ifdef STATS
+ ps->assumptions++;
+#endif
+ LOG ( fprintf (ps->out, "%sadecide %d\n", ps->prefix, LIT2INT (lit)));
+ ps->adecidelevel = ps->LEVEL + 1;
+
+ return lit;
+ }
+
+ return 0;
+}
+
+static void
+decide (PS * ps)
+{
+ Lit * lit;
+
+ assert (!satisfied (ps));
+ assert (!ps->conflict);
+
+ if (ps->alstail < ps->alshead && (lit = adecide (ps)))
+ ;
+ else if (ps->failed_assumption)
+ return;
+ else if (satisfied (ps))
+ return;
+ else if (!(lit = rdecide (ps)))
+ lit = sdecide (ps);
+
+ assert (lit);
+ assign_decision (ps, lit);
+
+ ps->levelsum += ps->LEVEL;
+ ps->decisions++;
+}
+
+static int
+sat (PS * ps, int l)
+{
+ int count = 0, backtracked;
+
+ if (!ps->conflict)
+ bcp (ps);
+
+ if (ps->conflict)
+ backtrack (ps);
+
+ if (ps->mtcls)
+ return PICOSAT_UNSATISFIABLE;
+
+ if (satisfied (ps))
+ goto SATISFIED;
+
+ if (ps->lsimplify <= ps->propagations)
+ simplify (ps, 0);
+
+ if (ps->mtcls)
+ return PICOSAT_UNSATISFIABLE;
+
+ if (satisfied (ps))
+ goto SATISFIED;
+
+ init_restart (ps);
+
+ if (!ps->lreduce)
+ init_reduce (ps);
+
+ ps->isimplify = ps->fixed;
+ backtracked = 0;
+
+ for (;;)
+ {
+ if (!ps->conflict)
+ bcp (ps);
+
+ if (ps->conflict)
+ {
+ incincs (ps);
+ backtrack (ps);
+
+ if (ps->mtcls)
+ return PICOSAT_UNSATISFIABLE;
+ backtracked = 1;
+ continue;
+ }
+
+ if (satisfied (ps))
+ {
+SATISFIED:
+#ifndef NDEBUG
+ original_clauses_satisfied (ps);
+ assumptions_satisfied (ps);
+#endif
+ return PICOSAT_SATISFIABLE;
+ }
+
+ if (backtracked)
+ {
+ backtracked = 0;
+ if (!ps->LEVEL && ps->isimplify < ps->fixed)
+ iteration (ps);
+ }
+
+ if (l >= 0 && count >= l) /* decision limit reached ? */
+ return PICOSAT_UNKNOWN;
+
+ if (ps->interrupt.function && /* external interrupt */
+ count > 0 && !(count % INTERRUPTLIM) &&
+ ps->interrupt.function (ps->interrupt.state))
+ return PICOSAT_UNKNOWN;
+
+ if (ps->propagations >= ps->lpropagations)/* propagation limit reached ? */
+ return PICOSAT_UNKNOWN;
+
+#ifndef NADC
+ if (!ps->adodisabled && ps->adoconflicts >= ps->adoconflictlimit)
+ {
+ assert (bcp_queue_is_empty (ps));
+ return PICOSAT_UNKNOWN;
+ }
+#endif
+
+ if (ps->fsimplify < ps->fixed && ps->lsimplify <= ps->propagations)
+ {
+ simplify (ps, 0);
+ if (!bcp_queue_is_empty (ps))
+ continue;
+#ifndef NFL
+ if (ps->mtcls)
+ return PICOSAT_UNSATISFIABLE;
+
+ if (satisfied (ps))
+ return PICOSAT_SATISFIABLE;
+
+ assert (!ps->LEVEL);
+#endif
+ }
+
+ if (need_to_reduce (ps))
+ reduce (ps, 50);
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v4 04/12] kconfig: Add picosat.c (3/3)
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
` (2 preceding siblings ...)
2024-07-10 6:52 ` [PATCH v4 03/12] kconfig: Add picosat.c (2/3) Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 05/12] kconfig: Add definitions Ole Schuerks
` (7 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
The third part of picosat.c
Signed-off-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/picosat.c | 2502 +++++++++++++++++++++++++++++++++++++
1 file changed, 2502 insertions(+)
diff --git a/scripts/kconfig/picosat.c b/scripts/kconfig/picosat.c
index c183dffb89a3..87931e864bfd 100644
--- a/scripts/kconfig/picosat.c
+++ b/scripts/kconfig/picosat.c
@@ -5998,3 +5998,2505 @@ sat (PS * ps, int l)
if (need_to_reduce (ps))
reduce (ps, 50);
+
+ if (ps->conflicts >= ps->lrestart && ps->LEVEL > 2)
+ restart (ps);
+
+ decide (ps);
+ if (ps->failed_assumption)
+ return PICOSAT_UNSATISFIABLE;
+ count++;
+ }
+}
+
+static void
+rebias (PS * ps)
+{
+ Cls ** p, * c;
+ Var * v;
+
+ for (v = ps->vars + 1; v <= ps->vars + ps->max_var; v++)
+ v->assigned = 0;
+
+ memset (ps->jwh, 0, 2 * (ps->max_var + 1) * sizeof *ps->jwh);
+
+ for (p = ps->oclauses; p < ps->ohead; p++)
+ {
+ c = *p;
+
+ if (!c)
+ continue;
+
+ if (c->learned)
+ continue;
+
+ incjwh (ps, c);
+ }
+}
+
+#ifdef TRACE
+
+static unsigned
+core (PS * ps)
+{
+ unsigned idx, prev, this, delta, i, lcore, vcore;
+ unsigned *stack, *shead, *eos;
+ Lit **q, **eol, *lit;
+ Cls *c, *reason;
+ Znt *p, byte;
+ Zhn *zhain;
+ Var *v;
+
+ assert (ps->trace);
+
+ assert (ps->mtcls || ps->failed_assumption);
+ if (ps->ocore >= 0)
+ return ps->ocore;
+
+ lcore = ps->ocore = vcore = 0;
+
+ stack = shead = eos = 0;
+ ENLARGE (stack, shead, eos);
+
+ if (ps->mtcls)
+ {
+ idx = CLS2IDX (ps->mtcls);
+ *shead++ = idx;
+ }
+ else
+ {
+ assert (ps->failed_assumption);
+ v = LIT2VAR (ps->failed_assumption);
+ reason = v->reason;
+ assert (reason);
+ idx = CLS2IDX (reason);
+ *shead++ = idx;
+ }
+
+ while (shead > stack)
+ {
+ idx = *--shead;
+ zhain = IDX2ZHN (idx);
+
+ if (zhain)
+ {
+ if (zhain->core)
+ continue;
+
+ zhain->core = 1;
+ lcore++;
+
+ c = IDX2CLS (idx);
+ if (c)
+ {
+ assert (!c->core);
+ c->core = 1;
+ }
+
+ i = 0;
+ delta = 0;
+ prev = 0;
+ for (p = zhain->znt; (byte = *p); p++, i += 7)
+ {
+ delta |= (byte & 0x7f) << i;
+ if (byte & 0x80)
+ continue;
+
+ this = prev + delta;
+ assert (prev < this); /* no overflow */
+
+ if (shead == eos)
+ ENLARGE (stack, shead, eos);
+ *shead++ = this;
+
+ prev = this;
+ delta = 0;
+ i = -7;
+ }
+ }
+ else
+ {
+ c = IDX2CLS (idx);
+
+ assert (c);
+ assert (!c->learned);
+
+ if (c->core)
+ continue;
+
+ c->core = 1;
+ ps->ocore++;
+
+ eol = end_of_lits (c);
+ for (q = c->lits; q < eol; q++)
+ {
+ lit = *q;
+ v = LIT2VAR (lit);
+ if (v->core)
+ continue;
+
+ v->core = 1;
+ vcore++;
+
+ if (!ps->failed_assumption) continue;
+ if (lit != ps->failed_assumption) continue;
+
+ reason = v->reason;
+ if (!reason) continue;
+ if (reason->core) continue;
+
+ idx = CLS2IDX (reason);
+ if (shead == eos)
+ ENLARGE (stack, shead, eos);
+ *shead++ = idx;
+ }
+ }
+ }
+
+ DELETEN (stack, eos - stack);
+
+ if (ps->verbosity)
+ fprintf (ps->out,
+ "%s%u core variables out of %u (%.1f%%)\n"
+ "%s%u core original clauses out of %u (%.1f%%)\n"
+ "%s%u core learned clauses out of %u (%.1f%%)\n",
+ ps->prefix, vcore, ps->max_var, PERCENT (vcore, ps->max_var),
+ ps->prefix, ps->ocore, ps->oadded, PERCENT (ps->ocore, ps->oadded),
+ ps->prefix, lcore, ps->ladded, PERCENT (lcore, ps->ladded));
+
+ return ps->ocore;
+}
+
+static void
+trace_lits (PS * ps, Cls * c, FILE * file)
+{
+ Lit **p, **eol = end_of_lits (c);
+
+ assert (c);
+ assert (c->core);
+
+ for (p = c->lits; p < eol; p++)
+ fprintf (file, "%d ", LIT2INT (*p));
+
+ fputc ('0', file);
+}
+
+static void
+write_idx (PS * ps, unsigned idx, FILE * file)
+{
+ fprintf (file, "%ld", EXPORTIDX (idx));
+}
+
+static void
+trace_clause (PS * ps, unsigned idx, Cls * c, FILE * file, int fmt)
+{
+ assert (c);
+ assert (c->core);
+ assert (fmt == RUP_TRACE_FMT || !c->learned);
+ assert (CLS2IDX (c) == idx);
+
+ if (fmt != RUP_TRACE_FMT)
+ {
+ write_idx (ps, idx, file);
+ fputc (' ', file);
+ }
+
+ trace_lits (ps, c, file);
+
+ if (fmt != RUP_TRACE_FMT)
+ fputs (" 0", file);
+
+ fputc ('\n', file);
+}
+
+static void
+trace_zhain (PS * ps, unsigned idx, Zhn * zhain, FILE * file, int fmt)
+{
+ unsigned prev, this, delta, i;
+ Znt *p, byte;
+ Cls * c;
+
+ assert (zhain);
+ assert (zhain->core);
+
+ write_idx (ps, idx, file);
+ fputc (' ', file);
+
+ if (fmt == EXTENDED_TRACECHECK_TRACE_FMT)
+ {
+ c = IDX2CLS (idx);
+ assert (c);
+ trace_lits (ps, c, file);
+ }
+ else
+ {
+ assert (fmt == COMPACT_TRACECHECK_TRACE_FMT);
+ putc ('*', file);
+ }
+
+ i = 0;
+ delta = 0;
+ prev = 0;
+
+ for (p = zhain->znt; (byte = *p); p++, i += 7)
+ {
+ delta |= (byte & 0x7f) << i;
+ if (byte & 0x80)
+ continue;
+
+ this = prev + delta;
+
+ putc (' ', file);
+ write_idx (ps, this, file);
+
+ prev = this;
+ delta = 0;
+ i = -7;
+ }
+
+ fputs (" 0\n", file);
+}
+
+static void
+write_core (PS * ps, FILE * file)
+{
+ Lit **q, **eol;
+ Cls **p, *c;
+
+ fprintf (file, "p cnf %u %u\n", ps->max_var, core (ps));
+
+ for (p = SOC; p != EOC; p = NXC (p))
+ {
+ c = *p;
+
+ if (!c || c->learned || !c->core)
+ continue;
+
+ eol = end_of_lits (c);
+ for (q = c->lits; q < eol; q++)
+ fprintf (file, "%d ", LIT2INT (*q));
+
+ fputs ("0\n", file);
+ }
+}
+
+#endif
+
+static void
+write_trace (PS * ps, FILE * file, int fmt)
+{
+#ifdef TRACE
+ Cls *c, ** p;
+ Zhn *zhain;
+ unsigned i;
+
+ core (ps);
+
+ if (fmt == RUP_TRACE_FMT)
+ {
+ ps->rupvariables = picosat_variables (ps),
+ ps->rupclauses = picosat_added_original_clauses (ps);
+ write_rup_header (ps, file);
+ }
+
+ for (p = SOC; p != EOC; p = NXC (p))
+ {
+ c = *p;
+
+ if (ps->oclauses <= p && p < ps->eoo)
+ {
+ i = OIDX2IDX (p - ps->oclauses);
+ assert (!c || CLS2IDX (c) == i);
+ }
+ else
+ {
+ assert (ps->lclauses <= p && p < ps->EOL);
+ i = LIDX2IDX (p - ps->lclauses);
+ }
+
+ zhain = IDX2ZHN (i);
+
+ if (zhain)
+ {
+ if (zhain->core)
+ {
+ if (fmt == RUP_TRACE_FMT)
+ trace_clause (ps,i, c, file, fmt);
+ else
+ trace_zhain (ps, i, zhain, file, fmt);
+ }
+ }
+ else if (c)
+ {
+ if (fmt != RUP_TRACE_FMT && c)
+ {
+ if (c->core)
+ trace_clause (ps, i, c, file, fmt);
+ }
+ }
+ }
+#else
+ (void) file;
+ (void) fmt;
+ (void) ps;
+#endif
+}
+
+static void
+write_core_wrapper (PS * ps, FILE * file, int fmt)
+{
+ (void) fmt;
+#ifdef TRACE
+ write_core (ps, file);
+#else
+ (void) ps;
+ (void) file;
+#endif
+}
+
+static Lit *
+import_lit (PS * ps, int lit, int nointernal)
+{
+ Lit * res;
+ Var * v;
+
+ ABORTIF (lit == INT_MIN, "API usage: INT_MIN literal");
+ ABORTIF (abs (lit) > (int) ps->max_var && ps->CLS != ps->clshead,
+ "API usage: new variable index after 'picosat_push'");
+
+ if (abs (lit) <= (int) ps->max_var)
+ {
+ res = int2lit (ps, lit);
+ v = LIT2VAR (res);
+ if (nointernal && v->internal)
+ ABORT ("API usage: trying to import invalid literal");
+ else if (!nointernal && !v->internal)
+ ABORT ("API usage: trying to import invalid context");
+ }
+ else
+ {
+ while (abs (lit) > (int) ps->max_var)
+ inc_max_var (ps);
+ res = int2lit (ps, lit);
+ }
+
+ return res;
+}
+
+#ifdef TRACE
+static void
+reset_core (PS * ps)
+{
+ Cls ** p, * c;
+ Zhn ** q, * z;
+ unsigned i;
+
+ for (i = 1; i <= ps->max_var; i++)
+ ps->vars[i].core = 0;
+
+ for (p = SOC; p != EOC; p = NXC (p))
+ if ((c = *p))
+ c->core = 0;
+
+ for (q = ps->zhains; q != ps->zhead; q++)
+ if ((z = *q))
+ z->core = 0;
+
+ ps->ocore = -1;
+}
+#endif
+
+static void
+reset_assumptions (PS * ps)
+{
+ Lit ** p;
+
+ ps->failed_assumption = 0;
+
+ if (ps->extracted_all_failed_assumptions)
+ {
+ for (p = ps->als; p < ps->alshead; p++)
+ LIT2VAR (*p)->failed = 0;
+
+ ps->extracted_all_failed_assumptions = 0;
+ }
+
+ ps->alstail = ps->alshead = ps->als;
+ ps->adecidelevel = 0;
+}
+
+static void
+check_ready (PS * ps)
+{
+ ABORTIF (!ps || ps->state == RESET, "API usage: uninitialized");
+}
+
+static void
+check_sat_state (PS * ps)
+{
+ ABORTIF (ps->state != SAT, "API usage: expected to be in SAT state");
+}
+
+static void
+check_unsat_state (PS * ps)
+{
+ ABORTIF (ps->state != UNSAT, "API usage: expected to be in UNSAT state");
+}
+
+static void
+check_sat_or_unsat_or_unknown_state (PS * ps)
+{
+ ABORTIF (ps->state != SAT && ps->state != UNSAT && ps->state != UNKNOWN,
+ "API usage: expected to be in SAT, UNSAT, or UNKNOWN state");
+}
+
+static void
+reset_partial (PS * ps)
+{
+ unsigned idx;
+ if (!ps->partial)
+ return;
+ for (idx = 1; idx <= ps->max_var; idx++)
+ ps->vars[idx].partial = 0;
+ ps->partial = 0;
+}
+
+static void
+reset_incremental_usage (PS * ps)
+{
+ unsigned num_non_false;
+ Lit * lit, ** q;
+
+ check_sat_or_unsat_or_unknown_state (ps);
+
+ LOG ( fprintf (ps->out, "%sRESET incremental usage\n", ps->prefix));
+
+ if (ps->LEVEL)
+ undo (ps, 0);
+
+ reset_assumptions (ps);
+
+ if (ps->conflict)
+ {
+ num_non_false = 0;
+ for (q = ps->conflict->lits; q < end_of_lits (ps->conflict); q++)
+ {
+ lit = *q;
+ if (lit->val != FALSE)
+ num_non_false++;
+ }
+
+ // assert (num_non_false >= 2); // TODO: why this assertion?
+#ifdef NO_BINARY_CLAUSES
+ if (ps->conflict == &ps->cimpl)
+ resetcimpl (ps);
+#endif
+#ifndef NADC
+ if (ps->conflict == ps->adoconflict)
+ resetadoconflict (ps);
+#endif
+ ps->conflict = 0;
+ }
+
+#ifdef TRACE
+ reset_core (ps);
+#endif
+
+ reset_partial (ps);
+
+ ps->saved_flips = ps->flips;
+ ps->min_flipped = UINT_MAX;
+ ps->saved_max_var = ps->max_var;
+
+ ps->state = READY;
+}
+
+static void
+enter (PS * ps)
+{
+ if (ps->nentered++)
+ return;
+
+ check_ready (ps);
+ ps->entered = picosat_time_stamp ();
+}
+
+static void
+leave (PS * ps)
+{
+ assert (ps->nentered);
+ if (--ps->nentered)
+ return;
+
+ sflush (ps);
+}
+
+static void
+check_trace_support_and_execute (PS * ps,
+ FILE * file,
+ void (*f)(PS*,FILE*,int), int fmt)
+{
+ check_ready (ps);
+ check_unsat_state (ps);
+#ifdef TRACE
+ ABORTIF (!ps->trace, "API usage: tracing disabled");
+ enter (ps);
+ f (ps, file, fmt);
+ leave (ps);
+#else
+ (void) file;
+ (void) fmt;
+ (void) f;
+ ABORT ("compiled without trace support");
+#endif
+}
+
+static void
+extract_all_failed_assumptions (PS * ps)
+{
+ Lit ** p, ** eol;
+ Var * v, * u;
+ int pos;
+ Cls * c;
+
+ assert (!ps->extracted_all_failed_assumptions);
+
+ assert (ps->failed_assumption);
+ assert (ps->mhead == ps->marked);
+
+ if (ps->marked == ps->eom)
+ ENLARGE (ps->marked, ps->mhead, ps->eom);
+
+ v = LIT2VAR (ps->failed_assumption);
+ mark_var (ps, v);
+ pos = 0;
+
+ while (pos < ps->mhead - ps->marked)
+ {
+ v = ps->marked[pos++];
+ assert (v->mark);
+ c = var2reason (ps, v);
+ if (!c)
+ continue;
+ eol = end_of_lits (c);
+ for (p = c->lits; p < eol; p++)
+ {
+ u = LIT2VAR (*p);
+ if (!u->mark)
+ mark_var (ps, u);
+ }
+#ifdef NO_BINARY_CLAUSES
+ if (c == &ps->impl)
+ resetimpl (ps);
+#endif
+ }
+
+ for (p = ps->als; p < ps->alshead; p++)
+ {
+ u = LIT2VAR (*p);
+ if (!u->mark) continue;
+ u->failed = 1;
+ LOG ( fprintf (ps->out,
+ "%sfailed assumption %d\n",
+ ps->prefix, LIT2INT (*p)));
+ }
+
+ while (ps->mhead > ps->marked)
+ (*--ps->mhead)->mark = 0;
+
+ ps->extracted_all_failed_assumptions = 1;
+}
+
+const char *
+picosat_copyright (void)
+{
+ return "Copyright (c) 2006 - 2014 Armin Biere JKU Linz";
+}
+
+PicoSAT *
+picosat_init (void)
+{
+ return init (0, 0, 0, 0);
+}
+
+PicoSAT *
+picosat_minit (void * pmgr,
+ picosat_malloc pnew,
+ picosat_realloc presize,
+ picosat_free pfree)
+{
+ ABORTIF (!pnew, "API usage: zero 'picosat_malloc' argument");
+ ABORTIF (!presize, "API usage: zero 'picosat_realloc' argument");
+ ABORTIF (!pfree, "API usage: zero 'picosat_free' argument");
+ return init (pmgr, pnew, presize, pfree);
+}
+
+
+void
+picosat_adjust (PS * ps, int new_max_var)
+{
+ unsigned new_size_vars;
+
+ ABORTIF (abs (new_max_var) > (int) ps->max_var && ps->CLS != ps->clshead,
+ "API usage: adjusting variable index after 'picosat_push'");
+ enter (ps);
+
+ new_max_var = abs (new_max_var);
+ new_size_vars = new_max_var + 1;
+
+ if (ps->size_vars < new_size_vars)
+ enlarge (ps, new_size_vars);
+
+ while (ps->max_var < (unsigned) new_max_var)
+ inc_max_var (ps);
+
+ leave (ps);
+}
+
+int
+picosat_inc_max_var (PS * ps)
+{
+ if (ps->measurealltimeinlib)
+ enter (ps);
+ else
+ check_ready (ps);
+
+ inc_max_var (ps);
+
+ if (ps->measurealltimeinlib)
+ leave (ps);
+
+ return ps->max_var;
+}
+
+int
+picosat_context (PS * ps)
+{
+ return ps->clshead == ps->CLS ? 0 : LIT2INT (ps->clshead[-1]);
+}
+
+int
+picosat_push (PS * ps)
+{
+ int res;
+ Lit *lit;
+ Var * v;
+
+ if (ps->measurealltimeinlib)
+ enter (ps);
+ else
+ check_ready (ps);
+
+ if (ps->state != READY)
+ reset_incremental_usage (ps);
+
+ if (ps->rils != ps->rilshead)
+ {
+ res = *--ps->rilshead;
+ assert (ps->vars[res].internal);
+ }
+ else
+ {
+ inc_max_var (ps);
+ res = ps->max_var;
+ v = ps->vars + res;
+ assert (!v->internal);
+ v->internal = 1;
+ ps->internals++;
+ LOG ( fprintf (ps->out, "%snew internal variable index %d\n", ps->prefix, res));
+ }
+
+ lit = int2lit (ps, res);
+
+ if (ps->clshead == ps->eocls)
+ ENLARGE (ps->CLS, ps->clshead, ps->eocls);
+ *ps->clshead++ = lit;
+
+ ps->contexts++;
+
+ LOG ( fprintf (ps->out, "%snew context %d at depth %ld after push\n",
+ ps->prefix, res, (long)(ps->clshead - ps->CLS)));
+
+ if (ps->measurealltimeinlib)
+ leave (ps);
+
+ return res;
+}
+
+int
+picosat_pop (PS * ps)
+{
+ Lit * lit;
+ int res;
+ ABORTIF (ps->CLS == ps->clshead, "API usage: too many 'picosat_pop'");
+ ABORTIF (ps->added != ps->ahead, "API usage: incomplete clause");
+
+ if (ps->measurealltimeinlib)
+ enter (ps);
+ else
+ check_ready (ps);
+
+ if (ps->state != READY)
+ reset_incremental_usage (ps);
+
+ assert (ps->CLS < ps->clshead);
+ lit = *--ps->clshead;
+ LOG ( fprintf (ps->out, "%sclosing context %d at depth %ld after pop\n",
+ ps->prefix, LIT2INT (lit), (long)(ps->clshead - ps->CLS) + 1));
+
+ if (ps->cilshead == ps->eocils)
+ ENLARGE (ps->cils, ps->cilshead, ps->eocils);
+ *ps->cilshead++ = LIT2INT (lit);
+
+ if (ps->cilshead - ps->cils > MAXCILS) {
+ LOG ( fprintf (ps->out,
+ "%srecycling %ld interals with forced simplification\n",
+ ps->prefix, (long)(ps->cilshead - ps->cils)));
+ simplify (ps, 1);
+ }
+
+ res = picosat_context (ps);
+ if (res)
+ LOG ( fprintf (ps->out, "%snew context %d at depth %ld after pop\n",
+ ps->prefix, res, (long)(ps->clshead - ps->CLS)));
+ else
+ LOG ( fprintf (ps->out, "%souter most context reached after pop\n", ps->prefix));
+
+ if (ps->measurealltimeinlib)
+ leave (ps);
+
+ return res;
+}
+
+void
+picosat_set_verbosity (PS * ps, int new_verbosity_level)
+{
+ check_ready (ps);
+ ps->verbosity = new_verbosity_level;
+}
+
+void
+picosat_set_plain (PS * ps, int new_plain_value)
+{
+ check_ready (ps);
+ ps->plain = new_plain_value;
+}
+
+int
+picosat_enable_trace_generation (PS * ps)
+{
+ int res = 0;
+ check_ready (ps);
+#ifdef TRACE
+ ABORTIF (ps->addedclauses,
+ "API usage: trace generation enabled after adding clauses");
+ res = ps->trace = 1;
+#endif
+ return res;
+}
+
+void
+picosat_set_incremental_rup_file (PS * ps, FILE * rup_file, int m, int n)
+{
+ check_ready (ps);
+ assert (!ps->rupstarted);
+ ps->rup = rup_file;
+ ps->rupvariables = m;
+ ps->rupclauses = n;
+}
+
+void
+picosat_set_output (PS * ps, FILE * output_file)
+{
+ check_ready (ps);
+ ps->out = output_file;
+}
+
+void
+picosat_measure_all_calls (PS * ps)
+{
+ check_ready (ps);
+ ps->measurealltimeinlib = 1;
+}
+
+void
+picosat_set_prefix (PS * ps, const char * str)
+{
+ check_ready (ps);
+ new_prefix (ps, str);
+}
+
+void
+picosat_set_seed (PS * ps, unsigned s)
+{
+ check_ready (ps);
+ ps->srng = s;
+}
+
+void
+picosat_reset (PS * ps)
+{
+ check_ready (ps);
+ reset (ps);
+}
+
+int
+picosat_add (PS * ps, int int_lit)
+{
+ int res = ps->oadded;
+ Lit *lit;
+
+ if (ps->measurealltimeinlib)
+ enter (ps);
+ else
+ check_ready (ps);
+
+ ABORTIF (ps->rup && ps->rupstarted && ps->oadded >= (unsigned)ps->rupclauses,
+ "API usage: adding too many clauses after RUP header written");
+#ifndef NADC
+ ABORTIF (ps->addingtoado,
+ "API usage: 'picosat_add' and 'picosat_add_ado_lit' mixed");
+#endif
+ if (ps->state != READY)
+ reset_incremental_usage (ps);
+
+ if (ps->saveorig)
+ {
+ if (ps->sohead == ps->eoso)
+ ENLARGE (ps->soclauses, ps->sohead, ps->eoso);
+
+ *ps->sohead++ = int_lit;
+ }
+
+ if (int_lit)
+ {
+ lit = import_lit (ps, int_lit, 1);
+ add_lit (ps, lit);
+ }
+ else
+ simplify_and_add_original_clause (ps);
+
+ if (ps->measurealltimeinlib)
+ leave (ps);
+
+ return res;
+}
+
+int
+picosat_add_arg (PS * ps, ...)
+{
+ int lit;
+ va_list ap;
+ va_start (ap, ps);
+ while ((lit = va_arg (ap, int)))
+ (void) picosat_add (ps, lit);
+ va_end (ap);
+ return picosat_add (ps, 0);
+}
+
+int
+picosat_add_lits (PS * ps, int * lits)
+{
+ const int * p;
+ int lit;
+ for (p = lits; (lit = *p); p++)
+ (void) picosat_add (ps, lit);
+ return picosat_add (ps, 0);
+}
+
+void
+picosat_add_ado_lit (PS * ps, int external_lit)
+{
+#ifndef NADC
+ Lit * internal_lit;
+
+ if (ps->measurealltimeinlib)
+ enter (ps);
+ else
+ check_ready (ps);
+
+ if (ps->state != READY)
+ reset_incremental_usage (ps);
+
+ ABORTIF (!ps->addingtoado && ps->ahead > ps->added,
+ "API usage: 'picosat_add' and 'picosat_add_ado_lit' mixed");
+
+ if (external_lit)
+ {
+ ps->addingtoado = 1;
+ internal_lit = import_lit (ps, external_lit, 1);
+ add_lit (ps, internal_lit);
+ }
+ else
+ {
+ ps->addingtoado = 0;
+ add_ado (ps);
+ }
+ if (ps->measurealltimeinlib)
+ leave (ps);
+#else
+ (void) ps;
+ (void) external_lit;
+ ABORT ("compiled without all different constraint support");
+#endif
+}
+
+static void
+assume (PS * ps, Lit * lit)
+{
+ if (ps->alshead == ps->eoals)
+ {
+ assert (ps->alstail == ps->als);
+ ENLARGE (ps->als, ps->alshead, ps->eoals);
+ ps->alstail = ps->als;
+ }
+
+ *ps->alshead++ = lit;
+ LOG ( fprintf (ps->out, "%sassumption %d\n", ps->prefix, LIT2INT (lit)));
+}
+
+static void
+assume_contexts (PS * ps)
+{
+ Lit ** p;
+ if (ps->als != ps->alshead)
+ return;
+ for (p = ps->CLS; p != ps->clshead; p++)
+ assume (ps, *p);
+}
+
+#ifndef RCODE
+static const char * enumstr (int i) {
+ int last = i % 10;
+ if (last == 1) return "st";
+ if (last == 2) return "nd";
+ if (last == 3) return "rd";
+ return "th";
+}
+#endif
+
+static int
+tderef (PS * ps, int int_lit)
+{
+ Lit * lit;
+ Var * v;
+
+ assert (abs (int_lit) <= (int) ps->max_var);
+
+ lit = int2lit (ps, int_lit);
+
+ v = LIT2VAR (lit);
+ if (v->level > 0)
+ return 0;
+
+ if (lit->val == TRUE)
+ return 1;
+
+ if (lit->val == FALSE)
+ return -1;
+
+ return 0;
+}
+
+static int
+pderef (PS * ps, int int_lit)
+{
+ Lit * lit;
+ Var * v;
+
+ assert (abs (int_lit) <= (int) ps->max_var);
+
+ v = ps->vars + abs (int_lit);
+ if (!v->partial)
+ return 0;
+
+ lit = int2lit (ps, int_lit);
+
+ if (lit->val == TRUE)
+ return 1;
+
+ if (lit->val == FALSE)
+ return -1;
+
+ return 0;
+}
+
+static void
+minautarky (PS * ps)
+{
+ unsigned * occs, maxoccs, tmpoccs, npartial;
+ int * p, * c, lit, best, val;
+#ifdef LOGGING
+ int tl;
+#endif
+
+ assert (!ps->partial);
+
+ npartial = 0;
+
+ NEWN (occs, 2*ps->max_var + 1);
+ CLRN (occs, 2*ps->max_var + 1);
+ occs += ps->max_var;
+ for (p = ps->soclauses; p < ps->sohead; p++)
+ occs[*p]++;
+ assert (occs[0] == ps->oadded);
+
+ for (c = ps->soclauses; c < ps->sohead; c = p + 1)
+ {
+#ifdef LOGGING
+ tl = 0;
+#endif
+ best = 0;
+ maxoccs = 0;
+ for (p = c; (lit = *p); p++)
+ {
+ val = tderef (ps, lit);
+ if (val < 0)
+ continue;
+ if (val > 0)
+ {
+#ifdef LOGGING
+ tl = 1;
+#endif
+ best = lit;
+ maxoccs = occs[lit];
+ }
+
+ val = pderef (ps, lit);
+ if (val > 0)
+ break;
+ if (val < 0)
+ continue;
+ val = int2lit (ps, lit)->val;
+ assert (val);
+ if (val < 0)
+ continue;
+ tmpoccs = occs[lit];
+ if (best && tmpoccs <= maxoccs)
+ continue;
+ best = lit;
+ maxoccs = tmpoccs;
+ }
+ if (!lit)
+ {
+ assert (best);
+ LOG ( fprintf (ps->out, "%sautark %d with %d occs%s\n",
+ ps->prefix, best, maxoccs, tl ? " (top)" : ""));
+ ps->vars[abs (best)].partial = 1;
+ npartial++;
+ }
+ for (p = c; (lit = *p); p++)
+ {
+ assert (occs[lit] > 0);
+ occs[lit]--;
+ }
+ }
+ occs -= ps->max_var;
+ DELETEN (occs, 2*ps->max_var + 1);
+ ps->partial = 1;
+
+ if (ps->verbosity)
+ fprintf (ps->out,
+ "%sautarky of size %u out of %u satisfying all clauses (%.1f%%)\n",
+ ps->prefix, npartial, ps->max_var, PERCENT (npartial, ps->max_var));
+}
+
+void
+picosat_assume (PS * ps, int int_lit)
+{
+ Lit *lit;
+
+ if (ps->measurealltimeinlib)
+ enter (ps);
+ else
+ check_ready (ps);
+
+ if (ps->state != READY)
+ reset_incremental_usage (ps);
+
+ assume_contexts (ps);
+ lit = import_lit (ps, int_lit, 1);
+ assume (ps, lit);
+
+ if (ps->measurealltimeinlib)
+ leave (ps);
+}
+
+int
+picosat_sat (PS * ps, int l)
+{
+ int res;
+ char ch;
+
+ enter (ps);
+
+ ps->calls++;
+ LOG ( fprintf (ps->out, "%sSTART call %u\n", ps->prefix, ps->calls));
+
+ if (ps->added < ps->ahead)
+ {
+#ifndef NADC
+ if (ps->addingtoado)
+ ABORT ("API usage: incomplete all different constraint");
+ else
+#endif
+ ABORT ("API usage: incomplete clause");
+ }
+
+ if (ps->state != READY)
+ reset_incremental_usage (ps);
+
+ assume_contexts (ps);
+
+ res = sat (ps, l);
+
+ assert (ps->state == READY);
+
+ switch (res)
+ {
+ case PICOSAT_UNSATISFIABLE:
+ ch = '0';
+ ps->state = UNSAT;
+ break;
+ case PICOSAT_SATISFIABLE:
+ ch = '1';
+ ps->state = SAT;
+ break;
+ default:
+ ch = '?';
+ ps->state = UNKNOWN;
+ break;
+ }
+
+ if (ps->verbosity)
+ {
+ report (ps, 1, ch);
+ rheader (ps);
+ }
+
+ leave (ps);
+ LOG ( fprintf (ps->out, "%sEND call %u result %d\n", ps->prefix, ps->calls, res));
+
+ ps->last_sat_call_result = res;
+
+ return res;
+}
+
+int
+picosat_res (PS * ps)
+{
+ return ps->last_sat_call_result;
+}
+
+int
+picosat_deref (PS * ps, int int_lit)
+{
+ Lit *lit;
+
+ check_ready (ps);
+ check_sat_state (ps);
+ ABORTIF (!int_lit, "API usage: can not deref zero literal");
+ ABORTIF (ps->mtcls, "API usage: deref after empty clause generated");
+
+#ifdef STATS
+ ps->derefs++;
+#endif
+
+ if (abs (int_lit) > (int) ps->max_var)
+ return 0;
+
+ lit = int2lit (ps, int_lit);
+
+ if (lit->val == TRUE)
+ return 1;
+
+ if (lit->val == FALSE)
+ return -1;
+
+ return 0;
+}
+
+int
+picosat_deref_toplevel (PS * ps, int int_lit)
+{
+ check_ready (ps);
+ ABORTIF (!int_lit, "API usage: can not deref zero literal");
+
+#ifdef STATS
+ ps->derefs++;
+#endif
+ if (abs (int_lit) > (int) ps->max_var)
+ return 0;
+
+ return tderef (ps, int_lit);
+}
+
+int
+picosat_inconsistent (PS * ps)
+{
+ check_ready (ps);
+ return ps->mtcls != 0;
+}
+
+int
+picosat_corelit (PS * ps, int int_lit)
+{
+ check_ready (ps);
+ check_unsat_state (ps);
+ ABORTIF (!int_lit, "API usage: zero literal can not be in core");
+
+ assert (ps->mtcls || ps->failed_assumption);
+
+#ifdef TRACE
+ {
+ int res = 0;
+ ABORTIF (!ps->trace, "tracing disabled");
+ if (ps->measurealltimeinlib)
+ enter (ps);
+ core (ps);
+ if (abs (int_lit) <= (int) ps->max_var)
+ res = ps->vars[abs (int_lit)].core;
+ assert (!res || ps->failed_assumption || ps->vars[abs (int_lit)].used);
+ if (ps->measurealltimeinlib)
+ leave (ps);
+ return res;
+ }
+#else
+ ABORT ("compiled without trace support");
+ return 0;
+#endif
+}
+
+int
+picosat_coreclause (PS * ps, int ocls)
+{
+ check_ready (ps);
+ check_unsat_state (ps);
+
+ ABORTIF (ocls < 0, "API usage: negative original clause index");
+ ABORTIF (ocls >= (int)ps->oadded, "API usage: original clause index exceeded");
+
+ assert (ps->mtcls || ps->failed_assumption);
+
+#ifdef TRACE
+ {
+ Cls ** clsptr, * c;
+ int res = 0;
+
+ ABORTIF (!ps->trace, "tracing disabled");
+ if (ps->measurealltimeinlib)
+ enter (ps);
+ core (ps);
+ clsptr = ps->oclauses + ocls;
+ assert (clsptr < ps->ohead);
+ c = *clsptr;
+ if (c)
+ res = c->core;
+ if (ps->measurealltimeinlib)
+ leave (ps);
+
+ return res;
+ }
+#else
+ ABORT ("compiled without trace support");
+ return 0;
+#endif
+}
+
+int
+picosat_failed_assumption (PS * ps, int int_lit)
+{
+ Lit * lit;
+ Var * v;
+ ABORTIF (!int_lit, "API usage: zero literal as assumption");
+ check_ready (ps);
+ check_unsat_state (ps);
+ if (ps->mtcls)
+ return 0;
+ assert (ps->failed_assumption);
+ if (abs (int_lit) > (int) ps->max_var)
+ return 0;
+ if (!ps->extracted_all_failed_assumptions)
+ extract_all_failed_assumptions (ps);
+ lit = import_lit (ps, int_lit, 1);
+ v = LIT2VAR (lit);
+ return v->failed;
+}
+
+int
+picosat_failed_context (PS * ps, int int_lit)
+{
+ Lit * lit;
+ Var * v;
+ ABORTIF (!int_lit, "API usage: zero literal as context");
+ ABORTIF (abs (int_lit) > (int) ps->max_var, "API usage: invalid context");
+ check_ready (ps);
+ check_unsat_state (ps);
+ assert (ps->failed_assumption);
+ if (!ps->extracted_all_failed_assumptions)
+ extract_all_failed_assumptions (ps);
+ lit = import_lit (ps, int_lit, 0);
+ v = LIT2VAR (lit);
+ return v->failed;
+}
+
+const int *
+picosat_failed_assumptions (PS * ps)
+{
+ Lit ** p, * lit;
+ Var * v;
+ int ilit;
+
+ ps->falshead = ps->fals;
+ check_ready (ps);
+ check_unsat_state (ps);
+ if (!ps->mtcls)
+ {
+ assert (ps->failed_assumption);
+ if (!ps->extracted_all_failed_assumptions)
+ extract_all_failed_assumptions (ps);
+
+ for (p = ps->als; p < ps->alshead; p++)
+ {
+ lit = *p;
+ v = LIT2VAR (*p);
+ if (!v->failed)
+ continue;
+ ilit = LIT2INT (lit);
+ if (ps->falshead == ps->eofals)
+ ENLARGE (ps->fals, ps->falshead, ps->eofals);
+ *ps->falshead++ = ilit;
+ }
+ }
+ if (ps->falshead == ps->eofals)
+ ENLARGE (ps->fals, ps->falshead, ps->eofals);
+ *ps->falshead++ = 0;
+ return ps->fals;
+}
+
+const int *
+picosat_mus_assumptions (PS * ps, void * s, void (*cb)(void*,const int*), int fix)
+{
+ int i, j, ilit, len, nwork, * work, res;
+ signed char * redundant;
+ Lit ** p, * lit;
+ int failed;
+ Var * v;
+#ifndef NDEBUG
+ int oldlen;
+#endif
+#ifndef RCODE
+ int norig = ps->alshead - ps->als;
+#endif
+
+ check_ready (ps);
+ check_unsat_state (ps);
+ len = 0;
+ if (!ps->mtcls)
+ {
+ assert (ps->failed_assumption);
+ if (!ps->extracted_all_failed_assumptions)
+ extract_all_failed_assumptions (ps);
+
+ for (p = ps->als; p < ps->alshead; p++)
+ if (LIT2VAR (*p)->failed)
+ len++;
+ }
+
+ if (ps->mass)
+ DELETEN (ps->mass, ps->szmass);
+ ps->szmass = len + 1;
+ NEWN (ps->mass, ps->szmass);
+
+ i = 0;
+ for (p = ps->als; p < ps->alshead; p++)
+ {
+ lit = *p;
+ v = LIT2VAR (lit);
+ if (!v->failed)
+ continue;
+ ilit = LIT2INT (lit);
+ assert (i < len);
+ ps->mass[i++] = ilit;
+ }
+ assert (i == len);
+ ps->mass[i] = 0;
+ if (ps->verbosity)
+ fprintf (ps->out,
+ "%sinitial set of failed assumptions of size %d out of %d (%.0f%%)\n",
+ ps->prefix, len, norig, PERCENT (len, norig));
+ if (cb)
+ cb (s, ps->mass);
+
+ nwork = len;
+ NEWN (work, nwork);
+ for (i = 0; i < len; i++)
+ work[i] = ps->mass[i];
+
+ NEWN (redundant, nwork);
+ CLRN (redundant, nwork);
+
+ for (i = 0; i < nwork; i++)
+ {
+ if (redundant[i])
+ continue;
+
+ if (ps->verbosity > 1)
+ fprintf (ps->out,
+ "%strying to drop %d%s assumption %d\n",
+ ps->prefix, i, enumstr (i), work[i]);
+ for (j = 0; j < nwork; j++)
+ {
+ if (i == j) continue;
+ if (j < i && fix) continue;
+ if (redundant[j]) continue;
+ picosat_assume (ps, work[j]);
+ }
+
+ res = picosat_sat (ps, -1);
+ if (res == 10)
+ {
+ if (ps->verbosity > 1)
+ fprintf (ps->out,
+ "%sfailed to drop %d%s assumption %d\n",
+ ps->prefix, i, enumstr (i), work[i]);
+
+ if (fix)
+ {
+ picosat_add (ps, work[i]);
+ picosat_add (ps, 0);
+ }
+ }
+ else
+ {
+ assert (res == 20);
+ if (ps->verbosity > 1)
+ fprintf (ps->out,
+ "%ssuceeded to drop %d%s assumption %d\n",
+ ps->prefix, i, enumstr (i), work[i]);
+ redundant[i] = 1;
+ for (j = 0; j < nwork; j++)
+ {
+ failed = picosat_failed_assumption (ps, work[j]);
+ if (j <= i)
+ {
+ assert ((j < i && fix) || redundant[j] == !failed);
+ continue;
+ }
+
+ if (!failed)
+ {
+ redundant[j] = -1;
+ if (ps->verbosity > 1)
+ fprintf (ps->out,
+ "%salso suceeded to drop %d%s assumption %d\n",
+ ps->prefix, j, enumstr (j), work[j]);
+ }
+ }
+
+#ifndef NDEBUG
+ oldlen = len;
+#endif
+ len = 0;
+ for (j = 0; j < nwork; j++)
+ if (!redundant[j])
+ ps->mass[len++] = work[j];
+ ps->mass[len] = 0;
+ assert (len < oldlen);
+
+ if (fix)
+ {
+ picosat_add (ps, -work[i]);
+ picosat_add (ps, 0);
+ }
+
+#ifndef NDEBUG
+ for (j = 0; j <= i; j++)
+ assert (redundant[j] >= 0);
+#endif
+ for (j = i + 1; j < nwork; j++)
+ {
+ if (redundant[j] >= 0)
+ continue;
+
+ if (fix)
+ {
+ picosat_add (ps, -work[j]);
+ picosat_add (ps, 0);
+ }
+
+ redundant[j] = 1;
+ }
+
+ if (ps->verbosity)
+ fprintf (ps->out,
+ "%sreduced set of failed assumptions of size %d out of %d (%.0f%%)\n",
+ ps->prefix, len, norig, PERCENT (len, norig));
+ if (cb)
+ cb (s, ps->mass);
+ }
+ }
+
+ DELETEN (work, nwork);
+ DELETEN (redundant, nwork);
+
+ if (ps->verbosity)
+ {
+ fprintf (ps->out, "%sreinitializing unsat state\n", ps->prefix);
+ fflush (ps->out);
+ }
+
+ for (i = 0; i < len; i++)
+ picosat_assume (ps, ps->mass[i]);
+
+#ifndef NDEBUG
+ res =
+#endif
+ picosat_sat (ps, -1);
+ assert (res == 20);
+
+ if (!ps->mtcls)
+ {
+ assert (!ps->extracted_all_failed_assumptions);
+ extract_all_failed_assumptions (ps);
+ }
+
+ return ps->mass;
+}
+
+static const int *
+mss (PS * ps, int * a, int size)
+{
+ int i, j, k, res;
+
+ assert (!ps->mtcls);
+
+ if (ps->szmssass)
+ DELETEN (ps->mssass, ps->szmssass);
+
+ ps->szmssass = 0;
+ ps->mssass = 0;
+
+ ps->szmssass = size + 1;
+ NEWN (ps->mssass, ps->szmssass);
+
+ LOG ( fprintf (ps->out, "%ssearch MSS over %d assumptions\n", ps->prefix, size));
+
+ k = 0;
+ for (i = k; i < size; i++)
+ {
+ for (j = 0; j < k; j++)
+ picosat_assume (ps, ps->mssass[j]);
+
+ LOG ( fprintf (ps->out,
+ "%strying to add assumption %d to MSS : %d\n",
+ ps->prefix, i, a[i]));
+
+ picosat_assume (ps, a[i]);
+
+ res = picosat_sat (ps, -1);
+ if (res == 10)
+ {
+ LOG ( fprintf (ps->out,
+ "%sadding assumption %d to MSS : %d\n", ps->prefix, i, a[i]));
+
+ ps->mssass[k++] = a[i];
+
+ for (j = i + 1; j < size; j++)
+ {
+ if (picosat_deref (ps, a[j]) <= 0)
+ continue;
+
+ LOG ( fprintf (ps->out,
+ "%salso adding assumption %d to MSS : %d\n",
+ ps->prefix, j, a[j]));
+
+ ps->mssass[k++] = a[j];
+
+ if (++i != j)
+ {
+ int tmp = a[i];
+ a[i] = a[j];
+ a[j] = tmp;
+ }
+ }
+ }
+ else
+ {
+ assert (res == 20);
+
+ LOG ( fprintf (ps->out,
+ "%signoring assumption %d in MSS : %d\n", ps->prefix, i, a[i]));
+ }
+ }
+ ps->mssass[k] = 0;
+ LOG ( fprintf (ps->out, "%sfound MSS of size %d\n", ps->prefix, k));
+
+ return ps->mssass;
+}
+
+static void
+reassume (PS * ps, const int * a, int size)
+{
+ int i;
+ LOG ( fprintf (ps->out, "%sreassuming all assumptions\n", ps->prefix));
+ for (i = 0; i < size; i++)
+ picosat_assume (ps, a[i]);
+}
+
+const int *
+picosat_maximal_satisfiable_subset_of_assumptions (PS * ps)
+{
+ const int * res;
+ int i, *a, size;
+
+ ABORTIF (ps->mtcls,
+ "API usage: CNF inconsistent (use 'picosat_inconsistent')");
+
+ enter (ps);
+
+ size = ps->alshead - ps->als;
+ NEWN (a, size);
+
+ for (i = 0; i < size; i++)
+ a[i] = LIT2INT (ps->als[i]);
+
+ res = mss (ps, a, size);
+ reassume (ps, a, size);
+
+ DELETEN (a, size);
+
+ leave (ps);
+
+ return res;
+}
+
+static void
+check_mss_flags_clean (PS * ps)
+{
+#ifndef NDEBUG
+ unsigned i;
+ for (i = 1; i <= ps->max_var; i++)
+ {
+ assert (!ps->vars[i].msspos);
+ assert (!ps->vars[i].mssneg);
+ }
+#else
+ (void) ps;
+#endif
+}
+
+static void
+push_mcsass (PS * ps, int lit)
+{
+ if (ps->nmcsass == ps->szmcsass)
+ {
+ ps->szmcsass = ps->szmcsass ? 2*ps->szmcsass : 1;
+ RESIZEN (ps->mcsass, ps->nmcsass, ps->szmcsass);
+ }
+
+ ps->mcsass[ps->nmcsass++] = lit;
+}
+
+static const int *
+next_mss (PS * ps, int mcs)
+{
+ int i, *a, size, mssize, mcsize, lit, inmss;
+ const int * res, * p;
+ Var * v;
+
+ if (ps->mtcls) return 0;
+
+ check_mss_flags_clean (ps);
+
+ if (mcs && ps->mcsass)
+ {
+ DELETEN (ps->mcsass, ps->szmcsass);
+ ps->nmcsass = ps->szmcsass = 0;
+ ps->mcsass = 0;
+ }
+
+ size = ps->alshead - ps->als;
+ NEWN (a, size);
+
+ for (i = 0; i < size; i++)
+ a[i] = LIT2INT (ps->als[i]);
+
+ (void) picosat_sat (ps, -1);
+
+ //TODO short cut for 'picosat_res () == 10'?
+
+ if (ps->mtcls)
+ {
+ assert (picosat_res (ps) == 20);
+ res = 0;
+ goto DONE;
+ }
+
+ res = mss (ps, a, size);
+
+ if (ps->mtcls)
+ {
+ res = 0;
+ goto DONE;
+ }
+
+ for (p = res; (lit = *p); p++)
+ {
+ v = ps->vars + abs (lit);
+ if (lit < 0)
+ {
+ assert (!v->msspos);
+ v->mssneg = 1;
+ }
+ else
+ {
+ assert (!v->mssneg);
+ v->msspos = 1;
+ }
+ }
+
+ mssize = p - res;
+ mcsize = INT_MIN;
+
+ for (i = 0; i < size; i++)
+ {
+ lit = a[i];
+ v = ps->vars + abs (lit);
+ if (lit > 0 && v->msspos)
+ inmss = 1;
+ else if (lit < 0 && v->mssneg)
+ inmss = 1;
+ else
+ inmss = 0;
+
+ if (mssize < mcsize)
+ {
+ if (inmss)
+ picosat_add (ps, -lit);
+ }
+ else
+ {
+ if (!inmss)
+ picosat_add (ps, lit);
+ }
+
+ if (!inmss && mcs)
+ push_mcsass (ps, lit);
+ }
+ picosat_add (ps, 0);
+ if (mcs)
+ push_mcsass (ps, 0);
+
+ for (i = 0; i < size; i++)
+ {
+ lit = a[i];
+ v = ps->vars + abs (lit);
+ v->msspos = 0;
+ v->mssneg = 0;
+ }
+
+DONE:
+
+ reassume (ps, a, size);
+ DELETEN (a, size);
+
+ return res;
+}
+
+const int *
+picosat_next_maximal_satisfiable_subset_of_assumptions (PS * ps)
+{
+ const int * res;
+ enter (ps);
+ res = next_mss (ps, 0);
+ leave (ps);
+ return res;
+}
+
+const int *
+picosat_next_minimal_correcting_subset_of_assumptions (PS * ps)
+{
+ const int * res, * tmp;
+ enter (ps);
+ tmp = next_mss (ps, 1);
+ res = tmp ? ps->mcsass : 0;
+ leave (ps);
+ return res;
+}
+
+const int *
+picosat_humus (PS * ps,
+ void (*callback)(void*state,int nmcs,int nhumus),
+ void * state)
+{
+ int lit, nmcs, j, nhumus;
+ const int * mcs, * p;
+ unsigned i;
+ Var * v;
+ enter (ps);
+#ifndef NDEBUG
+ for (i = 1; i <= ps->max_var; i++)
+ {
+ v = ps->vars + i;
+ assert (!v->humuspos);
+ assert (!v->humusneg);
+ }
+#endif
+ nhumus = nmcs = 0;
+ while ((mcs = picosat_next_minimal_correcting_subset_of_assumptions (ps)))
+ {
+ for (p = mcs; (lit = *p); p++)
+ {
+ v = ps->vars + abs (lit);
+ if (lit < 0)
+ {
+ if (!v->humusneg)
+ {
+ v->humusneg = 1;
+ nhumus++;
+ }
+ }
+ else
+ {
+ if (!v->humuspos)
+ {
+ v->humuspos = 1;
+ nhumus++;
+ }
+ }
+ }
+ nmcs++;
+ LOG ( fprintf (ps->out,
+ "%smcs %d of size %d humus %d\n",
+ ps->prefix, nmcs, (int)(p - mcs), nhumus));
+ if (callback)
+ callback (state, nmcs, nhumus);
+ }
+ assert (!ps->szhumus);
+ ps->szhumus = 1;
+ for (i = 1; i <= ps->max_var; i++)
+ {
+ v = ps->vars + i;
+ if (v->humuspos)
+ ps->szhumus++;
+ if (v->humusneg)
+ ps->szhumus++;
+ }
+ assert (nhumus + 1 == ps->szhumus);
+ NEWN (ps->humus, ps->szhumus);
+ j = 0;
+ for (i = 1; i <= ps->max_var; i++)
+ {
+ v = ps->vars + i;
+ if (v->humuspos)
+ {
+ assert (j < nhumus);
+ ps->humus[j++] = (int) i;
+ }
+ if (v->humusneg)
+ {
+ assert (j < nhumus);
+ assert (i < INT_MAX);
+ ps->humus[j++] = - (int) i;
+ }
+ }
+ assert (j == nhumus);
+ assert (j < ps->szhumus);
+ ps->humus[j] = 0;
+ leave (ps);
+ return ps->humus;
+}
+
+int
+picosat_usedlit (PS * ps, int int_lit)
+{
+ int res;
+ check_ready (ps);
+ check_sat_or_unsat_or_unknown_state (ps);
+ ABORTIF (!int_lit, "API usage: zero literal can not be used");
+ int_lit = abs (int_lit);
+ res = (int_lit <= (int) ps->max_var) ? ps->vars[int_lit].used : 0;
+ return res;
+}
+
+void
+picosat_write_clausal_core (PS * ps, FILE * file)
+{
+ check_trace_support_and_execute (ps, file, write_core_wrapper, 0);
+}
+
+void
+picosat_write_compact_trace (PS * ps, FILE * file)
+{
+ check_trace_support_and_execute (ps, file, write_trace,
+ COMPACT_TRACECHECK_TRACE_FMT);
+}
+
+void
+picosat_write_extended_trace (PS * ps, FILE * file)
+{
+ check_trace_support_and_execute (ps, file, write_trace,
+ EXTENDED_TRACECHECK_TRACE_FMT);
+}
+
+void
+picosat_write_rup_trace (PS * ps, FILE * file)
+{
+ check_trace_support_and_execute (ps, file, write_trace, RUP_TRACE_FMT);
+}
+
+size_t
+picosat_max_bytes_allocated (PS * ps)
+{
+ check_ready (ps);
+ return ps->max_bytes;
+}
+
+void
+picosat_set_propagation_limit (PS * ps, unsigned long long l)
+{
+ ps->lpropagations = l;
+}
+
+unsigned long long
+picosat_propagations (PS * ps)
+{
+ return ps->propagations;
+}
+
+unsigned long long
+picosat_visits (PS * ps)
+{
+ return ps->visits;
+}
+
+unsigned long long
+picosat_decisions (PS * ps)
+{
+ return ps->decisions;
+}
+
+int
+picosat_variables (PS * ps)
+{
+ check_ready (ps);
+ return (int) ps->max_var;
+}
+
+int
+picosat_added_original_clauses (PS * ps)
+{
+ check_ready (ps);
+ return (int) ps->oadded;
+}
+
+void
+picosat_stats (PS * ps)
+{
+#ifndef RCODE
+ unsigned redlits;
+#endif
+#ifdef STATS
+ check_ready (ps);
+ assert (ps->sdecisions + ps->rdecisions + ps->assumptions == ps->decisions);
+#endif
+ if (ps->calls > 1)
+ fprintf (ps->out, "%s%u calls\n", ps->prefix, ps->calls);
+ if (ps->contexts)
+ {
+ fprintf (ps->out, "%s%u contexts", ps->prefix, ps->contexts);
+#ifdef STATS
+ fprintf (ps->out, " %u internal variables", ps->internals);
+#endif
+ fprintf (ps->out, "\n");
+ }
+ fprintf (ps->out, "%s%u iterations\n", ps->prefix, ps->iterations);
+ fprintf (ps->out, "%s%u restarts", ps->prefix, ps->restarts);
+#ifdef STATS
+ fprintf (ps->out, " (%u skipped)", ps->skippedrestarts);
+#endif
+ fputc ('\n', ps->out);
+#ifndef NFL
+ fprintf (ps->out, "%s%u failed literals", ps->prefix, ps->failedlits);
+#ifdef STATS
+ fprintf (ps->out,
+ ", %u calls, %u rounds, %llu propagations",
+ ps->flcalls, ps->flrounds, ps->flprops);
+#endif
+ fputc ('\n', ps->out);
+#ifdef STATS
+ fprintf (ps->out,
+ "%sfl: %u = %.1f%% implicit, %llu oopsed, %llu tried, %llu skipped\n",
+ ps->prefix,
+ ps->ifailedlits, PERCENT (ps->ifailedlits, ps->failedlits),
+ ps->floopsed, ps->fltried, ps->flskipped);
+#endif
+#endif
+ fprintf (ps->out, "%s%u conflicts", ps->prefix, ps->conflicts);
+#ifdef STATS
+ fprintf (ps->out, " (%u uips = %.1f%%)\n", ps->uips, PERCENT(ps->uips,ps->conflicts));
+#else
+ fputc ('\n', ps->out);
+#endif
+#ifndef NADC
+ fprintf (ps->out, "%s%u adc conflicts\n", ps->prefix, ps->adoconflicts);
+#endif
+#ifdef STATS
+ fprintf (ps->out, "%s%llu dereferenced literals\n", ps->prefix, ps->derefs);
+#endif
+ fprintf (ps->out, "%s%u decisions", ps->prefix, ps->decisions);
+#ifdef STATS
+ fprintf (ps->out, " (%u random = %.2f%%",
+ ps->rdecisions, PERCENT (ps->rdecisions, ps->decisions));
+ fprintf (ps->out, ", %u assumptions", ps->assumptions);
+ fputc (')', ps->out);
+#endif
+ fputc ('\n', ps->out);
+#ifdef STATS
+ fprintf (ps->out,
+ "%s%u static phase decisions (%.1f%% of all variables)\n",
+ ps->prefix,
+ ps->staticphasedecisions, PERCENT (ps->staticphasedecisions, ps->max_var));
+#endif
+ fprintf (ps->out, "%s%u fixed variables\n", ps->prefix, ps->fixed);
+ assert (ps->nonminimizedllits >= ps->minimizedllits);
+#ifndef RCODE
+ redlits = ps->nonminimizedllits - ps->minimizedllits;
+#endif
+ fprintf (ps->out, "%s%u learned literals\n", ps->prefix, ps->llitsadded);
+ fprintf (ps->out, "%s%.1f%% deleted literals\n",
+ ps->prefix, PERCENT (redlits, ps->nonminimizedllits));
+
+#ifdef STATS
+#ifdef TRACE
+ fprintf (ps->out,
+ "%s%llu antecedents (%.1f antecedents per clause",
+ ps->prefix, ps->antecedents, AVERAGE (ps->antecedents, ps->conflicts));
+ if (ps->trace)
+ fprintf (ps->out, ", %.1f bytes/antecedent)", AVERAGE (ps->znts, ps->antecedents));
+ fputs (")\n", ps->out);
+#endif
+
+ fprintf (ps->out, "%s%llu propagations (%.1f propagations per decision)\n",
+ ps->prefix, ps->propagations, AVERAGE (ps->propagations, ps->decisions));
+ fprintf (ps->out, "%s%llu visits (%.1f per propagation)\n",
+ ps->prefix, ps->visits, AVERAGE (ps->visits, ps->propagations));
+ fprintf (ps->out,
+ "%s%llu binary clauses visited (%.1f%% %.1f per propagation)\n",
+ ps->prefix, ps->bvisits,
+ PERCENT (ps->bvisits, ps->visits),
+ AVERAGE (ps->bvisits, ps->propagations));
+ fprintf (ps->out,
+ "%s%llu ternary clauses visited (%.1f%% %.1f per propagation)\n",
+ ps->prefix, ps->tvisits,
+ PERCENT (ps->tvisits, ps->visits),
+ AVERAGE (ps->tvisits, ps->propagations));
+ fprintf (ps->out,
+ "%s%llu large clauses visited (%.1f%% %.1f per propagation)\n",
+ ps->prefix, ps->lvisits,
+ PERCENT (ps->lvisits, ps->visits),
+ AVERAGE (ps->lvisits, ps->propagations));
+ fprintf (ps->out, "%s%llu other true (%.1f%% of visited clauses)\n",
+ ps->prefix, ps->othertrue, PERCENT (ps->othertrue, ps->visits));
+ fprintf (ps->out,
+ "%s%llu other true in binary clauses (%.1f%%)"
+ ", %llu upper (%.1f%%)\n",
+ ps->prefix, ps->othertrue2, PERCENT (ps->othertrue2, ps->othertrue),
+ ps->othertrue2u, PERCENT (ps->othertrue2u, ps->othertrue2));
+ fprintf (ps->out,
+ "%s%llu other true in large clauses (%.1f%%)"
+ ", %llu upper (%.1f%%)\n",
+ ps->prefix, ps->othertruel, PERCENT (ps->othertruel, ps->othertrue),
+ ps->othertruelu, PERCENT (ps->othertruelu, ps->othertruel));
+ fprintf (ps->out, "%s%llu ternary and large traversals (%.1f per visit)\n",
+ ps->prefix, ps->traversals, AVERAGE (ps->traversals, ps->visits));
+ fprintf (ps->out, "%s%llu large traversals (%.1f per large visit)\n",
+ ps->prefix, ps->ltraversals, AVERAGE (ps->ltraversals, ps->lvisits));
+ fprintf (ps->out, "%s%llu assignments\n", ps->prefix, ps->assignments);
+#else
+ fprintf (ps->out, "%s%llu propagations\n", ps->prefix, picosat_propagations (ps));
+ fprintf (ps->out, "%s%llu visits\n", ps->prefix, picosat_visits (ps));
+#endif
+ fprintf (ps->out, "%s%.1f%% variables used\n", ps->prefix, PERCENT (ps->vused, ps->max_var));
+
+ sflush (ps);
+ fprintf (ps->out, "%s%.1f seconds in library\n", ps->prefix, ps->seconds);
+ fprintf (ps->out, "%s%.1f megaprops/second\n",
+ ps->prefix, AVERAGE (ps->propagations / 1e6f, ps->seconds));
+ fprintf (ps->out, "%s%.1f megavisits/second\n",
+ ps->prefix, AVERAGE (ps->visits / 1e6f, ps->seconds));
+ fprintf (ps->out, "%sprobing %.1f seconds %.0f%%\n",
+ ps->prefix, ps->flseconds, PERCENT (ps->flseconds, ps->seconds));
+#ifdef STATS
+ fprintf (ps->out,
+ "%srecycled %.1f MB in %u reductions\n",
+ ps->prefix, ps->rrecycled / (double) (1 << 20), ps->reductions);
+ fprintf (ps->out,
+ "%srecycled %.1f MB in %u simplifications\n",
+ ps->prefix, ps->srecycled / (double) (1 << 20), ps->simps);
+#else
+ fprintf (ps->out, "%s%u simplifications\n", ps->prefix, ps->simps);
+ fprintf (ps->out, "%s%u reductions\n", ps->prefix, ps->reductions);
+ fprintf (ps->out, "%s%.1f MB recycled\n", ps->prefix, ps->recycled / (double) (1 << 20));
+#endif
+ fprintf (ps->out, "%s%.1f MB maximally allocated\n",
+ ps->prefix, picosat_max_bytes_allocated (ps) / (double) (1 << 20));
+}
+
+#ifndef NGETRUSAGE
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/unistd.h>
+#endif
+
+double
+picosat_time_stamp (void)
+{
+ double res = -1;
+#ifndef NGETRUSAGE
+ struct rusage u;
+ res = 0;
+ if (!getrusage (RUSAGE_SELF, &u))
+ {
+ res += u.ru_utime.tv_sec + 1e-6 * u.ru_utime.tv_usec;
+ res += u.ru_stime.tv_sec + 1e-6 * u.ru_stime.tv_usec;
+ }
+#endif
+ return res;
+}
+
+double
+picosat_seconds (PS * ps)
+{
+ check_ready (ps);
+ return ps->seconds;
+}
+
+void
+picosat_print (PS * ps, FILE * file)
+{
+#ifdef NO_BINARY_CLAUSES
+ Lit * lit, *other, * last;
+ Ltk * stack;
+#endif
+ Lit **q, **eol;
+ Cls **p, *c;
+ unsigned n;
+
+ if (ps->measurealltimeinlib)
+ enter (ps);
+ else
+ check_ready (ps);
+
+ n = 0;
+ n += ps->alshead - ps->als;
+
+ for (p = SOC; p != EOC; p = NXC (p))
+ {
+ c = *p;
+
+ if (!c)
+ continue;
+
+#ifdef TRACE
+ if (c->collected)
+ continue;
+#endif
+ n++;
+ }
+
+#ifdef NO_BINARY_CLAUSES
+ last = int2lit (ps, -ps->max_var);
+ for (lit = int2lit (ps, 1); lit <= last; lit++)
+ {
+ stack = LIT2IMPLS (lit);
+ eol = stack->start + stack->count;
+ for (q = stack->start; q < eol; q++)
+ if (*q >= lit)
+ n++;
+ }
+#endif
+
+ fprintf (file, "p cnf %d %u\n", ps->max_var, n);
+
+ for (p = SOC; p != EOC; p = NXC (p))
+ {
+ c = *p;
+ if (!c)
+ continue;
+
+#ifdef TRACE
+ if (c->collected)
+ continue;
+#endif
+
+ eol = end_of_lits (c);
+ for (q = c->lits; q < eol; q++)
+ fprintf (file, "%d ", LIT2INT (*q));
+
+ fputs ("0\n", file);
+ }
+
+#ifdef NO_BINARY_CLAUSES
+ last = int2lit (ps, -ps->max_var);
+ for (lit = int2lit (ps, 1); lit <= last; lit++)
+ {
+ stack = LIT2IMPLS (lit);
+ eol = stack->start + stack->count;
+ for (q = stack->start; q < eol; q++)
+ if ((other = *q) >= lit)
+ fprintf (file, "%d %d 0\n", LIT2INT (lit), LIT2INT (other));
+ }
+#endif
+
+ {
+ Lit **r;
+ for (r = ps->als; r < ps->alshead; r++)
+ fprintf (file, "%d 0\n", LIT2INT (*r));
+ }
+
+ fflush (file);
+
+ if (ps->measurealltimeinlib)
+ leave (ps);
+}
+
+void
+picosat_enter (PS * ps)
+{
+ enter (ps);
+}
+
+void
+picosat_leave (PS * ps)
+{
+ leave (ps);
+}
+
+void
+picosat_message (PS * ps, int vlevel, const char * fmt, ...)
+{
+ va_list ap;
+
+ if (vlevel > ps->verbosity)
+ return;
+
+ fputs (ps->prefix, ps->out);
+ va_start (ap, fmt);
+ vfprintf (ps->out, fmt, ap);
+ va_end (ap);
+ fputc ('\n', ps->out);
+}
+
+int
+picosat_changed (PS * ps)
+{
+ int res;
+
+ check_ready (ps);
+ check_sat_state (ps);
+
+ res = (ps->min_flipped <= ps->saved_max_var);
+ assert (!res || ps->saved_flips != ps->flips);
+
+ return res;
+}
+
+void
+picosat_reset_phases (PS * ps)
+{
+ rebias (ps);
+}
+
+void
+picosat_reset_scores (PS * ps)
+{
+ Rnk * r;
+ ps->hhead = ps->heap + 1;
+ for (r = ps->rnks + 1; r <= ps->rnks + ps->max_var; r++)
+ {
+ CLR (r);
+ hpush (ps, r);
+ }
+}
+
+void
+picosat_remove_learned (PS * ps, unsigned percentage)
+{
+ enter (ps);
+ reset_incremental_usage (ps);
+ reduce (ps, percentage);
+ leave (ps);
+}
+
+void
+picosat_set_global_default_phase (PS * ps, int phase)
+{
+ check_ready (ps);
+ ABORTIF (phase < 0, "API usage: 'picosat_set_global_default_phase' "
+ "with negative argument");
+ ABORTIF (phase > 3, "API usage: 'picosat_set_global_default_phase' "
+ "with argument > 3");
+ ps->defaultphase = phase;
+}
+
+void
+picosat_set_default_phase_lit (PS * ps, int int_lit, int phase)
+{
+ unsigned newphase;
+ Lit * lit;
+ Var * v;
+
+ check_ready (ps);
+
+ lit = import_lit (ps, int_lit, 1);
+ v = LIT2VAR (lit);
+
+ if (phase)
+ {
+ newphase = (int_lit < 0) == (phase < 0);
+ v->defphase = v->phase = newphase;
+ v->usedefphase = v->assigned = 1;
+ }
+ else
+ {
+ v->usedefphase = v->assigned = 0;
+ }
+}
+
+void
+picosat_set_more_important_lit (PS * ps, int int_lit)
+{
+ Lit * lit;
+ Var * v;
+ Rnk * r;
+
+ check_ready (ps);
+
+ lit = import_lit (ps, int_lit, 1);
+ v = LIT2VAR (lit);
+ r = VAR2RNK (v);
+
+ ABORTIF (r->lessimportant, "can not mark variable more and less important");
+
+ if (r->moreimportant)
+ return;
+
+ r->moreimportant = 1;
+
+ if (r->pos)
+ hup (ps, r);
+}
+
+void
+picosat_set_less_important_lit (PS * ps, int int_lit)
+{
+ Lit * lit;
+ Var * v;
+ Rnk * r;
+
+ check_ready (ps);
+
+ lit = import_lit (ps, int_lit, 1);
+ v = LIT2VAR (lit);
+ r = VAR2RNK (v);
+
+ ABORTIF (r->moreimportant, "can not mark variable more and less important");
+
+ if (r->lessimportant)
+ return;
+
+ r->lessimportant = 1;
+
+ if (r->pos)
+ hdown (ps, r);
+}
+
+#ifndef NADC
+
+unsigned
+picosat_ado_conflicts (PS * ps)
+{
+ check_ready (ps);
+ return ps->adoconflicts;
+}
+
+void
+picosat_disable_ado (PS * ps)
+{
+ check_ready (ps);
+ assert (!ps->adodisabled);
+ ps->adodisabled = 1;
+}
+
+void
+picosat_enable_ado (PS * ps)
+{
+ check_ready (ps);
+ assert (ps->adodisabled);
+ ps->adodisabled = 0;
+}
+
+void
+picosat_set_ado_conflict_limit (PS * ps, unsigned newadoconflictlimit)
+{
+ check_ready (ps);
+ ps->adoconflictlimit = newadoconflictlimit;
+}
+
+#endif
+
+void
+picosat_simplify (PS * ps)
+{
+ enter (ps);
+ reset_incremental_usage (ps);
+ simplify (ps, 1);
+ leave (ps);
+}
+
+int
+picosat_haveados (void)
+{
+#ifndef NADC
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+void
+picosat_save_original_clauses (PS * ps)
+{
+ if (ps->saveorig) return;
+ ABORTIF (ps->oadded, "API usage: 'picosat_save_original_clauses' too late");
+ ps->saveorig = 1;
+}
+
+void picosat_set_interrupt (PicoSAT * ps,
+ void * external_state,
+ int (*interrupted)(void * external_state))
+{
+ ps->interrupt.state = external_state;
+ ps->interrupt.function = interrupted;
+}
+
+int
+picosat_deref_partial (PS * ps, int int_lit)
+{
+ check_ready (ps);
+ check_sat_state (ps);
+ ABORTIF (!int_lit, "API usage: can not partial deref zero literal");
+ ABORTIF (ps->mtcls, "API usage: deref partial after empty clause generated");
+ ABORTIF (!ps->saveorig, "API usage: 'picosat_save_original_clauses' missing");
+
+#ifdef STATS
+ ps->derefs++;
+#endif
+
+ if (!ps->partial)
+ minautarky (ps);
+
+ return pderef (ps, int_lit);
+}
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v4 05/12] kconfig: Add definitions
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
` (3 preceding siblings ...)
2024-07-10 6:52 ` [PATCH v4 04/12] kconfig: Add picosat.c (3/3) Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 06/12] kconfig: Add files for building constraints Ole Schuerks
` (6 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
We need to be able to store constraints for each symbol.
We therefore add several expressions for each such struct which we define
in a header-file.
Finally, we prepare the Makefile.
Co-developed-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Patrick Franz <deltaone@debian.org>
Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
Suggested-by: Sarah Nadi <nadi@ualberta.ca>
Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/Makefile | 13 +-
scripts/kconfig/cf_defs.h | 287 ++++++++++++++++++++++++++++++++++++++
scripts/kconfig/expr.h | 17 +++
3 files changed, 315 insertions(+), 2 deletions(-)
create mode 100644 scripts/kconfig/cf_defs.h
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index a0a0be38cbdc..53461b609bd2 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -43,6 +43,7 @@ menuconfig-prog := mconf
nconfig-prog := nconf
gconfig-prog := gconf
xconfig-prog := qconf
+cfoutconfig-prog := cfoutconfig
define config_rule
PHONY += $(1)
@@ -53,7 +54,7 @@ PHONY += build_$(1)
build_$(1): $(obj)/$($(1)-prog)
endef
-$(foreach c, config menuconfig nconfig gconfig xconfig, $(eval $(call config_rule,$(c))))
+$(foreach c, config menuconfig nconfig gconfig xconfig cfoutconfig, $(eval $(call config_rule,$(c))))
PHONY += localmodconfig localyesconfig
localyesconfig localmodconfig: $(obj)/conf
@@ -152,6 +153,7 @@ help:
@echo ' default value without prompting'
@echo ' tinyconfig - Configure the tiniest possible kernel'
@echo ' testconfig - Run Kconfig unit tests (requires python3 and pytest)'
+ @echo ' cfoutconfig - Print constraints and DIMACS-output into files'
@echo ''
@echo 'Configuration topic targets:'
@$(foreach f, $(all-config-fragments), \
@@ -196,10 +198,17 @@ $(foreach f, mconf.o $(lxdialog), \
$(obj)/mconf: | $(obj)/mconf-libs
$(addprefix $(obj)/, mconf.o $(lxdialog)): | $(obj)/mconf-cflags
+# configfix: Used for the xconfig target as well as for its debugging tools
+hostprogs += cfoutconfig
+cfconf-objs := configfix.o cf_constraints.o cf_expr.o cf_rangefix.o cf_utils.o picosat.o
+cfoutconfig-objs := cfoutconfig.o $(common-objs) $(cfconf-objs)
+
+HOSTCFLAGS_picosat.o = -DTRACE -Wno-missing-prototypes -Wno-pointer-compare
+
# qconf: Used for the xconfig target based on Qt
hostprogs += qconf
qconf-cxxobjs := qconf.o qconf-moc.o
-qconf-objs := images.o $(common-objs)
+qconf-objs := images.o $(common-objs) $(cfconf-objs)
HOSTLDLIBS_qconf = $(call read-file, $(obj)/qconf-libs)
HOSTCXXFLAGS_qconf.o = -std=c++11 -fPIC $(call read-file, $(obj)/qconf-cflags)
diff --git a/scripts/kconfig/cf_defs.h b/scripts/kconfig/cf_defs.h
new file mode 100644
index 000000000000..8bfff7db5c33
--- /dev/null
+++ b/scripts/kconfig/cf_defs.h
@@ -0,0 +1,287 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef DEFS_H
+#define DEFS_H
+
+/* global variables */
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "lkc.h"
+#include "expr.h"
+
+extern bool CFDEBUG;
+extern bool stop_rangefix;
+
+#define printd(fmt...) do { \
+ if (CFDEBUG) \
+ printf(fmt); \
+} while (0)
+
+/*
+ * For functions that construct nested pexpr expressions.
+ */
+enum pexpr_move {
+ PEXPR_ARG1, /* put reference to first pexpr */
+ PEXPR_ARG2, /* put reference to second pexpr */
+ PEXPR_ARGX /* put all references to pexpr's */
+};
+
+
+/* different types for f_expr */
+enum fexpr_type {
+ FE_SYMBOL,
+ FE_NPC, /* no prompt condition */
+ FE_TRUE, /* constant of value True */
+ FE_FALSE, /* constant of value False */
+ FE_NONBOOL, /* for all non-(boolean/tristate) known values */
+ FE_CHOICE, /* symbols of type choice */
+ FE_SELECT, /* auxiliary variable for selected symbols */
+ FE_TMPSATVAR /* temporary sat-variable (Tseytin) */
+};
+
+/* struct for a propositional logic formula */
+struct fexpr {
+ /* name of the feature expr */
+ struct gstr name;
+
+ /* associated symbol */
+ struct symbol *sym;
+
+ /* integer value for the SAT solver */
+ int satval;
+
+ /* assumption in the last call to PicoSAT */
+ bool assumption;
+
+ /* type of the fexpr */
+ enum fexpr_type type;
+
+ union {
+ /* symbol */
+ struct {
+ tristate tri;
+ };
+ /* EQUALS */
+ struct {
+ struct symbol *eqsym;
+ struct symbol *eqvalue;
+ };
+ /* HEX, INTEGER, STRING */
+ struct {
+ struct gstr nb_val;
+ };
+ };
+
+};
+
+struct fexpr_list {
+ struct fexpr_node *head, *tail;
+ unsigned int size;
+};
+
+struct fexpr_node {
+ struct fexpr *elem;
+ struct fexpr_node *next, *prev;
+};
+
+struct fexl_list {
+ struct fexl_node *head, *tail;
+ unsigned int size;
+};
+
+struct fexl_node {
+ struct fexpr_list *elem;
+ struct fexl_node *next, *prev;
+};
+
+enum pexpr_type {
+ PE_SYMBOL,
+ PE_AND,
+ PE_OR,
+ PE_NOT
+};
+
+union pexpr_data {
+ struct pexpr *pexpr;
+ struct fexpr *fexpr;
+};
+
+/**
+ * struct pexpr - a node in a tree representing a propositional formula
+ * @type: Type of the node
+ * @left: left-hand-side for AND and OR, the unique operand for NOT, and for
+ * SYMBOL it contains the fpexpr.
+ * @right: right-hand-side for AND and OR
+ * @ref_count: Number of calls to pexpr_put() that need to effectuated with this
+ * pexpr for it to get free'd.
+ *
+ * Functions that return new struct pexpr instances (like pexpr_or(),
+ * pexpr_or_share(), pexf(), ...) set @ref_count in a way that accounts for the
+ * new reference that they return (e.g. pexf() will always set it to 1).
+ * Functions with arguments of type ``struct pexpr *`` will generally keep the
+ * reference intact, so that for example
+ * ``e = pexf(sym); not_e = pexpr_not_share(e)`` would require
+ * ``pexpr_put(not_e)`` before not_e can be free'd and additionally
+ * ``pexpr_put(e)`` for e to get free'd. Some functions take an argument of type
+ * ``enum pexpr_move`` which function as a wrapper of sorts that first executes
+ * a function and then pexpr_put's the argument(s) specified by the
+ * ``enum pexpr_move`` argument (e.g. the normal function for OR is
+ * pexpr_or_share() and the wrapper is pexpr_or()).
+ */
+struct pexpr {
+ enum pexpr_type type;
+ union pexpr_data left, right;
+ unsigned int ref_count;
+};
+
+struct pexpr_list {
+ struct pexpr_node *head, *tail;
+ unsigned int size;
+};
+
+struct pexpr_node {
+ struct pexpr *elem;
+ struct pexpr_node *next, *prev;
+};
+
+/**
+ * struct default_map - Map entry from default values to their condition
+ * @val: value of the default property. Not 'owned' by this struct and
+ * therefore shouldn't be free'd.
+ * @e: condition that implies that the symbol assumes the @val. Needs to be
+ * pexpr_put when free'ing.
+ */
+struct default_map {
+ struct fexpr *val;
+ struct pexpr *e;
+};
+
+/**
+ * struct defm_list - Map from values of default properties of a symbol to their
+ * (accumulated) conditions
+ */
+struct defm_list {
+ struct defm_node *head, *tail;
+ unsigned int size;
+};
+
+struct defm_node {
+ struct default_map *elem;
+ struct defm_node *next, *prev;
+};
+
+enum symboldv_type {
+ SDV_BOOLEAN, /* boolean/tristate */
+ SDV_NONBOOLEAN /* string/int/hex */
+};
+
+struct symbol_dvalue {
+ struct symbol *sym;
+
+ enum symboldv_type type;
+
+ union {
+ /* boolean/tristate */
+ tristate tri;
+
+ /* string/int/hex */
+ struct gstr nb_val;
+ };
+};
+
+struct sdv_list {
+ struct sdv_node *head, *tail;
+ unsigned int size;
+};
+
+struct sdv_node {
+ struct symbol_dvalue *elem;
+ struct sdv_node *next, *prev;
+};
+
+enum symbolfix_type {
+ SF_BOOLEAN, /* boolean/tristate */
+ SF_NONBOOLEAN, /* string/int/hex */
+ SF_DISALLOWED /* disallowed non-boolean values */
+};
+
+struct symbol_fix {
+ struct symbol *sym;
+
+ enum symbolfix_type type;
+
+ union {
+ /* boolean/tristate */
+ tristate tri;
+
+ /* string/int/hex */
+ struct gstr nb_val;
+
+ /* disallowed non-boolean values */
+ struct gstr disallowed;
+ };
+};
+
+struct sfix_list {
+ struct sfix_node *head, *tail;
+ unsigned int size;
+};
+
+struct sfix_node {
+ struct symbol_fix *elem;
+ struct sfix_node *next, *prev;
+};
+
+struct sfl_list {
+ struct sfl_node *head, *tail;
+ unsigned int size;
+};
+
+struct sfl_node {
+ struct sfix_list *elem;
+ struct sfl_node *next, *prev;
+};
+
+struct sym_list {
+ struct sym_node *head, *tail;
+ unsigned int size;
+};
+
+struct sym_node {
+ struct symbol *elem;
+ struct sym_node *next, *prev;
+};
+
+struct prop_list {
+ struct prop_node *head, *tail;
+ unsigned int size;
+};
+
+struct prop_node {
+ struct property *elem;
+ struct prop_node *next, *prev;
+};
+
+struct constants {
+ struct fexpr *const_false;
+ struct fexpr *const_true;
+ struct fexpr *symbol_yes_fexpr;
+ struct fexpr *symbol_mod_fexpr;
+ struct fexpr *symbol_no_fexpr;
+};
+
+struct cfdata {
+ unsigned int sat_variable_nr;
+ unsigned int tmp_variable_nr;
+ struct fexpr **satmap; // map SAT variables to fexpr
+ size_t satmap_size;
+ struct constants *constants;
+ struct sdv_list *sdv_symbols; // array with conflict-symbols
+};
+
+#endif
diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index 8849a243b5e7..2582e948f4eb 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -127,6 +127,19 @@ struct symbol {
* "Weak" reverse dependencies through being implied by other symbols
*/
struct expr_value implied;
+
+ /*
+ * ConfigFix
+ */
+ struct fexpr *fexpr_y;
+ struct fexpr *fexpr_m;
+ struct fexpr *fexpr_sel_y;
+ struct fexpr *fexpr_sel_m;
+ struct pexpr *list_sel_y;
+ struct pexpr *list_sel_m;
+ struct fexpr *noPromptCond;
+ struct fexpr_list *nb_vals; /* used for non-booleans */
+ struct pexpr_list *constraints; /* list of constraints for symbol */
};
#define SYMBOL_CONST 0x0001 /* symbol is const */
@@ -190,6 +203,10 @@ struct property {
for (st = sym->prop; st; st = st->next) \
if (st->type == (tok))
#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT)
+#define for_all_choices(symbol, child, menu_ptr) \
+ list_for_each_entry(menu_ptr, &(symbol)->menus, link) \
+ for (child = (menu_ptr)->list; child; child = (child)->next) \
+ if ((child)->sym && sym_is_choice_value((child)->sym))
#define for_all_prompts(sym, st) \
for (st = sym->prop; st; st = st->next) \
if (st->text)
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v4 06/12] kconfig: Add files for building constraints
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
` (4 preceding siblings ...)
2024-07-10 6:52 ` [PATCH v4 05/12] kconfig: Add definitions Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-08-12 8:49 ` Masahiro Yamada
2024-07-10 6:52 ` [PATCH v4 07/12] kconfig: Add files for handling expressions Ole Schuerks
` (5 subsequent siblings)
11 siblings, 1 reply; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
These files translate the Kconfig-model into propositional logic and store
the constraints for each symbol in the corresponding struct.
Co-developed-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Patrick Franz <deltaone@debian.org>
Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
Suggested-by: Sarah Nadi <nadi@ualberta.ca>
Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/cf_constraints.c | 1720 ++++++++++++++++++++++++++++++
scripts/kconfig/cf_constraints.h | 26 +
2 files changed, 1746 insertions(+)
create mode 100644 scripts/kconfig/cf_constraints.c
create mode 100644 scripts/kconfig/cf_constraints.h
diff --git a/scripts/kconfig/cf_constraints.c b/scripts/kconfig/cf_constraints.c
new file mode 100644
index 000000000000..1c02a4b47383
--- /dev/null
+++ b/scripts/kconfig/cf_constraints.c
@@ -0,0 +1,1720 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#include "cf_defs.h"
+#include "expr.h"
+#define _GNU_SOURCE
+#include <assert.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "cf_utils.h"
+#include "internal.h"
+#include "cf_expr.h"
+#include "cf_constraints.h"
+
+#define KCR_CMP false
+#define NPC_OPTIMISATION true
+
+static void init_constraints(struct cfdata *data);
+static void get_constraints_bool(struct cfdata *data);
+static void get_constraints_select(struct cfdata *data);
+static void get_constraints_nonbool(struct cfdata *data);
+
+static void build_tristate_constraint_clause(struct symbol *sym,
+ struct cfdata *data);
+
+static void add_selects_kcr(struct symbol *sym, struct cfdata *data);
+static void add_selects(struct symbol *sym, struct cfdata *data);
+
+static void add_dependencies_bool(struct symbol *sym, struct cfdata *data);
+static void add_dependencies_bool_kcr(struct symbol *sym, struct cfdata *data);
+static void add_dependencies_nonbool(struct symbol *sym, struct cfdata *data);
+
+static void add_choice_prompt_cond(struct symbol *sym, struct cfdata *data);
+static void add_choice_dependencies(struct symbol *sym, struct cfdata *data);
+static void add_choice_constraints(struct symbol *sym, struct cfdata *data);
+static void add_invisible_constraints(struct symbol *sym, struct cfdata *data);
+static void sym_nonbool_at_least_1(struct symbol *sym, struct cfdata *data);
+static void sym_nonbool_at_most_1(struct symbol *sym, struct cfdata *data);
+static void sym_add_nonbool_values_from_default_range(struct symbol *sym,
+ struct cfdata *data);
+static void sym_add_range_constraints(struct symbol *sym, struct cfdata *data);
+static void sym_add_nonbool_prompt_constraint(struct symbol *sym,
+ struct cfdata *data);
+
+static struct default_map *create_default_map_entry(struct fexpr *val,
+ struct pexpr *e);
+static struct defm_list *get_defaults(struct symbol *sym, struct cfdata *data);
+static struct pexpr *get_default_y(struct defm_list *list, struct cfdata *data);
+static struct pexpr *get_default_m(struct defm_list *list, struct cfdata *data);
+static struct pexpr *get_default_any(struct symbol *sym, struct cfdata *data);
+static long sym_get_range_val(struct symbol *sym, int base);
+
+/* -------------------------------------- */
+
+/*
+ * build the constraints for each symbol
+ */
+void get_constraints(struct cfdata *data)
+{
+ printd("Building constraints...");
+
+ init_constraints(data);
+ get_constraints_bool(data);
+ get_constraints_select(data);
+ get_constraints_nonbool(data);
+}
+
+/*
+ * need to go through the constraints once to find all "known values"
+ * for the non-Boolean symbols (and add them to sym->nb_vals for the given
+ * symbols).
+ * expr_calculate_pexpr_both and get_defaults have the side effect of creating
+ * known values.
+ */
+static void init_constraints(struct cfdata *data)
+{
+ struct symbol *sym;
+ struct property *p;
+
+ for_all_symbols(sym) {
+ struct property *prompt;
+
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ if (sym_is_boolean(sym)) {
+ for_all_properties(sym, p, P_SELECT)
+ pexpr_put(expr_calculate_pexpr_both(p->visible.expr,
+ data));
+
+ for_all_properties(sym, p, P_IMPLY)
+ pexpr_put(expr_calculate_pexpr_both(p->visible.expr,
+ data));
+ }
+
+ if (sym->dir_dep.expr)
+ pexpr_put(expr_calculate_pexpr_both(sym->dir_dep.expr, data));
+
+ prompt = sym_get_prompt(sym);
+ if (prompt != NULL && prompt->visible.expr) {
+ pexpr_put(expr_calculate_pexpr_both(prompt->visible.expr, data));
+ defm_list_destruct(get_defaults(sym, data));
+ }
+
+ if (sym_is_nonboolean(sym)) {
+ const char *curr;
+
+ for_all_defaults(sym, p) {
+ if (p == NULL)
+ continue;
+
+ sym_create_nonbool_fexpr(
+ sym, p->expr->left.sym->name, data);
+ }
+ for_all_properties(sym, p, P_RANGE) {
+ if (p == NULL)
+ continue;
+
+ sym_create_nonbool_fexpr(
+ sym, p->expr->left.sym->name, data);
+ sym_create_nonbool_fexpr(
+ sym, p->expr->right.sym->name, data);
+ }
+ curr = sym_get_string_value(sym);
+ if (strcmp(curr, "") != 0)
+ sym_create_nonbool_fexpr(sym, (char *)curr,
+ data);
+ }
+
+ if (sym->type == S_HEX || sym->type == S_INT)
+ sym_add_nonbool_values_from_default_range(sym, data);
+ }
+}
+
+/*
+ * build constraints for boolean symbols
+ */
+static void get_constraints_bool(struct cfdata *data)
+{
+ struct symbol *sym;
+
+ for_all_symbols(sym) {
+ if (!sym_is_boolean(sym))
+ continue;
+
+ /* build tristate constraints */
+ if (sym->type == S_TRISTATE)
+ build_tristate_constraint_clause(sym, data);
+
+ /* build constraints for select statements
+ * need to treat choice symbols separately
+ */
+ if (!KCR_CMP) {
+ add_selects(sym, data);
+ } else {
+ if (sym->rev_dep.expr && !sym_is_choice(sym) &&
+ !sym_is_choice_value(sym))
+ add_selects_kcr(sym, data);
+ }
+
+ /* build constraints for dependencies for booleans */
+ if (sym->dir_dep.expr && !sym_is_choice(sym) &&
+ !sym_is_choice_value(sym)) {
+ if (!KCR_CMP)
+ add_dependencies_bool(sym, data);
+ else
+ add_dependencies_bool_kcr(sym, data);
+ }
+
+ /* build constraints for choice prompts */
+ if (sym_is_choice(sym))
+ add_choice_prompt_cond(sym, data);
+
+ /* build constraints for dependencies (choice symbols and options) */
+ if (sym_is_choice(sym) || sym_is_choice_value(sym))
+ add_choice_dependencies(sym, data);
+
+ /* build constraints for the choice groups */
+ if (sym_is_choice(sym))
+ add_choice_constraints(sym, data);
+
+ /* build invisible constraints */
+ add_invisible_constraints(sym, data);
+ }
+}
+
+/*
+ * build the constraints for select-variables
+ * skip non-Booleans, choice symbols/options och symbols without rev_dir
+ */
+static void get_constraints_select(struct cfdata *data)
+{
+ struct symbol *sym;
+
+ for_all_symbols(sym) {
+ struct pexpr *sel_y, *sel_m;
+ struct pexpr *c1, *c2;
+
+ if (KCR_CMP)
+ continue;
+
+ if (!sym_is_boolean(sym))
+ continue;
+
+ if (sym_is_choice(sym) || sym_is_choice_value(sym))
+ continue;
+
+ if (!sym->rev_dep.expr)
+ continue;
+
+ if (sym->list_sel_y == NULL)
+ continue;
+
+ sel_y = pexpr_implies(pexf(sym->fexpr_sel_y),
+ pexf(sym->fexpr_y), data,
+ PEXPR_ARGX);
+ sym_add_constraint(sym, sel_y, data);
+
+ c1 = pexpr_implies(pexf(sym->fexpr_sel_y), sym->list_sel_y,
+ data, PEXPR_ARG1);
+ sym_add_constraint(sym, c1, data);
+
+ /* only continue for tristates */
+ if (sym->type == S_BOOLEAN)
+ continue;
+
+ sel_m = pexpr_implies(pexf(sym->fexpr_sel_m),
+ sym_get_fexpr_both(sym, data), data,
+ PEXPR_ARGX);
+ sym_add_constraint(sym, sel_m, data);
+
+ c2 = pexpr_implies(pexf(sym->fexpr_sel_m), sym->list_sel_m,
+ data, PEXPR_ARG1);
+ sym_add_constraint(sym, c2, data);
+ PEXPR_PUT(sel_y, sel_m, c1, c2);
+ }
+}
+
+/*
+ * build constraints for non-booleans
+ */
+static void get_constraints_nonbool(struct cfdata *data)
+{
+ struct symbol *sym;
+
+ for_all_symbols(sym) {
+ if (!sym_is_nonboolean(sym))
+ continue;
+
+ /* the symbol must have a value, if there is a prompt */
+ if (sym_has_prompt(sym))
+ sym_add_nonbool_prompt_constraint(sym, data);
+
+ /* build the range constraints for int/hex */
+ if (sym->type == S_HEX || sym->type == S_INT)
+ sym_add_range_constraints(sym, data);
+
+ /* build constraints for dependencies for non-booleans */
+ if (sym->dir_dep.expr)
+ add_dependencies_nonbool(sym, data);
+
+ /* build invisible constraints */
+ add_invisible_constraints(sym, data);
+
+ /* exactly one of the symbols must be true */
+ sym_nonbool_at_least_1(sym, data);
+ sym_nonbool_at_most_1(sym, data);
+ }
+}
+
+/*
+ * enforce tristate constraints
+ */
+static void build_tristate_constraint_clause(struct symbol *sym,
+ struct cfdata *data)
+{
+ struct pexpr *X, *X_m, *modules, *c;
+
+ if (sym->type != S_TRISTATE)
+ return;
+
+ X = pexf(sym->fexpr_y);
+ X_m = pexf(sym->fexpr_m);
+ modules = pexf(modules_sym->fexpr_y);
+
+ /* -X v -X_m */
+ c = pexpr_or(pexpr_not_share(X, data), pexpr_not_share(X_m, data),
+ data, PEXPR_ARGX);
+ sym_add_constraint(sym, c, data);
+
+ /* X_m -> MODULES */
+ if (modules_sym->fexpr_y != NULL) {
+ struct pexpr *c2 = pexpr_implies_share(X_m, modules, data);
+
+ sym_add_constraint(sym, c2, data);
+ PEXPR_PUT(c2);
+ }
+ PEXPR_PUT(X, X_m, modules, c);
+}
+
+/*
+ * build the select constraints
+ * - RDep(X) implies X
+ */
+static void add_selects_kcr(struct symbol *sym, struct cfdata *data)
+{
+ struct pexpr *rdep_y = expr_calculate_pexpr_y(sym->rev_dep.expr, data);
+ struct pexpr *c1 = pexpr_implies(rdep_y, pexf(sym->fexpr_y), data,
+ PEXPR_ARG2);
+
+ struct pexpr *rdep_both =
+ expr_calculate_pexpr_both(sym->rev_dep.expr, data);
+ struct pexpr *c2 = pexpr_implies(
+ rdep_both, sym_get_fexpr_both(sym, data), data, PEXPR_ARG2);
+
+ sym_add_constraint(sym, c1, data);
+ sym_add_constraint(sym, c2, data);
+ PEXPR_PUT(rdep_y, c1, rdep_both, c2);
+}
+
+/*
+ * build the select constraints simplified
+ * - RDep(X) implies X
+ */
+static void add_selects(struct symbol *sym, struct cfdata *data)
+{
+ struct property *p;
+
+ if (!sym_is_boolean(sym))
+ return;
+
+ for_all_properties(sym, p, P_SELECT) {
+ struct symbol *selected = p->expr->left.sym;
+ struct pexpr *cond_y, *cond_both;
+
+ if (selected->type == S_UNKNOWN)
+ continue;
+
+ if (!selected->rev_dep.expr)
+ continue;
+
+ if (p->visible.expr) {
+ cond_y = expr_calculate_pexpr_y(p->visible.expr, data);
+ cond_both = expr_calculate_pexpr_both(p->visible.expr,
+ data);
+ } else {
+ cond_y = pexf(data->constants->const_true);
+ cond_both = pexf(data->constants->const_true);
+ }
+
+ if (selected->type == S_BOOLEAN) {
+ /* imply that symbol is selected to y */
+ struct pexpr *e1 = pexpr_and(
+ cond_both, sym_get_fexpr_both(sym, data), data,
+ PEXPR_ARG2);
+ struct pexpr *c1 = pexpr_implies(
+ e1, pexf(selected->fexpr_sel_y), data,
+ PEXPR_ARG2);
+
+ sym_add_constraint(selected, c1, data);
+
+ if (selected->list_sel_y == NULL)
+ selected->list_sel_y = pexpr_get(e1);
+ else
+ selected->list_sel_y =
+ pexpr_or(selected->list_sel_y, e1, data,
+ PEXPR_ARG1);
+ PEXPR_PUT(e1, c1);
+ }
+
+ if (selected->type == S_TRISTATE) {
+ struct pexpr *e2, *e3, *c2, *c3;
+
+ /* imply that symbol is selected to y */
+ e2 = pexpr_and(cond_y, pexf(sym->fexpr_y), data,
+ PEXPR_ARG2);
+ c2 = pexpr_implies(e2, pexf(selected->fexpr_sel_y),
+ data, PEXPR_ARG2);
+ sym_add_constraint(selected, c2, data);
+
+ if (selected->list_sel_y == NULL)
+ selected->list_sel_y = pexpr_get(e2);
+ else
+ selected->list_sel_y =
+ pexpr_or(selected->list_sel_y, e2,
+ data, PEXPR_ARG1);
+
+ /* imply that symbol is selected to m */
+ e3 = pexpr_and(cond_both,
+ sym_get_fexpr_both(sym, data), data,
+ PEXPR_ARG2);
+ c3 = pexpr_implies(e3, pexf(selected->fexpr_sel_m),
+ data, PEXPR_ARG2);
+ sym_add_constraint(selected, c3, data);
+
+ if (selected->list_sel_m == NULL)
+ selected->list_sel_m = pexpr_get(e3);
+ else
+ selected->list_sel_m =
+ pexpr_or(selected->list_sel_m, e3,
+ data, PEXPR_ARG1);
+ PEXPR_PUT(e2, c2, e3, c3);
+ }
+ PEXPR_PUT(cond_y, cond_both);
+ }
+}
+
+/*
+ * build the dependency constraints for booleans
+ * - X implies Dep(X) or RDep(X)
+ */
+static void add_dependencies_bool(struct symbol *sym, struct cfdata *data)
+{
+ struct pexpr *dep_both;
+ struct pexpr *visible_m;
+ struct pexpr *visible_y;
+ struct pexpr *visible_both;
+ struct property *prompt;
+ struct pexpr *has_prompt;
+ struct pexpr *sel_y;
+
+ if (!sym_is_boolean(sym) || !sym->dir_dep.expr)
+ return;
+
+ prompt = sym_get_prompt(sym);
+ if (!prompt) {
+ visible_m = pexf(data->constants->const_false);
+ visible_y = pexpr_get(visible_m);
+ visible_both = pexpr_get(visible_m);
+ } else if (prompt->expr == NULL) {
+ visible_m = pexf(data->constants->const_true);
+ visible_y = pexpr_get(visible_m);
+ visible_both = pexpr_get(visible_m);
+ } else {
+ visible_m = expr_calculate_pexpr_m(prompt->expr, data);
+ visible_y = expr_calculate_pexpr_y(prompt->expr, data);
+ visible_both = pexpr_or_share(visible_y, visible_m, data);
+ }
+
+ dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr, data);
+
+ sel_y = sym->rev_dep.expr ? pexf(sym->fexpr_sel_y) :
+ pexf(data->constants->const_false);
+ has_prompt = pexpr_get(visible_both);
+ has_prompt = pexpr_and(
+ has_prompt,
+ pexpr_not(pexpr_and_share(sel_y, visible_m, data),
+ data),
+ data, PEXPR_ARGX);
+
+ if (sym->type == S_TRISTATE) {
+ struct pexpr *c1;
+ struct pexpr *c2;
+ struct pexpr *dep_y =
+ expr_calculate_pexpr_y(sym->dir_dep.expr, data);
+ struct pexpr *sel_both = sym_get_fexpr_sel_both(sym, data);
+ struct pexpr *cond_y1;
+ struct pexpr *cond_y2;
+ struct pexpr *cond_y;
+ struct pexpr *cond_m1;
+ struct pexpr *cond_m2;
+ struct pexpr *cond_m;
+
+ cond_y1 = pexpr_implies(pexpr_not_share(has_prompt, data),
+ pexpr_or_share(dep_y, sel_y, data), data,
+ PEXPR_ARGX);
+ cond_y2 = pexpr_implies_share(has_prompt, visible_y, data);
+ cond_y = pexpr_and_share(cond_y1, cond_y2, data);
+ cond_m1 =
+ pexpr_implies(pexpr_not_share(has_prompt, data),
+ pexpr_or_share(dep_both, sel_both, data),
+ data, PEXPR_ARGX);
+ cond_m2 = pexpr_implies(has_prompt,
+ pexpr_not_share(sel_y, data), data,
+ PEXPR_ARG2);
+ cond_m = pexpr_and_share(cond_m1, cond_m2, data);
+ c1 = pexpr_implies(pexf(sym->fexpr_y), cond_y, data,
+ PEXPR_ARG1);
+ c2 = pexpr_implies(pexf(sym->fexpr_m), cond_m, data,
+ PEXPR_ARG1);
+
+ sym_add_constraint(sym, c1, data);
+ sym_add_constraint(sym, c2, data);
+ PEXPR_PUT(c1, c2, dep_y, sel_both, cond_y1,
+ cond_y2, cond_y, cond_m1, cond_m2, cond_m);
+ } else if (sym->type == S_BOOLEAN) {
+ struct pexpr *cond1;
+ struct pexpr *cond2;
+ struct pexpr *c;
+
+ cond1 = pexpr_implies(pexpr_not_share(has_prompt, data),
+ pexpr_or(dep_both, pexf(sym->fexpr_m),
+ data, PEXPR_ARG2),
+ data, PEXPR_ARGX);
+ cond2 = pexpr_implies_share(has_prompt, visible_y, data);
+ c = pexpr_implies(pexf(sym->fexpr_y),
+ pexpr_and_share(cond1, cond2, data), data,
+ PEXPR_ARGX);
+
+ sym_add_constraint(sym, c, data);
+ PEXPR_PUT(c, cond1, cond2);
+ }
+ PEXPR_PUT(dep_both, has_prompt, sel_y, visible_y, visible_m, visible_both);
+}
+
+/*
+ * build the dependency constraints for booleans (KCR)
+ * - X implies Dep(X) or RDep(X)
+ */
+static void add_dependencies_bool_kcr(struct symbol *sym, struct cfdata *data)
+{
+ struct pexpr *dep_both, *sel_both;
+
+ if (!sym_is_boolean(sym) || !sym->dir_dep.expr)
+ return;
+
+ dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr, data);
+
+ sel_both = sym->rev_dep.expr ?
+ expr_calculate_pexpr_both(sym->rev_dep.expr, data) :
+ pexf(data->constants->const_false);
+
+ if (sym->type == S_TRISTATE) {
+ struct pexpr *c1;
+ struct pexpr *c2;
+ {
+ struct pexpr *dep_y =
+ expr_calculate_pexpr_y(sym->dir_dep.expr, data);
+ struct pexpr *sel_y =
+ sym->rev_dep.expr ?
+ expr_calculate_pexpr_y(
+ sym->rev_dep.expr, data) :
+ pexf(data->constants->const_false);
+ c1 = pexpr_implies(pexf(sym->fexpr_y),
+ pexpr_or(dep_y, sel_y,
+ data, PEXPR_ARGX),
+ data, PEXPR_ARGX);
+ }
+ c2 = pexpr_implies(pexf(sym->fexpr_m),
+ pexpr_or_share(dep_both, sel_both,
+ data),
+ data, PEXPR_ARGX);
+
+ sym_add_constraint(sym, c1, data);
+ sym_add_constraint(sym, c2, data);
+ PEXPR_PUT(c1, c2);
+ } else if (sym->type == S_BOOLEAN) {
+ struct pexpr *c = pexpr_implies(
+ pexf(sym->fexpr_y),
+ pexpr_or_share(dep_both, sel_both, data), data,
+ PEXPR_ARGX);
+
+ sym_add_constraint(sym, c, data);
+ PEXPR_PUT(c);
+ }
+
+ PEXPR_PUT(dep_both, sel_both);
+}
+
+/*
+ * build the dependency constraints for non-booleans
+ *
+ * sym is not 'n' implies `sym->dir_dep`
+ */
+static void add_dependencies_nonbool(struct symbol *sym, struct cfdata *data)
+{
+ struct pexpr *dep_both;
+ struct pexpr *nb_vals; // "sym is set to some value" / "sym is not 'n'"
+ struct fexpr_node *node;
+ struct pexpr *c;
+
+ if (!sym_is_nonboolean(sym) || !sym->dir_dep.expr || sym->rev_dep.expr)
+ return;
+
+ dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr, data);
+
+ nb_vals = pexf(data->constants->const_false);
+ /* can skip the first non-boolean value, since this is 'n' */
+ fexpr_list_for_each(node, sym->nb_vals) {
+ if (node->prev == NULL)
+ continue;
+
+ nb_vals = pexpr_or(nb_vals, pexf(node->elem), data,
+ PEXPR_ARGX);
+ }
+
+ c = pexpr_implies(nb_vals, dep_both, data, PEXPR_ARGX);
+ sym_add_constraint(sym, c, data);
+ pexpr_put(c);
+}
+
+/*
+ * build the constraints for the choice prompt
+ */
+static void add_choice_prompt_cond(struct symbol *sym, struct cfdata *data)
+{
+ struct property *prompt;
+ struct pexpr *promptCondition;
+ struct pexpr *fe_both;
+ struct pexpr *pr_cond;
+ struct pexpr *req_cond;
+
+ if (!sym_is_boolean(sym))
+ return;
+
+ prompt = sym_get_prompt(sym);
+ if (prompt == NULL)
+ return;
+
+ promptCondition =
+ prompt->visible.expr ?
+ expr_calculate_pexpr_both(prompt->visible.expr, data) :
+ pexf(data->constants->const_true);
+ fe_both = sym_get_fexpr_both(sym, data);
+ req_cond = pexpr_implies_share(promptCondition, fe_both, data);
+ sym_add_constraint(sym, req_cond, data);
+ pr_cond = pexpr_implies_share(fe_both, promptCondition, data);
+ sym_add_constraint(sym, pr_cond, data);
+ PEXPR_PUT(promptCondition, fe_both, req_cond, pr_cond);
+}
+
+/*
+ * build constraints for dependencies (choice symbols and options)
+ */
+static void add_choice_dependencies(struct symbol *sym, struct cfdata *data)
+{
+ struct property *prompt;
+ struct expr *to_parse;
+ struct pexpr *dep_both;
+
+ if (!sym_is_choice(sym) || !sym_is_choice_value(sym))
+ return;
+
+ prompt = sym_get_prompt(sym);
+ if (prompt == NULL)
+ return;
+
+ if (sym_is_choice(sym)) {
+ if (!prompt->visible.expr)
+ return;
+ to_parse = prompt->visible.expr;
+ } else {
+ if (!sym->dir_dep.expr)
+ return;
+ to_parse = sym->dir_dep.expr;
+ }
+
+ dep_both = expr_calculate_pexpr_both(to_parse, data);
+
+ if (sym->type == S_TRISTATE) {
+ struct pexpr *dep_y = expr_calculate_pexpr_y(to_parse, data);
+ struct pexpr *c1 = pexpr_implies(pexf(sym->fexpr_y), dep_y,
+ data, PEXPR_ARG1);
+ struct pexpr *c2 = pexpr_implies(
+ pexf(sym->fexpr_m), dep_both, data, PEXPR_ARG1);
+
+ sym_add_constraint_eq(sym, c1, data);
+ sym_add_constraint_eq(sym, c2, data);
+ PEXPR_PUT(dep_y, c1, c2);
+ } else if (sym->type == S_BOOLEAN) {
+ struct pexpr *c = pexpr_implies(
+ pexf(sym->fexpr_y), dep_both, data, PEXPR_ARG1);
+
+ sym_add_constraint_eq(sym, c, data);
+ pexpr_put(c);
+ }
+ pexpr_put(dep_both);
+}
+
+/*
+ * build constraints for the choice groups
+ */
+static void add_choice_constraints(struct symbol *sym, struct cfdata *data)
+{
+ struct property *prompt;
+ struct symbol *choice, *choice2;
+ struct sym_node *node, *node2;
+ struct sym_list *items, *promptItems;
+ struct pexpr *c1;
+ struct menu *menu_ptr, *choiceval_menu;
+
+ if (!sym_is_boolean(sym))
+ return;
+
+ prompt = sym_get_prompt(sym);
+ if (prompt == NULL)
+ return;
+
+ /* create list of all choice options */
+ items = sym_list_init();
+ /* create list of choice options with a prompt */
+ promptItems = sym_list_init();
+
+ for_all_choices(sym, choiceval_menu, menu_ptr) {
+ choice = choiceval_menu->sym;
+
+ sym_list_add(items, choice);
+ if (sym_get_prompt(choice) != NULL)
+ sym_list_add(promptItems, choice);
+ }
+
+ /* if the choice is set to yes, at least one child must be set to yes */
+ c1 = NULL;
+ sym_list_for_each(node, promptItems) {
+ choice = node->elem;
+ c1 = node->prev == NULL ?
+ pexf(choice->fexpr_y) :
+ pexpr_or(c1, pexf(choice->fexpr_y), data,
+ PEXPR_ARGX);
+ }
+ if (c1 != NULL) {
+ struct pexpr *c2 = pexpr_implies(pexf(sym->fexpr_y), c1,
+ data, PEXPR_ARG1);
+
+ sym_add_constraint(sym, c2, data);
+ PEXPR_PUT(c1, c2);
+ }
+
+ /* every choice option (even those without a prompt) implies the choice */
+ sym_list_for_each(node, items) {
+ choice = node->elem;
+ c1 = pexpr_implies(sym_get_fexpr_both(choice, data),
+ sym_get_fexpr_both(sym, data), data,
+ PEXPR_ARGX);
+ sym_add_constraint(sym, c1, data);
+ pexpr_put(c1);
+ }
+
+ /* choice options can only select mod, if the entire choice is mod */
+ if (sym->type == S_TRISTATE) {
+ sym_list_for_each(node, items) {
+ choice = node->elem;
+ if (choice->type == S_TRISTATE) {
+ c1 = pexpr_implies(pexf(choice->fexpr_m),
+ pexf(sym->fexpr_m),
+ data, PEXPR_ARGX);
+ sym_add_constraint(sym, c1, data);
+ pexpr_put(c1);
+ }
+ }
+ }
+
+ /* tristate options cannot be m, if the choice symbol is boolean */
+ if (sym->type == S_BOOLEAN) {
+ sym_list_for_each(node, items) {
+ choice = node->elem;
+ if (choice->type == S_TRISTATE) {
+ struct pexpr *e = pexpr_not(pexf(choice->fexpr_m),
+ data);
+ sym_add_constraint(sym, e, data);
+ pexpr_put(e);
+ }
+ }
+ }
+
+ /* all choice options are mutually exclusive for yes */
+ sym_list_for_each(node, promptItems) {
+ choice = node->elem;
+ for (struct sym_node *node2 = node->next; node2 != NULL;
+ node2 = node2->next) {
+ choice2 = node2->elem;
+ c1 = pexpr_or(
+ pexpr_not(pexf(choice->fexpr_y), data),
+ pexpr_not(pexf(choice2->fexpr_y), data),
+ data, PEXPR_ARGX);
+ sym_add_constraint(sym, c1, data);
+ pexpr_put(c1);
+ }
+ }
+
+ /* if one choice option with a prompt is set to yes,
+ * then no other option may be set to mod
+ */
+ if (sym->type == S_TRISTATE) {
+ sym_list_for_each(node, promptItems) {
+ struct sym_list *tmp;
+
+ choice = node->elem;
+
+ tmp = sym_list_init();
+ for (struct sym_node *node2 = node->next; node2 != NULL;
+ node2 = node2->next) {
+ choice2 = node2->elem;
+ if (choice2->type == S_TRISTATE)
+ sym_list_add(tmp, choice2);
+ }
+ if (tmp->size == 0)
+ continue;
+
+ sym_list_for_each(node2, tmp) {
+ choice2 = node2->elem;
+ if (node2->prev == NULL)
+ c1 = pexpr_not(
+ pexf(choice2->fexpr_m), data);
+ else
+ c1 = pexpr_and(
+ c1,
+ pexpr_not(
+ pexf(choice2->fexpr_m),
+ data),
+ data, PEXPR_ARGX);
+ }
+ c1 = pexpr_implies(pexf(choice->fexpr_y), c1, data,
+ PEXPR_ARGX);
+ sym_add_constraint(sym, c1, data);
+ pexpr_put(c1);
+ }
+ }
+ sym_list_free(promptItems);
+ sym_list_free(items);
+}
+
+/*
+ * build the constraints for invisible options such as defaults
+ */
+static void add_invisible_constraints(struct symbol *sym, struct cfdata *data)
+{
+ struct property *prompt = sym_get_prompt(sym);
+ struct pexpr *promptCondition_both, *promptCondition_yes, *noPromptCond;
+ struct pexpr *npc;
+ struct defm_list *defaults;
+ struct pexpr *default_y, *default_m, *default_both;
+
+ /* no constraints for the prompt, nothing to do here */
+ if (prompt != NULL && !prompt->visible.expr)
+ return;
+
+ if (prompt == NULL) {
+ promptCondition_both = pexf(data->constants->const_false);
+ promptCondition_yes = pexf(data->constants->const_false);
+ noPromptCond = pexf(data->constants->const_true);
+ } else {
+ struct property *p;
+
+ promptCondition_both = pexf(data->constants->const_false);
+ promptCondition_yes = pexf(data->constants->const_false);
+
+ /* some symbols have multiple prompts */
+ for_all_prompts(sym, p) {
+ promptCondition_both =
+ pexpr_or(promptCondition_both,
+ expr_calculate_pexpr_both(
+ p->visible.expr, data),
+ data, PEXPR_ARGX);
+ promptCondition_yes = pexpr_or(
+ promptCondition_yes,
+ expr_calculate_pexpr_y(p->visible.expr, data),
+ data, PEXPR_ARGX);
+ }
+ noPromptCond = pexpr_not_share(promptCondition_both, data);
+ }
+
+ if (NPC_OPTIMISATION) {
+ struct fexpr *npc_fe =
+ fexpr_create(data->sat_variable_nr++, FE_NPC, "");
+
+ if (sym_is_choice(sym))
+ str_append(&npc_fe->name, "Choice_");
+
+ str_append(&npc_fe->name, sym_get_name(sym));
+ str_append(&npc_fe->name, "_NPC");
+ sym->noPromptCond = npc_fe;
+ fexpr_add_to_satmap(npc_fe, data);
+
+ npc = pexf(npc_fe);
+
+ if (!sym_is_choice_value(sym) && !sym_is_choice(sym)) {
+ struct pexpr *c =
+ pexpr_implies_share(noPromptCond, npc, data);
+ sym_add_constraint(sym, c, data);
+ pexpr_put(c);
+ }
+ } else {
+ npc = pexpr_get(noPromptCond);
+ }
+
+ defaults = get_defaults(sym, data);
+ default_y = get_default_y(defaults, data);
+ default_m = get_default_m(defaults, data);
+ default_both = pexpr_or_share(default_y, default_m, data);
+
+ /* tristate elements are only selectable as yes, if they are visible as yes */
+ if (sym->type == S_TRISTATE) {
+ struct pexpr *e1 = pexpr_implies(
+ promptCondition_both,
+ pexpr_implies(pexf(sym->fexpr_y),
+ promptCondition_yes, data,
+ PEXPR_ARG1),
+ data, PEXPR_ARG2);
+
+ sym_add_constraint(sym, e1, data);
+ pexpr_put(e1);
+ }
+
+ /* if invisible and off by default, then a symbol can only be deactivated by its reverse
+ * dependencies
+ */
+ if (sym->type == S_TRISTATE) {
+ struct pexpr *sel_y, *sel_m, *sel_both;
+ struct pexpr *c1, *c2, *c3;
+ struct pexpr *d1, *d2, *d3;
+ struct pexpr *e1, *e2, *e3;
+
+ if (sym->fexpr_sel_y != NULL) {
+ sel_y = pexpr_implies(pexf(sym->fexpr_y),
+ pexf(sym->fexpr_sel_y), data,
+ PEXPR_ARGX);
+ sel_m = pexpr_implies(pexf(sym->fexpr_m),
+ pexf(sym->fexpr_sel_m), data,
+ PEXPR_ARGX);
+ sel_both = pexpr_implies(
+ pexf(sym->fexpr_y),
+ pexpr_or(pexf(sym->fexpr_sel_m),
+ pexf(sym->fexpr_sel_y), data,
+ PEXPR_ARGX),
+ data, PEXPR_ARGX);
+ } else {
+ sel_y = pexpr_not(pexf(sym->fexpr_y), data);
+ sel_m = pexpr_not(pexf(sym->fexpr_m), data);
+ sel_both = pexpr_get(sel_y);
+ }
+
+ c1 = pexpr_implies(pexpr_not_share(default_y, data), sel_y,
+ data, PEXPR_ARG1);
+ c2 = pexpr_implies(pexf(modules_sym->fexpr_y), c1, data,
+ PEXPR_ARG1);
+ c3 = pexpr_implies_share(npc, c2, data);
+ sym_add_constraint(sym, c3, data);
+
+ d1 = pexpr_implies(pexpr_not_share(default_m, data), sel_m,
+ data, PEXPR_ARG1);
+ d2 = pexpr_implies(pexf(modules_sym->fexpr_y), d1, data,
+ PEXPR_ARG1);
+ d3 = pexpr_implies_share(npc, d2, data);
+ sym_add_constraint(sym, d3, data);
+
+ e1 = pexpr_implies(pexpr_not_share(default_both, data),
+ sel_both, data, PEXPR_ARG1);
+ e2 = pexpr_implies(
+ pexpr_not(pexf(modules_sym->fexpr_y), data), e1,
+ data, PEXPR_ARG1);
+ e3 = pexpr_implies_share(npc, e2, data);
+ sym_add_constraint(sym, e3, data);
+ PEXPR_PUT(sel_y, sel_m, sel_both, c1, c2, c3, d1, d2, d3, e1,
+ e2, e3);
+ } else if (sym->type == S_BOOLEAN) {
+ struct pexpr *sel_y;
+ struct pexpr *e1, *e2;
+
+ if (sym->fexpr_sel_y != NULL)
+ sel_y = pexpr_implies(pexf(sym->fexpr_y),
+ pexf(sym->fexpr_sel_y), data,
+ PEXPR_ARGX);
+ else
+ sel_y = pexpr_not(pexf(sym->fexpr_y), data);
+
+ e1 = pexpr_implies(pexpr_not_share(default_both, data),
+ sel_y, data, PEXPR_ARG1);
+ e2 = pexpr_implies_share(npc, e1, data);
+
+ sym_add_constraint_eq(sym, e2, data);
+ PEXPR_PUT(sel_y, e1, e2);
+ } else {
+ /* if non-boolean is invisible and no default's condition is
+ * fulfilled, then the symbol is not set
+ */
+ struct pexpr *default_any = get_default_any(sym, data);
+ struct pexpr *e1 = pexf(data->constants->const_true);
+ struct pexpr *e2, *e3;
+
+ /* e1 = "sym is not set" */
+ for (struct fexpr_node *node = sym->nb_vals->head->next;
+ node != NULL; node = node->next)
+ e1 = pexpr_and(
+ e1, pexpr_not(pexf(node->elem), data),
+ data, PEXPR_ARGX);
+
+ e2 = pexpr_implies(pexpr_not_share(default_any, data), e1,
+ data, PEXPR_ARG1);
+ e3 = pexpr_implies_share(npc, e2, data);
+
+ sym_add_constraint(sym, e3, data);
+ PEXPR_PUT(default_any, e1, e2, e3);
+ }
+
+ /* if invisible and on by default, then a symbol can only be deactivated by its
+ * dependencies
+ */
+ if (defaults->size == 0) {
+ // nothing to do
+ } else if (sym->type == S_TRISTATE) {
+ struct pexpr *e1;
+ struct pexpr *e2;
+
+ e1 = pexpr_implies(npc,
+ pexpr_implies(default_y,
+ pexf(sym->fexpr_y),
+ data, PEXPR_ARG2),
+ data, PEXPR_ARG2);
+ sym_add_constraint(sym, e1, data);
+
+ e2 = pexpr_implies(
+ npc,
+ pexpr_implies(default_m,
+ sym_get_fexpr_both(sym, data),
+ data, PEXPR_ARG2),
+ data, PEXPR_ARG2);
+ sym_add_constraint(sym, e2, data);
+ PEXPR_PUT(e1, e2);
+ } else if (sym->type == S_BOOLEAN) {
+ struct pexpr *c;
+ struct pexpr *c2;
+
+ c = pexpr_implies(default_both, pexf(sym->fexpr_y), data,
+ PEXPR_ARG2);
+
+ // TODO tristate choice hack
+
+ c2 = pexpr_implies_share(npc, c, data);
+ sym_add_constraint(sym, c2, data);
+ PEXPR_PUT(c, c2);
+ } else {
+ /* if non-boolean invisible, then it assumes the correct
+ * default (if any).
+ */
+ struct defm_node *node;
+ struct pexpr *cond, *c;
+ struct fexpr *f;
+
+ defm_list_for_each(node, defaults) {
+ f = node->elem->val;
+ cond = node->elem->e;
+ c = pexpr_implies(npc,
+ pexpr_implies(cond, pexf(f), data, PEXPR_ARG2),
+ data, PEXPR_ARG2);
+ sym_add_constraint(sym, c, data);
+ pexpr_put(c);
+ }
+ }
+
+ PEXPR_PUT(promptCondition_yes, promptCondition_both, noPromptCond, npc,
+ default_y, default_m, default_both);
+ defm_list_destruct(defaults);
+}
+
+/*
+ * add the known values from the default and range properties
+ */
+static void sym_add_nonbool_values_from_default_range(struct symbol *sym,
+ struct cfdata *data)
+{
+ struct property *p;
+
+ for_all_defaults(sym, p) {
+ if (p == NULL)
+ continue;
+
+ /* add the value to known values, if it doesn't exist yet */
+ sym_create_nonbool_fexpr(sym, p->expr->left.sym->name, data);
+ }
+
+ for_all_properties(sym, p, P_RANGE) {
+ if (p == NULL)
+ continue;
+
+ /* add the values to known values, if they don't exist yet */
+ sym_create_nonbool_fexpr(sym, p->expr->left.sym->name, data);
+ sym_create_nonbool_fexpr(sym, p->expr->right.sym->name, data);
+ }
+}
+
+/*
+ * build the range constraints for int/hex:
+ * For each range and each value in `sym->nb_vals` that's not in the range:
+ * If the range's condition is fulfilled, then sym can't have this value.
+ */
+static void sym_add_range_constraints(struct symbol *sym, struct cfdata *data)
+{
+ struct property *prop;
+ struct pexpr *prevs;
+ struct pexpr *propCond;
+ struct pexpr_list *prevCond; // list of all conditions of the ranges
+ // from the previous iterations
+ prevCond = pexpr_list_init();
+
+ for_all_properties(sym, prop, P_RANGE) {
+ int base;
+ long long range_min, range_max, tmp;
+ struct fexpr_node *node;
+
+ if (prop == NULL)
+ continue;
+
+ prevs = pexf(data->constants->const_true);
+ propCond = prop_get_condition(prop, data);
+
+ // construct prevs as "none of the previous ranges' conditions
+ // were fulfilled but this range's condition is"
+ if (prevCond->size == 0) {
+ pexpr_put(prevs);
+ prevs = pexpr_get(propCond);
+;
+ } else {
+ struct pexpr_node *node;
+
+ pexpr_list_for_each(node, prevCond)
+ prevs = pexpr_and(pexpr_not_share(node->elem,
+ data),
+ prevs, data, PEXPR_ARGX);
+
+ prevs = pexpr_and(propCond, prevs, data,
+ PEXPR_ARG2);
+ }
+ pexpr_list_add(prevCond, pexpr_get(propCond));
+
+ switch (sym->type) {
+ case S_INT:
+ base = 10;
+ break;
+ case S_HEX:
+ base = 16;
+ break;
+ default:
+ return;
+ }
+
+ range_min = sym_get_range_val(prop->expr->left.sym, base);
+ range_max = sym_get_range_val(prop->expr->right.sym, base);
+
+ /* can skip the first non-boolean value, since this is 'n' */
+ fexpr_list_for_each(node, sym->nb_vals) {
+ struct pexpr *not_nb_val;
+ struct pexpr *c;
+
+ if (node->prev == NULL)
+ continue;
+
+ tmp = strtoll(str_get(&node->elem->nb_val), NULL, base);
+
+ /* known value is in range, nothing to do here */
+ if (tmp >= range_min && tmp <= range_max)
+ continue;
+
+ not_nb_val = pexpr_not(pexf(node->elem), data);
+ c = pexpr_implies_share(prevs, not_nb_val, data);
+ sym_add_constraint(sym, c, data);
+ PEXPR_PUT(not_nb_val, c);
+ }
+ PEXPR_PUT(prevs, propCond);
+ }
+
+ pexpr_list_free_put(prevCond);
+
+}
+
+/*
+ * at least 1 of the known values for a non-boolean symbol must be true
+ */
+static void sym_nonbool_at_least_1(struct symbol *sym, struct cfdata *data)
+{
+ struct pexpr *e = NULL;
+ struct fexpr_node *node;
+
+ if (!sym_is_nonboolean(sym))
+ return;
+
+ fexpr_list_for_each(node, sym->nb_vals) {
+ if (node->prev == NULL)
+ e = pexf(node->elem);
+ else
+ e = pexpr_or(e, pexf(node->elem), data, PEXPR_ARGX);
+ }
+ sym_add_constraint(sym, e, data);
+ pexpr_put(e);
+}
+
+/*
+ * at most 1 of the known values for a non-boolean symbol can be true
+ */
+static void sym_nonbool_at_most_1(struct symbol *sym, struct cfdata *data)
+{
+ struct fexpr_node *node1;
+
+ if (!sym_is_nonboolean(sym))
+ return;
+
+ /* iterate over all subsets of sym->nb_vals of size 2 */
+ fexpr_list_for_each(node1, sym->nb_vals) {
+ struct pexpr *e1 = pexf(node1->elem);
+
+ for (struct fexpr_node *node2 = node1->next; node2 != NULL;
+ node2 = node2->next) {
+ struct pexpr *e2 = pexf(node2->elem);
+ struct pexpr *e = pexpr_or(pexpr_not_share(e1, data),
+ pexpr_not_share(e2, data),
+ data, PEXPR_ARGX);
+
+ sym_add_constraint(sym, e, data);
+ PEXPR_PUT(e, e2);
+ }
+ pexpr_put(e1);
+ }
+}
+
+/*
+ * a visible prompt for a non-boolean implies a value for the symbol
+ */
+static void sym_add_nonbool_prompt_constraint(struct symbol *sym,
+ struct cfdata *data)
+{
+ struct property *prompt;
+ struct pexpr *promptCondition;
+ struct pexpr *n;
+ struct pexpr *c = NULL;
+
+ prompt = sym_get_prompt(sym);
+ if (prompt == NULL)
+ return;
+
+ promptCondition = prop_get_condition(prompt, data);
+ n = pexf(sym_get_nonbool_fexpr(sym, "n"));
+
+ if (n->type != PE_SYMBOL || n->left.fexpr == NULL)
+ goto cleanup;
+
+ c = pexpr_implies(promptCondition, pexpr_not_share(n, data), data,
+ PEXPR_ARG2);
+
+ sym_add_constraint(sym, c, data);
+
+cleanup:
+ PEXPR_PUT(n, promptCondition, c);
+}
+
+static struct default_map *create_default_map_entry(struct fexpr *val,
+ struct pexpr *e)
+{
+ struct default_map *map = malloc(sizeof(struct default_map));
+
+ pexpr_get(e);
+ map->val = val;
+ map->e = e;
+
+ return map;
+}
+
+/**
+ * findDefaultEntry()
+ * @val: Value that the entry must have
+ * @defaults: List of defaults to search in
+ * @constants: To get ``constants->const_false`` from
+ *
+ * Finds an entry in @defaults whose &default_map.val attribute is the same
+ * pointer as the @val argument.
+ *
+ * Return: The condition &default_map.e of the found entry, or
+ * ``pexf(constants->const_false)`` if none was found. To be pexpr_put() by the
+ * caller.
+ */
+static struct pexpr *findDefaultEntry(struct fexpr *val,
+ struct defm_list *defaults,
+ struct constants *constants)
+{
+ struct defm_node *node;
+
+ defm_list_for_each(node, defaults) {
+ if (val == node->elem->val) {
+ pexpr_get(node->elem->e);
+ return node->elem->e;
+ }
+ }
+
+ return pexf(constants->const_false);
+}
+
+/*
+ * accumulated during execution of add_defaults(), a disjunction of the
+ * conditions for all default props of a symbol
+ */
+static struct pexpr *covered;
+
+static bool is_tri_as_num(struct symbol *sym)
+{
+ if (!sym->name)
+ return false;
+
+ return !strcmp(sym->name, "0")
+ || !strcmp(sym->name, "1")
+ || !strcmp(sym->name, "2");
+}
+
+/**
+ * add_to_default_map() - Add to or update an entry in a default list
+ * @entry: Will be consumed by this function, i.e. the caller should and need
+ * only access @entry via @defaults.
+ */
+static void add_to_default_map(struct defm_list *defaults,
+ struct default_map *entry, struct symbol *sym)
+{
+ /* as this is a map, the entry must be replaced if it already exists */
+ if (sym_is_boolean(sym)) {
+ struct default_map *map;
+ struct defm_node *node;
+
+ defm_list_for_each(node, defaults) {
+ map = node->elem;
+ if (map->val->sym == entry->val->sym) {
+ pexpr_put(map->e);
+ map->e = entry->e;
+ free(entry);
+ return;
+ }
+ }
+ defm_list_add(defaults, entry);
+ } else {
+ struct default_map *map;
+ struct defm_node *node;
+
+ defm_list_for_each(node, defaults) {
+ map = node->elem;
+ if (map->val->satval == entry->val->satval) {
+ pexpr_put(map->e);
+ map->e = entry->e;
+ free(entry);
+ return;
+ }
+ }
+ defm_list_add(defaults, entry);
+ }
+}
+
+/**
+ * updateDefaultList() - Update a default list with a new value-condition pair
+ * @val: The value whose condition will be updated
+ * @newCond: The condition of the default prop. Does not include the condition
+ * that the earlier default's conditions are not fulfilled.
+ * @result: the default list
+ * @sym: the symbol that the defaults belong to
+ *
+ * Update the condition that @val will be used for @sym by considering the next
+ * default property, whose condition is given by @newCond.
+ */
+static void updateDefaultList(struct fexpr *val, struct pexpr *newCond,
+ struct defm_list *result, struct symbol *sym,
+ struct cfdata *data)
+{
+ // The current condition of @val deduced from the previous default props
+ struct pexpr *prevCond = findDefaultEntry(val, result, data->constants);
+ // New combined condition for @val
+ struct pexpr *condUseVal =
+ pexpr_or(prevCond,
+ pexpr_and(newCond, pexpr_not_share(covered, data),
+ data, PEXPR_ARG2),
+ data, PEXPR_ARG2);
+ add_to_default_map(result, create_default_map_entry(val, condUseVal),
+ sym);
+ covered = pexpr_or(covered, newCond, data, PEXPR_ARG1);
+ PEXPR_PUT(prevCond, condUseVal);
+}
+
+/**
+ * add_defaults() - Generate list of default values and their conditions
+ * @defaults: List of the default properties
+ * @ctx: Additional condition that needs to be fulfilled for any default. May be
+ * NULL.
+ * @result: List that will be filled
+ * @sym: Symbol that the defaults belong to
+ *
+ * Creates a map from values that @sym can assume to the conditions under which
+ * they will be assumed. Without @ctx, this will only consider the conditions
+ * directly associated with the defaults, e.g. sym->dir_dep would not be
+ * considered.
+ *
+ * As a side effect, the &symbol->nb_vals of @sym will be added for
+ * all default values (as well as the @symbol->nb_vals of other symbols @sym has
+ * as default (recursively)).
+ */
+static void add_defaults(struct prop_list *defaults, struct expr *ctx,
+ struct defm_list *result, struct symbol *sym,
+ struct cfdata *data)
+{
+ struct prop_node *node;
+ struct property *p;
+ struct expr *expr;
+
+ prop_list_for_each(node, defaults) {
+ p = node->elem;
+ /* calculate expr as whether the default's condition (and the
+ * one inherited from ctx) is fulfilled
+ */
+ if (p->visible.expr) {
+ if (ctx == NULL)
+ expr = expr_copy(p->visible.expr);
+ else
+ expr = expr_alloc_and(
+ expr_copy(p->visible.expr),
+ expr_copy(ctx));
+ } else {
+ if (ctx == NULL)
+ expr = expr_alloc_symbol(&symbol_yes);
+ else
+ expr = expr_alloc_and(
+ expr_alloc_symbol(&symbol_yes),
+ expr_copy(ctx));
+ }
+
+ /* if tristate and def.value = y */
+ if (p->expr->type == E_SYMBOL && sym->type == S_TRISTATE &&
+ p->expr->left.sym == &symbol_yes) {
+ struct pexpr *expr_y =
+ expr_calculate_pexpr_y(expr, data);
+ struct pexpr *expr_m =
+ expr_calculate_pexpr_m(expr, data);
+
+ updateDefaultList(data->constants->symbol_yes_fexpr,
+ expr_y, result, sym, data);
+ updateDefaultList(data->constants->symbol_mod_fexpr,
+ expr_m, result, sym, data);
+ PEXPR_PUT(expr_y, expr_m);
+ }
+ /* if def.value = n/m/y */
+ else if (p->expr->type == E_SYMBOL &&
+ sym_is_tristate_constant(p->expr->left.sym) &&
+ sym_is_boolean(sym)) {
+ struct fexpr *s;
+ struct pexpr *expr_both =
+ expr_calculate_pexpr_both(expr, data);
+
+ if (p->expr->left.sym == &symbol_yes)
+ s = data->constants->symbol_yes_fexpr;
+ else if (p->expr->left.sym == &symbol_mod)
+ s = data->constants->symbol_mod_fexpr;
+ else
+ s = data->constants->symbol_no_fexpr;
+
+ updateDefaultList(s, expr_both, result, sym, data);
+ pexpr_put(expr_both);
+ }
+ /* if def.value = n/m/y, but written as 0/1/2 for a boolean */
+ else if (sym_is_boolean(sym) && p->expr->type == E_SYMBOL &&
+ p->expr->left.sym->type == S_UNKNOWN &&
+ is_tri_as_num(p->expr->left.sym)) {
+ struct fexpr *s;
+ struct pexpr *expr_both =
+ expr_calculate_pexpr_both(expr, data);
+
+ if (!strcmp(p->expr->left.sym->name, "0"))
+ s = data->constants->symbol_no_fexpr;
+ else if (!strcmp(p->expr->left.sym->name, "1"))
+ s = data->constants->symbol_mod_fexpr;
+ else
+ s = data->constants->symbol_yes_fexpr;
+
+ updateDefaultList(s, expr_both, result, sym, data);
+ pexpr_put(expr_both);
+ }
+ /* if def.value = non-boolean constant */
+ else if (expr_is_nonbool_constant(p->expr)) {
+ struct fexpr *s = sym_get_or_create_nonbool_fexpr(
+ sym, p->expr->left.sym->name, data);
+ struct pexpr *expr_both =
+ expr_calculate_pexpr_both(expr, data);
+
+ updateDefaultList(s, expr_both, result, sym, data);
+ pexpr_put(expr_both);
+ }
+ /* any expression which evaluates to n/m/y for a tristate */
+ else if (sym->type == S_TRISTATE) {
+ struct expr *e_tmp = expr_alloc_and(expr_copy(p->expr),
+ expr_copy(expr));
+ struct pexpr *expr_y =
+ expr_calculate_pexpr_y(e_tmp, data);
+ struct pexpr *expr_m =
+ expr_calculate_pexpr_m(e_tmp, data);
+
+ updateDefaultList(data->constants->symbol_yes_fexpr,
+ expr_y, result, sym, data);
+ updateDefaultList(data->constants->symbol_mod_fexpr,
+ expr_m, result, sym, data);
+ PEXPR_PUT(expr_y, expr_m);
+ expr_free(e_tmp);
+ }
+ /* if non-boolean && def.value = non-boolean symbol */
+ else if (p->expr->type == E_SYMBOL && sym_is_nonboolean(sym) &&
+ sym_is_nonboolean(p->expr->left.sym)) {
+ struct prop_list *nb_sym_defaults = prop_list_init();
+ struct property *p_tmp;
+
+ /* Add defaults of other symbol as possible defaults for
+ * this symbol
+ */
+ for_all_defaults(p->expr->left.sym, p_tmp)
+ prop_list_add(nb_sym_defaults, p_tmp);
+
+ add_defaults(nb_sym_defaults, expr, result, sym, data);
+ prop_list_free(nb_sym_defaults);
+ }
+ /* any expression which evaluates to n/m/y */
+ else {
+ struct expr *e_tmp = expr_alloc_and(expr_copy(p->expr),
+ expr_copy(expr));
+ struct pexpr *expr_both =
+ expr_calculate_pexpr_both(e_tmp, data);
+
+ updateDefaultList(data->constants->symbol_yes_fexpr,
+ expr_both, result, sym, data);
+
+ pexpr_put(expr_both);
+ expr_free(e_tmp);
+ }
+ expr_free(expr);
+ }
+}
+
+/**
+ * get_defaults() - Generate list of default values and their conditions
+ * @sym: Symbol whose defaults we want to look at
+ *
+ * Creates a map from values that @sym can assume to the conditions under which
+ * they will be assumed. This will only consider the conditions
+ * directly associated with the defaults, e.g. sym->dir_dep would not be
+ * considered.
+ *
+ * As a side effect, the &symbol->nb_vals of @sym will be added for
+ * all default values (as well as the @symbol->nb_vals of other symbols @sym has
+ * as default (recursively)).
+ */
+static struct defm_list *get_defaults(struct symbol *sym, struct cfdata *data)
+{
+ struct defm_list *result = defm_list_init();
+ struct prop_list *defaults; /* list of default props of sym */
+ struct property *p;
+
+ covered = pexf(data->constants->const_false);
+
+ defaults = prop_list_init();
+ for_all_defaults(sym, p)
+ prop_list_add(defaults, p);
+
+ add_defaults(defaults, NULL, result, sym, data);
+ prop_list_free(defaults);
+ pexpr_put(covered);
+
+ return result;
+}
+
+/*
+ * return the condition for "y", False if it doesn't exist
+ */
+static struct pexpr *get_default_y(struct defm_list *list, struct cfdata *data)
+{
+ struct default_map *entry;
+ struct defm_node *node;
+
+ defm_list_for_each(node, list) {
+ entry = node->elem;
+ if (entry->val->type == FE_SYMBOL &&
+ entry->val->sym == &symbol_yes) {
+ pexpr_get(entry->e);
+ return entry->e;
+ }
+ }
+
+ return pexf(data->constants->const_false);
+}
+
+/*
+ * return the condition for "m", False if it doesn't exist
+ */
+static struct pexpr *get_default_m(struct defm_list *list, struct cfdata *data)
+{
+ struct default_map *entry;
+ struct defm_node *node;
+
+ defm_list_for_each(node, list) {
+ entry = node->elem;
+ if (entry->val->type == FE_SYMBOL &&
+ entry->val->sym == &symbol_mod) {
+ pexpr_get(entry->e);
+ return entry->e;
+ }
+ }
+
+ return pexf(data->constants->const_false);
+}
+
+/*
+ * return the constraint when _some_ default value will be applied
+ */
+static struct pexpr *get_default_any(struct symbol *sym, struct cfdata *data)
+{
+ struct property *prop;
+ struct expr *e;
+ struct pexpr *p;
+
+ if (!sym_is_nonboolean(sym))
+ return NULL;
+
+ p = pexf(data->constants->const_false);
+ for_all_defaults(sym, prop) {
+ if (prop->visible.expr)
+ e = expr_copy(prop->visible.expr);
+ else
+ e = expr_alloc_symbol(&symbol_yes);
+
+ if (expr_can_evaluate_to_mod(e))
+ p = pexpr_or(p, expr_calculate_pexpr_both(e, data),
+ data, PEXPR_ARGX);
+
+ p = pexpr_or(p, expr_calculate_pexpr_y(e, data), data,
+ PEXPR_ARGX);
+
+ expr_free(e);
+ }
+
+ return p;
+}
+
+/*
+ * get the value for the range
+ */
+static long sym_get_range_val(struct symbol *sym, int base)
+{
+ sym_calc_value(sym);
+ switch (sym->type) {
+ case S_INT:
+ base = 10;
+ break;
+ case S_HEX:
+ base = 16;
+ break;
+ default:
+ break;
+ }
+ return strtol(sym->curr.val, NULL, base);
+}
+
+/*
+ * count the number of all constraints
+ */
+unsigned int count_counstraints(void)
+{
+ unsigned int c = 0;
+ struct symbol *sym;
+
+ for_all_symbols(sym) {
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ c += sym->constraints->size;
+ }
+
+ return c;
+}
+
+/*
+ * add a constraint for a symbol
+ */
+void sym_add_constraint(struct symbol *sym, struct pexpr *constraint,
+ struct cfdata *data)
+{
+ if (!constraint)
+ return;
+
+ /* no need to add that */
+ if (constraint->type == PE_SYMBOL &&
+ constraint->left.fexpr == data->constants->const_true)
+ return;
+
+ /* this should never happen */
+ if (constraint->type == PE_SYMBOL &&
+ constraint->left.fexpr == data->constants->const_false)
+ perror("Adding const_false.");
+
+ pexpr_list_add(sym->constraints, pexpr_get(constraint));
+
+ if (!pexpr_is_nnf(constraint))
+ pexpr_print("Not NNF:", constraint, -1);
+}
+
+/*
+ * add a constraint for a symbol, but check for duplicate constraints
+ */
+void sym_add_constraint_eq(struct symbol *sym, struct pexpr *constraint,
+ struct cfdata *data)
+{
+ struct pexpr_node *node;
+
+ if (!constraint)
+ return;
+
+ /* no need to add that */
+ if (constraint->type == PE_SYMBOL &&
+ constraint->left.fexpr == data->constants->const_true)
+ return;
+
+ /* this should never happen */
+ if (constraint->type == PE_SYMBOL &&
+ constraint->left.fexpr == data->constants->const_false)
+ perror("Adding const_false.");
+
+ /* check the constraints for the same symbol */
+ pexpr_list_for_each(node, sym->constraints)
+ if (pexpr_eq(constraint, node->elem, data))
+ return;
+
+ pexpr_list_add(sym->constraints, pexpr_get(constraint));
+
+ if (!pexpr_is_nnf(constraint))
+ pexpr_print("Not NNF:", constraint, -1);
+}
diff --git a/scripts/kconfig/cf_constraints.h b/scripts/kconfig/cf_constraints.h
new file mode 100644
index 000000000000..97a18eaf11ca
--- /dev/null
+++ b/scripts/kconfig/cf_constraints.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CF_CONSTRAINTS_H
+#define CF_CONSTRAINTS_H
+
+#include "cf_defs.h"
+#include "expr.h"
+
+/* build the constraints for each symbol */
+void get_constraints(struct cfdata *data);
+
+/* count the number of all constraints */
+unsigned int count_counstraints(void);
+
+/* add a constraint for a symbol */
+void sym_add_constraint(struct symbol *sym, struct pexpr *constraint, struct cfdata *data);
+
+void sym_add_constraint_fexpr(struct symbol *sym, struct fexpr *constraint);
+
+/* add a constraint for a symbol, but check for duplicate constraints */
+void sym_add_constraint_eq(struct symbol *sym, struct pexpr *constraint, struct cfdata *data);
+
+#endif
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH v4 06/12] kconfig: Add files for building constraints
2024-07-10 6:52 ` [PATCH v4 06/12] kconfig: Add files for building constraints Ole Schuerks
@ 2024-08-12 8:49 ` Masahiro Yamada
2024-08-12 10:00 ` Masahiro Yamada
0 siblings, 1 reply; 25+ messages in thread
From: Masahiro Yamada @ 2024-08-12 8:49 UTC (permalink / raw)
To: Ole Schuerks
Cc: linux-kbuild, jude.gyimah, thorsten.berger, deltaone,
jan.sollmann, mcgrof, linux-kernel
On Wed, Jul 10, 2024 at 3:54 PM Ole Schuerks <ole0811sch@gmail.com> wrote:
>
> These files translate the Kconfig-model into propositional logic and store
> the constraints for each symbol in the corresponding struct.
>
> Co-developed-by: Patrick Franz <deltaone@debian.org>
> Signed-off-by: Patrick Franz <deltaone@debian.org>
> Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
> Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
> Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
> Suggested-by: Sarah Nadi <nadi@ualberta.ca>
> Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
> Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
> Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
> ---
> scripts/kconfig/cf_constraints.c | 1720 ++++++++++++++++++++++++++++++
> scripts/kconfig/cf_constraints.h | 26 +
> 2 files changed, 1746 insertions(+)
> create mode 100644 scripts/kconfig/cf_constraints.c
> create mode 100644 scripts/kconfig/cf_constraints.h
>
> diff --git a/scripts/kconfig/cf_constraints.c b/scripts/kconfig/cf_constraints.c
> new file mode 100644
> index 000000000000..1c02a4b47383
> --- /dev/null
> +++ b/scripts/kconfig/cf_constraints.c
> @@ -0,0 +1,1720 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
> + */
> +
> +#include "cf_defs.h"
> +#include "expr.h"
> +#define _GNU_SOURCE
> +#include <assert.h>
> +#include <locale.h>
> +#include <stdarg.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <time.h>
> +#include <unistd.h>
> +
> +#include "cf_utils.h"
> +#include "internal.h"
> +#include "cf_expr.h"
> +#include "cf_constraints.h"
> +
> +#define KCR_CMP false
> +#define NPC_OPTIMISATION true
> +
> +static void init_constraints(struct cfdata *data);
> +static void get_constraints_bool(struct cfdata *data);
> +static void get_constraints_select(struct cfdata *data);
> +static void get_constraints_nonbool(struct cfdata *data);
> +
> +static void build_tristate_constraint_clause(struct symbol *sym,
> + struct cfdata *data);
> +
> +static void add_selects_kcr(struct symbol *sym, struct cfdata *data);
> +static void add_selects(struct symbol *sym, struct cfdata *data);
> +
> +static void add_dependencies_bool(struct symbol *sym, struct cfdata *data);
> +static void add_dependencies_bool_kcr(struct symbol *sym, struct cfdata *data);
> +static void add_dependencies_nonbool(struct symbol *sym, struct cfdata *data);
> +
> +static void add_choice_prompt_cond(struct symbol *sym, struct cfdata *data);
> +static void add_choice_dependencies(struct symbol *sym, struct cfdata *data);
> +static void add_choice_constraints(struct symbol *sym, struct cfdata *data);
> +static void add_invisible_constraints(struct symbol *sym, struct cfdata *data);
> +static void sym_nonbool_at_least_1(struct symbol *sym, struct cfdata *data);
> +static void sym_nonbool_at_most_1(struct symbol *sym, struct cfdata *data);
> +static void sym_add_nonbool_values_from_default_range(struct symbol *sym,
> + struct cfdata *data);
> +static void sym_add_range_constraints(struct symbol *sym, struct cfdata *data);
> +static void sym_add_nonbool_prompt_constraint(struct symbol *sym,
> + struct cfdata *data);
> +
> +static struct default_map *create_default_map_entry(struct fexpr *val,
> + struct pexpr *e);
> +static struct defm_list *get_defaults(struct symbol *sym, struct cfdata *data);
> +static struct pexpr *get_default_y(struct defm_list *list, struct cfdata *data);
> +static struct pexpr *get_default_m(struct defm_list *list, struct cfdata *data);
> +static struct pexpr *get_default_any(struct symbol *sym, struct cfdata *data);
> +static long sym_get_range_val(struct symbol *sym, int base);
> +
> +/* -------------------------------------- */
> +
> +/*
> + * build the constraints for each symbol
> + */
> +void get_constraints(struct cfdata *data)
> +{
> + printd("Building constraints...");
> +
> + init_constraints(data);
> + get_constraints_bool(data);
> + get_constraints_select(data);
> + get_constraints_nonbool(data);
> +}
> +
> +/*
> + * need to go through the constraints once to find all "known values"
> + * for the non-Boolean symbols (and add them to sym->nb_vals for the given
> + * symbols).
> + * expr_calculate_pexpr_both and get_defaults have the side effect of creating
> + * known values.
> + */
> +static void init_constraints(struct cfdata *data)
> +{
> + struct symbol *sym;
> + struct property *p;
> +
> + for_all_symbols(sym) {
> + struct property *prompt;
> +
> + if (sym->type == S_UNKNOWN)
> + continue;
> +
> + if (sym_is_boolean(sym)) {
> + for_all_properties(sym, p, P_SELECT)
> + pexpr_put(expr_calculate_pexpr_both(p->visible.expr,
> + data));
> +
> + for_all_properties(sym, p, P_IMPLY)
> + pexpr_put(expr_calculate_pexpr_both(p->visible.expr,
> + data));
Does 'imply' give any constraint?
> + }
> +
> + if (sym->dir_dep.expr)
> + pexpr_put(expr_calculate_pexpr_both(sym->dir_dep.expr, data));
> +
> + prompt = sym_get_prompt(sym);
> + if (prompt != NULL && prompt->visible.expr) {
> + pexpr_put(expr_calculate_pexpr_both(prompt->visible.expr, data));
> + defm_list_destruct(get_defaults(sym, data));
> + }
> +
> + if (sym_is_nonboolean(sym)) {
> + const char *curr;
> +
> + for_all_defaults(sym, p) {
> + if (p == NULL)
> + continue;
> +
> + sym_create_nonbool_fexpr(
> + sym, p->expr->left.sym->name, data);
> + }
> + for_all_properties(sym, p, P_RANGE) {
> + if (p == NULL)
> + continue;
> +
> + sym_create_nonbool_fexpr(
> + sym, p->expr->left.sym->name, data);
> + sym_create_nonbool_fexpr(
> + sym, p->expr->right.sym->name, data);
> + }
> + curr = sym_get_string_value(sym);
> + if (strcmp(curr, "") != 0)
> + sym_create_nonbool_fexpr(sym, (char *)curr,
> + data);
> + }
> +
> + if (sym->type == S_HEX || sym->type == S_INT)
> + sym_add_nonbool_values_from_default_range(sym, data);
> + }
> +}
> +
> +/*
> + * build constraints for boolean symbols
> + */
> +static void get_constraints_bool(struct cfdata *data)
> +{
> + struct symbol *sym;
> +
> + for_all_symbols(sym) {
> + if (!sym_is_boolean(sym))
> + continue;
> +
> + /* build tristate constraints */
> + if (sym->type == S_TRISTATE)
> + build_tristate_constraint_clause(sym, data);
> +
> + /* build constraints for select statements
> + * need to treat choice symbols separately
> + */
> + if (!KCR_CMP) {
> + add_selects(sym, data);
> + } else {
> + if (sym->rev_dep.expr && !sym_is_choice(sym) &&
> + !sym_is_choice_value(sym))
> + add_selects_kcr(sym, data);
> + }
> +
> + /* build constraints for dependencies for booleans */
> + if (sym->dir_dep.expr && !sym_is_choice(sym) &&
> + !sym_is_choice_value(sym)) {
> + if (!KCR_CMP)
> + add_dependencies_bool(sym, data);
> + else
> + add_dependencies_bool_kcr(sym, data);
> + }
> +
> + /* build constraints for choice prompts */
> + if (sym_is_choice(sym))
> + add_choice_prompt_cond(sym, data);
> +
> + /* build constraints for dependencies (choice symbols and options) */
> + if (sym_is_choice(sym) || sym_is_choice_value(sym))
> + add_choice_dependencies(sym, data);
> +
> + /* build constraints for the choice groups */
> + if (sym_is_choice(sym))
> + add_choice_constraints(sym, data);
> +
> + /* build invisible constraints */
> + add_invisible_constraints(sym, data);
> + }
> +}
> +
> +/*
> + * build the constraints for select-variables
> + * skip non-Booleans, choice symbols/options och symbols without rev_dir
> + */
> +static void get_constraints_select(struct cfdata *data)
> +{
> + struct symbol *sym;
> +
> + for_all_symbols(sym) {
> + struct pexpr *sel_y, *sel_m;
> + struct pexpr *c1, *c2;
> +
> + if (KCR_CMP)
> + continue;
> +
> + if (!sym_is_boolean(sym))
> + continue;
> +
> + if (sym_is_choice(sym) || sym_is_choice_value(sym))
> + continue;
> +
> + if (!sym->rev_dep.expr)
> + continue;
> +
> + if (sym->list_sel_y == NULL)
> + continue;
> +
> + sel_y = pexpr_implies(pexf(sym->fexpr_sel_y),
> + pexf(sym->fexpr_y), data,
> + PEXPR_ARGX);
> + sym_add_constraint(sym, sel_y, data);
> +
> + c1 = pexpr_implies(pexf(sym->fexpr_sel_y), sym->list_sel_y,
> + data, PEXPR_ARG1);
> + sym_add_constraint(sym, c1, data);
> +
> + /* only continue for tristates */
> + if (sym->type == S_BOOLEAN)
> + continue;
> +
> + sel_m = pexpr_implies(pexf(sym->fexpr_sel_m),
> + sym_get_fexpr_both(sym, data), data,
> + PEXPR_ARGX);
> + sym_add_constraint(sym, sel_m, data);
> +
> + c2 = pexpr_implies(pexf(sym->fexpr_sel_m), sym->list_sel_m,
> + data, PEXPR_ARG1);
> + sym_add_constraint(sym, c2, data);
> + PEXPR_PUT(sel_y, sel_m, c1, c2);
> + }
> +}
> +
> +/*
> + * build constraints for non-booleans
> + */
> +static void get_constraints_nonbool(struct cfdata *data)
> +{
> + struct symbol *sym;
> +
> + for_all_symbols(sym) {
> + if (!sym_is_nonboolean(sym))
> + continue;
> +
> + /* the symbol must have a value, if there is a prompt */
> + if (sym_has_prompt(sym))
> + sym_add_nonbool_prompt_constraint(sym, data);
> +
> + /* build the range constraints for int/hex */
> + if (sym->type == S_HEX || sym->type == S_INT)
> + sym_add_range_constraints(sym, data);
> +
> + /* build constraints for dependencies for non-booleans */
> + if (sym->dir_dep.expr)
> + add_dependencies_nonbool(sym, data);
> +
> + /* build invisible constraints */
> + add_invisible_constraints(sym, data);
> +
> + /* exactly one of the symbols must be true */
> + sym_nonbool_at_least_1(sym, data);
> + sym_nonbool_at_most_1(sym, data);
> + }
> +}
> +
> +/*
> + * enforce tristate constraints
> + */
> +static void build_tristate_constraint_clause(struct symbol *sym,
> + struct cfdata *data)
> +{
> + struct pexpr *X, *X_m, *modules, *c;
> +
> + if (sym->type != S_TRISTATE)
> + return;
> +
> + X = pexf(sym->fexpr_y);
> + X_m = pexf(sym->fexpr_m);
> + modules = pexf(modules_sym->fexpr_y);
> +
> + /* -X v -X_m */
> + c = pexpr_or(pexpr_not_share(X, data), pexpr_not_share(X_m, data),
> + data, PEXPR_ARGX);
> + sym_add_constraint(sym, c, data);
> +
> + /* X_m -> MODULES */
> + if (modules_sym->fexpr_y != NULL) {
> + struct pexpr *c2 = pexpr_implies_share(X_m, modules, data);
> +
> + sym_add_constraint(sym, c2, data);
> + PEXPR_PUT(c2);
> + }
> + PEXPR_PUT(X, X_m, modules, c);
> +}
> +
> +/*
> + * build the select constraints
> + * - RDep(X) implies X
> + */
> +static void add_selects_kcr(struct symbol *sym, struct cfdata *data)
> +{
> + struct pexpr *rdep_y = expr_calculate_pexpr_y(sym->rev_dep.expr, data);
> + struct pexpr *c1 = pexpr_implies(rdep_y, pexf(sym->fexpr_y), data,
> + PEXPR_ARG2);
> +
> + struct pexpr *rdep_both =
> + expr_calculate_pexpr_both(sym->rev_dep.expr, data);
> + struct pexpr *c2 = pexpr_implies(
> + rdep_both, sym_get_fexpr_both(sym, data), data, PEXPR_ARG2);
> +
> + sym_add_constraint(sym, c1, data);
> + sym_add_constraint(sym, c2, data);
> + PEXPR_PUT(rdep_y, c1, rdep_both, c2);
> +}
> +
> +/*
> + * build the select constraints simplified
> + * - RDep(X) implies X
> + */
> +static void add_selects(struct symbol *sym, struct cfdata *data)
> +{
> + struct property *p;
> +
> + if (!sym_is_boolean(sym))
> + return;
> +
> + for_all_properties(sym, p, P_SELECT) {
> + struct symbol *selected = p->expr->left.sym;
> + struct pexpr *cond_y, *cond_both;
> +
> + if (selected->type == S_UNKNOWN)
> + continue;
> +
> + if (!selected->rev_dep.expr)
> + continue;
> +
> + if (p->visible.expr) {
> + cond_y = expr_calculate_pexpr_y(p->visible.expr, data);
> + cond_both = expr_calculate_pexpr_both(p->visible.expr,
> + data);
> + } else {
> + cond_y = pexf(data->constants->const_true);
> + cond_both = pexf(data->constants->const_true);
> + }
> +
> + if (selected->type == S_BOOLEAN) {
> + /* imply that symbol is selected to y */
> + struct pexpr *e1 = pexpr_and(
> + cond_both, sym_get_fexpr_both(sym, data), data,
> + PEXPR_ARG2);
> + struct pexpr *c1 = pexpr_implies(
> + e1, pexf(selected->fexpr_sel_y), data,
> + PEXPR_ARG2);
> +
> + sym_add_constraint(selected, c1, data);
> +
> + if (selected->list_sel_y == NULL)
> + selected->list_sel_y = pexpr_get(e1);
> + else
> + selected->list_sel_y =
> + pexpr_or(selected->list_sel_y, e1, data,
> + PEXPR_ARG1);
> + PEXPR_PUT(e1, c1);
> + }
> +
> + if (selected->type == S_TRISTATE) {
> + struct pexpr *e2, *e3, *c2, *c3;
> +
> + /* imply that symbol is selected to y */
> + e2 = pexpr_and(cond_y, pexf(sym->fexpr_y), data,
> + PEXPR_ARG2);
> + c2 = pexpr_implies(e2, pexf(selected->fexpr_sel_y),
> + data, PEXPR_ARG2);
> + sym_add_constraint(selected, c2, data);
> +
> + if (selected->list_sel_y == NULL)
> + selected->list_sel_y = pexpr_get(e2);
> + else
> + selected->list_sel_y =
> + pexpr_or(selected->list_sel_y, e2,
> + data, PEXPR_ARG1);
> +
> + /* imply that symbol is selected to m */
> + e3 = pexpr_and(cond_both,
> + sym_get_fexpr_both(sym, data), data,
> + PEXPR_ARG2);
> + c3 = pexpr_implies(e3, pexf(selected->fexpr_sel_m),
> + data, PEXPR_ARG2);
> + sym_add_constraint(selected, c3, data);
> +
> + if (selected->list_sel_m == NULL)
> + selected->list_sel_m = pexpr_get(e3);
> + else
> + selected->list_sel_m =
> + pexpr_or(selected->list_sel_m, e3,
> + data, PEXPR_ARG1);
> + PEXPR_PUT(e2, c2, e3, c3);
> + }
> + PEXPR_PUT(cond_y, cond_both);
> + }
> +}
> +
> +/*
> + * build the dependency constraints for booleans
> + * - X implies Dep(X) or RDep(X)
> + */
> +static void add_dependencies_bool(struct symbol *sym, struct cfdata *data)
> +{
> + struct pexpr *dep_both;
> + struct pexpr *visible_m;
> + struct pexpr *visible_y;
> + struct pexpr *visible_both;
> + struct property *prompt;
> + struct pexpr *has_prompt;
> + struct pexpr *sel_y;
> +
> + if (!sym_is_boolean(sym) || !sym->dir_dep.expr)
> + return;
> +
> + prompt = sym_get_prompt(sym);
> + if (!prompt) {
> + visible_m = pexf(data->constants->const_false);
> + visible_y = pexpr_get(visible_m);
> + visible_both = pexpr_get(visible_m);
> + } else if (prompt->expr == NULL) {
> + visible_m = pexf(data->constants->const_true);
> + visible_y = pexpr_get(visible_m);
> + visible_both = pexpr_get(visible_m);
> + } else {
> + visible_m = expr_calculate_pexpr_m(prompt->expr, data);
> + visible_y = expr_calculate_pexpr_y(prompt->expr, data);
> + visible_both = pexpr_or_share(visible_y, visible_m, data);
> + }
> +
> + dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr, data);
> +
> + sel_y = sym->rev_dep.expr ? pexf(sym->fexpr_sel_y) :
> + pexf(data->constants->const_false);
> + has_prompt = pexpr_get(visible_both);
> + has_prompt = pexpr_and(
> + has_prompt,
> + pexpr_not(pexpr_and_share(sel_y, visible_m, data),
> + data),
> + data, PEXPR_ARGX);
> +
> + if (sym->type == S_TRISTATE) {
> + struct pexpr *c1;
> + struct pexpr *c2;
> + struct pexpr *dep_y =
> + expr_calculate_pexpr_y(sym->dir_dep.expr, data);
> + struct pexpr *sel_both = sym_get_fexpr_sel_both(sym, data);
> + struct pexpr *cond_y1;
> + struct pexpr *cond_y2;
> + struct pexpr *cond_y;
> + struct pexpr *cond_m1;
> + struct pexpr *cond_m2;
> + struct pexpr *cond_m;
> +
> + cond_y1 = pexpr_implies(pexpr_not_share(has_prompt, data),
> + pexpr_or_share(dep_y, sel_y, data), data,
> + PEXPR_ARGX);
> + cond_y2 = pexpr_implies_share(has_prompt, visible_y, data);
> + cond_y = pexpr_and_share(cond_y1, cond_y2, data);
> + cond_m1 =
> + pexpr_implies(pexpr_not_share(has_prompt, data),
> + pexpr_or_share(dep_both, sel_both, data),
> + data, PEXPR_ARGX);
> + cond_m2 = pexpr_implies(has_prompt,
> + pexpr_not_share(sel_y, data), data,
> + PEXPR_ARG2);
> + cond_m = pexpr_and_share(cond_m1, cond_m2, data);
> + c1 = pexpr_implies(pexf(sym->fexpr_y), cond_y, data,
> + PEXPR_ARG1);
> + c2 = pexpr_implies(pexf(sym->fexpr_m), cond_m, data,
> + PEXPR_ARG1);
> +
> + sym_add_constraint(sym, c1, data);
> + sym_add_constraint(sym, c2, data);
> + PEXPR_PUT(c1, c2, dep_y, sel_both, cond_y1,
> + cond_y2, cond_y, cond_m1, cond_m2, cond_m);
> + } else if (sym->type == S_BOOLEAN) {
> + struct pexpr *cond1;
> + struct pexpr *cond2;
> + struct pexpr *c;
> +
> + cond1 = pexpr_implies(pexpr_not_share(has_prompt, data),
> + pexpr_or(dep_both, pexf(sym->fexpr_m),
> + data, PEXPR_ARG2),
> + data, PEXPR_ARGX);
> + cond2 = pexpr_implies_share(has_prompt, visible_y, data);
> + c = pexpr_implies(pexf(sym->fexpr_y),
> + pexpr_and_share(cond1, cond2, data), data,
> + PEXPR_ARGX);
> +
> + sym_add_constraint(sym, c, data);
> + PEXPR_PUT(c, cond1, cond2);
> + }
> + PEXPR_PUT(dep_both, has_prompt, sel_y, visible_y, visible_m, visible_both);
> +}
> +
> +/*
> + * build the dependency constraints for booleans (KCR)
> + * - X implies Dep(X) or RDep(X)
> + */
> +static void add_dependencies_bool_kcr(struct symbol *sym, struct cfdata *data)
> +{
> + struct pexpr *dep_both, *sel_both;
> +
> + if (!sym_is_boolean(sym) || !sym->dir_dep.expr)
> + return;
> +
> + dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr, data);
> +
> + sel_both = sym->rev_dep.expr ?
> + expr_calculate_pexpr_both(sym->rev_dep.expr, data) :
> + pexf(data->constants->const_false);
> +
> + if (sym->type == S_TRISTATE) {
> + struct pexpr *c1;
> + struct pexpr *c2;
> + {
> + struct pexpr *dep_y =
> + expr_calculate_pexpr_y(sym->dir_dep.expr, data);
> + struct pexpr *sel_y =
> + sym->rev_dep.expr ?
> + expr_calculate_pexpr_y(
> + sym->rev_dep.expr, data) :
> + pexf(data->constants->const_false);
> + c1 = pexpr_implies(pexf(sym->fexpr_y),
> + pexpr_or(dep_y, sel_y,
> + data, PEXPR_ARGX),
> + data, PEXPR_ARGX);
> + }
> + c2 = pexpr_implies(pexf(sym->fexpr_m),
> + pexpr_or_share(dep_both, sel_both,
> + data),
> + data, PEXPR_ARGX);
> +
> + sym_add_constraint(sym, c1, data);
> + sym_add_constraint(sym, c2, data);
> + PEXPR_PUT(c1, c2);
> + } else if (sym->type == S_BOOLEAN) {
> + struct pexpr *c = pexpr_implies(
> + pexf(sym->fexpr_y),
> + pexpr_or_share(dep_both, sel_both, data), data,
> + PEXPR_ARGX);
> +
> + sym_add_constraint(sym, c, data);
> + PEXPR_PUT(c);
> + }
> +
> + PEXPR_PUT(dep_both, sel_both);
> +}
> +
> +/*
> + * build the dependency constraints for non-booleans
> + *
> + * sym is not 'n' implies `sym->dir_dep`
> + */
> +static void add_dependencies_nonbool(struct symbol *sym, struct cfdata *data)
> +{
> + struct pexpr *dep_both;
> + struct pexpr *nb_vals; // "sym is set to some value" / "sym is not 'n'"
> + struct fexpr_node *node;
> + struct pexpr *c;
> +
> + if (!sym_is_nonboolean(sym) || !sym->dir_dep.expr || sym->rev_dep.expr)
> + return;
> +
> + dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr, data);
> +
> + nb_vals = pexf(data->constants->const_false);
> + /* can skip the first non-boolean value, since this is 'n' */
> + fexpr_list_for_each(node, sym->nb_vals) {
> + if (node->prev == NULL)
> + continue;
> +
> + nb_vals = pexpr_or(nb_vals, pexf(node->elem), data,
> + PEXPR_ARGX);
> + }
> +
> + c = pexpr_implies(nb_vals, dep_both, data, PEXPR_ARGX);
> + sym_add_constraint(sym, c, data);
> + pexpr_put(c);
> +}
> +
> +/*
> + * build the constraints for the choice prompt
> + */
> +static void add_choice_prompt_cond(struct symbol *sym, struct cfdata *data)
> +{
> + struct property *prompt;
> + struct pexpr *promptCondition;
> + struct pexpr *fe_both;
> + struct pexpr *pr_cond;
> + struct pexpr *req_cond;
> +
> + if (!sym_is_boolean(sym))
> + return;
> +
> + prompt = sym_get_prompt(sym);
> + if (prompt == NULL)
> + return;
> +
> + promptCondition =
> + prompt->visible.expr ?
> + expr_calculate_pexpr_both(prompt->visible.expr, data) :
> + pexf(data->constants->const_true);
> + fe_both = sym_get_fexpr_both(sym, data);
> + req_cond = pexpr_implies_share(promptCondition, fe_both, data);
> + sym_add_constraint(sym, req_cond, data);
> + pr_cond = pexpr_implies_share(fe_both, promptCondition, data);
> + sym_add_constraint(sym, pr_cond, data);
> + PEXPR_PUT(promptCondition, fe_both, req_cond, pr_cond);
> +}
> +
> +/*
> + * build constraints for dependencies (choice symbols and options)
> + */
> +static void add_choice_dependencies(struct symbol *sym, struct cfdata *data)
> +{
> + struct property *prompt;
> + struct expr *to_parse;
> + struct pexpr *dep_both;
> +
> + if (!sym_is_choice(sym) || !sym_is_choice_value(sym))
> + return;
> +
> + prompt = sym_get_prompt(sym);
> + if (prompt == NULL)
> + return;
> +
> + if (sym_is_choice(sym)) {
> + if (!prompt->visible.expr)
> + return;
> + to_parse = prompt->visible.expr;
> + } else {
> + if (!sym->dir_dep.expr)
> + return;
> + to_parse = sym->dir_dep.expr;
> + }
> +
> + dep_both = expr_calculate_pexpr_both(to_parse, data);
> +
> + if (sym->type == S_TRISTATE) {
> + struct pexpr *dep_y = expr_calculate_pexpr_y(to_parse, data);
> + struct pexpr *c1 = pexpr_implies(pexf(sym->fexpr_y), dep_y,
> + data, PEXPR_ARG1);
> + struct pexpr *c2 = pexpr_implies(
> + pexf(sym->fexpr_m), dep_both, data, PEXPR_ARG1);
> +
> + sym_add_constraint_eq(sym, c1, data);
> + sym_add_constraint_eq(sym, c2, data);
> + PEXPR_PUT(dep_y, c1, c2);
> + } else if (sym->type == S_BOOLEAN) {
> + struct pexpr *c = pexpr_implies(
> + pexf(sym->fexpr_y), dep_both, data, PEXPR_ARG1);
> +
> + sym_add_constraint_eq(sym, c, data);
> + pexpr_put(c);
> + }
> + pexpr_put(dep_both);
> +}
> +
> +/*
> + * build constraints for the choice groups
> + */
> +static void add_choice_constraints(struct symbol *sym, struct cfdata *data)
> +{
> + struct property *prompt;
> + struct symbol *choice, *choice2;
> + struct sym_node *node, *node2;
> + struct sym_list *items, *promptItems;
> + struct pexpr *c1;
> + struct menu *menu_ptr, *choiceval_menu;
> +
> + if (!sym_is_boolean(sym))
> + return;
> +
> + prompt = sym_get_prompt(sym);
> + if (prompt == NULL)
> + return;
> +
> + /* create list of all choice options */
> + items = sym_list_init();
> + /* create list of choice options with a prompt */
> + promptItems = sym_list_init();
> +
> + for_all_choices(sym, choiceval_menu, menu_ptr) {
> + choice = choiceval_menu->sym;
> +
> + sym_list_add(items, choice);
> + if (sym_get_prompt(choice) != NULL)
> + sym_list_add(promptItems, choice);
> + }
> +
> + /* if the choice is set to yes, at least one child must be set to yes */
> + c1 = NULL;
> + sym_list_for_each(node, promptItems) {
> + choice = node->elem;
> + c1 = node->prev == NULL ?
> + pexf(choice->fexpr_y) :
> + pexpr_or(c1, pexf(choice->fexpr_y), data,
> + PEXPR_ARGX);
> + }
> + if (c1 != NULL) {
> + struct pexpr *c2 = pexpr_implies(pexf(sym->fexpr_y), c1,
> + data, PEXPR_ARG1);
> +
> + sym_add_constraint(sym, c2, data);
> + PEXPR_PUT(c1, c2);
> + }
> +
> + /* every choice option (even those without a prompt) implies the choice */
> + sym_list_for_each(node, items) {
> + choice = node->elem;
> + c1 = pexpr_implies(sym_get_fexpr_both(choice, data),
> + sym_get_fexpr_both(sym, data), data,
> + PEXPR_ARGX);
> + sym_add_constraint(sym, c1, data);
> + pexpr_put(c1);
> + }
> +
> + /* choice options can only select mod, if the entire choice is mod */
> + if (sym->type == S_TRISTATE) {
> + sym_list_for_each(node, items) {
> + choice = node->elem;
> + if (choice->type == S_TRISTATE) {
> + c1 = pexpr_implies(pexf(choice->fexpr_m),
> + pexf(sym->fexpr_m),
> + data, PEXPR_ARGX);
> + sym_add_constraint(sym, c1, data);
> + pexpr_put(c1);
> + }
> + }
> + }
> +
> + /* tristate options cannot be m, if the choice symbol is boolean */
> + if (sym->type == S_BOOLEAN) {
> + sym_list_for_each(node, items) {
> + choice = node->elem;
> + if (choice->type == S_TRISTATE) {
> + struct pexpr *e = pexpr_not(pexf(choice->fexpr_m),
> + data);
> + sym_add_constraint(sym, e, data);
> + pexpr_put(e);
> + }
> + }
> + }
> +
> + /* all choice options are mutually exclusive for yes */
> + sym_list_for_each(node, promptItems) {
> + choice = node->elem;
> + for (struct sym_node *node2 = node->next; node2 != NULL;
> + node2 = node2->next) {
> + choice2 = node2->elem;
> + c1 = pexpr_or(
> + pexpr_not(pexf(choice->fexpr_y), data),
> + pexpr_not(pexf(choice2->fexpr_y), data),
> + data, PEXPR_ARGX);
> + sym_add_constraint(sym, c1, data);
> + pexpr_put(c1);
> + }
> + }
> +
> + /* if one choice option with a prompt is set to yes,
> + * then no other option may be set to mod
> + */
> + if (sym->type == S_TRISTATE) {
> + sym_list_for_each(node, promptItems) {
> + struct sym_list *tmp;
> +
> + choice = node->elem;
> +
> + tmp = sym_list_init();
> + for (struct sym_node *node2 = node->next; node2 != NULL;
> + node2 = node2->next) {
> + choice2 = node2->elem;
> + if (choice2->type == S_TRISTATE)
> + sym_list_add(tmp, choice2);
> + }
> + if (tmp->size == 0)
> + continue;
> +
> + sym_list_for_each(node2, tmp) {
> + choice2 = node2->elem;
> + if (node2->prev == NULL)
> + c1 = pexpr_not(
> + pexf(choice2->fexpr_m), data);
> + else
> + c1 = pexpr_and(
> + c1,
> + pexpr_not(
> + pexf(choice2->fexpr_m),
> + data),
> + data, PEXPR_ARGX);
> + }
> + c1 = pexpr_implies(pexf(choice->fexpr_y), c1, data,
> + PEXPR_ARGX);
> + sym_add_constraint(sym, c1, data);
> + pexpr_put(c1);
> + }
> + }
> + sym_list_free(promptItems);
> + sym_list_free(items);
> +}
> +
> +/*
> + * build the constraints for invisible options such as defaults
> + */
> +static void add_invisible_constraints(struct symbol *sym, struct cfdata *data)
> +{
> + struct property *prompt = sym_get_prompt(sym);
> + struct pexpr *promptCondition_both, *promptCondition_yes, *noPromptCond;
> + struct pexpr *npc;
> + struct defm_list *defaults;
> + struct pexpr *default_y, *default_m, *default_both;
> +
> + /* no constraints for the prompt, nothing to do here */
> + if (prompt != NULL && !prompt->visible.expr)
> + return;
> +
> + if (prompt == NULL) {
> + promptCondition_both = pexf(data->constants->const_false);
> + promptCondition_yes = pexf(data->constants->const_false);
> + noPromptCond = pexf(data->constants->const_true);
> + } else {
> + struct property *p;
> +
> + promptCondition_both = pexf(data->constants->const_false);
> + promptCondition_yes = pexf(data->constants->const_false);
> +
> + /* some symbols have multiple prompts */
> + for_all_prompts(sym, p) {
> + promptCondition_both =
> + pexpr_or(promptCondition_both,
> + expr_calculate_pexpr_both(
> + p->visible.expr, data),
> + data, PEXPR_ARGX);
> + promptCondition_yes = pexpr_or(
> + promptCondition_yes,
> + expr_calculate_pexpr_y(p->visible.expr, data),
> + data, PEXPR_ARGX);
> + }
> + noPromptCond = pexpr_not_share(promptCondition_both, data);
> + }
> +
> + if (NPC_OPTIMISATION) {
> + struct fexpr *npc_fe =
> + fexpr_create(data->sat_variable_nr++, FE_NPC, "");
> +
> + if (sym_is_choice(sym))
> + str_append(&npc_fe->name, "Choice_");
> +
> + str_append(&npc_fe->name, sym_get_name(sym));
> + str_append(&npc_fe->name, "_NPC");
> + sym->noPromptCond = npc_fe;
> + fexpr_add_to_satmap(npc_fe, data);
> +
> + npc = pexf(npc_fe);
> +
> + if (!sym_is_choice_value(sym) && !sym_is_choice(sym)) {
> + struct pexpr *c =
> + pexpr_implies_share(noPromptCond, npc, data);
> + sym_add_constraint(sym, c, data);
> + pexpr_put(c);
> + }
> + } else {
> + npc = pexpr_get(noPromptCond);
> + }
> +
> + defaults = get_defaults(sym, data);
> + default_y = get_default_y(defaults, data);
> + default_m = get_default_m(defaults, data);
> + default_both = pexpr_or_share(default_y, default_m, data);
> +
> + /* tristate elements are only selectable as yes, if they are visible as yes */
> + if (sym->type == S_TRISTATE) {
> + struct pexpr *e1 = pexpr_implies(
> + promptCondition_both,
> + pexpr_implies(pexf(sym->fexpr_y),
> + promptCondition_yes, data,
> + PEXPR_ARG1),
> + data, PEXPR_ARG2);
> +
> + sym_add_constraint(sym, e1, data);
> + pexpr_put(e1);
> + }
> +
> + /* if invisible and off by default, then a symbol can only be deactivated by its reverse
> + * dependencies
> + */
> + if (sym->type == S_TRISTATE) {
> + struct pexpr *sel_y, *sel_m, *sel_both;
> + struct pexpr *c1, *c2, *c3;
> + struct pexpr *d1, *d2, *d3;
> + struct pexpr *e1, *e2, *e3;
> +
> + if (sym->fexpr_sel_y != NULL) {
> + sel_y = pexpr_implies(pexf(sym->fexpr_y),
> + pexf(sym->fexpr_sel_y), data,
> + PEXPR_ARGX);
> + sel_m = pexpr_implies(pexf(sym->fexpr_m),
> + pexf(sym->fexpr_sel_m), data,
> + PEXPR_ARGX);
> + sel_both = pexpr_implies(
> + pexf(sym->fexpr_y),
> + pexpr_or(pexf(sym->fexpr_sel_m),
> + pexf(sym->fexpr_sel_y), data,
> + PEXPR_ARGX),
> + data, PEXPR_ARGX);
> + } else {
> + sel_y = pexpr_not(pexf(sym->fexpr_y), data);
> + sel_m = pexpr_not(pexf(sym->fexpr_m), data);
> + sel_both = pexpr_get(sel_y);
> + }
> +
> + c1 = pexpr_implies(pexpr_not_share(default_y, data), sel_y,
> + data, PEXPR_ARG1);
> + c2 = pexpr_implies(pexf(modules_sym->fexpr_y), c1, data,
> + PEXPR_ARG1);
> + c3 = pexpr_implies_share(npc, c2, data);
> + sym_add_constraint(sym, c3, data);
> +
> + d1 = pexpr_implies(pexpr_not_share(default_m, data), sel_m,
> + data, PEXPR_ARG1);
> + d2 = pexpr_implies(pexf(modules_sym->fexpr_y), d1, data,
> + PEXPR_ARG1);
> + d3 = pexpr_implies_share(npc, d2, data);
> + sym_add_constraint(sym, d3, data);
> +
> + e1 = pexpr_implies(pexpr_not_share(default_both, data),
> + sel_both, data, PEXPR_ARG1);
> + e2 = pexpr_implies(
> + pexpr_not(pexf(modules_sym->fexpr_y), data), e1,
> + data, PEXPR_ARG1);
> + e3 = pexpr_implies_share(npc, e2, data);
> + sym_add_constraint(sym, e3, data);
> + PEXPR_PUT(sel_y, sel_m, sel_both, c1, c2, c3, d1, d2, d3, e1,
> + e2, e3);
> + } else if (sym->type == S_BOOLEAN) {
> + struct pexpr *sel_y;
> + struct pexpr *e1, *e2;
> +
> + if (sym->fexpr_sel_y != NULL)
> + sel_y = pexpr_implies(pexf(sym->fexpr_y),
> + pexf(sym->fexpr_sel_y), data,
> + PEXPR_ARGX);
> + else
> + sel_y = pexpr_not(pexf(sym->fexpr_y), data);
> +
> + e1 = pexpr_implies(pexpr_not_share(default_both, data),
> + sel_y, data, PEXPR_ARG1);
> + e2 = pexpr_implies_share(npc, e1, data);
> +
> + sym_add_constraint_eq(sym, e2, data);
> + PEXPR_PUT(sel_y, e1, e2);
> + } else {
> + /* if non-boolean is invisible and no default's condition is
> + * fulfilled, then the symbol is not set
> + */
> + struct pexpr *default_any = get_default_any(sym, data);
> + struct pexpr *e1 = pexf(data->constants->const_true);
> + struct pexpr *e2, *e3;
> +
> + /* e1 = "sym is not set" */
> + for (struct fexpr_node *node = sym->nb_vals->head->next;
> + node != NULL; node = node->next)
> + e1 = pexpr_and(
> + e1, pexpr_not(pexf(node->elem), data),
> + data, PEXPR_ARGX);
> +
> + e2 = pexpr_implies(pexpr_not_share(default_any, data), e1,
> + data, PEXPR_ARG1);
> + e3 = pexpr_implies_share(npc, e2, data);
> +
> + sym_add_constraint(sym, e3, data);
> + PEXPR_PUT(default_any, e1, e2, e3);
> + }
> +
> + /* if invisible and on by default, then a symbol can only be deactivated by its
> + * dependencies
> + */
> + if (defaults->size == 0) {
> + // nothing to do
> + } else if (sym->type == S_TRISTATE) {
> + struct pexpr *e1;
> + struct pexpr *e2;
> +
> + e1 = pexpr_implies(npc,
> + pexpr_implies(default_y,
> + pexf(sym->fexpr_y),
> + data, PEXPR_ARG2),
> + data, PEXPR_ARG2);
> + sym_add_constraint(sym, e1, data);
> +
> + e2 = pexpr_implies(
> + npc,
> + pexpr_implies(default_m,
> + sym_get_fexpr_both(sym, data),
> + data, PEXPR_ARG2),
> + data, PEXPR_ARG2);
> + sym_add_constraint(sym, e2, data);
> + PEXPR_PUT(e1, e2);
> + } else if (sym->type == S_BOOLEAN) {
> + struct pexpr *c;
> + struct pexpr *c2;
> +
> + c = pexpr_implies(default_both, pexf(sym->fexpr_y), data,
> + PEXPR_ARG2);
> +
> + // TODO tristate choice hack
> +
> + c2 = pexpr_implies_share(npc, c, data);
> + sym_add_constraint(sym, c2, data);
> + PEXPR_PUT(c, c2);
> + } else {
> + /* if non-boolean invisible, then it assumes the correct
> + * default (if any).
> + */
> + struct defm_node *node;
> + struct pexpr *cond, *c;
> + struct fexpr *f;
> +
> + defm_list_for_each(node, defaults) {
> + f = node->elem->val;
> + cond = node->elem->e;
> + c = pexpr_implies(npc,
> + pexpr_implies(cond, pexf(f), data, PEXPR_ARG2),
> + data, PEXPR_ARG2);
> + sym_add_constraint(sym, c, data);
> + pexpr_put(c);
> + }
> + }
> +
> + PEXPR_PUT(promptCondition_yes, promptCondition_both, noPromptCond, npc,
> + default_y, default_m, default_both);
> + defm_list_destruct(defaults);
> +}
> +
> +/*
> + * add the known values from the default and range properties
> + */
> +static void sym_add_nonbool_values_from_default_range(struct symbol *sym,
> + struct cfdata *data)
> +{
> + struct property *p;
> +
> + for_all_defaults(sym, p) {
> + if (p == NULL)
> + continue;
> +
> + /* add the value to known values, if it doesn't exist yet */
> + sym_create_nonbool_fexpr(sym, p->expr->left.sym->name, data);
> + }
> +
> + for_all_properties(sym, p, P_RANGE) {
> + if (p == NULL)
> + continue;
> +
> + /* add the values to known values, if they don't exist yet */
> + sym_create_nonbool_fexpr(sym, p->expr->left.sym->name, data);
> + sym_create_nonbool_fexpr(sym, p->expr->right.sym->name, data);
> + }
> +}
> +
> +/*
> + * build the range constraints for int/hex:
> + * For each range and each value in `sym->nb_vals` that's not in the range:
> + * If the range's condition is fulfilled, then sym can't have this value.
> + */
> +static void sym_add_range_constraints(struct symbol *sym, struct cfdata *data)
> +{
> + struct property *prop;
> + struct pexpr *prevs;
> + struct pexpr *propCond;
> + struct pexpr_list *prevCond; // list of all conditions of the ranges
> + // from the previous iterations
> + prevCond = pexpr_list_init();
> +
> + for_all_properties(sym, prop, P_RANGE) {
> + int base;
> + long long range_min, range_max, tmp;
> + struct fexpr_node *node;
> +
> + if (prop == NULL)
> + continue;
> +
> + prevs = pexf(data->constants->const_true);
> + propCond = prop_get_condition(prop, data);
> +
> + // construct prevs as "none of the previous ranges' conditions
> + // were fulfilled but this range's condition is"
> + if (prevCond->size == 0) {
> + pexpr_put(prevs);
> + prevs = pexpr_get(propCond);
> +;
> + } else {
> + struct pexpr_node *node;
> +
> + pexpr_list_for_each(node, prevCond)
> + prevs = pexpr_and(pexpr_not_share(node->elem,
> + data),
> + prevs, data, PEXPR_ARGX);
> +
> + prevs = pexpr_and(propCond, prevs, data,
> + PEXPR_ARG2);
> + }
> + pexpr_list_add(prevCond, pexpr_get(propCond));
> +
> + switch (sym->type) {
> + case S_INT:
> + base = 10;
> + break;
> + case S_HEX:
> + base = 16;
> + break;
> + default:
> + return;
> + }
> +
> + range_min = sym_get_range_val(prop->expr->left.sym, base);
> + range_max = sym_get_range_val(prop->expr->right.sym, base);
> +
> + /* can skip the first non-boolean value, since this is 'n' */
> + fexpr_list_for_each(node, sym->nb_vals) {
> + struct pexpr *not_nb_val;
> + struct pexpr *c;
> +
> + if (node->prev == NULL)
> + continue;
> +
> + tmp = strtoll(str_get(&node->elem->nb_val), NULL, base);
> +
> + /* known value is in range, nothing to do here */
> + if (tmp >= range_min && tmp <= range_max)
> + continue;
> +
> + not_nb_val = pexpr_not(pexf(node->elem), data);
> + c = pexpr_implies_share(prevs, not_nb_val, data);
> + sym_add_constraint(sym, c, data);
> + PEXPR_PUT(not_nb_val, c);
> + }
> + PEXPR_PUT(prevs, propCond);
> + }
> +
> + pexpr_list_free_put(prevCond);
> +
> +}
> +
> +/*
> + * at least 1 of the known values for a non-boolean symbol must be true
> + */
> +static void sym_nonbool_at_least_1(struct symbol *sym, struct cfdata *data)
> +{
> + struct pexpr *e = NULL;
> + struct fexpr_node *node;
> +
> + if (!sym_is_nonboolean(sym))
> + return;
> +
> + fexpr_list_for_each(node, sym->nb_vals) {
> + if (node->prev == NULL)
> + e = pexf(node->elem);
> + else
> + e = pexpr_or(e, pexf(node->elem), data, PEXPR_ARGX);
> + }
> + sym_add_constraint(sym, e, data);
> + pexpr_put(e);
> +}
> +
> +/*
> + * at most 1 of the known values for a non-boolean symbol can be true
> + */
> +static void sym_nonbool_at_most_1(struct symbol *sym, struct cfdata *data)
> +{
> + struct fexpr_node *node1;
> +
> + if (!sym_is_nonboolean(sym))
> + return;
> +
> + /* iterate over all subsets of sym->nb_vals of size 2 */
> + fexpr_list_for_each(node1, sym->nb_vals) {
> + struct pexpr *e1 = pexf(node1->elem);
> +
> + for (struct fexpr_node *node2 = node1->next; node2 != NULL;
> + node2 = node2->next) {
> + struct pexpr *e2 = pexf(node2->elem);
> + struct pexpr *e = pexpr_or(pexpr_not_share(e1, data),
> + pexpr_not_share(e2, data),
> + data, PEXPR_ARGX);
> +
> + sym_add_constraint(sym, e, data);
> + PEXPR_PUT(e, e2);
> + }
> + pexpr_put(e1);
> + }
> +}
> +
> +/*
> + * a visible prompt for a non-boolean implies a value for the symbol
> + */
> +static void sym_add_nonbool_prompt_constraint(struct symbol *sym,
> + struct cfdata *data)
> +{
> + struct property *prompt;
> + struct pexpr *promptCondition;
> + struct pexpr *n;
> + struct pexpr *c = NULL;
> +
> + prompt = sym_get_prompt(sym);
> + if (prompt == NULL)
> + return;
> +
> + promptCondition = prop_get_condition(prompt, data);
> + n = pexf(sym_get_nonbool_fexpr(sym, "n"));
> +
> + if (n->type != PE_SYMBOL || n->left.fexpr == NULL)
> + goto cleanup;
> +
> + c = pexpr_implies(promptCondition, pexpr_not_share(n, data), data,
> + PEXPR_ARG2);
> +
> + sym_add_constraint(sym, c, data);
> +
> +cleanup:
> + PEXPR_PUT(n, promptCondition, c);
> +}
> +
> +static struct default_map *create_default_map_entry(struct fexpr *val,
> + struct pexpr *e)
> +{
> + struct default_map *map = malloc(sizeof(struct default_map));
> +
> + pexpr_get(e);
> + map->val = val;
> + map->e = e;
> +
> + return map;
> +}
> +
> +/**
> + * findDefaultEntry()
> + * @val: Value that the entry must have
> + * @defaults: List of defaults to search in
> + * @constants: To get ``constants->const_false`` from
> + *
> + * Finds an entry in @defaults whose &default_map.val attribute is the same
> + * pointer as the @val argument.
> + *
> + * Return: The condition &default_map.e of the found entry, or
> + * ``pexf(constants->const_false)`` if none was found. To be pexpr_put() by the
> + * caller.
> + */
> +static struct pexpr *findDefaultEntry(struct fexpr *val,
> + struct defm_list *defaults,
> + struct constants *constants)
> +{
> + struct defm_node *node;
> +
> + defm_list_for_each(node, defaults) {
> + if (val == node->elem->val) {
> + pexpr_get(node->elem->e);
> + return node->elem->e;
> + }
> + }
> +
> + return pexf(constants->const_false);
> +}
> +
> +/*
> + * accumulated during execution of add_defaults(), a disjunction of the
> + * conditions for all default props of a symbol
> + */
> +static struct pexpr *covered;
> +
> +static bool is_tri_as_num(struct symbol *sym)
> +{
> + if (!sym->name)
> + return false;
> +
> + return !strcmp(sym->name, "0")
> + || !strcmp(sym->name, "1")
> + || !strcmp(sym->name, "2");
> +}
> +
> +/**
> + * add_to_default_map() - Add to or update an entry in a default list
> + * @entry: Will be consumed by this function, i.e. the caller should and need
> + * only access @entry via @defaults.
> + */
> +static void add_to_default_map(struct defm_list *defaults,
> + struct default_map *entry, struct symbol *sym)
> +{
> + /* as this is a map, the entry must be replaced if it already exists */
> + if (sym_is_boolean(sym)) {
> + struct default_map *map;
> + struct defm_node *node;
> +
> + defm_list_for_each(node, defaults) {
> + map = node->elem;
> + if (map->val->sym == entry->val->sym) {
> + pexpr_put(map->e);
> + map->e = entry->e;
> + free(entry);
> + return;
> + }
> + }
> + defm_list_add(defaults, entry);
> + } else {
> + struct default_map *map;
> + struct defm_node *node;
> +
> + defm_list_for_each(node, defaults) {
> + map = node->elem;
> + if (map->val->satval == entry->val->satval) {
> + pexpr_put(map->e);
> + map->e = entry->e;
> + free(entry);
> + return;
> + }
> + }
> + defm_list_add(defaults, entry);
> + }
> +}
> +
> +/**
> + * updateDefaultList() - Update a default list with a new value-condition pair
> + * @val: The value whose condition will be updated
> + * @newCond: The condition of the default prop. Does not include the condition
> + * that the earlier default's conditions are not fulfilled.
> + * @result: the default list
> + * @sym: the symbol that the defaults belong to
> + *
> + * Update the condition that @val will be used for @sym by considering the next
> + * default property, whose condition is given by @newCond.
> + */
> +static void updateDefaultList(struct fexpr *val, struct pexpr *newCond,
> + struct defm_list *result, struct symbol *sym,
> + struct cfdata *data)
> +{
> + // The current condition of @val deduced from the previous default props
> + struct pexpr *prevCond = findDefaultEntry(val, result, data->constants);
> + // New combined condition for @val
> + struct pexpr *condUseVal =
> + pexpr_or(prevCond,
> + pexpr_and(newCond, pexpr_not_share(covered, data),
> + data, PEXPR_ARG2),
> + data, PEXPR_ARG2);
> + add_to_default_map(result, create_default_map_entry(val, condUseVal),
> + sym);
> + covered = pexpr_or(covered, newCond, data, PEXPR_ARG1);
> + PEXPR_PUT(prevCond, condUseVal);
> +}
> +
> +/**
> + * add_defaults() - Generate list of default values and their conditions
> + * @defaults: List of the default properties
> + * @ctx: Additional condition that needs to be fulfilled for any default. May be
> + * NULL.
> + * @result: List that will be filled
> + * @sym: Symbol that the defaults belong to
> + *
> + * Creates a map from values that @sym can assume to the conditions under which
> + * they will be assumed. Without @ctx, this will only consider the conditions
> + * directly associated with the defaults, e.g. sym->dir_dep would not be
> + * considered.
> + *
> + * As a side effect, the &symbol->nb_vals of @sym will be added for
> + * all default values (as well as the @symbol->nb_vals of other symbols @sym has
> + * as default (recursively)).
> + */
> +static void add_defaults(struct prop_list *defaults, struct expr *ctx,
> + struct defm_list *result, struct symbol *sym,
> + struct cfdata *data)
> +{
> + struct prop_node *node;
> + struct property *p;
> + struct expr *expr;
> +
> + prop_list_for_each(node, defaults) {
> + p = node->elem;
> + /* calculate expr as whether the default's condition (and the
> + * one inherited from ctx) is fulfilled
> + */
> + if (p->visible.expr) {
> + if (ctx == NULL)
> + expr = expr_copy(p->visible.expr);
> + else
> + expr = expr_alloc_and(
> + expr_copy(p->visible.expr),
> + expr_copy(ctx));
> + } else {
> + if (ctx == NULL)
> + expr = expr_alloc_symbol(&symbol_yes);
> + else
> + expr = expr_alloc_and(
> + expr_alloc_symbol(&symbol_yes),
> + expr_copy(ctx));
> + }
> +
> + /* if tristate and def.value = y */
> + if (p->expr->type == E_SYMBOL && sym->type == S_TRISTATE &&
> + p->expr->left.sym == &symbol_yes) {
> + struct pexpr *expr_y =
> + expr_calculate_pexpr_y(expr, data);
> + struct pexpr *expr_m =
> + expr_calculate_pexpr_m(expr, data);
> +
> + updateDefaultList(data->constants->symbol_yes_fexpr,
> + expr_y, result, sym, data);
> + updateDefaultList(data->constants->symbol_mod_fexpr,
> + expr_m, result, sym, data);
> + PEXPR_PUT(expr_y, expr_m);
> + }
> + /* if def.value = n/m/y */
> + else if (p->expr->type == E_SYMBOL &&
> + sym_is_tristate_constant(p->expr->left.sym) &&
> + sym_is_boolean(sym)) {
> + struct fexpr *s;
> + struct pexpr *expr_both =
> + expr_calculate_pexpr_both(expr, data);
> +
> + if (p->expr->left.sym == &symbol_yes)
> + s = data->constants->symbol_yes_fexpr;
> + else if (p->expr->left.sym == &symbol_mod)
> + s = data->constants->symbol_mod_fexpr;
> + else
> + s = data->constants->symbol_no_fexpr;
> +
> + updateDefaultList(s, expr_both, result, sym, data);
> + pexpr_put(expr_both);
> + }
> + /* if def.value = n/m/y, but written as 0/1/2 for a boolean */
> + else if (sym_is_boolean(sym) && p->expr->type == E_SYMBOL &&
> + p->expr->left.sym->type == S_UNKNOWN &&
> + is_tri_as_num(p->expr->left.sym)) {
> + struct fexpr *s;
> + struct pexpr *expr_both =
> + expr_calculate_pexpr_both(expr, data);
> +
> + if (!strcmp(p->expr->left.sym->name, "0"))
> + s = data->constants->symbol_no_fexpr;
> + else if (!strcmp(p->expr->left.sym->name, "1"))
> + s = data->constants->symbol_mod_fexpr;
> + else
> + s = data->constants->symbol_yes_fexpr;
> +
> + updateDefaultList(s, expr_both, result, sym, data);
> + pexpr_put(expr_both);
> + }
> + /* if def.value = non-boolean constant */
> + else if (expr_is_nonbool_constant(p->expr)) {
> + struct fexpr *s = sym_get_or_create_nonbool_fexpr(
> + sym, p->expr->left.sym->name, data);
> + struct pexpr *expr_both =
> + expr_calculate_pexpr_both(expr, data);
> +
> + updateDefaultList(s, expr_both, result, sym, data);
> + pexpr_put(expr_both);
> + }
> + /* any expression which evaluates to n/m/y for a tristate */
> + else if (sym->type == S_TRISTATE) {
> + struct expr *e_tmp = expr_alloc_and(expr_copy(p->expr),
> + expr_copy(expr));
> + struct pexpr *expr_y =
> + expr_calculate_pexpr_y(e_tmp, data);
> + struct pexpr *expr_m =
> + expr_calculate_pexpr_m(e_tmp, data);
> +
> + updateDefaultList(data->constants->symbol_yes_fexpr,
> + expr_y, result, sym, data);
> + updateDefaultList(data->constants->symbol_mod_fexpr,
> + expr_m, result, sym, data);
> + PEXPR_PUT(expr_y, expr_m);
> + expr_free(e_tmp);
> + }
> + /* if non-boolean && def.value = non-boolean symbol */
> + else if (p->expr->type == E_SYMBOL && sym_is_nonboolean(sym) &&
> + sym_is_nonboolean(p->expr->left.sym)) {
> + struct prop_list *nb_sym_defaults = prop_list_init();
> + struct property *p_tmp;
> +
> + /* Add defaults of other symbol as possible defaults for
> + * this symbol
> + */
> + for_all_defaults(p->expr->left.sym, p_tmp)
> + prop_list_add(nb_sym_defaults, p_tmp);
> +
> + add_defaults(nb_sym_defaults, expr, result, sym, data);
> + prop_list_free(nb_sym_defaults);
> + }
> + /* any expression which evaluates to n/m/y */
> + else {
> + struct expr *e_tmp = expr_alloc_and(expr_copy(p->expr),
> + expr_copy(expr));
> + struct pexpr *expr_both =
> + expr_calculate_pexpr_both(e_tmp, data);
> +
> + updateDefaultList(data->constants->symbol_yes_fexpr,
> + expr_both, result, sym, data);
> +
> + pexpr_put(expr_both);
> + expr_free(e_tmp);
> + }
> + expr_free(expr);
> + }
> +}
> +
> +/**
> + * get_defaults() - Generate list of default values and their conditions
> + * @sym: Symbol whose defaults we want to look at
> + *
> + * Creates a map from values that @sym can assume to the conditions under which
> + * they will be assumed. This will only consider the conditions
> + * directly associated with the defaults, e.g. sym->dir_dep would not be
> + * considered.
> + *
> + * As a side effect, the &symbol->nb_vals of @sym will be added for
> + * all default values (as well as the @symbol->nb_vals of other symbols @sym has
> + * as default (recursively)).
> + */
> +static struct defm_list *get_defaults(struct symbol *sym, struct cfdata *data)
> +{
> + struct defm_list *result = defm_list_init();
> + struct prop_list *defaults; /* list of default props of sym */
> + struct property *p;
> +
> + covered = pexf(data->constants->const_false);
> +
> + defaults = prop_list_init();
> + for_all_defaults(sym, p)
> + prop_list_add(defaults, p);
> +
> + add_defaults(defaults, NULL, result, sym, data);
> + prop_list_free(defaults);
> + pexpr_put(covered);
> +
> + return result;
> +}
> +
> +/*
> + * return the condition for "y", False if it doesn't exist
> + */
> +static struct pexpr *get_default_y(struct defm_list *list, struct cfdata *data)
> +{
> + struct default_map *entry;
> + struct defm_node *node;
> +
> + defm_list_for_each(node, list) {
> + entry = node->elem;
> + if (entry->val->type == FE_SYMBOL &&
> + entry->val->sym == &symbol_yes) {
> + pexpr_get(entry->e);
> + return entry->e;
> + }
> + }
> +
> + return pexf(data->constants->const_false);
> +}
> +
> +/*
> + * return the condition for "m", False if it doesn't exist
> + */
> +static struct pexpr *get_default_m(struct defm_list *list, struct cfdata *data)
> +{
> + struct default_map *entry;
> + struct defm_node *node;
> +
> + defm_list_for_each(node, list) {
> + entry = node->elem;
> + if (entry->val->type == FE_SYMBOL &&
> + entry->val->sym == &symbol_mod) {
> + pexpr_get(entry->e);
> + return entry->e;
> + }
> + }
> +
> + return pexf(data->constants->const_false);
> +}
> +
> +/*
> + * return the constraint when _some_ default value will be applied
> + */
> +static struct pexpr *get_default_any(struct symbol *sym, struct cfdata *data)
> +{
> + struct property *prop;
> + struct expr *e;
> + struct pexpr *p;
> +
> + if (!sym_is_nonboolean(sym))
> + return NULL;
> +
> + p = pexf(data->constants->const_false);
> + for_all_defaults(sym, prop) {
> + if (prop->visible.expr)
> + e = expr_copy(prop->visible.expr);
> + else
> + e = expr_alloc_symbol(&symbol_yes);
> +
> + if (expr_can_evaluate_to_mod(e))
> + p = pexpr_or(p, expr_calculate_pexpr_both(e, data),
> + data, PEXPR_ARGX);
> +
> + p = pexpr_or(p, expr_calculate_pexpr_y(e, data), data,
> + PEXPR_ARGX);
> +
> + expr_free(e);
> + }
> +
> + return p;
> +}
> +
> +/*
> + * get the value for the range
> + */
> +static long sym_get_range_val(struct symbol *sym, int base)
> +{
> + sym_calc_value(sym);
> + switch (sym->type) {
> + case S_INT:
> + base = 10;
> + break;
> + case S_HEX:
> + base = 16;
> + break;
> + default:
> + break;
> + }
> + return strtol(sym->curr.val, NULL, base);
> +}
> +
> +/*
> + * count the number of all constraints
> + */
> +unsigned int count_counstraints(void)
> +{
> + unsigned int c = 0;
> + struct symbol *sym;
> +
> + for_all_symbols(sym) {
> + if (sym->type == S_UNKNOWN)
> + continue;
> +
> + c += sym->constraints->size;
> + }
> +
> + return c;
> +}
> +
> +/*
> + * add a constraint for a symbol
> + */
> +void sym_add_constraint(struct symbol *sym, struct pexpr *constraint,
> + struct cfdata *data)
> +{
> + if (!constraint)
> + return;
> +
> + /* no need to add that */
> + if (constraint->type == PE_SYMBOL &&
> + constraint->left.fexpr == data->constants->const_true)
> + return;
> +
> + /* this should never happen */
> + if (constraint->type == PE_SYMBOL &&
> + constraint->left.fexpr == data->constants->const_false)
> + perror("Adding const_false.");
> +
> + pexpr_list_add(sym->constraints, pexpr_get(constraint));
> +
> + if (!pexpr_is_nnf(constraint))
> + pexpr_print("Not NNF:", constraint, -1);
> +}
> +
> +/*
> + * add a constraint for a symbol, but check for duplicate constraints
> + */
> +void sym_add_constraint_eq(struct symbol *sym, struct pexpr *constraint,
> + struct cfdata *data)
> +{
> + struct pexpr_node *node;
> +
> + if (!constraint)
> + return;
> +
> + /* no need to add that */
> + if (constraint->type == PE_SYMBOL &&
> + constraint->left.fexpr == data->constants->const_true)
> + return;
> +
> + /* this should never happen */
> + if (constraint->type == PE_SYMBOL &&
> + constraint->left.fexpr == data->constants->const_false)
> + perror("Adding const_false.");
> +
> + /* check the constraints for the same symbol */
> + pexpr_list_for_each(node, sym->constraints)
> + if (pexpr_eq(constraint, node->elem, data))
> + return;
> +
> + pexpr_list_add(sym->constraints, pexpr_get(constraint));
> +
> + if (!pexpr_is_nnf(constraint))
> + pexpr_print("Not NNF:", constraint, -1);
> +}
> diff --git a/scripts/kconfig/cf_constraints.h b/scripts/kconfig/cf_constraints.h
> new file mode 100644
> index 000000000000..97a18eaf11ca
> --- /dev/null
> +++ b/scripts/kconfig/cf_constraints.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
> + */
> +
> +#ifndef CF_CONSTRAINTS_H
> +#define CF_CONSTRAINTS_H
> +
> +#include "cf_defs.h"
> +#include "expr.h"
> +
> +/* build the constraints for each symbol */
> +void get_constraints(struct cfdata *data);
> +
> +/* count the number of all constraints */
> +unsigned int count_counstraints(void);
> +
> +/* add a constraint for a symbol */
> +void sym_add_constraint(struct symbol *sym, struct pexpr *constraint, struct cfdata *data);
> +
> +void sym_add_constraint_fexpr(struct symbol *sym, struct fexpr *constraint);
> +
> +/* add a constraint for a symbol, but check for duplicate constraints */
> +void sym_add_constraint_eq(struct symbol *sym, struct pexpr *constraint, struct cfdata *data);
> +
> +#endif
> --
> 2.39.2
>
--
Best Regards
Masahiro Yamada
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v4 06/12] kconfig: Add files for building constraints
2024-08-12 8:49 ` Masahiro Yamada
@ 2024-08-12 10:00 ` Masahiro Yamada
0 siblings, 0 replies; 25+ messages in thread
From: Masahiro Yamada @ 2024-08-12 10:00 UTC (permalink / raw)
To: Ole Schuerks
Cc: linux-kbuild, jude.gyimah, thorsten.berger, deltaone,
jan.sollmann, mcgrof, linux-kernel
On Mon, Aug 12, 2024 at 5:49 PM Masahiro Yamada <masahiroy@kernel.org> wrote:
>
> On Wed, Jul 10, 2024 at 3:54 PM Ole Schuerks <ole0811sch@gmail.com> wrote:
> >
> > These files translate the Kconfig-model into propositional logic and store
> > the constraints for each symbol in the corresponding struct.
> >
> > Co-developed-by: Patrick Franz <deltaone@debian.org>
> > Signed-off-by: Patrick Franz <deltaone@debian.org>
> > Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
> > Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
> > Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
> > Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
> > Suggested-by: Sarah Nadi <nadi@ualberta.ca>
> > Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
> > Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
> > Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
> > ---
> > scripts/kconfig/cf_constraints.c | 1720 ++++++++++++++++++++++++++++++
> > scripts/kconfig/cf_constraints.h | 26 +
> > 2 files changed, 1746 insertions(+)
> > create mode 100644 scripts/kconfig/cf_constraints.c
> > create mode 100644 scripts/kconfig/cf_constraints.h
> >
> > diff --git a/scripts/kconfig/cf_constraints.c b/scripts/kconfig/cf_constraints.c
> > new file mode 100644
> > index 000000000000..1c02a4b47383
> > --- /dev/null
> > +++ b/scripts/kconfig/cf_constraints.c
> > @@ -0,0 +1,1720 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
> > + */
> > +
> > +#include "cf_defs.h"
> > +#include "expr.h"
> > +#define _GNU_SOURCE
> > +#include <assert.h>
> > +#include <locale.h>
> > +#include <stdarg.h>
> > +#include <stdbool.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <time.h>
> > +#include <unistd.h>
> > +
> > +#include "cf_utils.h"
> > +#include "internal.h"
> > +#include "cf_expr.h"
> > +#include "cf_constraints.h"
> > +
> > +#define KCR_CMP false
> > +#define NPC_OPTIMISATION true
> > +
> > +static void init_constraints(struct cfdata *data);
> > +static void get_constraints_bool(struct cfdata *data);
> > +static void get_constraints_select(struct cfdata *data);
> > +static void get_constraints_nonbool(struct cfdata *data);
> > +
> > +static void build_tristate_constraint_clause(struct symbol *sym,
> > + struct cfdata *data);
> > +
> > +static void add_selects_kcr(struct symbol *sym, struct cfdata *data);
> > +static void add_selects(struct symbol *sym, struct cfdata *data);
> > +
> > +static void add_dependencies_bool(struct symbol *sym, struct cfdata *data);
> > +static void add_dependencies_bool_kcr(struct symbol *sym, struct cfdata *data);
> > +static void add_dependencies_nonbool(struct symbol *sym, struct cfdata *data);
> > +
> > +static void add_choice_prompt_cond(struct symbol *sym, struct cfdata *data);
> > +static void add_choice_dependencies(struct symbol *sym, struct cfdata *data);
> > +static void add_choice_constraints(struct symbol *sym, struct cfdata *data);
> > +static void add_invisible_constraints(struct symbol *sym, struct cfdata *data);
> > +static void sym_nonbool_at_least_1(struct symbol *sym, struct cfdata *data);
> > +static void sym_nonbool_at_most_1(struct symbol *sym, struct cfdata *data);
> > +static void sym_add_nonbool_values_from_default_range(struct symbol *sym,
> > + struct cfdata *data);
> > +static void sym_add_range_constraints(struct symbol *sym, struct cfdata *data);
> > +static void sym_add_nonbool_prompt_constraint(struct symbol *sym,
> > + struct cfdata *data);
> > +
> > +static struct default_map *create_default_map_entry(struct fexpr *val,
> > + struct pexpr *e);
> > +static struct defm_list *get_defaults(struct symbol *sym, struct cfdata *data);
> > +static struct pexpr *get_default_y(struct defm_list *list, struct cfdata *data);
> > +static struct pexpr *get_default_m(struct defm_list *list, struct cfdata *data);
> > +static struct pexpr *get_default_any(struct symbol *sym, struct cfdata *data);
> > +static long sym_get_range_val(struct symbol *sym, int base);
> > +
> > +/* -------------------------------------- */
> > +
> > +/*
> > + * build the constraints for each symbol
> > + */
> > +void get_constraints(struct cfdata *data)
> > +{
> > + printd("Building constraints...");
> > +
> > + init_constraints(data);
> > + get_constraints_bool(data);
> > + get_constraints_select(data);
> > + get_constraints_nonbool(data);
> > +}
> > +
> > +/*
> > + * need to go through the constraints once to find all "known values"
> > + * for the non-Boolean symbols (and add them to sym->nb_vals for the given
> > + * symbols).
> > + * expr_calculate_pexpr_both and get_defaults have the side effect of creating
> > + * known values.
> > + */
> > +static void init_constraints(struct cfdata *data)
> > +{
> > + struct symbol *sym;
> > + struct property *p;
> > +
> > + for_all_symbols(sym) {
> > + struct property *prompt;
> > +
> > + if (sym->type == S_UNKNOWN)
> > + continue;
> > +
> > + if (sym_is_boolean(sym)) {
> > + for_all_properties(sym, p, P_SELECT)
> > + pexpr_put(expr_calculate_pexpr_both(p->visible.expr,
> > + data));
> > +
> > + for_all_properties(sym, p, P_IMPLY)
> > + pexpr_put(expr_calculate_pexpr_both(p->visible.expr,
> > + data));
>
>
>
> Does 'imply' give any constraint?
I take back this comment.
'imply' does give a constraint when the implied symbol has
no prompt.
--
Best Regards
Masahiro Yamada
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v4 07/12] kconfig: Add files for handling expressions
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
` (5 preceding siblings ...)
2024-07-10 6:52 ` [PATCH v4 06/12] kconfig: Add files for building constraints Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-08-12 8:46 ` Masahiro Yamada
2024-07-10 6:52 ` [PATCH v4 08/12] kconfig: Add files for RangeFix Ole Schuerks
` (4 subsequent siblings)
11 siblings, 1 reply; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
To translate the Kconfig-model into propositional logic and resolve
conflicts, we need to handle propostional formulas.
These files contain many functions and macros to deal with
propositional formulas.
Co-developed-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Patrick Franz <deltaone@debian.org>
Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
Suggested-by: Sarah Nadi <nadi@ualberta.ca>
Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/cf_expr.c | 2594 +++++++++++++++++++++++++++++++++++++
scripts/kconfig/cf_expr.h | 296 +++++
2 files changed, 2890 insertions(+)
create mode 100644 scripts/kconfig/cf_expr.c
create mode 100644 scripts/kconfig/cf_expr.h
diff --git a/scripts/kconfig/cf_expr.c b/scripts/kconfig/cf_expr.c
new file mode 100644
index 000000000000..f015f91ec8c6
--- /dev/null
+++ b/scripts/kconfig/cf_expr.c
@@ -0,0 +1,2594 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "cf_expr.h"
+#include "cf_defs.h"
+#include "cf_utils.h"
+
+static void create_fexpr_bool(struct symbol *sym, struct cfdata *data);
+static void create_fexpr_nonbool(struct symbol *sym, struct cfdata *data);
+static void create_fexpr_unknown(struct symbol *sym, struct cfdata *data);
+static void create_fexpr_choice(struct symbol *sym, struct cfdata *data);
+
+static void pexpr_print_util(struct pexpr *e, int prevtoken);
+static void pexpr_shallow_copy(struct pexpr *dest, struct pexpr *org, unsigned int ref_count);
+
+static struct pexpr *pexpr_move_wrapper(
+ struct pexpr *a, struct pexpr *b, struct cfdata *data,
+ enum pexpr_move move,
+ struct pexpr *(*func)(struct pexpr *, struct pexpr *, struct cfdata *));
+
+static int trans_count;
+
+
+/*
+ * create a fexpr
+ */
+struct fexpr *fexpr_create(int satval, enum fexpr_type type, char *name)
+{
+ struct fexpr *e = xcalloc(1, sizeof(*e));
+
+ e->satval = satval;
+ e->type = type;
+ e->name = str_new();
+ e->assumption = false;
+ str_append(&e->name, name);
+
+ return e;
+}
+
+/*
+ * create the fexpr for a symbol
+ */
+void sym_create_fexpr(struct symbol *sym, struct cfdata *data)
+{
+ if (sym_is_choice(sym))
+ create_fexpr_choice(sym, data);
+ else if (sym_is_boolean(sym))
+ create_fexpr_bool(sym, data);
+ else if (sym_is_nonboolean(sym))
+ create_fexpr_nonbool(sym, data);
+ else
+ create_fexpr_unknown(sym, data);
+}
+
+/*
+ * create the fexpr for symbols with reverse dependencies
+ */
+static void create_fexpr_selected(struct symbol *sym, struct cfdata *data)
+{
+ struct fexpr *fexpr_sel_y;
+ struct fexpr *fexpr_sel_m;
+
+ /* fexpr_sel_y */
+ fexpr_sel_y = fexpr_create(data->sat_variable_nr++, FE_SELECT, sym->name);
+ str_append(&fexpr_sel_y->name, "_sel_y");
+ fexpr_sel_y->sym = sym;
+ fexpr_add_to_satmap(fexpr_sel_y, data);
+
+ sym->fexpr_sel_y = fexpr_sel_y;
+
+ /* fexpr_sel_m */
+ if (sym->type == S_BOOLEAN)
+ return;
+
+ fexpr_sel_m = fexpr_create(data->sat_variable_nr++, FE_SELECT, sym->name);
+ str_append(&fexpr_sel_m->name, "_sel_m");
+ fexpr_sel_m->sym = sym;
+ fexpr_add_to_satmap(fexpr_sel_m, data);
+
+ sym->fexpr_sel_m = fexpr_sel_m;
+}
+
+/*
+ * create the fexpr for a boolean/tristate symbol
+ */
+static void create_fexpr_bool(struct symbol *sym, struct cfdata *data)
+{
+ struct fexpr *fexpr_y;
+ struct fexpr *fexpr_m;
+
+ fexpr_y = fexpr_create(data->sat_variable_nr++, FE_SYMBOL, sym->name);
+ fexpr_y->sym = sym;
+ fexpr_y->tri = yes;
+ fexpr_add_to_satmap(fexpr_y, data);
+
+ sym->fexpr_y = fexpr_y;
+
+
+ if (sym->type == S_TRISTATE) {
+ fexpr_m = fexpr_create(data->sat_variable_nr++, FE_SYMBOL, sym->name);
+ str_append(&fexpr_m->name, "_MODULE");
+ fexpr_m->sym = sym;
+ fexpr_m->tri = mod;
+ fexpr_add_to_satmap(fexpr_m, data);
+ } else {
+ fexpr_m = data->constants->const_false;
+ }
+
+ sym->fexpr_m = fexpr_m;
+
+ if (sym->rev_dep.expr)
+ create_fexpr_selected(sym, data);
+}
+
+/*
+ * create the fexpr for a non-boolean symbol
+ */
+static void create_fexpr_nonbool(struct symbol *sym, struct cfdata *data)
+{
+ /* default values */
+ char int_values[][2] = {"n", "0", "1"};
+ char hex_values[][4] = {"n", "0x0", "0x1"};
+ char string_values[][9] = {"n", "", "nonempty"};
+
+ sym->fexpr_y = data->constants->const_false;
+ sym->fexpr_m = data->constants->const_false;
+ sym->nb_vals = fexpr_list_init();
+
+ for (int i = 0; i < 3; i++) {
+ struct fexpr *e = fexpr_create(data->sat_variable_nr++, FE_NONBOOL, sym->name);
+
+ e->sym = sym;
+ str_append(&e->name, "=");
+ e->nb_val = str_new();
+
+ switch (sym->type) {
+ case S_INT:
+ str_append(&e->name, int_values[i]);
+ str_append(&e->nb_val, int_values[i]);
+ break;
+ case S_HEX:
+ str_append(&e->name, hex_values[i]);
+ str_append(&e->nb_val, hex_values[i]);
+ break;
+ case S_STRING:
+ str_append(&e->name, string_values[i]);
+ str_append(&e->nb_val, string_values[i]);
+ break;
+ default:
+ break;
+ }
+
+ fexpr_list_add(sym->nb_vals, e);
+ fexpr_add_to_satmap(e, data);
+ }
+}
+
+/*
+ * set fexpr_y and fexpr_m simply to False
+ */
+static void create_fexpr_unknown(struct symbol *sym, struct cfdata *data)
+{
+ sym->fexpr_y = data->constants->const_false;
+ sym->fexpr_m = data->constants->const_false;
+}
+
+/*
+ * create the fexpr for a choice symbol
+ */
+static void create_fexpr_choice(struct symbol *sym, struct cfdata *data)
+{
+ struct property *prompt;
+ char *name, *write, *read;
+ struct fexpr *fexpr_y;
+ struct fexpr *fexpr_m;
+
+ if (!sym_is_boolean(sym))
+ return;
+
+ prompt = sym_get_prompt(sym);
+ if (prompt == NULL) {
+ perror("Choice symbol should have a prompt.");
+ return;
+ }
+
+ name = strdup(prompt->text);
+
+ /* remove spaces */
+ write = name;
+ read = name;
+ do {
+ if (*read != ' ')
+ *write++ = *read;
+ } while (*read++);
+
+ fexpr_y = fexpr_create(data->sat_variable_nr++, FE_CHOICE, "Choice_");
+ str_append(&fexpr_y->name, name);
+ fexpr_y->sym = sym;
+ fexpr_y->tri = yes;
+ fexpr_add_to_satmap(fexpr_y, data);
+
+ sym->fexpr_y = fexpr_y;
+
+ if (sym->type == S_TRISTATE) {
+ fexpr_m = fexpr_create(data->sat_variable_nr++, FE_CHOICE, "Choice_");
+ str_append(&fexpr_m->name, name);
+ str_append(&fexpr_m->name, "_MODULE");
+ fexpr_m->sym = sym;
+ fexpr_m->tri = mod;
+ fexpr_add_to_satmap(fexpr_m, data);
+ } else {
+ fexpr_m = data->constants->const_false;
+ }
+ sym->fexpr_m = fexpr_m;
+ free(name);
+}
+
+/*
+ * evaluate an unequality between a non-Boolean symbol and a constant
+ */
+static struct pexpr *expr_eval_unequal_nonbool_const(struct symbol *sym, struct symbol *compval,
+ enum expr_type type, struct cfdata *data)
+{
+ int base;
+ struct pexpr *c;
+ long val;
+ struct fexpr_node *node;
+ struct fexpr *fe;
+
+ if (!sym || !compval)
+ return pexf(data->constants->const_false);
+
+ base = 0;
+ switch (sym->type) {
+ case S_INT:
+ base = 10;
+ break;
+ case S_HEX:
+ base = 16;
+ break;
+ default:
+ break;
+ }
+
+ c = pexf(data->constants->const_false);
+ val = strtol(compval->name, NULL, base);
+ for (node = sym->nb_vals->head->next; node != NULL; node = node->next) {
+ long symval;
+
+ fe = node->elem;
+ symval = strtol(str_get(&fe->nb_val), NULL, base);
+
+ switch (type) {
+ case E_LTH:
+ if (symval < val)
+ c = pexpr_or(c, pexf(fe), data, PEXPR_ARGX);
+ break;
+ case E_LEQ:
+ if (symval <= val)
+ c = pexpr_or(c, pexf(fe), data, PEXPR_ARGX);
+ break;
+ case E_GTH:
+ if (symval > val)
+ c = pexpr_or(c, pexf(fe), data, PEXPR_ARGX);
+ break;
+ case E_GEQ:
+ if (symval >= val)
+ c = pexpr_or(c, pexf(fe), data, PEXPR_ARGX);
+ break;
+ default:
+ perror("Illegal unequal.");
+ }
+ }
+
+ return c;
+}
+
+/*
+ * evaluate an unequality between 2 Boolean symbols
+ */
+static struct pexpr *expr_eval_unequal_bool(struct symbol *left, struct symbol *right,
+ enum expr_type type, struct cfdata *data)
+{
+ struct pexpr *c;
+
+ if (!left || !right)
+ return pexf(data->constants->const_false);
+
+ if (!sym_is_boolean(left) || !sym_is_boolean(right)) {
+ perror("Comparing 2 symbols that should be boolean.");
+ return pexf(data->constants->const_false);
+ }
+
+ switch (type) {
+ case E_LTH:
+ c = pexpr_and(pexpr_not(sym_get_fexpr_both(left, data), data),
+ sym_get_fexpr_both(right, data), data,
+ PEXPR_ARGX);
+ if (left->type == S_TRISTATE)
+ c = pexpr_or(c,
+ pexpr_and(pexf(left->fexpr_m),
+ pexf(right->fexpr_y), data,
+ PEXPR_ARGX),
+ data, PEXPR_ARGX);
+ break;
+ case E_LEQ:
+ c = pexpr_and(pexf(left->fexpr_y), pexf(right->fexpr_y), data,
+ PEXPR_ARGX);
+ if (left->type == S_TRISTATE)
+ c = pexpr_or(c,
+ pexpr_and(pexf(left->fexpr_m),
+ sym_get_fexpr_both(right, data),
+ data, PEXPR_ARGX),
+ data, PEXPR_ARGX);
+ c = pexpr_or(c, pexpr_not(sym_get_fexpr_both(left, data), data),
+ data, PEXPR_ARGX);
+ break;
+ case E_GTH:
+ c = pexpr_and(sym_get_fexpr_both(left, data),
+ pexpr_not(sym_get_fexpr_both(right, data), data),
+ data, PEXPR_ARGX);
+ if (right->type == S_TRISTATE)
+ c = pexpr_or(c,
+ pexpr_and(pexf(left->fexpr_y),
+ pexf(right->fexpr_m), data,
+ PEXPR_ARGX),
+ data, PEXPR_ARGX);
+ break;
+ case E_GEQ:
+ c = pexpr_and(pexf(left->fexpr_y), pexf(right->fexpr_y), data,
+ PEXPR_ARGX);
+ if (right->type == S_TRISTATE)
+ c = pexpr_or(c,
+ pexpr_and(sym_get_fexpr_both(left, data),
+ pexf(right->fexpr_m), data,
+ PEXPR_ARGX),
+ data, PEXPR_ARGX);
+ c = pexpr_or(c,
+ pexpr_not(sym_get_fexpr_both(right, data), data),
+ data, PEXPR_ARGX);
+ break;
+ default:
+ fprintf(stderr, "Wrong type - %s", __func__);
+ c = pexf(data->constants->const_false);
+ }
+
+ return c;
+}
+/*
+ * calculate, when expr will evaluate to yes or mod
+ */
+struct pexpr *expr_calculate_pexpr_both(struct expr *e, struct cfdata *data)
+{
+ if (!e)
+ return pexf(data->constants->const_false);
+
+ if (!expr_can_evaluate_to_mod(e))
+ return expr_calculate_pexpr_y(e, data);
+
+ switch (e->type) {
+ case E_SYMBOL:
+ return pexpr_or(expr_calculate_pexpr_m(e, data), expr_calculate_pexpr_y(e, data),
+ data, PEXPR_ARGX);
+ case E_AND:
+ return expr_calculate_pexpr_both_and(e->left.expr, e->right.expr, data);
+ case E_OR:
+ return expr_calculate_pexpr_both_or(e->left.expr, e->right.expr, data);
+ case E_NOT:
+ return pexpr_or(expr_calculate_pexpr_m(e, data), expr_calculate_pexpr_y(e, data),
+ data, PEXPR_ARGX);
+ case E_EQUAL:
+ return expr_calculate_pexpr_y_equals(e, data);
+ case E_UNEQUAL:
+ return expr_calculate_pexpr_y_unequals(e, data);
+ case E_LTH:
+ case E_LEQ:
+ case E_GTH:
+ case E_GEQ:
+ return expr_calculate_pexpr_y_comp(e, data);
+ default:
+ // TODO
+ fprintf(stderr, "Unhandled type - %s", __func__);
+ return NULL;
+ }
+}
+
+/*
+ * calculate, when expr will evaluate to yes
+ */
+struct pexpr *expr_calculate_pexpr_y(struct expr *e, struct cfdata *data)
+{
+ if (!e)
+ return NULL;
+
+ switch (e->type) {
+ case E_SYMBOL:
+ return pexf(e->left.sym->fexpr_y);
+ case E_AND:
+ return expr_calculate_pexpr_y_and(e->left.expr, e->right.expr, data);
+ case E_OR:
+ return expr_calculate_pexpr_y_or(e->left.expr, e->right.expr, data);
+ case E_NOT:
+ return expr_calculate_pexpr_y_not(e->left.expr, data);
+ case E_EQUAL:
+ return expr_calculate_pexpr_y_equals(e, data);
+ case E_UNEQUAL:
+ return expr_calculate_pexpr_y_unequals(e, data);
+ case E_LTH:
+ case E_LEQ:
+ case E_GTH:
+ case E_GEQ:
+ return expr_calculate_pexpr_y_comp(e, data);
+ default:
+ fprintf(stderr, "Unhandled type - %s", __func__);
+ return NULL;
+ }
+}
+
+/*
+ * calculate, when expr will evaluate to mod
+ */
+struct pexpr *expr_calculate_pexpr_m(struct expr *e, struct cfdata *data)
+{
+ if (!e)
+ return NULL;
+
+ if (!expr_can_evaluate_to_mod(e))
+ return pexf(data->constants->const_false);
+
+ switch (e->type) {
+ case E_SYMBOL:
+ return pexf(e->left.sym->fexpr_m);
+ case E_AND:
+ return expr_calculate_pexpr_m_and(e->left.expr, e->right.expr, data);
+ case E_OR:
+ return expr_calculate_pexpr_m_or(e->left.expr, e->right.expr, data);
+ case E_NOT:
+ return expr_calculate_pexpr_m_not(e->left.expr, data);
+ default:
+ perror("Trying to evaluate to mod.");
+ return NULL;
+ }
+}
+
+/*
+ * calculate, when expr of type AND will evaluate to yes
+ * A && B
+ */
+struct pexpr *expr_calculate_pexpr_y_and(struct expr *a, struct expr *b, struct cfdata *data)
+{
+ return pexpr_and(expr_calculate_pexpr_y(a, data),
+ expr_calculate_pexpr_y(b, data), data,
+ PEXPR_ARGX);
+}
+
+/*
+ * calculate, when expr of type AND will evaluate to mod
+ * (A || A_m) && (B || B_m) && !(A && B)
+ */
+struct pexpr *expr_calculate_pexpr_m_and(struct expr *a, struct expr *b,
+ struct cfdata *data)
+{
+ struct pexpr *topright =
+ pexpr_not(pexpr_and(expr_calculate_pexpr_y(a, data),
+ expr_calculate_pexpr_y(b, data),
+ data, PEXPR_ARGX),
+ data);
+ struct pexpr *ll_left = pexpr_or(expr_calculate_pexpr_y(a, data),
+ expr_calculate_pexpr_m(a, data), data,
+ PEXPR_ARGX);
+ struct pexpr *ll_right = pexpr_or(expr_calculate_pexpr_y(b, data),
+ expr_calculate_pexpr_m(b, data), data,
+ PEXPR_ARGX);
+ struct pexpr *topleft = pexpr_and(ll_left, ll_right, data, PEXPR_ARGX);
+
+ return pexpr_and(topleft, topright, data, PEXPR_ARGX);
+}
+
+/*
+ * calculate, when expr of type AND will evaluate to mod or yes
+ * (A || A_m) && (B || B_m)
+ */
+struct pexpr *expr_calculate_pexpr_both_and(struct expr *a, struct expr *b,
+ struct cfdata *data)
+{
+ struct pexpr *left = pexpr_or(expr_calculate_pexpr_y(a, data),
+ expr_calculate_pexpr_m(a, data), data,
+ PEXPR_ARGX);
+ struct pexpr *right = pexpr_or(expr_calculate_pexpr_y(b, data),
+ expr_calculate_pexpr_m(b, data), data,
+ PEXPR_ARGX);
+
+ return pexpr_and(left, right, data, PEXPR_ARGX);
+}
+
+/*
+ * calculate, when expr of type OR will evaluate to yes
+ * A || B
+ */
+struct pexpr *expr_calculate_pexpr_y_or(struct expr *a, struct expr *b,
+ struct cfdata *data)
+{
+ return pexpr_or(expr_calculate_pexpr_y(a, data),
+ expr_calculate_pexpr_y(b, data), data, PEXPR_ARGX);
+}
+
+/*
+ * calculate, when expr of type OR will evaluate to mod
+ * (A_m || B_m) && !A && !B
+ */
+struct pexpr *expr_calculate_pexpr_m_or(struct expr *a, struct expr *b,
+ struct cfdata *data)
+{
+ struct pexpr *topright =
+ pexpr_not(expr_calculate_pexpr_y(b, data), data);
+ struct pexpr *lowerleft = pexpr_or(expr_calculate_pexpr_m(a, data),
+ expr_calculate_pexpr_m(b, data),
+ data, PEXPR_ARGX);
+ struct pexpr *topleft = pexpr_and(
+ lowerleft,
+ pexpr_not(expr_calculate_pexpr_y(a, data), data), data,
+ PEXPR_ARGX);
+
+ return pexpr_and(topleft, topright, data, PEXPR_ARGX);
+}
+
+/*
+ * calculate, when expr of type OR will evaluate to mod or yes
+ * (A_m || A || B_m || B)
+ */
+struct pexpr *expr_calculate_pexpr_both_or(struct expr *a, struct expr *b,
+ struct cfdata *data)
+{
+ struct pexpr *left = pexpr_or(expr_calculate_pexpr_y(a, data),
+ expr_calculate_pexpr_m(a, data), data,
+ PEXPR_ARGX);
+ struct pexpr *right = pexpr_or(expr_calculate_pexpr_y(b, data),
+ expr_calculate_pexpr_m(b, data), data,
+ PEXPR_ARGX);
+
+ return pexpr_or(left, right, data, PEXPR_ARGX);
+}
+
+/*
+ * calculate, when expr of type NOT will evaluate to yes
+ * !(A || A_m)
+ */
+struct pexpr *expr_calculate_pexpr_y_not(struct expr *e, struct cfdata *data)
+{
+ return pexpr_not(pexpr_or(expr_calculate_pexpr_y(e, data),
+ expr_calculate_pexpr_m(e, data),
+ data, PEXPR_ARGX),
+ data);
+}
+
+/*
+ * calculate, when expr of type NOT will evaluate to mod
+ * A_m
+ */
+struct pexpr *expr_calculate_pexpr_m_not(struct expr *e, struct cfdata *data)
+{
+ return expr_calculate_pexpr_m(e, data);
+}
+
+static struct pexpr *equiv_pexpr_share(struct pexpr *a, struct pexpr *b,
+ struct cfdata *data)
+{
+ struct pexpr *yes = pexpr_and_share(a, b, data);
+ struct pexpr *not = pexpr_and(pexpr_not_share(a, data),
+ pexpr_not_share(b, data), data,
+ PEXPR_ARGX);
+
+ return pexpr_or(yes, not, data, PEXPR_ARGX);
+}
+
+static struct pexpr *equiv_pexpr_move(struct pexpr *a, struct pexpr *b,
+ struct cfdata *data,
+ enum pexpr_move move)
+{
+ return pexpr_move_wrapper(a, b, data, move, equiv_pexpr_share);
+}
+
+/*
+ * create the fexpr of a non-boolean symbol for a specific value
+ */
+struct fexpr *sym_create_nonbool_fexpr(struct symbol *sym, char *value,
+ struct cfdata *data)
+{
+ struct fexpr *e;
+ char *s;
+
+ if (!strcmp(value, "")) {
+ if (sym->type == S_STRING)
+ return sym->nb_vals->head->next->elem;
+ else
+ return sym->nb_vals->head->elem;
+ }
+
+ e = sym_get_nonbool_fexpr(sym, value);
+
+ /* fexpr already exists */
+ if (e != NULL)
+ return e;
+
+ s = value;
+ if (sym->type == S_INT && !string_is_number(value)) {
+ struct symbol *tmp = sym_find(value);
+
+ if (tmp != NULL)
+ s = (char *) tmp->curr.val;
+ } else if (sym->type == S_HEX && !string_is_hex(value)) {
+ struct symbol *tmp = sym_find(value);
+
+ if (tmp != NULL)
+ s = (char *) tmp->curr.val;
+ } else if (sym->type == S_STRING) {
+ struct symbol *tmp = sym_find(value);
+
+ if (tmp != NULL)
+ s = (char *) tmp->curr.val;
+ }
+
+ if (!strcmp(s, "")) {
+ if (sym->type == S_STRING)
+ return sym->nb_vals->head->next->elem;
+ else
+ return sym->nb_vals->head->elem;
+ }
+
+ e = sym_get_nonbool_fexpr(sym, s);
+ if (e != NULL)
+ return e;
+
+ e = fexpr_create(data->sat_variable_nr++, FE_NONBOOL, sym->name);
+ e->sym = sym;
+ str_append(&e->name, "=");
+ str_append(&e->name, s);
+ e->nb_val = str_new();
+ str_append(&e->nb_val, s);
+
+ fexpr_list_add(sym->nb_vals, e);
+ fexpr_add_to_satmap(e, data);
+
+ return e;
+}
+
+/*
+ * return the fexpr of a non-boolean symbol for a specific value, NULL if
+ * non-existent
+ */
+struct fexpr *sym_get_nonbool_fexpr(struct symbol *sym, char *value)
+{
+ struct fexpr_node *e;
+
+ fexpr_list_for_each(e, sym->nb_vals) {
+ if (strcmp(str_get(&e->elem->nb_val), value) == 0)
+ return e->elem;
+ }
+
+ return NULL;
+}
+
+/*
+ * return the fexpr of a non-boolean symbol for a specific value, if it exists
+ * otherwise create it
+ */
+struct fexpr *sym_get_or_create_nonbool_fexpr(struct symbol *sym, char *value,
+ struct cfdata *data)
+{
+ struct fexpr *e = sym_get_nonbool_fexpr(sym, value);
+
+ if (e != NULL)
+ return e;
+ else
+ return sym_create_nonbool_fexpr(sym, value, data);
+}
+
+/*
+ * calculate, when expr of type EQUAL will evaluate to yes
+ * Side effect: May create certain values in e->{left,right}.sym.nb_vals
+ */
+struct pexpr *expr_calculate_pexpr_y_equals(struct expr *e, struct cfdata *data)
+{
+ /* comparing 2 tristate constants */
+ if (sym_is_tristate_constant(e->left.sym) &&
+ sym_is_tristate_constant(e->right.sym))
+ return e->left.sym == e->right.sym ?
+ pexf(data->constants->const_true) :
+ pexf(data->constants->const_false);
+
+ /* comparing 2 nonboolean constants */
+ if (sym_is_nonbool_constant(e->left.sym) &&
+ sym_is_nonbool_constant(e->right.sym))
+ return strcmp(e->left.sym->name, e->right.sym->name) == 0 ?
+ pexf(data->constants->const_true) :
+ pexf(data->constants->const_false);
+
+ /* comparing 2 boolean/tristate incl. yes/mod/no constants */
+ if (sym_is_bool_or_triconst(e->left.sym) &&
+ sym_is_bool_or_triconst(e->right.sym)) {
+ struct pexpr *yes = equiv_pexpr_move(
+ pexf(e->left.sym->fexpr_y), pexf(e->right.sym->fexpr_y),
+ data, PEXPR_ARGX);
+ struct pexpr *mod = equiv_pexpr_move(
+ pexf(e->left.sym->fexpr_m), pexf(e->right.sym->fexpr_m),
+ data, PEXPR_ARGX);
+
+ return pexpr_and(yes, mod, data, PEXPR_ARGX);
+ }
+
+ /* comparing nonboolean with a constant */
+ if (sym_is_nonboolean(e->left.sym) &&
+ sym_is_nonbool_constant(e->right.sym))
+ return pexf(sym_get_or_create_nonbool_fexpr(
+ e->left.sym, e->right.sym->name, data));
+
+ if (sym_is_nonbool_constant(e->left.sym) &&
+ sym_is_nonboolean(e->right.sym))
+ return pexf(sym_get_or_create_nonbool_fexpr(
+ e->right.sym, e->left.sym->name, data));
+
+ /* comparing nonboolean with tristate constant, will never be true */
+ if (sym_is_nonboolean(e->left.sym) &&
+ sym_is_tristate_constant(e->right.sym))
+ return pexf(data->constants->const_false);
+ if (sym_is_tristate_constant(e->left.sym) &&
+ sym_is_nonboolean(e->right.sym))
+ return pexf(data->constants->const_false);
+
+ /* comparing 2 nonboolean symbols */
+ if (sym_is_nonboolean(e->left.sym) && sym_is_nonboolean(e->right.sym)) {
+ struct pexpr *c = pexf(data->constants->const_false);
+ struct fexpr *e1, *e2;
+
+ for (struct fexpr_node *node1 =
+ e->left.sym->nb_vals->head->next;
+ node1 != NULL; node1 = node1->next) {
+ e1 = node1->elem;
+ for (struct fexpr_node *node2 =
+ e->right.sym->nb_vals->head->next;
+ node2 != NULL; node2 = node2->next) {
+ e2 = node2->elem;
+ if (!strcmp(str_get(&e1->nb_val),
+ str_get(&e2->nb_val))) {
+ c = pexpr_or(
+ c,
+ pexpr_and(pexf(e1),
+ pexf(e2), data,
+ PEXPR_ARGX),
+ data, PEXPR_ARGX);
+ break;
+ }
+ }
+ }
+ return c;
+ }
+
+ /* comparing boolean item with nonboolean constant, will never be true */
+ if (sym_is_tristate_constant(e->left.sym) &&
+ sym_is_nonbool_constant(e->right.sym))
+ return pexf(data->constants->const_false);
+ if (sym_is_nonbool_constant(e->left.sym) &&
+ sym_is_tristate_constant(e->right.sym))
+ return pexf(data->constants->const_false);
+
+ /* comparing symbol of type unknown with tristate constant */
+ if (e->left.sym->type == S_UNKNOWN &&
+ sym_is_tristate_constant(e->right.sym))
+ return pexf(data->constants->const_false);
+ if (sym_is_tristate_constant(e->left.sym) &&
+ e->right.sym->type == S_UNKNOWN)
+ return pexf(data->constants->const_false);
+
+ /* any other comparison is not supported and should not be executed */
+ fprintf(stderr, "Unsupported equality.");
+ print_expr(":", e, 0);
+
+ return pexf(data->constants->const_false);
+}
+
+/*
+ * transform an UNEQUAL into a Not(EQUAL)
+ */
+struct pexpr *expr_calculate_pexpr_y_unequals(struct expr *e, struct cfdata *data)
+{
+ return pexpr_not(expr_calculate_pexpr_y_equals(e, data), data);
+}
+
+struct pexpr *expr_calculate_pexpr_y_comp(struct expr *e, struct cfdata *data)
+{
+ if (!e)
+ return NULL;
+
+ switch (e->type) {
+ case E_LTH:
+ case E_LEQ:
+ case E_GTH:
+ case E_GEQ:
+ /* compare non-Boolean symbol with constant */
+ if (sym_is_nonboolean(e->left.sym) &&
+ e->right.sym->type == S_UNKNOWN &&
+ string_is_number(e->right.sym->name)
+ ) {
+ return expr_eval_unequal_nonbool_const(e->left.sym, e->right.sym, e->type,
+ data);
+ }
+ if (sym_is_nonboolean(e->right.sym) &&
+ e->left.sym->type == S_UNKNOWN &&
+ string_is_number(e->left.sym->name)
+ ) {
+ return expr_eval_unequal_nonbool_const(e->right.sym, e->left.sym, e->type,
+ data);
+ }
+
+ /* compare 2 Boolean symbols */
+ if (sym_is_boolean(e->left.sym) && sym_is_boolean(e->right.sym))
+ return expr_eval_unequal_bool(e->left.sym, e->right.sym, e->type, data);
+
+ return pexf(data->constants->const_false);
+ default:
+ fprintf(stderr, "Unhandled type - %s", __func__);
+ return NULL;
+ }
+}
+
+static struct pexpr *pexpr_move_wrapper(
+ struct pexpr *a, struct pexpr *b, struct cfdata *data,
+ enum pexpr_move move,
+ struct pexpr *(*func)(struct pexpr *, struct pexpr *, struct cfdata *))
+{
+ struct pexpr *retval = func(a, b, data);
+
+ switch (move) {
+ case PEXPR_ARG1:
+ pexpr_put(a);
+ break;
+ case PEXPR_ARG2:
+ pexpr_put(b);
+ break;
+ case PEXPR_ARGX:
+ pexpr_put(a);
+ pexpr_put(b);
+ break;
+ default:
+ fprintf(stderr, "%s: invalid value for @move - %d\n", __func__,
+ move);
+ }
+ return retval;
+}
+
+struct pexpr *pexpr_and(struct pexpr *a, struct pexpr *b, struct cfdata *data, enum pexpr_move move)
+{
+ return pexpr_move_wrapper(a, b, data, move, pexpr_and_share);
+}
+
+/*
+ * macro to create a pexpr of type AND
+ */
+struct pexpr *pexpr_and_share(struct pexpr *a, struct pexpr *b, struct cfdata *data)
+{
+ struct pexpr *e;
+
+ /* A && A -> A */
+ if (a == b || pexpr_eq(a, b, data)) {
+ pexpr_get(a);
+ return a;
+ }
+
+ /* simplifications:
+ * expr && False -> False
+ * expr && True -> expr
+ */
+ if ((a->type == PE_SYMBOL &&
+ a->left.fexpr == data->constants->const_false) ||
+ (b->type == PE_SYMBOL &&
+ b->left.fexpr == data->constants->const_true)) {
+ pexpr_get(a);
+ return a;
+ }
+
+ if ((b->type == PE_SYMBOL &&
+ b->left.fexpr == data->constants->const_false) ||
+ (a->type == PE_SYMBOL &&
+ a->left.fexpr == data->constants->const_true)) {
+ pexpr_get(b);
+ return b;
+ }
+
+ /* (A && B) && C -> A && B if B == C */
+ if (a->type == PE_AND && pexpr_eq(a->right.pexpr, b, data)) {
+ pexpr_get(a);
+ return a;
+ }
+
+ /* A && (B && C) -> B && C if A == B */
+ if (b->type == PE_AND && pexpr_eq(a, b->left.pexpr, data)) {
+ pexpr_get(b);
+ return b;
+ }
+
+ if (a->type == PE_OR && b->type == PE_OR) {
+ e = NULL;
+ /* (A || B) && (C || D) -> A || (B && D) if A == C */
+ if (pexpr_eq(a->left.pexpr, b->left.pexpr, data)) {
+ e = pexpr_or(a->left.pexpr,
+ pexpr_and_share(a->right.pexpr,
+ b->right.pexpr, data),
+ data, PEXPR_ARG2);
+ }
+ /* (A || B) && (C || D) -> B || (A && C) if B == D */
+ else if (pexpr_eq(a->right.pexpr, b->right.pexpr, data)) {
+ e = pexpr_or(a->right.pexpr,
+ pexpr_and_share(a->left.pexpr,
+ b->left.pexpr, data),
+ data, PEXPR_ARG2);
+ }
+ /* (A || B) && (C || D) -> A || (B && C) if A == D */
+ else if (pexpr_eq(a->left.pexpr, b->right.pexpr, data)) {
+ e = pexpr_or(a->left.pexpr,
+ pexpr_and_share(a->right.pexpr,
+ b->left.pexpr, data),
+ data, PEXPR_ARG2);
+ }
+ /* (A || B) && (C || D) -> B || (A && D) if B == C */
+ else if (pexpr_eq(a->right.pexpr, b->left.pexpr, data)) {
+ e = pexpr_or(a->right.pexpr,
+ pexpr_and_share(a->left.pexpr,
+ b->right.pexpr, data),
+ data, PEXPR_ARG2);
+ }
+ if (e)
+ return e;
+ }
+
+ /* general case */
+ e = xcalloc(1, sizeof(*e));
+ pexpr_get(a);
+ pexpr_get(b);
+ pexpr_construct_and(e, a, b, 1);
+ return e;
+}
+
+struct pexpr *pexpr_or(struct pexpr *a, struct pexpr *b, struct cfdata *data, enum pexpr_move move)
+{
+ return pexpr_move_wrapper(a, b, data, move, pexpr_or_share);
+}
+
+/*
+ * macro to create a pexpr of type OR
+ */
+struct pexpr *pexpr_or_share(struct pexpr *a, struct pexpr *b, struct cfdata *data)
+{
+ struct pexpr *e;
+ bool cond1, cond2;
+
+ /* A || A -> A */
+ if (a == b || pexpr_eq(a, b, data)) {
+ pexpr_get(a);
+ return a;
+ }
+
+ /* simplifications:
+ * A || False -> A
+ * A || True -> True
+ */
+ cond1 = a->type == PE_SYMBOL && a->left.fexpr == data->constants->const_false;
+ cond2 = b->type == PE_SYMBOL && b->left.fexpr == data->constants->const_true;
+ if (cond1 || cond2) {
+ pexpr_get(b);
+ return b;
+ }
+ cond1 = b->type == PE_SYMBOL && b->left.fexpr == data->constants->const_false;
+ cond2 = a->type == PE_SYMBOL && a->left.fexpr == data->constants->const_true;
+ if (cond1 || cond2) {
+ pexpr_get(a);
+ return a;
+ }
+
+ /* A || (B && C) -> A if (A == B || A == C) */
+ if (b->type == PE_AND && (
+ pexpr_eq(a, b->left.pexpr, data) || pexpr_eq(a, b->right.pexpr, data)
+ )) {
+ pexpr_get(a);
+ return a;
+ }
+ /* (A && B) || C -> C if (A == C || B == C) */
+ if (a->type == PE_AND && (
+ pexpr_eq(a->left.pexpr, b, data) || pexpr_eq(a->right.pexpr, b, data)
+ )) {
+ pexpr_get(b);
+ return b;
+ }
+
+ /* -A || B -> True if A == B
+ * A || -B -> True if A == B
+ */
+ cond1 = a->type == PE_NOT && pexpr_eq(a->left.pexpr, b, data);
+ cond2 = b->type == PE_NOT && pexpr_eq(a, b->left.pexpr, data);
+ if (cond1 || cond2)
+ return pexf(data->constants->const_true);
+
+ if (a->type == PE_AND && b->type == PE_AND) {
+ e = NULL;
+ /* (A && B) || (C && D) -> A && (B || D) if (A == C) */
+ if (pexpr_eq(a->left.pexpr, b->left.pexpr, data)) {
+ e = pexpr_and(a->left.pexpr,
+ pexpr_or_share(a->right.pexpr, b->right.pexpr, data), data,
+ PEXPR_ARG2);
+ }
+ /* (A && B) || (C && D) -> B && (A || C) if (B == D) */
+ if (pexpr_eq(a->right.pexpr, b->right.pexpr, data)) {
+ e = pexpr_and(a->right.pexpr,
+ pexpr_or_share(a->left.pexpr, b->left.pexpr, data), data,
+ PEXPR_ARG2);
+ }
+ /* (A && B) || (C && D) -> A && (B || C) if (A == D) */
+ if (pexpr_eq(a->left.pexpr, b->right.pexpr, data)) {
+ e = pexpr_and(a->left.pexpr,
+ pexpr_or_share(a->right.pexpr, b->left.pexpr, data), data,
+ PEXPR_ARG2);
+ }
+ /* (A && B) || (C && D) -> B && (A || D) if (B == C) */
+ if (pexpr_eq(a->right.pexpr, b->left.pexpr, data)) {
+ e = pexpr_and(a->right.pexpr,
+ pexpr_or_share(a->left.pexpr, b->right.pexpr, data), data,
+ PEXPR_ARG2);
+ }
+ if (e)
+ return e;
+ }
+
+ /* (A && B) || (C || D) -> C || D if
+ * A == C || A == D || B == C || B == D
+ */
+ if (a->type == PE_AND && b->type == PE_OR && (
+ pexpr_eq(a->left.pexpr, b->left.pexpr, data) ||
+ pexpr_eq(a->left.pexpr, b->right.pexpr, data) ||
+ pexpr_eq(a->right.pexpr, b->left.pexpr, data) ||
+ pexpr_eq(a->right.pexpr, b->right.pexpr, data)
+ )) {
+ pexpr_get(b);
+ return b;
+ }
+ /* (C || D) || (A && B) -> C || D if
+ * A == C || A == D || B == C || B == D
+ */
+ if (a->type == PE_OR && b->type == PE_AND && (
+ pexpr_eq(a->left.pexpr, b->left.pexpr, data) ||
+ pexpr_eq(a->left.pexpr, b->right.pexpr, data) ||
+ pexpr_eq(a->right.pexpr, b->left.pexpr, data) ||
+ pexpr_eq(a->right.pexpr, b->right.pexpr, data)
+ )) {
+ pexpr_get(a);
+ return a;
+ }
+
+ /* general case */
+ e = xcalloc(1, sizeof(*e));
+ pexpr_get(a);
+ pexpr_get(b);
+ pexpr_construct_or(e, a, b, 1);
+
+ return e;
+}
+
+struct pexpr *pexpr_not(struct pexpr *a, struct cfdata *data)
+{
+ struct pexpr *retval = pexpr_not_share(a, data);
+
+ pexpr_put(a);
+ return retval;
+}
+
+/*
+ * Builds NOT(@a)
+ */
+struct pexpr *pexpr_not_share(struct pexpr *a, struct cfdata *data)
+{
+ struct pexpr *ret_val;
+
+ if (a->type == PE_SYMBOL &&
+ a->left.fexpr == data->constants->const_false)
+ ret_val = pexf(data->constants->const_true);
+ else if (a->type == PE_SYMBOL &&
+ a->left.fexpr == data->constants->const_true)
+ ret_val = pexf(data->constants->const_false);
+ /* eliminate double negation */
+ else if (a->type == PE_NOT) {
+ ret_val = a->left.pexpr;
+ pexpr_get(ret_val);
+ }
+ /* De Morgan */
+ else if (a->type == PE_AND) {
+ ret_val = xmalloc(sizeof(*ret_val));
+ pexpr_construct_or(ret_val,
+ pexpr_not_share(a->left.pexpr, data),
+ pexpr_not_share(a->right.pexpr, data), 1);
+ } else if (a->type == PE_OR) {
+ ret_val = xmalloc(sizeof(*ret_val));
+ pexpr_construct_and(ret_val,
+ pexpr_not_share(a->left.pexpr, data),
+ pexpr_not_share(a->right.pexpr, data), 1);
+ } else {
+ ret_val = xmalloc(sizeof(*ret_val));
+ pexpr_get(a);
+ pexpr_construct_not(ret_val, a, 1);
+ }
+
+ return ret_val;
+}
+
+struct pexpr *pexpr_implies(struct pexpr *a, struct pexpr *b, struct cfdata *data,
+ enum pexpr_move move)
+{
+ return pexpr_move_wrapper(a, b, data, move, pexpr_implies_share);
+}
+
+/*
+ * macro to construct a pexpr for "A implies B"
+ */
+struct pexpr *pexpr_implies_share(struct pexpr *a, struct pexpr *b, struct cfdata *data)
+{
+ /* A => B -> True if A == B */
+ if (a == b || pexpr_eq(a, b, data))
+ return pexf(data->constants->const_true);
+
+ /* (A => B && C) -> (A => C) if A == B */
+ if (b->type == PE_AND && pexpr_eq(a, b->left.pexpr, data))
+ return pexpr_implies_share(a, b->right.pexpr, data);
+ /* (A => B && C) -> (A => B) if A == C */
+ if (b->type == PE_AND && pexpr_eq(a, b->right.pexpr, data))
+ return pexpr_implies_share(a, b->left.pexpr, data);
+
+ /* (A => B || C) -> True if (A == B || A == C) */
+ if (b->type == PE_OR && (
+ pexpr_eq(a, b->left.pexpr, data) || pexpr_eq(a, b->right.pexpr, data)
+ ))
+ return pexf(data->constants->const_true);
+
+ /* (A && B => C) -> True if (A == C || B == C) */
+ if (a->type == PE_AND && (
+ pexpr_eq(a->left.pexpr, b, data) || pexpr_eq(a->right.pexpr, b, data)
+ ))
+ return pexf(data->constants->const_true);
+
+ return pexpr_or(pexpr_not_share(a, data), b, data, PEXPR_ARG1);
+}
+
+/*
+ * check whether a pexpr is in CNF
+ */
+bool pexpr_is_cnf(struct pexpr *e)
+{
+ if (!e)
+ return false;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ return true;
+ case PE_AND:
+ return false;
+ case PE_OR:
+ return pexpr_is_cnf(e->left.pexpr) &&
+ pexpr_is_cnf(e->right.pexpr);
+ case PE_NOT:
+ return e->left.pexpr->type == PE_SYMBOL;
+ }
+
+ return false;
+}
+
+/*
+ * check whether a pexpr is in NNF
+ */
+bool pexpr_is_nnf(struct pexpr *e)
+{
+ if (!e)
+ return false;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ return true;
+ case PE_AND:
+ case PE_OR:
+ return pexpr_is_nnf(e->left.pexpr) && pexpr_is_nnf(e->right.pexpr);
+ case PE_NOT:
+ return e->left.pexpr->type == PE_SYMBOL;
+ }
+
+ return false;
+}
+
+/*
+ * return fexpr_both for a symbol
+ */
+struct pexpr *sym_get_fexpr_both(struct symbol *sym, struct cfdata *data)
+{
+ return sym->type == S_TRISTATE ?
+ pexpr_or(pexf(sym->fexpr_m), pexf(sym->fexpr_y),
+ data, PEXPR_ARGX) :
+ pexf(sym->fexpr_y);
+}
+
+/*
+ * return fexpr_sel_both for a symbol
+ */
+struct pexpr *sym_get_fexpr_sel_both(struct symbol *sym, struct cfdata *data)
+{
+ if (!sym->rev_dep.expr)
+ return pexf(data->constants->const_false);
+
+ return sym->type == S_TRISTATE ?
+ pexpr_or(pexf(sym->fexpr_sel_m),
+ pexf(sym->fexpr_sel_y), data, PEXPR_ARGX) :
+ pexf(sym->fexpr_sel_y);
+}
+
+/*
+ * check, if the fexpr is a symbol, a True/False-constant, a literal symbolizing a non-boolean or
+ * a choice symbol
+ */
+bool fexpr_is_symbol(struct fexpr *e)
+{
+ return e->type == FE_SYMBOL || e->type == FE_FALSE || e->type == FE_TRUE ||
+ e->type == FE_NONBOOL || e->type == FE_CHOICE || e->type == FE_SELECT ||
+ e->type == FE_NPC;
+}
+
+/*
+ * check whether a pexpr is a symbol or a negated symbol
+ */
+bool pexpr_is_symbol(struct pexpr *e)
+{
+ return e->type == PE_SYMBOL || (e->type == PE_NOT && e->left.pexpr->type == PE_SYMBOL);
+}
+
+/*
+ * check whether the fexpr is a constant (true/false)
+ */
+bool fexpr_is_constant(struct fexpr *e, struct cfdata *data)
+{
+ return e == data->constants->const_true || e == data->constants->const_false;
+}
+
+/*
+ * add a fexpr to the satmap
+ */
+void fexpr_add_to_satmap(struct fexpr *e, struct cfdata *data)
+{
+ if (e->satval >= data->satmap_size) {
+ data->satmap =
+ xrealloc(data->satmap, data->satmap_size * 2 * sizeof(**data->satmap));
+ data->satmap_size *= 2;
+ }
+
+ data->satmap[e->satval] = e;
+}
+
+/*
+ * print a fexpr
+ */
+void fexpr_print(char *tag, struct fexpr *e)
+{
+ if (!e)
+ return;
+
+ printf("%s: %s\n", tag, str_get(&e->name));
+}
+
+/*
+ * write an fexpr into a string (format needed for testing)
+ */
+void fexpr_as_char(struct fexpr *e, struct gstr *s)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case FE_SYMBOL:
+ case FE_CHOICE:
+ case FE_SELECT:
+ case FE_NPC:
+ case FE_NONBOOL:
+ str_append(s, "definedEx(");
+ str_append(s, str_get(&e->name));
+ str_append(s, ")");
+ return;
+ case FE_FALSE:
+ str_append(s, "0");
+ return;
+ case FE_TRUE:
+ str_append(s, "1");
+ return;
+ default:
+ return;
+ }
+}
+
+/*
+ * write a pexpr into a string
+ */
+void pexpr_as_char(struct pexpr *e, struct gstr *s, int parent, struct cfdata *data)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ if (e->left.fexpr == data->constants->const_false) {
+ str_append(s, "0");
+ return;
+ }
+ if (e->left.fexpr == data->constants->const_true) {
+ str_append(s, "1");
+ return;
+ }
+ str_append(s, "definedEx(");
+ str_append(s, str_get(&e->left.fexpr->name));
+ str_append(s, ")");
+ return;
+ case PE_AND:
+ if (parent != PE_AND)
+ str_append(s, "(");
+ pexpr_as_char(e->left.pexpr, s, PE_AND, data);
+ str_append(s, " && ");
+ pexpr_as_char(e->right.pexpr, s, PE_AND, data);
+ if (parent != PE_AND)
+ str_append(s, ")");
+ return;
+ case PE_OR:
+ if (parent != PE_OR)
+ str_append(s, "(");
+ pexpr_as_char(e->left.pexpr, s, PE_OR, data);
+ str_append(s, " || ");
+ pexpr_as_char(e->right.pexpr, s, PE_OR, data);
+ if (parent != PE_OR)
+ str_append(s, ")");
+ return;
+ case PE_NOT:
+ str_append(s, "!");
+ pexpr_as_char(e->left.pexpr, s, PE_NOT, data);
+ return;
+ }
+}
+
+/*
+ * write a pexpr into a string
+ */
+void pexpr_as_char_short(struct pexpr *e, struct gstr *s, int parent)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ str_append(s, str_get(&e->left.fexpr->name));
+ return;
+ case PE_AND:
+ if (parent != PE_AND)
+ str_append(s, "(");
+ pexpr_as_char_short(e->left.pexpr, s, PE_AND);
+ str_append(s, " && ");
+ pexpr_as_char_short(e->right.pexpr, s, PE_AND);
+ if (parent != PE_AND)
+ str_append(s, ")");
+ return;
+ case PE_OR:
+ if (parent != PE_OR)
+ str_append(s, "(");
+ pexpr_as_char_short(e->left.pexpr, s, PE_OR);
+ str_append(s, " || ");
+ pexpr_as_char_short(e->right.pexpr, s, PE_OR);
+ if (parent != PE_OR)
+ str_append(s, ")");
+ return;
+ case PE_NOT:
+ str_append(s, "!");
+ pexpr_as_char_short(e->left.pexpr, s, PE_NOT);
+ return;
+ }
+}
+
+/*
+ * check whether a pexpr contains a specific fexpr
+ */
+bool pexpr_contains_fexpr(struct pexpr *e, struct fexpr *fe)
+{
+ if (!e)
+ return false;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ return e->left.fexpr->satval == fe->satval;
+ case PE_AND:
+ case PE_OR:
+ return pexpr_contains_fexpr(e->left.pexpr, fe) ||
+ pexpr_contains_fexpr(e->right.pexpr, fe);
+ case PE_NOT:
+ return e->left.pexpr->left.fexpr->satval == fe->satval;
+ }
+
+ return false;
+}
+
+/*
+ * init list of fexpr
+ */
+struct fexpr_list *fexpr_list_init(void)
+{
+ struct fexpr_list *list = xcalloc(1, sizeof(*list));
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->size = 0;
+
+ return list;
+}
+
+/*
+ * init list of fexpr_list
+ */
+struct fexl_list *fexl_list_init(void)
+{
+ struct fexl_list *list = xcalloc(1, sizeof(*list));
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->size = 0;
+
+ return list;
+}
+
+/*
+ * init list of pexpr
+ */
+struct pexpr_list *pexpr_list_init(void)
+{
+ struct pexpr_list *list = xcalloc(1, sizeof(*list));
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->size = 0;
+
+ return list;
+}
+
+/*
+ * init list of symbol_fix
+ */
+struct sfix_list *sfix_list_init(void)
+{
+ struct sfix_list *list = xcalloc(1, sizeof(*list));
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->size = 0;
+
+ return list;
+}
+
+/*
+ * init list of symbol_fix
+ */
+struct sfl_list *sfl_list_init(void)
+{
+ struct sfl_list *list = xcalloc(1, sizeof(*list));
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->size = 0;
+
+ return list;
+}
+
+/*
+ * init list of symbol_dvalue
+ */
+struct sdv_list *sdv_list_init(void)
+{
+ struct sdv_list *list = xcalloc(1, sizeof(*list));
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->size = 0;
+
+ return list;
+}
+
+/*
+ * init list of symbols
+ */
+struct sym_list *sym_list_init(void)
+{
+ struct sym_list *list = xcalloc(1, sizeof(*list));
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->size = 0;
+
+ return list;
+}
+
+/*
+ * init list of default_maps
+ */
+struct defm_list *defm_list_init(void)
+{
+ struct defm_list *list = xcalloc(1, sizeof(*list));
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->size = 0;
+
+ return list;
+}
+
+/*
+ * init list of properties
+ */
+struct prop_list *prop_list_init(void)
+{
+ struct prop_list *list = xcalloc(1, sizeof(*list));
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->size = 0;
+
+ return list;
+}
+
+/*
+ * add element to tail of a fexpr_list
+ */
+void fexpr_list_add(struct fexpr_list *list, struct fexpr *fe)
+{
+ struct fexpr_node *node = xcalloc(1, sizeof(*node));
+
+ node->elem = fe;
+
+ if (list->size == 0) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ node->prev = list->tail;
+ list->tail = node;
+ node->prev->next = node;
+ }
+
+ list->size++;
+}
+
+/*
+ * add element to tail of a fexl_list
+ */
+void fexl_list_add(struct fexl_list *list, struct fexpr_list *elem)
+{
+ struct fexl_node *node = xcalloc(1, sizeof(*node));
+
+ node->elem = elem;
+
+ if (list->size == 0) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ node->prev = list->tail;
+ list->tail = node;
+ node->prev->next = node;
+ }
+
+ list->size++;
+}
+
+/*
+ * add element to tail of a pexpr_list
+ */
+void pexpr_list_add(struct pexpr_list *list, struct pexpr *e)
+{
+ struct pexpr_node *node = xcalloc(1, sizeof(*node));
+
+ node->elem = e;
+
+ if (list->size == 0) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ node->prev = list->tail;
+ list->tail = node;
+ node->prev->next = node;
+ }
+
+ list->size++;
+}
+
+/*
+ * add element to tail of a sfix_list
+ */
+void sfix_list_add(struct sfix_list *list, struct symbol_fix *fix)
+{
+ struct sfix_node *node = xcalloc(1, sizeof(*node));
+
+ node->elem = fix;
+
+ if (list->size == 0) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ node->prev = list->tail;
+ list->tail = node;
+ node->prev->next = node;
+ }
+
+ list->size++;
+}
+
+/*
+ * add element to tail of a sfl_list
+ */
+void sfl_list_add(struct sfl_list *list, struct sfix_list *elem)
+{
+ struct sfl_node *node = xcalloc(1, sizeof(*node));
+
+ node->elem = elem;
+
+ if (list->size == 0) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ node->prev = list->tail;
+ list->tail = node;
+ node->prev->next = node;
+ }
+
+ list->size++;
+}
+
+/*
+ * add element to tail of a sdv_list
+ */
+void sdv_list_add(struct sdv_list *list, struct symbol_dvalue *sdv)
+{
+ struct sdv_node *node = xcalloc(1, sizeof(*node));
+
+ node->elem = sdv;
+
+ if (list->size == 0) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ node->prev = list->tail;
+ list->tail = node;
+ node->prev->next = node;
+ }
+
+ list->size++;
+}
+
+/*
+ * add element to tail of a sym_list
+ */
+void sym_list_add(struct sym_list *list, struct symbol *sym)
+{
+ struct sym_node *node = xcalloc(1, sizeof(*node));
+
+ node->elem = sym;
+
+ if (list->size == 0) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ node->prev = list->tail;
+ list->tail = node;
+ node->prev->next = node;
+ }
+
+ list->size++;
+}
+
+/*
+ * add element to tail of a defm_list
+ */
+void defm_list_add(struct defm_list *list, struct default_map *map)
+{
+ struct defm_node *node = xcalloc(1, sizeof(*node));
+
+ node->elem = map;
+
+ if (list->size == 0) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ node->prev = list->tail;
+ list->tail = node;
+ node->prev->next = node;
+ }
+
+ list->size++;
+}
+
+/*
+ * add element to tail of a prop_list
+ */
+void prop_list_add(struct prop_list *list, struct property *prop)
+{
+ struct prop_node *node = xcalloc(1, sizeof(*node));
+
+ node->elem = prop;
+
+ if (list->size == 0) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ node->prev = list->tail;
+ list->tail = node;
+ node->prev->next = node;
+ }
+
+ list->size++;
+}
+
+/*
+ * delete an element from a fexpr_list
+ */
+void fexpr_list_delete(struct fexpr_list *list, struct fexpr_node *node)
+{
+ if (list->size == 0 || node == NULL)
+ return;
+
+ if (node == list->head)
+ list->head = node->next;
+ else
+ node->prev->next = node->next;
+
+ if (node == list->tail)
+ list->tail = node->prev;
+ else
+ node->next->prev = node->prev;
+
+ list->size--;
+ free(node);
+}
+
+/*
+ * delete an element from a fexpr_list
+ */
+void sfix_list_delete(struct sfix_list *list, struct sfix_node *node)
+{
+ if (list->size == 0 || node == NULL)
+ return;
+
+ if (node == list->head)
+ list->head = node->next;
+ else
+ node->prev->next = node->next;
+
+ if (node == list->tail)
+ list->tail = node->prev;
+ else
+ node->next->prev = node->prev;
+
+ list->size--;
+ free(node);
+}
+
+/*
+ * delete an element from a fexpr_list
+ */
+void pexpr_list_delete(struct pexpr_list *list, struct pexpr_node *node)
+{
+ if (list->size == 0 || node == NULL)
+ return;
+
+ if (node == list->head)
+ list->head = node->next;
+ else
+ node->prev->next = node->next;
+
+ if (node == list->tail)
+ list->tail = node->prev;
+ else
+ node->next->prev = node->prev;
+
+ list->size--;
+ free(node);
+}
+
+/*
+ * delete an element from a fexl_list
+ */
+void fexl_list_delete(struct fexl_list *list, struct fexl_node *node)
+{
+ if (list->size == 0 || node == NULL)
+ return;
+
+ if (node == list->head)
+ list->head = node->next;
+ else
+ node->prev->next = node->next;
+
+ if (node == list->tail)
+ list->tail = node->prev;
+ else
+ node->next->prev = node->prev;
+
+ list->size--;
+ free(node);
+}
+
+/*
+ * delete the first occurrence of elem in an fexl_list
+ */
+void fexl_list_delete_elem(struct fexl_list *list, struct fexpr_list *elem)
+{
+ struct fexl_node *node, *to_delete = NULL;
+
+ fexl_list_for_each(node, list) {
+ if (node->elem == elem) {
+ to_delete = node;
+ break;
+ }
+ }
+
+ if (to_delete != NULL)
+ fexl_list_delete(list, to_delete);
+}
+
+/*
+ * make a shallow copy of a fexpr_list
+ */
+struct fexpr_list *fexpr_list_copy(struct fexpr_list *list)
+{
+ struct fexpr_list *ret = fexpr_list_init();
+ struct fexpr_node *node;
+
+ fexpr_list_for_each(node, list)
+ fexpr_list_add(ret, node->elem);
+
+ return ret;
+}
+
+/*
+ * make a shallow copy of a fexl_list
+ */
+struct fexl_list *fexl_list_copy(struct fexl_list *list)
+{
+ struct fexl_list *ret = fexl_list_init();
+ struct fexl_node *node;
+
+ fexl_list_for_each(node, list)
+ fexl_list_add(ret, node->elem);
+
+ return ret;
+}
+
+/*
+ * make a shallow copy of a sdv_list
+ */
+struct sdv_list *sdv_list_copy(struct sdv_list *list)
+{
+ struct sdv_list *ret = sdv_list_init();
+ struct sdv_node *node;
+
+ sdv_list_for_each(node, list)
+ sdv_list_add(ret, node->elem);
+
+
+ return ret;
+}
+
+/*
+ * make a shallow copy of a sfix_list
+ */
+struct sfix_list *sfix_list_copy(struct sfix_list *list)
+{
+ struct sfix_list *ret = sfix_list_init();
+ struct sfix_node *node;
+
+ sfix_list_for_each(node, list)
+ sfix_list_add(ret, node->elem);
+
+ return ret;
+}
+
+/*
+ * print a fexpr_list
+ */
+void fexpr_list_print(char *title, struct fexpr_list *list)
+{
+ struct fexpr_node *node;
+
+ printf("%s: [", title);
+
+ fexpr_list_for_each(node, list) {
+ printf("%s", str_get(&node->elem->name));
+ if (node->next != NULL)
+ printf(", ");
+ }
+
+ printf("]\n");
+}
+
+/*
+ * print a fexl_list
+ */
+void fexl_list_print(char *title, struct fexl_list *list)
+{
+ struct fexl_node *node;
+
+ printf("%s:\n", title);
+
+ fexl_list_for_each(node, list)
+ fexpr_list_print(":", node->elem);
+}
+
+/*
+ * print a pexpr_list
+ */
+void pexpr_list_print(char *title, struct pexpr_list *list)
+{
+ struct pexpr_node *node;
+
+ printf("%s: [", title);
+
+ pexpr_list_for_each(node, list) {
+ pexpr_print_util(node->elem, -1);
+ if (node->next != NULL)
+ printf(", ");
+ }
+
+ printf("]\n");
+}
+
+/*
+ * free an fexpr_list
+ */
+void fexpr_list_free(struct fexpr_list *list)
+{
+ struct fexpr_node *node = list->head, *tmp;
+
+ while (node != NULL) {
+ tmp = node->next;
+ free(node);
+ node = tmp;
+ }
+
+ free(list);
+}
+
+/*
+ * free an defm_list (and pexpr_put the conditions of the maps and free the
+ * node->element's)
+ */
+void defm_list_destruct(struct defm_list *list)
+{
+ struct defm_node *node = list->head, *tmp;
+
+ while (node != NULL) {
+ tmp = node->next;
+ pexpr_put(node->elem->e);
+ free(node->elem);
+ free(node);
+ node = tmp;
+ }
+
+ free(list);
+}
+
+/*
+ * free an fexl_list
+ */
+void fexl_list_free(struct fexl_list *list)
+{
+ struct fexl_node *node = list->head, *tmp;
+
+ while (node != NULL) {
+ tmp = node->next;
+ free(node);
+ node = tmp;
+ }
+
+ free(list);
+}
+
+/*
+ * free a sdv_list
+ */
+void sdv_list_free(struct sdv_list *list)
+{
+ struct sdv_node *node = list->head, *tmp;
+
+ while (node != NULL) {
+ tmp = node->next;
+ free(node);
+ node = tmp;
+ }
+
+ free(list);
+}
+
+/*
+ * free a pexpr_list (and pexpr_put the elements)
+ */
+void pexpr_list_free_put(struct pexpr_list *list)
+{
+ struct pexpr_node *node = list->head, *tmp;
+
+ while (node != NULL) {
+ tmp = node->next;
+ pexpr_put(node->elem);
+ free(node);
+ node = tmp;
+ }
+
+ free(list);
+}
+
+/*
+ * free a prop_list
+ */
+void prop_list_free(struct prop_list *list)
+{
+ struct prop_node *node = list->head, *tmp;
+
+ while (node != NULL) {
+ tmp = node->next;
+ free(node);
+ node = tmp;
+ }
+
+ free(list);
+}
+
+/*
+ * free a sym_list
+ */
+void sym_list_free(struct sym_list *list)
+{
+ struct sym_node *node = list->head, *tmp;
+
+ while (node != NULL) {
+ tmp = node->next;
+ free(node);
+ node = tmp;
+ }
+
+ free(list);
+}
+
+/*
+ * simplify a pexpr in-place
+ * pexpr && False -> False
+ * pexpr && True -> pexpr
+ * pexpr || False -> pexpr
+ * pexpr || True -> True
+ */
+static void pexpr_eliminate_yn(struct pexpr *e, struct cfdata *data)
+{
+ struct pexpr *tmp;
+ unsigned int ref_count;
+
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case PE_AND:
+ pexpr_eliminate_yn(e->left.pexpr, data);
+ pexpr_eliminate_yn(e->right.pexpr, data);
+ if (e->left.pexpr->type == PE_SYMBOL) {
+ if (e->left.pexpr->left.fexpr == data->constants->const_false) {
+ pexpr_put(e->left.pexpr);
+ pexpr_put(e->right.pexpr);
+ ref_count = e->ref_count;
+ pexpr_construct_sym(
+ e, data->constants->const_false,
+ ref_count);
+ return;
+ } else if (e->left.pexpr->left.fexpr == data->constants->const_true) {
+ pexpr_put(e->left.pexpr);
+ tmp = e->right.pexpr;
+ ref_count = e->ref_count;
+ pexpr_shallow_copy(e, tmp, ref_count);
+ pexpr_put(tmp);
+ return;
+ }
+ }
+ if (e->right.pexpr->type == PE_SYMBOL) {
+ if (e->right.pexpr->left.fexpr == data->constants->const_false) {
+ pexpr_put(e->left.pexpr);
+ pexpr_put(e->right.pexpr);
+ ref_count = e->ref_count;
+ pexpr_construct_sym(
+ e, data->constants->const_false,
+ ref_count);
+ return;
+ } else if (e->right.pexpr->left.fexpr == data->constants->const_true) {
+ pexpr_put(e->right.pexpr);
+ tmp = e->left.pexpr;
+ ref_count = e->ref_count;
+ pexpr_shallow_copy(e, tmp, ref_count);
+ pexpr_put(tmp);
+ return;
+ }
+ }
+ break;
+ case PE_OR:
+ pexpr_eliminate_yn(e->left.pexpr, data);
+ pexpr_eliminate_yn(e->right.pexpr, data);
+ if (e->left.pexpr->type == PE_SYMBOL) {
+ if (e->left.pexpr->left.fexpr == data->constants->const_false) {
+ pexpr_put(e->left.pexpr);
+ tmp = e->right.pexpr;
+ ref_count = e->ref_count;
+ pexpr_shallow_copy(e, tmp, ref_count);
+ pexpr_put(tmp);
+ return;
+ } else if (e->left.pexpr->left.fexpr == data->constants->const_true) {
+ pexpr_put(e->left.pexpr);
+ pexpr_put(e->right.pexpr);
+ ref_count = e->ref_count;
+ pexpr_construct_sym(
+ e, data->constants->const_true,
+ ref_count);
+ return;
+ }
+ }
+ if (e->right.pexpr->type == PE_SYMBOL) {
+ if (e->right.pexpr->left.fexpr == data->constants->const_false) {
+ pexpr_put(e->right.pexpr);
+ tmp = e->left.pexpr;
+ ref_count = e->ref_count;
+ pexpr_shallow_copy(e, tmp, ref_count);
+ pexpr_put(tmp);
+ return;
+ } else if (e->right.pexpr->left.fexpr == data->constants->const_true) {
+ pexpr_put(e->left.pexpr);
+ pexpr_put(e->right.pexpr);
+ ref_count = e->ref_count;
+ pexpr_construct_sym(e,
+ data->constants->const_true,
+ ref_count);
+ return;
+ }
+ }
+ default:
+ break;
+ }
+}
+
+static void pexpr_shallow_copy(struct pexpr *dest, struct pexpr *org, unsigned int ref_count)
+{
+ struct pexpr inter;
+
+ inter.type = org->type;
+ inter.left = org->left;
+ inter.right = org->right;
+ if (org->type == PE_OR || org->type == PE_AND) {
+ pexpr_get(org->left.pexpr);
+ pexpr_get(org->right.pexpr);
+ } else if (org->type == PE_NOT) {
+ pexpr_get(org->left.pexpr);
+ }
+ inter.ref_count = ref_count;
+ *dest = inter;
+}
+
+/*
+ * copy a pexpr
+ */
+struct pexpr *pexpr_deep_copy(const struct pexpr *org)
+{
+ struct pexpr *e;
+
+ if (!org)
+ return NULL;
+
+ e = xmalloc(sizeof(*org));
+ memcpy(e, org, sizeof(*org));
+ e->ref_count = 1;
+ switch (org->type) {
+ case PE_SYMBOL:
+ e->left = org->left;
+ break;
+ case PE_AND:
+ case PE_OR:
+ e->left.pexpr = pexpr_deep_copy(org->left.pexpr);
+ e->right.pexpr = pexpr_deep_copy(org->right.pexpr);
+ break;
+ case PE_NOT:
+ e->left.pexpr = pexpr_deep_copy(org->left.pexpr);
+ break;
+ }
+
+ return e;
+}
+
+/*
+ * free a pexpr
+ */
+void pexpr_free_depr(struct pexpr *e)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ break;
+ case PE_AND:
+ case PE_OR:
+ pexpr_free_depr(e->left.pexpr);
+ pexpr_free_depr(e->right.pexpr);
+ break;
+ case PE_NOT:
+ pexpr_free_depr(e->left.pexpr);
+ break;
+ }
+
+ free(e);
+}
+
+/*
+ * Increments ref_count and returns @e
+ */
+struct pexpr *pexpr_get(struct pexpr *e)
+{
+ ++e->ref_count;
+ return e;
+}
+
+/*
+ * Decrements ref_count and if it becomes 0, it recursively puts the references
+ * to its children and calls ``free(e)``. If @e == NULL, it does nothing.
+ */
+void pexpr_put(struct pexpr *e)
+{
+ if (!e)
+ return;
+
+ if (e->ref_count == 0) {
+ printd("Invalid call to %s - ref_count is zero\n", __func__);
+ return;
+ }
+
+ --e->ref_count;
+ if (e->ref_count > 0)
+ return;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ break;
+ case PE_AND:
+ case PE_OR:
+ pexpr_put(e->left.pexpr);
+ pexpr_put(e->right.pexpr);
+ break;
+ case PE_NOT:
+ pexpr_put(e->left.pexpr);
+ break;
+ }
+
+ free(e);
+}
+
+/*
+ * calls pexpr_put for a NULL-terminated array of struct pexpr *
+ */
+void _pexpr_put_list(struct pexpr **es)
+{
+ for (; *es != NULL; es++)
+ pexpr_put(*es);
+}
+
+#define e1 (*ep1)
+#define e2 (*ep2)
+/*
+ * pexpr_eliminate_eq() helper
+ */
+static void __pexpr_eliminate_eq(enum pexpr_type type, struct pexpr **ep1, struct pexpr **ep2,
+ struct cfdata *data)
+{
+ /* recurse down to the leaves */
+ if (e1->type == type) {
+ __pexpr_eliminate_eq(type, &e1->left.pexpr, &e2, data);
+ __pexpr_eliminate_eq(type, &e1->right.pexpr, &e2, data);
+ return;
+ }
+ if (e2->type == type) {
+ __pexpr_eliminate_eq(type, &e1, &e2->left.pexpr, data);
+ __pexpr_eliminate_eq(type, &e1, &e2->right.pexpr, data);
+ return;
+ }
+
+ /* e1 and e2 are leaves. Compare them. */
+ if (e1->type == PE_SYMBOL && e2->type == PE_SYMBOL &&
+ e1->left.fexpr->satval == e2->left.fexpr->satval &&
+ (e1->left.fexpr == data->constants->const_true ||
+ e2->left.fexpr == data->constants->const_false))
+ return;
+ if (!pexpr_eq(e1, e2, data))
+ return;
+
+ /* e1 and e2 are equal leaves. Prepare them for elimination. */
+ trans_count++;
+ pexpr_put(e1);
+ pexpr_put(e2);
+ switch (type) {
+ case PE_AND:
+ e1 = pexf(data->constants->const_true);
+ e2 = pexf(data->constants->const_true);
+ break;
+ case PE_OR:
+ e1 = pexf(data->constants->const_false);
+ e2 = pexf(data->constants->const_false);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * rewrite pexpr ep1 and ep2 to remove operands common to both
+ */
+static void pexpr_eliminate_eq(struct pexpr **ep1, struct pexpr **ep2, struct cfdata *data)
+{
+ if (!e1 || !e2)
+ return;
+
+ switch (e1->type) {
+ case PE_AND:
+ case PE_OR:
+ __pexpr_eliminate_eq(e1->type, ep1, ep2, data);
+ default:
+ break;
+ }
+ if (e1->type != e2->type)
+ switch (e2->type) {
+ case PE_AND:
+ case PE_OR:
+ __pexpr_eliminate_eq(e2->type, ep1, ep2, data);
+ default:
+ break;
+ }
+ pexpr_eliminate_yn(e1, data);
+ pexpr_eliminate_yn(e2, data);
+}
+#undef e1
+#undef e2
+
+/*
+ * check whether 2 pexpr are equal
+ */
+bool pexpr_eq(struct pexpr *e1, struct pexpr *e2, struct cfdata *data)
+{
+ bool res;
+ int old_count;
+
+ if (!e1 || !e2)
+ return false;
+
+ if (e1->type != e2->type)
+ return false;
+
+ switch (e1->type) {
+ case PE_SYMBOL:
+ return e1->left.fexpr->satval == e2->left.fexpr->satval;
+ case PE_AND:
+ case PE_OR:
+ e1 = pexpr_deep_copy(e1);
+ e2 = pexpr_deep_copy(e2);
+ old_count = trans_count;
+ pexpr_eliminate_eq(&e1, &e2, data);
+ res = (e1->type == PE_SYMBOL && e2->type == PE_SYMBOL &&
+ e1->left.fexpr->satval == e2->left.fexpr->satval);
+ pexpr_put(e1);
+ pexpr_put(e2);
+ trans_count = old_count;
+ return res;
+ case PE_NOT:
+ return pexpr_eq(e1->left.pexpr, e2->left.pexpr, data);
+ }
+
+ return false;
+}
+
+/*
+ * print a pexpr
+ */
+static void pexpr_print_util(struct pexpr *e, int prevtoken)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ printf("%s", str_get(&e->left.fexpr->name));
+ break;
+ case PE_AND:
+ if (prevtoken != PE_AND && prevtoken != -1)
+ printf("(");
+ pexpr_print_util(e->left.pexpr, PE_AND);
+ printf(" && ");
+ pexpr_print_util(e->right.pexpr, PE_AND);
+ if (prevtoken != PE_AND && prevtoken != -1)
+ printf(")");
+ break;
+ case PE_OR:
+ if (prevtoken != PE_OR && prevtoken != -1)
+ printf("(");
+ pexpr_print_util(e->left.pexpr, PE_OR);
+ printf(" || ");
+ pexpr_print_util(e->right.pexpr, PE_OR);
+ if (prevtoken != PE_OR && prevtoken != -1)
+ printf(")");
+ break;
+ case PE_NOT:
+ printf("!");
+ pexpr_print_util(e->left.pexpr, PE_NOT);
+ break;
+ }
+}
+void pexpr_print(char *tag, struct pexpr *e, int prevtoken)
+{
+ printf("%s: ", tag);
+ pexpr_print_util(e, prevtoken);
+ printf("\n");
+}
+
+/*
+ * convert a fexpr to a pexpr
+ */
+struct pexpr *pexf(struct fexpr *fe)
+{
+ struct pexpr *pe = xcalloc(1, sizeof(*pe));
+
+ pexpr_construct_sym(pe, fe, 1);
+ return pe;
+}
+
+void pexpr_construct_or(struct pexpr *e, struct pexpr *left,
+ struct pexpr *right, unsigned int ref_count)
+{
+ e->type = PE_OR;
+ e->left.pexpr = left;
+ e->right.pexpr = right;
+ e->ref_count = ref_count;
+}
+
+void pexpr_construct_and(struct pexpr *e, struct pexpr *left,
+ struct pexpr *right, unsigned int ref_count)
+{
+ e->type = PE_AND;
+ e->left.pexpr = left;
+ e->right.pexpr = right;
+ e->ref_count = ref_count;
+}
+
+void pexpr_construct_not(struct pexpr *e, struct pexpr *left,
+ unsigned int ref_count)
+{
+ e->type = PE_NOT;
+ e->left.pexpr = left;
+ e->right.pexpr = NULL;
+ e->ref_count = ref_count;
+}
+
+void pexpr_construct_sym(struct pexpr *e, struct fexpr *left,
+ unsigned int ref_count)
+{
+ e->type = PE_SYMBOL;
+ e->left.fexpr = left;
+ e->right.pexpr = NULL;
+ e->ref_count = ref_count;
+}
+
+static struct pexpr *pexpr_join_or(struct pexpr *e1, struct pexpr *e2, struct cfdata *data)
+{
+ if (pexpr_eq(e1, e2, data))
+ return pexpr_deep_copy(e1);
+ else
+ return NULL;
+}
+
+static struct pexpr *pexpr_join_and(struct pexpr *e1, struct pexpr *e2, struct cfdata *data)
+{
+ if (pexpr_eq(e1, e2, data))
+ return pexpr_deep_copy(e1);
+ else
+ return NULL;
+}
+
+/*
+ * pexpr_eliminate_dups() helper.
+ */
+static void pexpr_eliminate_dups1(enum pexpr_type type, struct pexpr **ep1, struct pexpr **ep2,
+ struct cfdata *data)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+
+ struct pexpr *tmp;
+
+ /* recurse down to leaves */
+ if (e1->type == type) {
+ pexpr_eliminate_dups1(type, &e1->left.pexpr, &e2, data);
+ pexpr_eliminate_dups1(type, &e1->right.pexpr, &e2, data);
+ return;
+ }
+ if (e2->type == type) {
+ pexpr_eliminate_dups1(type, &e1, &e2->left.pexpr, data);
+ pexpr_eliminate_dups1(type, &e1, &e2->right.pexpr, data);
+ return;
+ }
+
+ /* e1 and e2 are leaves. Compare them. */
+
+ if (e1 == e2)
+ return;
+
+ switch (e1->type) {
+ case PE_AND:
+ case PE_OR:
+ pexpr_eliminate_dups1(e1->type, &e1, &e1, data);
+ default:
+ break;
+ }
+
+ switch (type) {
+ case PE_AND:
+ tmp = pexpr_join_and(e1, e2, data);
+ if (tmp) {
+ pexpr_put(e1);
+ pexpr_put(e2);
+ e1 = pexf(data->constants->const_true);
+ e2 = tmp;
+ trans_count++;
+ }
+ break;
+ case PE_OR:
+ tmp = pexpr_join_or(e1, e2, data);
+ if (tmp) {
+ pexpr_put(e1);
+ pexpr_put(e2);
+ e1 = pexf(data->constants->const_false);
+ e2 = tmp;
+ trans_count++;
+ }
+ break;
+ default:
+ break;
+ }
+
+#undef e1
+#undef e2
+}
+
+/*
+ * eliminate duplicate and redundant operands
+ */
+struct pexpr *pexpr_eliminate_dups(struct pexpr *e, struct cfdata *data)
+{
+ int oldcount;
+
+ if (!e)
+ return e;
+
+ oldcount = trans_count;
+ while (true) {
+ trans_count = 0;
+ switch (e->type) {
+ case PE_AND:
+ case PE_OR:
+ pexpr_eliminate_dups1(e->type, &e, &e, data);
+ default:
+ break;
+ }
+ if (!trans_count)
+ /* no simplification done in this pass. We're done. */
+ break;
+ pexpr_eliminate_yn(e, data);
+ }
+ trans_count = oldcount;
+ return e;
+}
diff --git a/scripts/kconfig/cf_expr.h b/scripts/kconfig/cf_expr.h
new file mode 100644
index 000000000000..07435ae381e6
--- /dev/null
+++ b/scripts/kconfig/cf_expr.h
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CF_EXPR_H
+#define CF_EXPR_H
+
+#include <stdbool.h>
+
+#include "cf_defs.h"
+
+#define fexpr_list_for_each(node, list) \
+ for (node = list->head; node != NULL; node = node->next)
+
+#define fexl_list_for_each(node, list) \
+ fexpr_list_for_each(node, list)
+
+#define pexpr_list_for_each(node, list) \
+ fexpr_list_for_each(node, list)
+
+#define sdv_list_for_each(node, list) \
+ fexpr_list_for_each(node, list)
+
+#define sfix_list_for_each(node, list) \
+ fexpr_list_for_each(node, list)
+
+#define sfl_list_for_each(node, list) \
+ fexpr_list_for_each(node, list)
+
+#define sym_list_for_each(node, list) \
+ fexpr_list_for_each(node, list)
+
+#define defm_list_for_each(node, list) \
+ fexpr_list_for_each(node, list)
+
+#define prop_list_for_each(node, list) \
+ fexpr_list_for_each(node, list)
+
+/* call pexpr_put for a list of pexpr's */
+#define PEXPR_PUT(...) _pexpr_put_list((struct pexpr *[]){ __VA_ARGS__, NULL })
+
+/* create a fexpr */
+struct fexpr *fexpr_create(int satval, enum fexpr_type type, char *name);
+
+/* create the fexpr for a symbol */
+void sym_create_fexpr(struct symbol *sym, struct cfdata *data);
+
+struct pexpr *expr_calculate_pexpr_both(struct expr *e, struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_y(struct expr *e, struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_m(struct expr *e, struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_y_and(struct expr *a, struct expr *b,
+ struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_m_and(struct expr *a, struct expr *b,
+ struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_both_and(struct expr *a, struct expr *b,
+ struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_y_or(struct expr *a, struct expr *b,
+ struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_m_or(struct expr *a, struct expr *b,
+ struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_both_or(struct expr *a, struct expr *b,
+ struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_y_not(struct expr *e, struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_m_not(struct expr *e, struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_y_equals(struct expr *e,
+ struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_y_unequals(struct expr *e,
+ struct cfdata *data);
+struct pexpr *expr_calculate_pexpr_y_comp(struct expr *e, struct cfdata *data);
+
+/* macro to create a pexpr of type AND */
+struct pexpr *pexpr_and_share(struct pexpr *a, struct pexpr *b,
+ struct cfdata *data);
+struct pexpr *pexpr_and(struct pexpr *a, struct pexpr *b, struct cfdata *data,
+ enum pexpr_move move);
+
+/* macro to create a pexpr of type OR */
+struct pexpr *pexpr_or_share(struct pexpr *a, struct pexpr *b,
+ struct cfdata *data);
+struct pexpr *pexpr_or(struct pexpr *a, struct pexpr *b, struct cfdata *data,
+ enum pexpr_move move);
+
+/* macro to create a pexpr of type NOT */
+struct pexpr *pexpr_not_share(struct pexpr *a, struct cfdata *data);
+struct pexpr *pexpr_not(struct pexpr *a, struct cfdata *data);
+
+/* check whether a pexpr is in CNF */
+bool pexpr_is_cnf(struct pexpr *e);
+
+/* check whether a pexpr is in NNF */
+bool pexpr_is_nnf(struct pexpr *e);
+
+/* return fexpr_both for a symbol */
+struct pexpr *sym_get_fexpr_both(struct symbol *sym, struct cfdata *data);
+
+/* return fexpr_sel_both for a symbol */
+struct pexpr *sym_get_fexpr_sel_both(struct symbol *sym, struct cfdata *data);
+
+/* create the fexpr of a non-boolean symbol for a specific value */
+struct fexpr *sym_create_nonbool_fexpr(struct symbol *sym, char *value,
+ struct cfdata *data);
+
+/*
+ * return the fexpr of a non-boolean symbol for a specific value, NULL if
+ * non-existent
+ */
+struct fexpr *sym_get_nonbool_fexpr(struct symbol *sym, char *value);
+
+/*
+ * return the fexpr of a non-boolean symbol for a specific value, if it exists
+ * otherwise create it
+ */
+struct fexpr *sym_get_or_create_nonbool_fexpr(struct symbol *sym, char *value, struct cfdata *data);
+
+/* macro to construct a pexpr for "A implies B" */
+struct pexpr *pexpr_implies_share(struct pexpr *a, struct pexpr *b, struct cfdata *data);
+struct pexpr *pexpr_implies(struct pexpr *a, struct pexpr *b, struct cfdata *data,
+ enum pexpr_move move);
+
+/* check, if the fexpr is a symbol, a True/False-constant, a literal symbolising a non-boolean or
+ * a choice symbol
+ */
+bool fexpr_is_symbol(struct fexpr *e);
+
+/* check whether a pexpr is a symbol or a negated symbol */
+bool pexpr_is_symbol(struct pexpr *e);
+
+/* check whether the fexpr is a constant (true/false) */
+bool fexpr_is_constant(struct fexpr *e, struct cfdata *data);
+
+/* add a fexpr to the satmap */
+void fexpr_add_to_satmap(struct fexpr *e, struct cfdata *data);
+
+/* print an fexpr */
+void fexpr_print(char *tag, struct fexpr *e);
+
+/* write an fexpr into a string (format needed for testing) */
+void fexpr_as_char(struct fexpr *e, struct gstr *s);
+
+/* write pn pexpr into a string */
+void pexpr_as_char_short(struct pexpr *e, struct gstr *s, int parent);
+
+/* write an fexpr into a string (format needed for testing) */
+void pexpr_as_char(struct pexpr *e, struct gstr *s, int parent, struct cfdata *data);
+
+/* check whether a pexpr contains a specific fexpr */
+bool pexpr_contains_fexpr(struct pexpr *e, struct fexpr *fe);
+
+/* init list of fexpr */
+struct fexpr_list *fexpr_list_init(void);
+
+/* init list of fexpr_list */
+struct fexl_list *fexl_list_init(void);
+
+/* init list of pexpr */
+struct pexpr_list *pexpr_list_init(void);
+
+/* init list of symbol_fix */
+struct sfix_list *sfix_list_init(void);
+
+/* init list of sfix_list */
+struct sfl_list *sfl_list_init(void);
+
+/* init list of symbol_dvalue */
+struct sdv_list *sdv_list_init(void);
+
+/* init list of symbols */
+struct sym_list *sym_list_init(void);
+
+/* init list of default_maps */
+struct defm_list *defm_list_init(void);
+
+/* init list of properties */
+struct prop_list *prop_list_init(void);
+
+/* add element to tail of a fexpr_list */
+void fexpr_list_add(struct fexpr_list *list, struct fexpr *fe);
+
+/* add element to tail of a fexl_list */
+void fexl_list_add(struct fexl_list *list, struct fexpr_list *elem);
+
+/* add element to tail of a pexpr_list */
+void pexpr_list_add(struct pexpr_list *list, struct pexpr *e);
+
+/* add element to tail of a sfix_list */
+void sfix_list_add(struct sfix_list *list, struct symbol_fix *fix);
+
+/* add element to tail of a sfl_list */
+void sfl_list_add(struct sfl_list *list, struct sfix_list *elem);
+
+/* add element to tail of a sdv_list */
+void sdv_list_add(struct sdv_list *list, struct symbol_dvalue *sdv);
+
+/* add element to tail of a sym_list */
+void sym_list_add(struct sym_list *list, struct symbol *sym);
+
+/* add element to tail of a defm_list */
+void defm_list_add(struct defm_list *list, struct default_map *map);
+
+/* add element to tail of a prop_list */
+void prop_list_add(struct prop_list *list, struct property *prop);
+
+/* delete an element from a fexpr_list */
+void fexpr_list_delete(struct fexpr_list *list, struct fexpr_node *node);
+
+/* delete an element from a fexpr_list */
+void fexl_list_delete(struct fexl_list *list, struct fexl_node *node);
+
+/* delete the first occurrence of elem in an fexl_list */
+void fexl_list_delete_elem(struct fexl_list *list, struct fexpr_list *elem);
+
+/* delete an element from a pexpr_list */
+void pexpr_list_delete(struct pexpr_list *list, struct pexpr_node *node);
+
+/* delete an element from a sfix_list */
+void sfix_list_delete(struct sfix_list *list, struct sfix_node *node);
+
+/* make a shallow copy of a fexpr_list */
+struct fexpr_list *fexpr_list_copy(struct fexpr_list *list);
+
+/* make a shallow copy of a fexpr_list */
+struct fexl_list *fexl_list_copy(struct fexl_list *list);
+
+/* make a shallow copy of a sdv_list */
+struct sdv_list *sdv_list_copy(struct sdv_list *list);
+
+/* make a shallow copy of a sfix_list */
+struct sfix_list *sfix_list_copy(struct sfix_list *list);
+
+/* print a fexpr_list */
+void fexpr_list_print(char *title, struct fexpr_list *list);
+
+/* print a fexl_list */
+void fexl_list_print(char *title, struct fexl_list *list);
+
+/* print a pexpr_list */
+void pexpr_list_print(char *title, struct pexpr_list *list);
+
+/* free an fexpr_list */
+void fexpr_list_free(struct fexpr_list *list);
+
+/* free an pexpr_list (and pexpr_put the elements) */
+void pexpr_list_free_put(struct pexpr_list *list);
+
+/* free an fexl_list */
+void fexl_list_free(struct fexl_list *list);
+
+/* free a sdv_list */
+void sdv_list_free(struct sdv_list *list);
+
+/* free a prop_list */
+void prop_list_free(struct prop_list *list);
+
+/* free a defm_list (and pexpr_put the conditions of the maps) */
+void defm_list_destruct(struct defm_list *list);
+
+/* free a sym_list */
+void sym_list_free(struct sym_list *list);
+
+/* check whether 2 pexpr are equal */
+bool pexpr_eq(struct pexpr *e1, struct pexpr *e2, struct cfdata *data);
+
+/* copy a pexpr */
+struct pexpr *pexpr_deep_copy(const struct pexpr *org);
+
+void pexpr_construct_sym(struct pexpr *e, struct fexpr *left,
+ unsigned int ref_count);
+void pexpr_construct_not(struct pexpr *e, struct pexpr *left,
+ unsigned int ref_count);
+void pexpr_construct_and(struct pexpr *e, struct pexpr *left,
+ struct pexpr *right, unsigned int ref_count);
+void pexpr_construct_or(struct pexpr *e, struct pexpr *left,
+ struct pexpr *right, unsigned int ref_count);
+
+/* free a pexpr */
+void pexpr_free_depr(struct pexpr *e);
+
+/* give up a reference to e. Also see struct pexpr. */
+void pexpr_put(struct pexpr *e);
+/* Used by PEXPR_PUT(). Not to be used directly. */
+void _pexpr_put_list(struct pexpr **es);
+
+/* acquire a reference to e. Also see struct pexpr. */
+struct pexpr *pexpr_get(struct pexpr *e);
+
+/* print a pexpr */
+void pexpr_print(char *tag, struct pexpr *e, int prevtoken);
+
+/* convert a fexpr to a pexpr */
+struct pexpr *pexf(struct fexpr *fe);
+
+/* eliminate duplicate and redundant operands */
+struct pexpr *pexpr_eliminate_dups(struct pexpr *e, struct cfdata *data);
+
+#endif
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH v4 07/12] kconfig: Add files for handling expressions
2024-07-10 6:52 ` [PATCH v4 07/12] kconfig: Add files for handling expressions Ole Schuerks
@ 2024-08-12 8:46 ` Masahiro Yamada
2024-08-16 10:23 ` Ole Schuerks
0 siblings, 1 reply; 25+ messages in thread
From: Masahiro Yamada @ 2024-08-12 8:46 UTC (permalink / raw)
To: Ole Schuerks
Cc: linux-kbuild, jude.gyimah, thorsten.berger, deltaone,
jan.sollmann, mcgrof, linux-kernel
On Wed, Jul 10, 2024 at 3:54 PM Ole Schuerks <ole0811sch@gmail.com> wrote:
>
> To translate the Kconfig-model into propositional logic and resolve
> conflicts, we need to handle propostional formulas.
> These files contain many functions and macros to deal with
> propositional formulas.
>
> Co-developed-by: Patrick Franz <deltaone@debian.org>
> Signed-off-by: Patrick Franz <deltaone@debian.org>
> Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
> Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
> Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
> Suggested-by: Sarah Nadi <nadi@ualberta.ca>
> Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
> Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
> Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
> ---
> scripts/kconfig/cf_expr.c | 2594 +++++++++++++++++++++++++++++++++++++
> scripts/kconfig/cf_expr.h | 296 +++++
> 2 files changed, 2890 insertions(+)
> create mode 100644 scripts/kconfig/cf_expr.c
> create mode 100644 scripts/kconfig/cf_expr.h
>
> diff --git a/scripts/kconfig/cf_expr.c b/scripts/kconfig/cf_expr.c
> new file mode 100644
> index 000000000000..f015f91ec8c6
> --- /dev/null
> +++ b/scripts/kconfig/cf_expr.c
> @@ -0,0 +1,2594 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
> + */
> +
> +#define _GNU_SOURCE
> +#include <assert.h>
> +#include <locale.h>
> +#include <stdarg.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <time.h>
> +#include <unistd.h>
> +
> +#include "cf_expr.h"
> +#include "cf_defs.h"
> +#include "cf_utils.h"
> +
> +static void create_fexpr_bool(struct symbol *sym, struct cfdata *data);
> +static void create_fexpr_nonbool(struct symbol *sym, struct cfdata *data);
> +static void create_fexpr_unknown(struct symbol *sym, struct cfdata *data);
> +static void create_fexpr_choice(struct symbol *sym, struct cfdata *data);
> +
> +static void pexpr_print_util(struct pexpr *e, int prevtoken);
> +static void pexpr_shallow_copy(struct pexpr *dest, struct pexpr *org, unsigned int ref_count);
> +
> +static struct pexpr *pexpr_move_wrapper(
> + struct pexpr *a, struct pexpr *b, struct cfdata *data,
> + enum pexpr_move move,
> + struct pexpr *(*func)(struct pexpr *, struct pexpr *, struct cfdata *));
> +
> +static int trans_count;
> +
> +
> +/*
> + * create a fexpr
> + */
> +struct fexpr *fexpr_create(int satval, enum fexpr_type type, char *name)
> +{
> + struct fexpr *e = xcalloc(1, sizeof(*e));
Why xcalloc() instead of xmalloc()?
(same for all other places)
> +
> + e->satval = satval;
> + e->type = type;
> + e->name = str_new();
> + e->assumption = false;
> + str_append(&e->name, name);
> +
> + return e;
> +}
> +
> +/*
> + * create the fexpr for a symbol
> + */
> +void sym_create_fexpr(struct symbol *sym, struct cfdata *data)
> +{
> + if (sym_is_choice(sym))
> + create_fexpr_choice(sym, data);
> + else if (sym_is_boolean(sym))
> + create_fexpr_bool(sym, data);
> + else if (sym_is_nonboolean(sym))
> + create_fexpr_nonbool(sym, data);
> + else
> + create_fexpr_unknown(sym, data);
> +}
> +
> +/*
> + * create the fexpr for symbols with reverse dependencies
> + */
> +static void create_fexpr_selected(struct symbol *sym, struct cfdata *data)
> +{
> + struct fexpr *fexpr_sel_y;
> + struct fexpr *fexpr_sel_m;
> +
> + /* fexpr_sel_y */
> + fexpr_sel_y = fexpr_create(data->sat_variable_nr++, FE_SELECT, sym->name);
> + str_append(&fexpr_sel_y->name, "_sel_y");
> + fexpr_sel_y->sym = sym;
> + fexpr_add_to_satmap(fexpr_sel_y, data);
> +
> + sym->fexpr_sel_y = fexpr_sel_y;
> +
> + /* fexpr_sel_m */
> + if (sym->type == S_BOOLEAN)
> + return;
> +
> + fexpr_sel_m = fexpr_create(data->sat_variable_nr++, FE_SELECT, sym->name);
> + str_append(&fexpr_sel_m->name, "_sel_m");
> + fexpr_sel_m->sym = sym;
> + fexpr_add_to_satmap(fexpr_sel_m, data);
> +
> + sym->fexpr_sel_m = fexpr_sel_m;
> +}
> +
> +/*
> + * create the fexpr for a boolean/tristate symbol
> + */
> +static void create_fexpr_bool(struct symbol *sym, struct cfdata *data)
> +{
> + struct fexpr *fexpr_y;
> + struct fexpr *fexpr_m;
> +
> + fexpr_y = fexpr_create(data->sat_variable_nr++, FE_SYMBOL, sym->name);
> + fexpr_y->sym = sym;
> + fexpr_y->tri = yes;
> + fexpr_add_to_satmap(fexpr_y, data);
> +
> + sym->fexpr_y = fexpr_y;
> +
> +
> + if (sym->type == S_TRISTATE) {
> + fexpr_m = fexpr_create(data->sat_variable_nr++, FE_SYMBOL, sym->name);
> + str_append(&fexpr_m->name, "_MODULE");
> + fexpr_m->sym = sym;
> + fexpr_m->tri = mod;
> + fexpr_add_to_satmap(fexpr_m, data);
> + } else {
> + fexpr_m = data->constants->const_false;
> + }
> +
> + sym->fexpr_m = fexpr_m;
> +
> + if (sym->rev_dep.expr)
> + create_fexpr_selected(sym, data);
> +}
> +
> +/*
> + * create the fexpr for a non-boolean symbol
> + */
> +static void create_fexpr_nonbool(struct symbol *sym, struct cfdata *data)
> +{
> + /* default values */
> + char int_values[][2] = {"n", "0", "1"};
> + char hex_values[][4] = {"n", "0x0", "0x1"};
> + char string_values[][9] = {"n", "", "nonempty"};
> +
> + sym->fexpr_y = data->constants->const_false;
> + sym->fexpr_m = data->constants->const_false;
> + sym->nb_vals = fexpr_list_init();
> +
> + for (int i = 0; i < 3; i++) {
> + struct fexpr *e = fexpr_create(data->sat_variable_nr++, FE_NONBOOL, sym->name);
> +
> + e->sym = sym;
> + str_append(&e->name, "=");
> + e->nb_val = str_new();
> +
> + switch (sym->type) {
> + case S_INT:
> + str_append(&e->name, int_values[i]);
> + str_append(&e->nb_val, int_values[i]);
> + break;
> + case S_HEX:
> + str_append(&e->name, hex_values[i]);
> + str_append(&e->nb_val, hex_values[i]);
> + break;
> + case S_STRING:
> + str_append(&e->name, string_values[i]);
> + str_append(&e->nb_val, string_values[i]);
> + break;
> + default:
> + break;
> + }
> +
> + fexpr_list_add(sym->nb_vals, e);
> + fexpr_add_to_satmap(e, data);
> + }
> +}
> +
> +/*
> + * set fexpr_y and fexpr_m simply to False
> + */
> +static void create_fexpr_unknown(struct symbol *sym, struct cfdata *data)
> +{
> + sym->fexpr_y = data->constants->const_false;
> + sym->fexpr_m = data->constants->const_false;
> +}
> +
> +/*
> + * create the fexpr for a choice symbol
> + */
> +static void create_fexpr_choice(struct symbol *sym, struct cfdata *data)
> +{
> + struct property *prompt;
> + char *name, *write, *read;
> + struct fexpr *fexpr_y;
> + struct fexpr *fexpr_m;
> +
> + if (!sym_is_boolean(sym))
> + return;
> +
> + prompt = sym_get_prompt(sym);
> + if (prompt == NULL) {
> + perror("Choice symbol should have a prompt.");
> + return;
> + }
> +
> + name = strdup(prompt->text);
> +
> + /* remove spaces */
> + write = name;
> + read = name;
> + do {
> + if (*read != ' ')
> + *write++ = *read;
> + } while (*read++);
> +
> + fexpr_y = fexpr_create(data->sat_variable_nr++, FE_CHOICE, "Choice_");
> + str_append(&fexpr_y->name, name);
> + fexpr_y->sym = sym;
> + fexpr_y->tri = yes;
> + fexpr_add_to_satmap(fexpr_y, data);
> +
> + sym->fexpr_y = fexpr_y;
> +
> + if (sym->type == S_TRISTATE) {
> + fexpr_m = fexpr_create(data->sat_variable_nr++, FE_CHOICE, "Choice_");
> + str_append(&fexpr_m->name, name);
> + str_append(&fexpr_m->name, "_MODULE");
> + fexpr_m->sym = sym;
> + fexpr_m->tri = mod;
> + fexpr_add_to_satmap(fexpr_m, data);
> + } else {
> + fexpr_m = data->constants->const_false;
> + }
> + sym->fexpr_m = fexpr_m;
> + free(name);
> +}
> +
> +/*
> + * evaluate an unequality between a non-Boolean symbol and a constant
> + */
> +static struct pexpr *expr_eval_unequal_nonbool_const(struct symbol *sym, struct symbol *compval,
> + enum expr_type type, struct cfdata *data)
> +{
> + int base;
> + struct pexpr *c;
> + long val;
> + struct fexpr_node *node;
> + struct fexpr *fe;
> +
> + if (!sym || !compval)
> + return pexf(data->constants->const_false);
> +
> + base = 0;
> + switch (sym->type) {
> + case S_INT:
> + base = 10;
> + break;
> + case S_HEX:
> + base = 16;
> + break;
> + default:
> + break;
> + }
> +
> + c = pexf(data->constants->const_false);
> + val = strtol(compval->name, NULL, base);
> + for (node = sym->nb_vals->head->next; node != NULL; node = node->next) {
> + long symval;
> +
> + fe = node->elem;
> + symval = strtol(str_get(&fe->nb_val), NULL, base);
> +
> + switch (type) {
> + case E_LTH:
> + if (symval < val)
> + c = pexpr_or(c, pexf(fe), data, PEXPR_ARGX);
> + break;
> + case E_LEQ:
> + if (symval <= val)
> + c = pexpr_or(c, pexf(fe), data, PEXPR_ARGX);
> + break;
> + case E_GTH:
> + if (symval > val)
> + c = pexpr_or(c, pexf(fe), data, PEXPR_ARGX);
> + break;
> + case E_GEQ:
> + if (symval >= val)
> + c = pexpr_or(c, pexf(fe), data, PEXPR_ARGX);
> + break;
> + default:
> + perror("Illegal unequal.");
> + }
> + }
> +
> + return c;
> +}
> +
> +/*
> + * evaluate an unequality between 2 Boolean symbols
> + */
> +static struct pexpr *expr_eval_unequal_bool(struct symbol *left, struct symbol *right,
> + enum expr_type type, struct cfdata *data)
> +{
> + struct pexpr *c;
> +
> + if (!left || !right)
> + return pexf(data->constants->const_false);
> +
> + if (!sym_is_boolean(left) || !sym_is_boolean(right)) {
> + perror("Comparing 2 symbols that should be boolean.");
> + return pexf(data->constants->const_false);
> + }
> +
> + switch (type) {
> + case E_LTH:
> + c = pexpr_and(pexpr_not(sym_get_fexpr_both(left, data), data),
> + sym_get_fexpr_both(right, data), data,
> + PEXPR_ARGX);
> + if (left->type == S_TRISTATE)
> + c = pexpr_or(c,
> + pexpr_and(pexf(left->fexpr_m),
> + pexf(right->fexpr_y), data,
> + PEXPR_ARGX),
> + data, PEXPR_ARGX);
> + break;
> + case E_LEQ:
> + c = pexpr_and(pexf(left->fexpr_y), pexf(right->fexpr_y), data,
> + PEXPR_ARGX);
> + if (left->type == S_TRISTATE)
> + c = pexpr_or(c,
> + pexpr_and(pexf(left->fexpr_m),
> + sym_get_fexpr_both(right, data),
> + data, PEXPR_ARGX),
> + data, PEXPR_ARGX);
> + c = pexpr_or(c, pexpr_not(sym_get_fexpr_both(left, data), data),
> + data, PEXPR_ARGX);
> + break;
> + case E_GTH:
> + c = pexpr_and(sym_get_fexpr_both(left, data),
> + pexpr_not(sym_get_fexpr_both(right, data), data),
> + data, PEXPR_ARGX);
> + if (right->type == S_TRISTATE)
> + c = pexpr_or(c,
> + pexpr_and(pexf(left->fexpr_y),
> + pexf(right->fexpr_m), data,
> + PEXPR_ARGX),
> + data, PEXPR_ARGX);
> + break;
> + case E_GEQ:
> + c = pexpr_and(pexf(left->fexpr_y), pexf(right->fexpr_y), data,
> + PEXPR_ARGX);
> + if (right->type == S_TRISTATE)
> + c = pexpr_or(c,
> + pexpr_and(sym_get_fexpr_both(left, data),
> + pexf(right->fexpr_m), data,
> + PEXPR_ARGX),
> + data, PEXPR_ARGX);
> + c = pexpr_or(c,
> + pexpr_not(sym_get_fexpr_both(right, data), data),
> + data, PEXPR_ARGX);
> + break;
> + default:
> + fprintf(stderr, "Wrong type - %s", __func__);
> + c = pexf(data->constants->const_false);
> + }
> +
> + return c;
> +}
> +/*
> + * calculate, when expr will evaluate to yes or mod
> + */
> +struct pexpr *expr_calculate_pexpr_both(struct expr *e, struct cfdata *data)
> +{
> + if (!e)
> + return pexf(data->constants->const_false);
> +
> + if (!expr_can_evaluate_to_mod(e))
> + return expr_calculate_pexpr_y(e, data);
> +
> + switch (e->type) {
> + case E_SYMBOL:
> + return pexpr_or(expr_calculate_pexpr_m(e, data), expr_calculate_pexpr_y(e, data),
> + data, PEXPR_ARGX);
> + case E_AND:
> + return expr_calculate_pexpr_both_and(e->left.expr, e->right.expr, data);
> + case E_OR:
> + return expr_calculate_pexpr_both_or(e->left.expr, e->right.expr, data);
> + case E_NOT:
> + return pexpr_or(expr_calculate_pexpr_m(e, data), expr_calculate_pexpr_y(e, data),
> + data, PEXPR_ARGX);
> + case E_EQUAL:
> + return expr_calculate_pexpr_y_equals(e, data);
> + case E_UNEQUAL:
> + return expr_calculate_pexpr_y_unequals(e, data);
> + case E_LTH:
> + case E_LEQ:
> + case E_GTH:
> + case E_GEQ:
> + return expr_calculate_pexpr_y_comp(e, data);
> + default:
> + // TODO
> + fprintf(stderr, "Unhandled type - %s", __func__);
> + return NULL;
> + }
> +}
> +
> +/*
> + * calculate, when expr will evaluate to yes
> + */
> +struct pexpr *expr_calculate_pexpr_y(struct expr *e, struct cfdata *data)
> +{
> + if (!e)
> + return NULL;
> +
> + switch (e->type) {
> + case E_SYMBOL:
> + return pexf(e->left.sym->fexpr_y);
> + case E_AND:
> + return expr_calculate_pexpr_y_and(e->left.expr, e->right.expr, data);
> + case E_OR:
> + return expr_calculate_pexpr_y_or(e->left.expr, e->right.expr, data);
> + case E_NOT:
> + return expr_calculate_pexpr_y_not(e->left.expr, data);
> + case E_EQUAL:
> + return expr_calculate_pexpr_y_equals(e, data);
> + case E_UNEQUAL:
> + return expr_calculate_pexpr_y_unequals(e, data);
> + case E_LTH:
> + case E_LEQ:
> + case E_GTH:
> + case E_GEQ:
> + return expr_calculate_pexpr_y_comp(e, data);
> + default:
> + fprintf(stderr, "Unhandled type - %s", __func__);
> + return NULL;
> + }
> +}
> +
> +/*
> + * calculate, when expr will evaluate to mod
> + */
> +struct pexpr *expr_calculate_pexpr_m(struct expr *e, struct cfdata *data)
> +{
> + if (!e)
> + return NULL;
> +
> + if (!expr_can_evaluate_to_mod(e))
> + return pexf(data->constants->const_false);
> +
> + switch (e->type) {
> + case E_SYMBOL:
> + return pexf(e->left.sym->fexpr_m);
> + case E_AND:
> + return expr_calculate_pexpr_m_and(e->left.expr, e->right.expr, data);
> + case E_OR:
> + return expr_calculate_pexpr_m_or(e->left.expr, e->right.expr, data);
> + case E_NOT:
> + return expr_calculate_pexpr_m_not(e->left.expr, data);
> + default:
> + perror("Trying to evaluate to mod.");
> + return NULL;
> + }
> +}
> +
> +/*
> + * calculate, when expr of type AND will evaluate to yes
> + * A && B
> + */
> +struct pexpr *expr_calculate_pexpr_y_and(struct expr *a, struct expr *b, struct cfdata *data)
> +{
> + return pexpr_and(expr_calculate_pexpr_y(a, data),
> + expr_calculate_pexpr_y(b, data), data,
> + PEXPR_ARGX);
> +}
> +
> +/*
> + * calculate, when expr of type AND will evaluate to mod
> + * (A || A_m) && (B || B_m) && !(A && B)
> + */
> +struct pexpr *expr_calculate_pexpr_m_and(struct expr *a, struct expr *b,
> + struct cfdata *data)
> +{
> + struct pexpr *topright =
> + pexpr_not(pexpr_and(expr_calculate_pexpr_y(a, data),
> + expr_calculate_pexpr_y(b, data),
> + data, PEXPR_ARGX),
> + data);
> + struct pexpr *ll_left = pexpr_or(expr_calculate_pexpr_y(a, data),
> + expr_calculate_pexpr_m(a, data), data,
> + PEXPR_ARGX);
> + struct pexpr *ll_right = pexpr_or(expr_calculate_pexpr_y(b, data),
> + expr_calculate_pexpr_m(b, data), data,
> + PEXPR_ARGX);
> + struct pexpr *topleft = pexpr_and(ll_left, ll_right, data, PEXPR_ARGX);
> +
> + return pexpr_and(topleft, topright, data, PEXPR_ARGX);
> +}
> +
> +/*
> + * calculate, when expr of type AND will evaluate to mod or yes
> + * (A || A_m) && (B || B_m)
> + */
> +struct pexpr *expr_calculate_pexpr_both_and(struct expr *a, struct expr *b,
> + struct cfdata *data)
> +{
> + struct pexpr *left = pexpr_or(expr_calculate_pexpr_y(a, data),
> + expr_calculate_pexpr_m(a, data), data,
> + PEXPR_ARGX);
> + struct pexpr *right = pexpr_or(expr_calculate_pexpr_y(b, data),
> + expr_calculate_pexpr_m(b, data), data,
> + PEXPR_ARGX);
> +
> + return pexpr_and(left, right, data, PEXPR_ARGX);
> +}
> +
> +/*
> + * calculate, when expr of type OR will evaluate to yes
> + * A || B
> + */
> +struct pexpr *expr_calculate_pexpr_y_or(struct expr *a, struct expr *b,
> + struct cfdata *data)
> +{
> + return pexpr_or(expr_calculate_pexpr_y(a, data),
> + expr_calculate_pexpr_y(b, data), data, PEXPR_ARGX);
> +}
> +
> +/*
> + * calculate, when expr of type OR will evaluate to mod
> + * (A_m || B_m) && !A && !B
> + */
> +struct pexpr *expr_calculate_pexpr_m_or(struct expr *a, struct expr *b,
> + struct cfdata *data)
> +{
> + struct pexpr *topright =
> + pexpr_not(expr_calculate_pexpr_y(b, data), data);
> + struct pexpr *lowerleft = pexpr_or(expr_calculate_pexpr_m(a, data),
> + expr_calculate_pexpr_m(b, data),
> + data, PEXPR_ARGX);
> + struct pexpr *topleft = pexpr_and(
> + lowerleft,
> + pexpr_not(expr_calculate_pexpr_y(a, data), data), data,
> + PEXPR_ARGX);
> +
> + return pexpr_and(topleft, topright, data, PEXPR_ARGX);
> +}
> +
> +/*
> + * calculate, when expr of type OR will evaluate to mod or yes
> + * (A_m || A || B_m || B)
> + */
> +struct pexpr *expr_calculate_pexpr_both_or(struct expr *a, struct expr *b,
> + struct cfdata *data)
> +{
> + struct pexpr *left = pexpr_or(expr_calculate_pexpr_y(a, data),
> + expr_calculate_pexpr_m(a, data), data,
> + PEXPR_ARGX);
> + struct pexpr *right = pexpr_or(expr_calculate_pexpr_y(b, data),
> + expr_calculate_pexpr_m(b, data), data,
> + PEXPR_ARGX);
> +
> + return pexpr_or(left, right, data, PEXPR_ARGX);
> +}
> +
> +/*
> + * calculate, when expr of type NOT will evaluate to yes
> + * !(A || A_m)
> + */
> +struct pexpr *expr_calculate_pexpr_y_not(struct expr *e, struct cfdata *data)
> +{
> + return pexpr_not(pexpr_or(expr_calculate_pexpr_y(e, data),
> + expr_calculate_pexpr_m(e, data),
> + data, PEXPR_ARGX),
> + data);
> +}
> +
> +/*
> + * calculate, when expr of type NOT will evaluate to mod
> + * A_m
> + */
> +struct pexpr *expr_calculate_pexpr_m_not(struct expr *e, struct cfdata *data)
> +{
> + return expr_calculate_pexpr_m(e, data);
> +}
> +
> +static struct pexpr *equiv_pexpr_share(struct pexpr *a, struct pexpr *b,
> + struct cfdata *data)
> +{
> + struct pexpr *yes = pexpr_and_share(a, b, data);
> + struct pexpr *not = pexpr_and(pexpr_not_share(a, data),
> + pexpr_not_share(b, data), data,
> + PEXPR_ARGX);
> +
> + return pexpr_or(yes, not, data, PEXPR_ARGX);
> +}
> +
> +static struct pexpr *equiv_pexpr_move(struct pexpr *a, struct pexpr *b,
> + struct cfdata *data,
> + enum pexpr_move move)
> +{
> + return pexpr_move_wrapper(a, b, data, move, equiv_pexpr_share);
> +}
> +
> +/*
> + * create the fexpr of a non-boolean symbol for a specific value
> + */
> +struct fexpr *sym_create_nonbool_fexpr(struct symbol *sym, char *value,
> + struct cfdata *data)
> +{
> + struct fexpr *e;
> + char *s;
> +
> + if (!strcmp(value, "")) {
> + if (sym->type == S_STRING)
> + return sym->nb_vals->head->next->elem;
> + else
> + return sym->nb_vals->head->elem;
> + }
> +
> + e = sym_get_nonbool_fexpr(sym, value);
> +
> + /* fexpr already exists */
> + if (e != NULL)
> + return e;
> +
> + s = value;
> + if (sym->type == S_INT && !string_is_number(value)) {
> + struct symbol *tmp = sym_find(value);
> +
> + if (tmp != NULL)
> + s = (char *) tmp->curr.val;
> + } else if (sym->type == S_HEX && !string_is_hex(value)) {
> + struct symbol *tmp = sym_find(value);
> +
> + if (tmp != NULL)
> + s = (char *) tmp->curr.val;
> + } else if (sym->type == S_STRING) {
> + struct symbol *tmp = sym_find(value);
> +
> + if (tmp != NULL)
> + s = (char *) tmp->curr.val;
> + }
> +
> + if (!strcmp(s, "")) {
> + if (sym->type == S_STRING)
> + return sym->nb_vals->head->next->elem;
> + else
> + return sym->nb_vals->head->elem;
> + }
> +
> + e = sym_get_nonbool_fexpr(sym, s);
> + if (e != NULL)
> + return e;
> +
> + e = fexpr_create(data->sat_variable_nr++, FE_NONBOOL, sym->name);
> + e->sym = sym;
> + str_append(&e->name, "=");
> + str_append(&e->name, s);
> + e->nb_val = str_new();
> + str_append(&e->nb_val, s);
> +
> + fexpr_list_add(sym->nb_vals, e);
> + fexpr_add_to_satmap(e, data);
> +
> + return e;
> +}
> +
> +/*
> + * return the fexpr of a non-boolean symbol for a specific value, NULL if
> + * non-existent
> + */
> +struct fexpr *sym_get_nonbool_fexpr(struct symbol *sym, char *value)
> +{
> + struct fexpr_node *e;
> +
> + fexpr_list_for_each(e, sym->nb_vals) {
> + if (strcmp(str_get(&e->elem->nb_val), value) == 0)
> + return e->elem;
> + }
> +
> + return NULL;
> +}
> +
> +/*
> + * return the fexpr of a non-boolean symbol for a specific value, if it exists
> + * otherwise create it
> + */
> +struct fexpr *sym_get_or_create_nonbool_fexpr(struct symbol *sym, char *value,
> + struct cfdata *data)
> +{
> + struct fexpr *e = sym_get_nonbool_fexpr(sym, value);
> +
> + if (e != NULL)
> + return e;
> + else
> + return sym_create_nonbool_fexpr(sym, value, data);
> +}
> +
> +/*
> + * calculate, when expr of type EQUAL will evaluate to yes
> + * Side effect: May create certain values in e->{left,right}.sym.nb_vals
> + */
> +struct pexpr *expr_calculate_pexpr_y_equals(struct expr *e, struct cfdata *data)
> +{
> + /* comparing 2 tristate constants */
> + if (sym_is_tristate_constant(e->left.sym) &&
> + sym_is_tristate_constant(e->right.sym))
> + return e->left.sym == e->right.sym ?
> + pexf(data->constants->const_true) :
> + pexf(data->constants->const_false);
> +
> + /* comparing 2 nonboolean constants */
> + if (sym_is_nonbool_constant(e->left.sym) &&
> + sym_is_nonbool_constant(e->right.sym))
> + return strcmp(e->left.sym->name, e->right.sym->name) == 0 ?
> + pexf(data->constants->const_true) :
> + pexf(data->constants->const_false);
> +
> + /* comparing 2 boolean/tristate incl. yes/mod/no constants */
> + if (sym_is_bool_or_triconst(e->left.sym) &&
> + sym_is_bool_or_triconst(e->right.sym)) {
> + struct pexpr *yes = equiv_pexpr_move(
> + pexf(e->left.sym->fexpr_y), pexf(e->right.sym->fexpr_y),
> + data, PEXPR_ARGX);
> + struct pexpr *mod = equiv_pexpr_move(
> + pexf(e->left.sym->fexpr_m), pexf(e->right.sym->fexpr_m),
> + data, PEXPR_ARGX);
> +
> + return pexpr_and(yes, mod, data, PEXPR_ARGX);
> + }
> +
> + /* comparing nonboolean with a constant */
> + if (sym_is_nonboolean(e->left.sym) &&
> + sym_is_nonbool_constant(e->right.sym))
> + return pexf(sym_get_or_create_nonbool_fexpr(
> + e->left.sym, e->right.sym->name, data));
> +
> + if (sym_is_nonbool_constant(e->left.sym) &&
> + sym_is_nonboolean(e->right.sym))
> + return pexf(sym_get_or_create_nonbool_fexpr(
> + e->right.sym, e->left.sym->name, data));
> +
> + /* comparing nonboolean with tristate constant, will never be true */
> + if (sym_is_nonboolean(e->left.sym) &&
> + sym_is_tristate_constant(e->right.sym))
> + return pexf(data->constants->const_false);
> + if (sym_is_tristate_constant(e->left.sym) &&
> + sym_is_nonboolean(e->right.sym))
> + return pexf(data->constants->const_false);
> +
> + /* comparing 2 nonboolean symbols */
> + if (sym_is_nonboolean(e->left.sym) && sym_is_nonboolean(e->right.sym)) {
> + struct pexpr *c = pexf(data->constants->const_false);
> + struct fexpr *e1, *e2;
> +
> + for (struct fexpr_node *node1 =
> + e->left.sym->nb_vals->head->next;
> + node1 != NULL; node1 = node1->next) {
> + e1 = node1->elem;
> + for (struct fexpr_node *node2 =
> + e->right.sym->nb_vals->head->next;
> + node2 != NULL; node2 = node2->next) {
> + e2 = node2->elem;
> + if (!strcmp(str_get(&e1->nb_val),
> + str_get(&e2->nb_val))) {
> + c = pexpr_or(
> + c,
> + pexpr_and(pexf(e1),
> + pexf(e2), data,
> + PEXPR_ARGX),
> + data, PEXPR_ARGX);
> + break;
> + }
> + }
> + }
> + return c;
> + }
> +
> + /* comparing boolean item with nonboolean constant, will never be true */
> + if (sym_is_tristate_constant(e->left.sym) &&
> + sym_is_nonbool_constant(e->right.sym))
> + return pexf(data->constants->const_false);
> + if (sym_is_nonbool_constant(e->left.sym) &&
> + sym_is_tristate_constant(e->right.sym))
> + return pexf(data->constants->const_false);
> +
> + /* comparing symbol of type unknown with tristate constant */
> + if (e->left.sym->type == S_UNKNOWN &&
> + sym_is_tristate_constant(e->right.sym))
> + return pexf(data->constants->const_false);
> + if (sym_is_tristate_constant(e->left.sym) &&
> + e->right.sym->type == S_UNKNOWN)
> + return pexf(data->constants->const_false);
> +
> + /* any other comparison is not supported and should not be executed */
> + fprintf(stderr, "Unsupported equality.");
> + print_expr(":", e, 0);
> +
> + return pexf(data->constants->const_false);
> +}
> +
> +/*
> + * transform an UNEQUAL into a Not(EQUAL)
> + */
> +struct pexpr *expr_calculate_pexpr_y_unequals(struct expr *e, struct cfdata *data)
> +{
> + return pexpr_not(expr_calculate_pexpr_y_equals(e, data), data);
> +}
> +
> +struct pexpr *expr_calculate_pexpr_y_comp(struct expr *e, struct cfdata *data)
> +{
> + if (!e)
> + return NULL;
> +
> + switch (e->type) {
> + case E_LTH:
> + case E_LEQ:
> + case E_GTH:
> + case E_GEQ:
> + /* compare non-Boolean symbol with constant */
> + if (sym_is_nonboolean(e->left.sym) &&
> + e->right.sym->type == S_UNKNOWN &&
> + string_is_number(e->right.sym->name)
> + ) {
> + return expr_eval_unequal_nonbool_const(e->left.sym, e->right.sym, e->type,
> + data);
> + }
> + if (sym_is_nonboolean(e->right.sym) &&
> + e->left.sym->type == S_UNKNOWN &&
> + string_is_number(e->left.sym->name)
> + ) {
> + return expr_eval_unequal_nonbool_const(e->right.sym, e->left.sym, e->type,
> + data);
> + }
> +
> + /* compare 2 Boolean symbols */
> + if (sym_is_boolean(e->left.sym) && sym_is_boolean(e->right.sym))
> + return expr_eval_unequal_bool(e->left.sym, e->right.sym, e->type, data);
> +
> + return pexf(data->constants->const_false);
> + default:
> + fprintf(stderr, "Unhandled type - %s", __func__);
> + return NULL;
> + }
> +}
> +
> +static struct pexpr *pexpr_move_wrapper(
> + struct pexpr *a, struct pexpr *b, struct cfdata *data,
> + enum pexpr_move move,
> + struct pexpr *(*func)(struct pexpr *, struct pexpr *, struct cfdata *))
> +{
> + struct pexpr *retval = func(a, b, data);
> +
> + switch (move) {
> + case PEXPR_ARG1:
> + pexpr_put(a);
> + break;
> + case PEXPR_ARG2:
> + pexpr_put(b);
> + break;
> + case PEXPR_ARGX:
> + pexpr_put(a);
> + pexpr_put(b);
> + break;
> + default:
> + fprintf(stderr, "%s: invalid value for @move - %d\n", __func__,
> + move);
> + }
> + return retval;
> +}
> +
> +struct pexpr *pexpr_and(struct pexpr *a, struct pexpr *b, struct cfdata *data, enum pexpr_move move)
> +{
> + return pexpr_move_wrapper(a, b, data, move, pexpr_and_share);
> +}
> +
> +/*
> + * macro to create a pexpr of type AND
> + */
> +struct pexpr *pexpr_and_share(struct pexpr *a, struct pexpr *b, struct cfdata *data)
> +{
> + struct pexpr *e;
> +
> + /* A && A -> A */
> + if (a == b || pexpr_eq(a, b, data)) {
> + pexpr_get(a);
> + return a;
> + }
> +
> + /* simplifications:
> + * expr && False -> False
> + * expr && True -> expr
> + */
> + if ((a->type == PE_SYMBOL &&
> + a->left.fexpr == data->constants->const_false) ||
> + (b->type == PE_SYMBOL &&
> + b->left.fexpr == data->constants->const_true)) {
> + pexpr_get(a);
> + return a;
> + }
> +
> + if ((b->type == PE_SYMBOL &&
> + b->left.fexpr == data->constants->const_false) ||
> + (a->type == PE_SYMBOL &&
> + a->left.fexpr == data->constants->const_true)) {
> + pexpr_get(b);
> + return b;
> + }
> +
> + /* (A && B) && C -> A && B if B == C */
> + if (a->type == PE_AND && pexpr_eq(a->right.pexpr, b, data)) {
> + pexpr_get(a);
> + return a;
> + }
> +
> + /* A && (B && C) -> B && C if A == B */
> + if (b->type == PE_AND && pexpr_eq(a, b->left.pexpr, data)) {
> + pexpr_get(b);
> + return b;
> + }
> +
> + if (a->type == PE_OR && b->type == PE_OR) {
> + e = NULL;
> + /* (A || B) && (C || D) -> A || (B && D) if A == C */
> + if (pexpr_eq(a->left.pexpr, b->left.pexpr, data)) {
> + e = pexpr_or(a->left.pexpr,
> + pexpr_and_share(a->right.pexpr,
> + b->right.pexpr, data),
> + data, PEXPR_ARG2);
> + }
> + /* (A || B) && (C || D) -> B || (A && C) if B == D */
> + else if (pexpr_eq(a->right.pexpr, b->right.pexpr, data)) {
> + e = pexpr_or(a->right.pexpr,
> + pexpr_and_share(a->left.pexpr,
> + b->left.pexpr, data),
> + data, PEXPR_ARG2);
> + }
> + /* (A || B) && (C || D) -> A || (B && C) if A == D */
> + else if (pexpr_eq(a->left.pexpr, b->right.pexpr, data)) {
> + e = pexpr_or(a->left.pexpr,
> + pexpr_and_share(a->right.pexpr,
> + b->left.pexpr, data),
> + data, PEXPR_ARG2);
> + }
> + /* (A || B) && (C || D) -> B || (A && D) if B == C */
> + else if (pexpr_eq(a->right.pexpr, b->left.pexpr, data)) {
> + e = pexpr_or(a->right.pexpr,
> + pexpr_and_share(a->left.pexpr,
> + b->right.pexpr, data),
> + data, PEXPR_ARG2);
> + }
> + if (e)
> + return e;
> + }
> +
> + /* general case */
> + e = xcalloc(1, sizeof(*e));
> + pexpr_get(a);
> + pexpr_get(b);
> + pexpr_construct_and(e, a, b, 1);
> + return e;
> +}
> +
> +struct pexpr *pexpr_or(struct pexpr *a, struct pexpr *b, struct cfdata *data, enum pexpr_move move)
> +{
> + return pexpr_move_wrapper(a, b, data, move, pexpr_or_share);
> +}
> +
> +/*
> + * macro to create a pexpr of type OR
> + */
> +struct pexpr *pexpr_or_share(struct pexpr *a, struct pexpr *b, struct cfdata *data)
> +{
> + struct pexpr *e;
> + bool cond1, cond2;
> +
> + /* A || A -> A */
> + if (a == b || pexpr_eq(a, b, data)) {
> + pexpr_get(a);
> + return a;
> + }
> +
> + /* simplifications:
> + * A || False -> A
> + * A || True -> True
> + */
> + cond1 = a->type == PE_SYMBOL && a->left.fexpr == data->constants->const_false;
> + cond2 = b->type == PE_SYMBOL && b->left.fexpr == data->constants->const_true;
> + if (cond1 || cond2) {
> + pexpr_get(b);
> + return b;
> + }
> + cond1 = b->type == PE_SYMBOL && b->left.fexpr == data->constants->const_false;
> + cond2 = a->type == PE_SYMBOL && a->left.fexpr == data->constants->const_true;
> + if (cond1 || cond2) {
> + pexpr_get(a);
> + return a;
> + }
> +
> + /* A || (B && C) -> A if (A == B || A == C) */
> + if (b->type == PE_AND && (
> + pexpr_eq(a, b->left.pexpr, data) || pexpr_eq(a, b->right.pexpr, data)
> + )) {
> + pexpr_get(a);
> + return a;
> + }
> + /* (A && B) || C -> C if (A == C || B == C) */
> + if (a->type == PE_AND && (
> + pexpr_eq(a->left.pexpr, b, data) || pexpr_eq(a->right.pexpr, b, data)
> + )) {
> + pexpr_get(b);
> + return b;
> + }
> +
> + /* -A || B -> True if A == B
> + * A || -B -> True if A == B
> + */
> + cond1 = a->type == PE_NOT && pexpr_eq(a->left.pexpr, b, data);
> + cond2 = b->type == PE_NOT && pexpr_eq(a, b->left.pexpr, data);
> + if (cond1 || cond2)
> + return pexf(data->constants->const_true);
> +
> + if (a->type == PE_AND && b->type == PE_AND) {
> + e = NULL;
> + /* (A && B) || (C && D) -> A && (B || D) if (A == C) */
> + if (pexpr_eq(a->left.pexpr, b->left.pexpr, data)) {
> + e = pexpr_and(a->left.pexpr,
> + pexpr_or_share(a->right.pexpr, b->right.pexpr, data), data,
> + PEXPR_ARG2);
> + }
> + /* (A && B) || (C && D) -> B && (A || C) if (B == D) */
> + if (pexpr_eq(a->right.pexpr, b->right.pexpr, data)) {
> + e = pexpr_and(a->right.pexpr,
> + pexpr_or_share(a->left.pexpr, b->left.pexpr, data), data,
> + PEXPR_ARG2);
> + }
> + /* (A && B) || (C && D) -> A && (B || C) if (A == D) */
> + if (pexpr_eq(a->left.pexpr, b->right.pexpr, data)) {
> + e = pexpr_and(a->left.pexpr,
> + pexpr_or_share(a->right.pexpr, b->left.pexpr, data), data,
> + PEXPR_ARG2);
> + }
> + /* (A && B) || (C && D) -> B && (A || D) if (B == C) */
> + if (pexpr_eq(a->right.pexpr, b->left.pexpr, data)) {
> + e = pexpr_and(a->right.pexpr,
> + pexpr_or_share(a->left.pexpr, b->right.pexpr, data), data,
> + PEXPR_ARG2);
> + }
> + if (e)
> + return e;
> + }
> +
> + /* (A && B) || (C || D) -> C || D if
> + * A == C || A == D || B == C || B == D
> + */
> + if (a->type == PE_AND && b->type == PE_OR && (
> + pexpr_eq(a->left.pexpr, b->left.pexpr, data) ||
> + pexpr_eq(a->left.pexpr, b->right.pexpr, data) ||
> + pexpr_eq(a->right.pexpr, b->left.pexpr, data) ||
> + pexpr_eq(a->right.pexpr, b->right.pexpr, data)
> + )) {
> + pexpr_get(b);
> + return b;
> + }
> + /* (C || D) || (A && B) -> C || D if
> + * A == C || A == D || B == C || B == D
> + */
> + if (a->type == PE_OR && b->type == PE_AND && (
> + pexpr_eq(a->left.pexpr, b->left.pexpr, data) ||
> + pexpr_eq(a->left.pexpr, b->right.pexpr, data) ||
> + pexpr_eq(a->right.pexpr, b->left.pexpr, data) ||
> + pexpr_eq(a->right.pexpr, b->right.pexpr, data)
> + )) {
> + pexpr_get(a);
> + return a;
> + }
> +
> + /* general case */
> + e = xcalloc(1, sizeof(*e));
> + pexpr_get(a);
> + pexpr_get(b);
> + pexpr_construct_or(e, a, b, 1);
> +
> + return e;
> +}
> +
> +struct pexpr *pexpr_not(struct pexpr *a, struct cfdata *data)
> +{
> + struct pexpr *retval = pexpr_not_share(a, data);
> +
> + pexpr_put(a);
> + return retval;
> +}
> +
> +/*
> + * Builds NOT(@a)
> + */
> +struct pexpr *pexpr_not_share(struct pexpr *a, struct cfdata *data)
> +{
> + struct pexpr *ret_val;
> +
> + if (a->type == PE_SYMBOL &&
> + a->left.fexpr == data->constants->const_false)
> + ret_val = pexf(data->constants->const_true);
> + else if (a->type == PE_SYMBOL &&
> + a->left.fexpr == data->constants->const_true)
> + ret_val = pexf(data->constants->const_false);
> + /* eliminate double negation */
> + else if (a->type == PE_NOT) {
> + ret_val = a->left.pexpr;
> + pexpr_get(ret_val);
> + }
> + /* De Morgan */
> + else if (a->type == PE_AND) {
> + ret_val = xmalloc(sizeof(*ret_val));
> + pexpr_construct_or(ret_val,
> + pexpr_not_share(a->left.pexpr, data),
> + pexpr_not_share(a->right.pexpr, data), 1);
> + } else if (a->type == PE_OR) {
> + ret_val = xmalloc(sizeof(*ret_val));
> + pexpr_construct_and(ret_val,
> + pexpr_not_share(a->left.pexpr, data),
> + pexpr_not_share(a->right.pexpr, data), 1);
> + } else {
> + ret_val = xmalloc(sizeof(*ret_val));
> + pexpr_get(a);
> + pexpr_construct_not(ret_val, a, 1);
> + }
> +
> + return ret_val;
> +}
> +
> +struct pexpr *pexpr_implies(struct pexpr *a, struct pexpr *b, struct cfdata *data,
> + enum pexpr_move move)
> +{
> + return pexpr_move_wrapper(a, b, data, move, pexpr_implies_share);
> +}
> +
> +/*
> + * macro to construct a pexpr for "A implies B"
> + */
> +struct pexpr *pexpr_implies_share(struct pexpr *a, struct pexpr *b, struct cfdata *data)
> +{
> + /* A => B -> True if A == B */
> + if (a == b || pexpr_eq(a, b, data))
> + return pexf(data->constants->const_true);
> +
> + /* (A => B && C) -> (A => C) if A == B */
> + if (b->type == PE_AND && pexpr_eq(a, b->left.pexpr, data))
> + return pexpr_implies_share(a, b->right.pexpr, data);
> + /* (A => B && C) -> (A => B) if A == C */
> + if (b->type == PE_AND && pexpr_eq(a, b->right.pexpr, data))
> + return pexpr_implies_share(a, b->left.pexpr, data);
> +
> + /* (A => B || C) -> True if (A == B || A == C) */
> + if (b->type == PE_OR && (
> + pexpr_eq(a, b->left.pexpr, data) || pexpr_eq(a, b->right.pexpr, data)
> + ))
> + return pexf(data->constants->const_true);
> +
> + /* (A && B => C) -> True if (A == C || B == C) */
> + if (a->type == PE_AND && (
> + pexpr_eq(a->left.pexpr, b, data) || pexpr_eq(a->right.pexpr, b, data)
> + ))
> + return pexf(data->constants->const_true);
> +
> + return pexpr_or(pexpr_not_share(a, data), b, data, PEXPR_ARG1);
> +}
> +
> +/*
> + * check whether a pexpr is in CNF
> + */
> +bool pexpr_is_cnf(struct pexpr *e)
> +{
> + if (!e)
> + return false;
> +
> + switch (e->type) {
> + case PE_SYMBOL:
> + return true;
> + case PE_AND:
> + return false;
> + case PE_OR:
> + return pexpr_is_cnf(e->left.pexpr) &&
> + pexpr_is_cnf(e->right.pexpr);
> + case PE_NOT:
> + return e->left.pexpr->type == PE_SYMBOL;
> + }
> +
> + return false;
> +}
> +
> +/*
> + * check whether a pexpr is in NNF
> + */
> +bool pexpr_is_nnf(struct pexpr *e)
> +{
> + if (!e)
> + return false;
> +
> + switch (e->type) {
> + case PE_SYMBOL:
> + return true;
> + case PE_AND:
> + case PE_OR:
> + return pexpr_is_nnf(e->left.pexpr) && pexpr_is_nnf(e->right.pexpr);
> + case PE_NOT:
> + return e->left.pexpr->type == PE_SYMBOL;
> + }
> +
> + return false;
> +}
> +
> +/*
> + * return fexpr_both for a symbol
> + */
> +struct pexpr *sym_get_fexpr_both(struct symbol *sym, struct cfdata *data)
> +{
> + return sym->type == S_TRISTATE ?
> + pexpr_or(pexf(sym->fexpr_m), pexf(sym->fexpr_y),
> + data, PEXPR_ARGX) :
> + pexf(sym->fexpr_y);
> +}
> +
> +/*
> + * return fexpr_sel_both for a symbol
> + */
> +struct pexpr *sym_get_fexpr_sel_both(struct symbol *sym, struct cfdata *data)
> +{
> + if (!sym->rev_dep.expr)
> + return pexf(data->constants->const_false);
> +
> + return sym->type == S_TRISTATE ?
> + pexpr_or(pexf(sym->fexpr_sel_m),
> + pexf(sym->fexpr_sel_y), data, PEXPR_ARGX) :
> + pexf(sym->fexpr_sel_y);
> +}
> +
> +/*
> + * check, if the fexpr is a symbol, a True/False-constant, a literal symbolizing a non-boolean or
> + * a choice symbol
> + */
> +bool fexpr_is_symbol(struct fexpr *e)
> +{
> + return e->type == FE_SYMBOL || e->type == FE_FALSE || e->type == FE_TRUE ||
> + e->type == FE_NONBOOL || e->type == FE_CHOICE || e->type == FE_SELECT ||
> + e->type == FE_NPC;
> +}
> +
> +/*
> + * check whether a pexpr is a symbol or a negated symbol
> + */
> +bool pexpr_is_symbol(struct pexpr *e)
> +{
> + return e->type == PE_SYMBOL || (e->type == PE_NOT && e->left.pexpr->type == PE_SYMBOL);
> +}
> +
> +/*
> + * check whether the fexpr is a constant (true/false)
> + */
> +bool fexpr_is_constant(struct fexpr *e, struct cfdata *data)
> +{
> + return e == data->constants->const_true || e == data->constants->const_false;
> +}
> +
> +/*
> + * add a fexpr to the satmap
> + */
> +void fexpr_add_to_satmap(struct fexpr *e, struct cfdata *data)
> +{
> + if (e->satval >= data->satmap_size) {
> + data->satmap =
> + xrealloc(data->satmap, data->satmap_size * 2 * sizeof(**data->satmap));
> + data->satmap_size *= 2;
> + }
> +
> + data->satmap[e->satval] = e;
I see a bug here.
Size mismatch for memory allocation.
(much bigger than used)
> +}
> +
> +/*
> + * print a fexpr
> + */
> +void fexpr_print(char *tag, struct fexpr *e)
> +{
> + if (!e)
> + return;
> +
> + printf("%s: %s\n", tag, str_get(&e->name));
> +}
> +
> +/*
> + * write an fexpr into a string (format needed for testing)
> + */
> +void fexpr_as_char(struct fexpr *e, struct gstr *s)
> +{
> + if (!e)
> + return;
> +
> + switch (e->type) {
> + case FE_SYMBOL:
> + case FE_CHOICE:
> + case FE_SELECT:
> + case FE_NPC:
> + case FE_NONBOOL:
> + str_append(s, "definedEx(");
> + str_append(s, str_get(&e->name));
> + str_append(s, ")");
> + return;
> + case FE_FALSE:
> + str_append(s, "0");
> + return;
> + case FE_TRUE:
> + str_append(s, "1");
> + return;
> + default:
> + return;
> + }
> +}
> +
> +/*
> + * write a pexpr into a string
> + */
> +void pexpr_as_char(struct pexpr *e, struct gstr *s, int parent, struct cfdata *data)
> +{
> + if (!e)
> + return;
> +
> + switch (e->type) {
> + case PE_SYMBOL:
> + if (e->left.fexpr == data->constants->const_false) {
> + str_append(s, "0");
> + return;
> + }
> + if (e->left.fexpr == data->constants->const_true) {
> + str_append(s, "1");
> + return;
> + }
> + str_append(s, "definedEx(");
> + str_append(s, str_get(&e->left.fexpr->name));
> + str_append(s, ")");
> + return;
> + case PE_AND:
> + if (parent != PE_AND)
> + str_append(s, "(");
> + pexpr_as_char(e->left.pexpr, s, PE_AND, data);
> + str_append(s, " && ");
> + pexpr_as_char(e->right.pexpr, s, PE_AND, data);
> + if (parent != PE_AND)
> + str_append(s, ")");
> + return;
> + case PE_OR:
> + if (parent != PE_OR)
> + str_append(s, "(");
> + pexpr_as_char(e->left.pexpr, s, PE_OR, data);
> + str_append(s, " || ");
> + pexpr_as_char(e->right.pexpr, s, PE_OR, data);
> + if (parent != PE_OR)
> + str_append(s, ")");
> + return;
> + case PE_NOT:
> + str_append(s, "!");
> + pexpr_as_char(e->left.pexpr, s, PE_NOT, data);
> + return;
> + }
> +}
> +
> +/*
> + * write a pexpr into a string
> + */
> +void pexpr_as_char_short(struct pexpr *e, struct gstr *s, int parent)
> +{
> + if (!e)
> + return;
> +
> + switch (e->type) {
> + case PE_SYMBOL:
> + str_append(s, str_get(&e->left.fexpr->name));
> + return;
> + case PE_AND:
> + if (parent != PE_AND)
> + str_append(s, "(");
> + pexpr_as_char_short(e->left.pexpr, s, PE_AND);
> + str_append(s, " && ");
> + pexpr_as_char_short(e->right.pexpr, s, PE_AND);
> + if (parent != PE_AND)
> + str_append(s, ")");
> + return;
> + case PE_OR:
> + if (parent != PE_OR)
> + str_append(s, "(");
> + pexpr_as_char_short(e->left.pexpr, s, PE_OR);
> + str_append(s, " || ");
> + pexpr_as_char_short(e->right.pexpr, s, PE_OR);
> + if (parent != PE_OR)
> + str_append(s, ")");
> + return;
> + case PE_NOT:
> + str_append(s, "!");
> + pexpr_as_char_short(e->left.pexpr, s, PE_NOT);
> + return;
> + }
> +}
> +
> +/*
> + * check whether a pexpr contains a specific fexpr
> + */
> +bool pexpr_contains_fexpr(struct pexpr *e, struct fexpr *fe)
> +{
> + if (!e)
> + return false;
> +
> + switch (e->type) {
> + case PE_SYMBOL:
> + return e->left.fexpr->satval == fe->satval;
> + case PE_AND:
> + case PE_OR:
> + return pexpr_contains_fexpr(e->left.pexpr, fe) ||
> + pexpr_contains_fexpr(e->right.pexpr, fe);
> + case PE_NOT:
> + return e->left.pexpr->left.fexpr->satval == fe->satval;
> + }
> +
> + return false;
> +}
> +
> +/*
> + * init list of fexpr
> + */
> +struct fexpr_list *fexpr_list_init(void)
> +{
> + struct fexpr_list *list = xcalloc(1, sizeof(*list));
> +
> + list->head = NULL;
> + list->tail = NULL;
> + list->size = 0;
> +
> + return list;
> +}
> +
> +/*
> + * init list of fexpr_list
> + */
> +struct fexl_list *fexl_list_init(void)
> +{
> + struct fexl_list *list = xcalloc(1, sizeof(*list));
> +
> + list->head = NULL;
> + list->tail = NULL;
> + list->size = 0;
> +
> + return list;
> +}
> +
> +/*
> + * init list of pexpr
> + */
> +struct pexpr_list *pexpr_list_init(void)
> +{
> + struct pexpr_list *list = xcalloc(1, sizeof(*list));
> +
> + list->head = NULL;
> + list->tail = NULL;
> + list->size = 0;
> +
> + return list;
> +}
> +
> +/*
> + * init list of symbol_fix
> + */
> +struct sfix_list *sfix_list_init(void)
> +{
> + struct sfix_list *list = xcalloc(1, sizeof(*list));
> +
> + list->head = NULL;
> + list->tail = NULL;
> + list->size = 0;
> +
> + return list;
> +}
> +
> +/*
> + * init list of symbol_fix
> + */
> +struct sfl_list *sfl_list_init(void)
> +{
> + struct sfl_list *list = xcalloc(1, sizeof(*list));
> +
> + list->head = NULL;
> + list->tail = NULL;
> + list->size = 0;
> +
> + return list;
> +}
> +
> +/*
> + * init list of symbol_dvalue
> + */
> +struct sdv_list *sdv_list_init(void)
> +{
> + struct sdv_list *list = xcalloc(1, sizeof(*list));
> +
> + list->head = NULL;
> + list->tail = NULL;
> + list->size = 0;
> +
> + return list;
> +}
> +
> +/*
> + * init list of symbols
> + */
> +struct sym_list *sym_list_init(void)
> +{
> + struct sym_list *list = xcalloc(1, sizeof(*list));
> +
> + list->head = NULL;
> + list->tail = NULL;
> + list->size = 0;
> +
> + return list;
> +}
> +
> +/*
> + * init list of default_maps
> + */
> +struct defm_list *defm_list_init(void)
> +{
> + struct defm_list *list = xcalloc(1, sizeof(*list));
> +
> + list->head = NULL;
> + list->tail = NULL;
> + list->size = 0;
> +
> + return list;
> +}
> +
> +/*
> + * init list of properties
> + */
> +struct prop_list *prop_list_init(void)
> +{
> + struct prop_list *list = xcalloc(1, sizeof(*list));
> +
> + list->head = NULL;
> + list->tail = NULL;
> + list->size = 0;
> +
> + return list;
> +}
A bunch of similar *_list_init() functions.
Do not do this.
Kconfig uses the similar liss.h to the kernel space.
Remove all the duplications.
> +/*
> + * add element to tail of a fexpr_list
> + */
> +void fexpr_list_add(struct fexpr_list *list, struct fexpr *fe)
> +{
> + struct fexpr_node *node = xcalloc(1, sizeof(*node));
> +
> + node->elem = fe;
> +
> + if (list->size == 0) {
> + list->head = node;
> + list->tail = node;
> + } else {
> + node->prev = list->tail;
> + list->tail = node;
> + node->prev->next = node;
> + }
> +
> + list->size++;
> +}
> +
> +/*
> + * add element to tail of a fexl_list
> + */
> +void fexl_list_add(struct fexl_list *list, struct fexpr_list *elem)
> +{
> + struct fexl_node *node = xcalloc(1, sizeof(*node));
> +
> + node->elem = elem;
> +
> + if (list->size == 0) {
> + list->head = node;
> + list->tail = node;
> + } else {
> + node->prev = list->tail;
> + list->tail = node;
> + node->prev->next = node;
> + }
> +
> + list->size++;
> +}
> +
> +/*
> + * add element to tail of a pexpr_list
> + */
> +void pexpr_list_add(struct pexpr_list *list, struct pexpr *e)
> +{
> + struct pexpr_node *node = xcalloc(1, sizeof(*node));
> +
> + node->elem = e;
> +
> + if (list->size == 0) {
> + list->head = node;
> + list->tail = node;
> + } else {
> + node->prev = list->tail;
> + list->tail = node;
> + node->prev->next = node;
> + }
> +
> + list->size++;
> +}
> +
> +/*
> + * add element to tail of a sfix_list
> + */
> +void sfix_list_add(struct sfix_list *list, struct symbol_fix *fix)
> +{
> + struct sfix_node *node = xcalloc(1, sizeof(*node));
> +
> + node->elem = fix;
> +
> + if (list->size == 0) {
> + list->head = node;
> + list->tail = node;
> + } else {
> + node->prev = list->tail;
> + list->tail = node;
> + node->prev->next = node;
> + }
> +
> + list->size++;
> +}
> +
> +/*
> + * add element to tail of a sfl_list
> + */
> +void sfl_list_add(struct sfl_list *list, struct sfix_list *elem)
> +{
> + struct sfl_node *node = xcalloc(1, sizeof(*node));
> +
> + node->elem = elem;
> +
> + if (list->size == 0) {
> + list->head = node;
> + list->tail = node;
> + } else {
> + node->prev = list->tail;
> + list->tail = node;
> + node->prev->next = node;
> + }
> +
> + list->size++;
> +}
> +
> +/*
> + * add element to tail of a sdv_list
> + */
> +void sdv_list_add(struct sdv_list *list, struct symbol_dvalue *sdv)
> +{
> + struct sdv_node *node = xcalloc(1, sizeof(*node));
> +
> + node->elem = sdv;
> +
> + if (list->size == 0) {
> + list->head = node;
> + list->tail = node;
> + } else {
> + node->prev = list->tail;
> + list->tail = node;
> + node->prev->next = node;
> + }
> +
> + list->size++;
> +}
> +
> +/*
> + * add element to tail of a sym_list
> + */
> +void sym_list_add(struct sym_list *list, struct symbol *sym)
> +{
> + struct sym_node *node = xcalloc(1, sizeof(*node));
> +
> + node->elem = sym;
> +
> + if (list->size == 0) {
> + list->head = node;
> + list->tail = node;
> + } else {
> + node->prev = list->tail;
> + list->tail = node;
> + node->prev->next = node;
> + }
> +
> + list->size++;
> +}
> +
> +/*
> + * add element to tail of a defm_list
> + */
> +void defm_list_add(struct defm_list *list, struct default_map *map)
> +{
> + struct defm_node *node = xcalloc(1, sizeof(*node));
> +
> + node->elem = map;
> +
> + if (list->size == 0) {
> + list->head = node;
> + list->tail = node;
> + } else {
> + node->prev = list->tail;
> + list->tail = node;
> + node->prev->next = node;
> + }
> +
> + list->size++;
> +}
> +
> +/*
> + * add element to tail of a prop_list
> + */
> +void prop_list_add(struct prop_list *list, struct property *prop)
> +{
> + struct prop_node *node = xcalloc(1, sizeof(*node));
> +
> + node->elem = prop;
> +
> + if (list->size == 0) {
> + list->head = node;
> + list->tail = node;
> + } else {
> + node->prev = list->tail;
> + list->tail = node;
> + node->prev->next = node;
> + }
> +
> + list->size++;
> +}
Again. A bunch of *_list_add().
> +/*
> + * delete an element from a fexpr_list
> + */
> +void fexpr_list_delete(struct fexpr_list *list, struct fexpr_node *node)
> +{
> + if (list->size == 0 || node == NULL)
> + return;
> +
> + if (node == list->head)
> + list->head = node->next;
> + else
> + node->prev->next = node->next;
> +
> + if (node == list->tail)
> + list->tail = node->prev;
> + else
> + node->next->prev = node->prev;
> +
> + list->size--;
> + free(node);
> +}
> +
> +/*
> + * delete an element from a fexpr_list
> + */
> +void sfix_list_delete(struct sfix_list *list, struct sfix_node *node)
> +{
> + if (list->size == 0 || node == NULL)
> + return;
> +
> + if (node == list->head)
> + list->head = node->next;
> + else
> + node->prev->next = node->next;
> +
> + if (node == list->tail)
> + list->tail = node->prev;
> + else
> + node->next->prev = node->prev;
> +
> + list->size--;
> + free(node);
> +}
> +
> +/*
> + * delete an element from a fexpr_list
> + */
> +void pexpr_list_delete(struct pexpr_list *list, struct pexpr_node *node)
> +{
> + if (list->size == 0 || node == NULL)
> + return;
> +
> + if (node == list->head)
> + list->head = node->next;
> + else
> + node->prev->next = node->next;
> +
> + if (node == list->tail)
> + list->tail = node->prev;
> + else
> + node->next->prev = node->prev;
> +
> + list->size--;
> + free(node);
> +}
> +
> +/*
> + * delete an element from a fexl_list
> + */
> +void fexl_list_delete(struct fexl_list *list, struct fexl_node *node)
> +{
> + if (list->size == 0 || node == NULL)
> + return;
> +
> + if (node == list->head)
> + list->head = node->next;
> + else
> + node->prev->next = node->next;
> +
> + if (node == list->tail)
> + list->tail = node->prev;
> + else
> + node->next->prev = node->prev;
> +
> + list->size--;
> + free(node);
> +}
> +
> +/*
> + * delete the first occurrence of elem in an fexl_list
> + */
> +void fexl_list_delete_elem(struct fexl_list *list, struct fexpr_list *elem)
> +{
> + struct fexl_node *node, *to_delete = NULL;
> +
> + fexl_list_for_each(node, list) {
> + if (node->elem == elem) {
> + to_delete = node;
> + break;
> + }
> + }
> +
> + if (to_delete != NULL)
> + fexl_list_delete(list, to_delete);
> +}
> +
> +/*
> + * make a shallow copy of a fexpr_list
> + */
> +struct fexpr_list *fexpr_list_copy(struct fexpr_list *list)
> +{
> + struct fexpr_list *ret = fexpr_list_init();
> + struct fexpr_node *node;
> +
> + fexpr_list_for_each(node, list)
> + fexpr_list_add(ret, node->elem);
> +
> + return ret;
> +}
> +
> +/*
> + * make a shallow copy of a fexl_list
> + */
> +struct fexl_list *fexl_list_copy(struct fexl_list *list)
> +{
> + struct fexl_list *ret = fexl_list_init();
> + struct fexl_node *node;
> +
> + fexl_list_for_each(node, list)
> + fexl_list_add(ret, node->elem);
> +
> + return ret;
> +}
> +
> +/*
> + * make a shallow copy of a sdv_list
> + */
> +struct sdv_list *sdv_list_copy(struct sdv_list *list)
> +{
> + struct sdv_list *ret = sdv_list_init();
> + struct sdv_node *node;
> +
> + sdv_list_for_each(node, list)
> + sdv_list_add(ret, node->elem);
> +
> +
> + return ret;
> +}
> +
> +/*
> + * make a shallow copy of a sfix_list
> + */
> +struct sfix_list *sfix_list_copy(struct sfix_list *list)
> +{
> + struct sfix_list *ret = sfix_list_init();
> + struct sfix_node *node;
> +
> + sfix_list_for_each(node, list)
> + sfix_list_add(ret, node->elem);
> +
> + return ret;
> +}
> +
> +/*
> + * print a fexpr_list
> + */
> +void fexpr_list_print(char *title, struct fexpr_list *list)
> +{
> + struct fexpr_node *node;
> +
> + printf("%s: [", title);
> +
> + fexpr_list_for_each(node, list) {
> + printf("%s", str_get(&node->elem->name));
> + if (node->next != NULL)
> + printf(", ");
> + }
> +
> + printf("]\n");
> +}
> +
> +/*
> + * print a fexl_list
> + */
> +void fexl_list_print(char *title, struct fexl_list *list)
> +{
> + struct fexl_node *node;
> +
> + printf("%s:\n", title);
> +
> + fexl_list_for_each(node, list)
> + fexpr_list_print(":", node->elem);
> +}
> +
> +/*
> + * print a pexpr_list
> + */
> +void pexpr_list_print(char *title, struct pexpr_list *list)
> +{
> + struct pexpr_node *node;
> +
> + printf("%s: [", title);
> +
> + pexpr_list_for_each(node, list) {
> + pexpr_print_util(node->elem, -1);
> + if (node->next != NULL)
> + printf(", ");
> + }
> +
> + printf("]\n");
> +}
> +
> +/*
> + * free an fexpr_list
> + */
> +void fexpr_list_free(struct fexpr_list *list)
> +{
> + struct fexpr_node *node = list->head, *tmp;
> +
> + while (node != NULL) {
> + tmp = node->next;
> + free(node);
> + node = tmp;
> + }
> +
> + free(list);
> +}
> +
> +/*
> + * free an defm_list (and pexpr_put the conditions of the maps and free the
> + * node->element's)
> + */
> +void defm_list_destruct(struct defm_list *list)
> +{
> + struct defm_node *node = list->head, *tmp;
> +
> + while (node != NULL) {
> + tmp = node->next;
> + pexpr_put(node->elem->e);
> + free(node->elem);
> + free(node);
> + node = tmp;
> + }
> +
> + free(list);
> +}
> +
> +/*
> + * free an fexl_list
> + */
> +void fexl_list_free(struct fexl_list *list)
> +{
> + struct fexl_node *node = list->head, *tmp;
> +
> + while (node != NULL) {
> + tmp = node->next;
> + free(node);
> + node = tmp;
> + }
> +
> + free(list);
> +}
> +
> +/*
> + * free a sdv_list
> + */
> +void sdv_list_free(struct sdv_list *list)
> +{
> + struct sdv_node *node = list->head, *tmp;
> +
> + while (node != NULL) {
> + tmp = node->next;
> + free(node);
> + node = tmp;
> + }
> +
> + free(list);
> +}
> +
> +/*
> + * free a pexpr_list (and pexpr_put the elements)
> + */
> +void pexpr_list_free_put(struct pexpr_list *list)
> +{
> + struct pexpr_node *node = list->head, *tmp;
> +
> + while (node != NULL) {
> + tmp = node->next;
> + pexpr_put(node->elem);
> + free(node);
> + node = tmp;
> + }
> +
> + free(list);
> +}
> +
> +/*
> + * free a prop_list
> + */
> +void prop_list_free(struct prop_list *list)
> +{
> + struct prop_node *node = list->head, *tmp;
> +
> + while (node != NULL) {
> + tmp = node->next;
> + free(node);
> + node = tmp;
> + }
> +
> + free(list);
> +}
> +
> +/*
> + * free a sym_list
> + */
> +void sym_list_free(struct sym_list *list)
> +{
> + struct sym_node *node = list->head, *tmp;
> +
> + while (node != NULL) {
> + tmp = node->next;
> + free(node);
> + node = tmp;
> + }
> +
> + free(list);
> +}
> +
> +/*
> + * simplify a pexpr in-place
> + * pexpr && False -> False
> + * pexpr && True -> pexpr
> + * pexpr || False -> pexpr
> + * pexpr || True -> True
> + */
> +static void pexpr_eliminate_yn(struct pexpr *e, struct cfdata *data)
> +{
> + struct pexpr *tmp;
> + unsigned int ref_count;
> +
> + if (!e)
> + return;
> +
> + switch (e->type) {
> + case PE_AND:
> + pexpr_eliminate_yn(e->left.pexpr, data);
> + pexpr_eliminate_yn(e->right.pexpr, data);
> + if (e->left.pexpr->type == PE_SYMBOL) {
> + if (e->left.pexpr->left.fexpr == data->constants->const_false) {
> + pexpr_put(e->left.pexpr);
> + pexpr_put(e->right.pexpr);
> + ref_count = e->ref_count;
> + pexpr_construct_sym(
> + e, data->constants->const_false,
> + ref_count);
> + return;
> + } else if (e->left.pexpr->left.fexpr == data->constants->const_true) {
> + pexpr_put(e->left.pexpr);
> + tmp = e->right.pexpr;
> + ref_count = e->ref_count;
> + pexpr_shallow_copy(e, tmp, ref_count);
> + pexpr_put(tmp);
> + return;
> + }
> + }
> + if (e->right.pexpr->type == PE_SYMBOL) {
> + if (e->right.pexpr->left.fexpr == data->constants->const_false) {
> + pexpr_put(e->left.pexpr);
> + pexpr_put(e->right.pexpr);
> + ref_count = e->ref_count;
> + pexpr_construct_sym(
> + e, data->constants->const_false,
> + ref_count);
> + return;
> + } else if (e->right.pexpr->left.fexpr == data->constants->const_true) {
> + pexpr_put(e->right.pexpr);
> + tmp = e->left.pexpr;
> + ref_count = e->ref_count;
> + pexpr_shallow_copy(e, tmp, ref_count);
> + pexpr_put(tmp);
> + return;
> + }
> + }
> + break;
> + case PE_OR:
> + pexpr_eliminate_yn(e->left.pexpr, data);
> + pexpr_eliminate_yn(e->right.pexpr, data);
> + if (e->left.pexpr->type == PE_SYMBOL) {
> + if (e->left.pexpr->left.fexpr == data->constants->const_false) {
> + pexpr_put(e->left.pexpr);
> + tmp = e->right.pexpr;
> + ref_count = e->ref_count;
> + pexpr_shallow_copy(e, tmp, ref_count);
> + pexpr_put(tmp);
> + return;
> + } else if (e->left.pexpr->left.fexpr == data->constants->const_true) {
> + pexpr_put(e->left.pexpr);
> + pexpr_put(e->right.pexpr);
> + ref_count = e->ref_count;
> + pexpr_construct_sym(
> + e, data->constants->const_true,
> + ref_count);
> + return;
> + }
> + }
> + if (e->right.pexpr->type == PE_SYMBOL) {
> + if (e->right.pexpr->left.fexpr == data->constants->const_false) {
> + pexpr_put(e->right.pexpr);
> + tmp = e->left.pexpr;
> + ref_count = e->ref_count;
> + pexpr_shallow_copy(e, tmp, ref_count);
> + pexpr_put(tmp);
> + return;
> + } else if (e->right.pexpr->left.fexpr == data->constants->const_true) {
> + pexpr_put(e->left.pexpr);
> + pexpr_put(e->right.pexpr);
> + ref_count = e->ref_count;
> + pexpr_construct_sym(e,
> + data->constants->const_true,
> + ref_count);
> + return;
> + }
> + }
> + default:
> + break;
> + }
> +}
> +
> +static void pexpr_shallow_copy(struct pexpr *dest, struct pexpr *org, unsigned int ref_count)
> +{
> + struct pexpr inter;
> +
> + inter.type = org->type;
> + inter.left = org->left;
> + inter.right = org->right;
> + if (org->type == PE_OR || org->type == PE_AND) {
> + pexpr_get(org->left.pexpr);
> + pexpr_get(org->right.pexpr);
> + } else if (org->type == PE_NOT) {
> + pexpr_get(org->left.pexpr);
> + }
> + inter.ref_count = ref_count;
> + *dest = inter;
> +}
> +
> +/*
> + * copy a pexpr
> + */
> +struct pexpr *pexpr_deep_copy(const struct pexpr *org)
> +{
> + struct pexpr *e;
> +
> + if (!org)
> + return NULL;
> +
> + e = xmalloc(sizeof(*org));
> + memcpy(e, org, sizeof(*org));
> + e->ref_count = 1;
> + switch (org->type) {
> + case PE_SYMBOL:
> + e->left = org->left;
> + break;
> + case PE_AND:
> + case PE_OR:
> + e->left.pexpr = pexpr_deep_copy(org->left.pexpr);
> + e->right.pexpr = pexpr_deep_copy(org->right.pexpr);
> + break;
> + case PE_NOT:
> + e->left.pexpr = pexpr_deep_copy(org->left.pexpr);
> + break;
> + }
> +
> + return e;
> +}
> +
> +/*
> + * free a pexpr
> + */
> +void pexpr_free_depr(struct pexpr *e)
> +{
> + if (!e)
> + return;
> +
> + switch (e->type) {
> + case PE_SYMBOL:
> + break;
> + case PE_AND:
> + case PE_OR:
> + pexpr_free_depr(e->left.pexpr);
> + pexpr_free_depr(e->right.pexpr);
> + break;
> + case PE_NOT:
> + pexpr_free_depr(e->left.pexpr);
> + break;
> + }
> +
> + free(e);
> +}
> +
> +/*
> + * Increments ref_count and returns @e
> + */
> +struct pexpr *pexpr_get(struct pexpr *e)
> +{
> + ++e->ref_count;
> + return e;
> +}
> +
> +/*
> + * Decrements ref_count and if it becomes 0, it recursively puts the references
> + * to its children and calls ``free(e)``. If @e == NULL, it does nothing.
> + */
> +void pexpr_put(struct pexpr *e)
> +{
> + if (!e)
> + return;
> +
> + if (e->ref_count == 0) {
> + printd("Invalid call to %s - ref_count is zero\n", __func__);
> + return;
> + }
> +
> + --e->ref_count;
> + if (e->ref_count > 0)
> + return;
> +
> + switch (e->type) {
> + case PE_SYMBOL:
> + break;
> + case PE_AND:
> + case PE_OR:
> + pexpr_put(e->left.pexpr);
> + pexpr_put(e->right.pexpr);
> + break;
> + case PE_NOT:
> + pexpr_put(e->left.pexpr);
> + break;
> + }
> +
> + free(e);
> +}
> +
> +/*
> + * calls pexpr_put for a NULL-terminated array of struct pexpr *
> + */
> +void _pexpr_put_list(struct pexpr **es)
> +{
> + for (; *es != NULL; es++)
> + pexpr_put(*es);
> +}
> +
> +#define e1 (*ep1)
> +#define e2 (*ep2)
> +/*
> + * pexpr_eliminate_eq() helper
> + */
> +static void __pexpr_eliminate_eq(enum pexpr_type type, struct pexpr **ep1, struct pexpr **ep2,
> + struct cfdata *data)
> +{
> + /* recurse down to the leaves */
> + if (e1->type == type) {
> + __pexpr_eliminate_eq(type, &e1->left.pexpr, &e2, data);
> + __pexpr_eliminate_eq(type, &e1->right.pexpr, &e2, data);
> + return;
> + }
> + if (e2->type == type) {
> + __pexpr_eliminate_eq(type, &e1, &e2->left.pexpr, data);
> + __pexpr_eliminate_eq(type, &e1, &e2->right.pexpr, data);
> + return;
> + }
> +
> + /* e1 and e2 are leaves. Compare them. */
> + if (e1->type == PE_SYMBOL && e2->type == PE_SYMBOL &&
> + e1->left.fexpr->satval == e2->left.fexpr->satval &&
> + (e1->left.fexpr == data->constants->const_true ||
> + e2->left.fexpr == data->constants->const_false))
> + return;
> + if (!pexpr_eq(e1, e2, data))
> + return;
> +
> + /* e1 and e2 are equal leaves. Prepare them for elimination. */
> + trans_count++;
> + pexpr_put(e1);
> + pexpr_put(e2);
> + switch (type) {
> + case PE_AND:
> + e1 = pexf(data->constants->const_true);
> + e2 = pexf(data->constants->const_true);
> + break;
> + case PE_OR:
> + e1 = pexf(data->constants->const_false);
> + e2 = pexf(data->constants->const_false);
> + break;
> + default:
> + break;
> + }
> +}
> +
> +/*
> + * rewrite pexpr ep1 and ep2 to remove operands common to both
> + */
> +static void pexpr_eliminate_eq(struct pexpr **ep1, struct pexpr **ep2, struct cfdata *data)
> +{
> + if (!e1 || !e2)
> + return;
> +
> + switch (e1->type) {
> + case PE_AND:
> + case PE_OR:
> + __pexpr_eliminate_eq(e1->type, ep1, ep2, data);
> + default:
> + break;
> + }
> + if (e1->type != e2->type)
> + switch (e2->type) {
> + case PE_AND:
> + case PE_OR:
> + __pexpr_eliminate_eq(e2->type, ep1, ep2, data);
> + default:
> + break;
> + }
> + pexpr_eliminate_yn(e1, data);
> + pexpr_eliminate_yn(e2, data);
> +}
> +#undef e1
> +#undef e2
> +
> +/*
> + * check whether 2 pexpr are equal
> + */
> +bool pexpr_eq(struct pexpr *e1, struct pexpr *e2, struct cfdata *data)
> +{
> + bool res;
> + int old_count;
> +
> + if (!e1 || !e2)
> + return false;
> +
> + if (e1->type != e2->type)
> + return false;
> +
> + switch (e1->type) {
> + case PE_SYMBOL:
> + return e1->left.fexpr->satval == e2->left.fexpr->satval;
> + case PE_AND:
> + case PE_OR:
> + e1 = pexpr_deep_copy(e1);
> + e2 = pexpr_deep_copy(e2);
> + old_count = trans_count;
> + pexpr_eliminate_eq(&e1, &e2, data);
> + res = (e1->type == PE_SYMBOL && e2->type == PE_SYMBOL &&
> + e1->left.fexpr->satval == e2->left.fexpr->satval);
> + pexpr_put(e1);
> + pexpr_put(e2);
> + trans_count = old_count;
> + return res;
> + case PE_NOT:
> + return pexpr_eq(e1->left.pexpr, e2->left.pexpr, data);
> + }
> +
> + return false;
> +}
> +
> +/*
> + * print a pexpr
> + */
> +static void pexpr_print_util(struct pexpr *e, int prevtoken)
> +{
> + if (!e)
> + return;
> +
> + switch (e->type) {
> + case PE_SYMBOL:
> + printf("%s", str_get(&e->left.fexpr->name));
> + break;
> + case PE_AND:
> + if (prevtoken != PE_AND && prevtoken != -1)
> + printf("(");
> + pexpr_print_util(e->left.pexpr, PE_AND);
> + printf(" && ");
> + pexpr_print_util(e->right.pexpr, PE_AND);
> + if (prevtoken != PE_AND && prevtoken != -1)
> + printf(")");
> + break;
> + case PE_OR:
> + if (prevtoken != PE_OR && prevtoken != -1)
> + printf("(");
> + pexpr_print_util(e->left.pexpr, PE_OR);
> + printf(" || ");
> + pexpr_print_util(e->right.pexpr, PE_OR);
> + if (prevtoken != PE_OR && prevtoken != -1)
> + printf(")");
> + break;
> + case PE_NOT:
> + printf("!");
> + pexpr_print_util(e->left.pexpr, PE_NOT);
> + break;
> + }
> +}
> +void pexpr_print(char *tag, struct pexpr *e, int prevtoken)
> +{
> + printf("%s: ", tag);
> + pexpr_print_util(e, prevtoken);
> + printf("\n");
> +}
> +
> +/*
> + * convert a fexpr to a pexpr
> + */
> +struct pexpr *pexf(struct fexpr *fe)
Not only this one, but more descriptive function name please.
"pexf", so what?
I do not understand what it is doing from the name.
> +{
> + struct pexpr *pe = xcalloc(1, sizeof(*pe));
> +
> + pexpr_construct_sym(pe, fe, 1);
> + return pe;
> +}
> +
> +void pexpr_construct_or(struct pexpr *e, struct pexpr *left,
> + struct pexpr *right, unsigned int ref_count)
> +{
> + e->type = PE_OR;
> + e->left.pexpr = left;
> + e->right.pexpr = right;
> + e->ref_count = ref_count;
> +}
> +
> +void pexpr_construct_and(struct pexpr *e, struct pexpr *left,
> + struct pexpr *right, unsigned int ref_count)
> +{
> + e->type = PE_AND;
> + e->left.pexpr = left;
> + e->right.pexpr = right;
> + e->ref_count = ref_count;
> +}
> +
> +void pexpr_construct_not(struct pexpr *e, struct pexpr *left,
> + unsigned int ref_count)
> +{
> + e->type = PE_NOT;
> + e->left.pexpr = left;
> + e->right.pexpr = NULL;
> + e->ref_count = ref_count;
> +}
> +
> +void pexpr_construct_sym(struct pexpr *e, struct fexpr *left,
> + unsigned int ref_count)
> +{
> + e->type = PE_SYMBOL;
> + e->left.fexpr = left;
> + e->right.pexpr = NULL;
> + e->ref_count = ref_count;
> +}
> +
> +static struct pexpr *pexpr_join_or(struct pexpr *e1, struct pexpr *e2, struct cfdata *data)
> +{
> + if (pexpr_eq(e1, e2, data))
> + return pexpr_deep_copy(e1);
> + else
> + return NULL;
> +}
> +
> +static struct pexpr *pexpr_join_and(struct pexpr *e1, struct pexpr *e2, struct cfdata *data)
> +{
> + if (pexpr_eq(e1, e2, data))
> + return pexpr_deep_copy(e1);
> + else
> + return NULL;
> +}
> +
> +/*
> + * pexpr_eliminate_dups() helper.
> + */
> +static void pexpr_eliminate_dups1(enum pexpr_type type, struct pexpr **ep1, struct pexpr **ep2,
> + struct cfdata *data)
> +{
> +#define e1 (*ep1)
> +#define e2 (*ep2)
> +
> + struct pexpr *tmp;
> +
> + /* recurse down to leaves */
> + if (e1->type == type) {
> + pexpr_eliminate_dups1(type, &e1->left.pexpr, &e2, data);
> + pexpr_eliminate_dups1(type, &e1->right.pexpr, &e2, data);
> + return;
> + }
> + if (e2->type == type) {
> + pexpr_eliminate_dups1(type, &e1, &e2->left.pexpr, data);
> + pexpr_eliminate_dups1(type, &e1, &e2->right.pexpr, data);
> + return;
> + }
> +
> + /* e1 and e2 are leaves. Compare them. */
> +
> + if (e1 == e2)
> + return;
> +
> + switch (e1->type) {
> + case PE_AND:
> + case PE_OR:
> + pexpr_eliminate_dups1(e1->type, &e1, &e1, data);
> + default:
> + break;
> + }
> +
> + switch (type) {
> + case PE_AND:
> + tmp = pexpr_join_and(e1, e2, data);
> + if (tmp) {
> + pexpr_put(e1);
> + pexpr_put(e2);
> + e1 = pexf(data->constants->const_true);
> + e2 = tmp;
> + trans_count++;
> + }
> + break;
> + case PE_OR:
> + tmp = pexpr_join_or(e1, e2, data);
> + if (tmp) {
> + pexpr_put(e1);
> + pexpr_put(e2);
> + e1 = pexf(data->constants->const_false);
> + e2 = tmp;
> + trans_count++;
> + }
> + break;
> + default:
> + break;
> + }
> +
> +#undef e1
> +#undef e2
> +}
> +
> +/*
> + * eliminate duplicate and redundant operands
> + */
> +struct pexpr *pexpr_eliminate_dups(struct pexpr *e, struct cfdata *data)
> +{
> + int oldcount;
> +
> + if (!e)
> + return e;
> +
> + oldcount = trans_count;
> + while (true) {
> + trans_count = 0;
> + switch (e->type) {
> + case PE_AND:
> + case PE_OR:
> + pexpr_eliminate_dups1(e->type, &e, &e, data);
> + default:
> + break;
> + }
> + if (!trans_count)
> + /* no simplification done in this pass. We're done. */
> + break;
> + pexpr_eliminate_yn(e, data);
> + }
> + trans_count = oldcount;
> + return e;
> +}
This is a full copy-paste of scripts/kconfig/expr.c
If you need all of these, please referector
to avoid code duplication.
> diff --git a/scripts/kconfig/cf_expr.h b/scripts/kconfig/cf_expr.h
> new file mode 100644
> index 000000000000..07435ae381e6
> --- /dev/null
> +++ b/scripts/kconfig/cf_expr.h
> @@ -0,0 +1,296 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
> + */
> +
> +#ifndef CF_EXPR_H
> +#define CF_EXPR_H
> +
> +#include <stdbool.h>
> +
> +#include "cf_defs.h"(
> +
> +#define fexpr_list_for_each(node, list) \
> + for (node = list->head; node != NULL; node = node->next)
> +
> +#define fexl_list_for_each(node, list) \
> + fexpr_list_for_each(node, list)
> +
> +#define pexpr_list_for_each(node, list) \
> + fexpr_list_for_each(node, list)
> +
> +#define sdv_list_for_each(node, list) \
> + fexpr_list_for_each(node, list)
> +
> +#define sfix_list_for_each(node, list) \
> + fexpr_list_for_each(node, list)
> +
> +#define sfl_list_for_each(node, list) \
> + fexpr_list_for_each(node, list)
> +
> +#define sym_list_for_each(node, list) \
> + fexpr_list_for_each(node, list)
> +
> +#define defm_list_for_each(node, list) \
> + fexpr_list_for_each(node, list)
> +
> +#define prop_list_for_each(node, list) \
> + fexpr_list_for_each(node, list)
All of these are the same as fexpr_list_for_each(),
which is the same as list_for_each().
kconfig already use list.h
Please do not proliferate similar code.
> +
> +/* call pexpr_put for a list of pexpr's */
> +#define PEXPR_PUT(...) _pexpr_put_list((struct pexpr *[]){ __VA_ARGS__, NULL })
> +
> +/* create a fexpr */
> +struct fexpr *fexpr_create(int satval, enum fexpr_type type, char *name);
> +
> +/* create the fexpr for a symbol */
> +void sym_create_fexpr(struct symbol *sym, struct cfdata *data);
> +
> +struct pexpr *expr_calculate_pexpr_both(struct expr *e, struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_y(struct expr *e, struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_m(struct expr *e, struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_y_and(struct expr *a, struct expr *b,
> + struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_m_and(struct expr *a, struct expr *b,
> + struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_both_and(struct expr *a, struct expr *b,
> + struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_y_or(struct expr *a, struct expr *b,
> + struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_m_or(struct expr *a, struct expr *b,
> + struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_both_or(struct expr *a, struct expr *b,
> + struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_y_not(struct expr *e, struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_m_not(struct expr *e, struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_y_equals(struct expr *e,
> + struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_y_unequals(struct expr *e,
> + struct cfdata *data);
> +struct pexpr *expr_calculate_pexpr_y_comp(struct expr *e, struct cfdata *data);
> +
> +/* macro to create a pexpr of type AND */
> +struct pexpr *pexpr_and_share(struct pexpr *a, struct pexpr *b,
> + struct cfdata *data);
> +struct pexpr *pexpr_and(struct pexpr *a, struct pexpr *b, struct cfdata *data,
> + enum pexpr_move move);
> +
> +/* macro to create a pexpr of type OR */
> +struct pexpr *pexpr_or_share(struct pexpr *a, struct pexpr *b,
> + struct cfdata *data);
> +struct pexpr *pexpr_or(struct pexpr *a, struct pexpr *b, struct cfdata *data,
> + enum pexpr_move move);
> +
> +/* macro to create a pexpr of type NOT */
> +struct pexpr *pexpr_not_share(struct pexpr *a, struct cfdata *data);
> +struct pexpr *pexpr_not(struct pexpr *a, struct cfdata *data);
> +
> +/* check whether a pexpr is in CNF */
> +bool pexpr_is_cnf(struct pexpr *e);
> +
> +/* check whether a pexpr is in NNF */
> +bool pexpr_is_nnf(struct pexpr *e);
> +
> +/* return fexpr_both for a symbol */
> +struct pexpr *sym_get_fexpr_both(struct symbol *sym, struct cfdata *data);
> +
> +/* return fexpr_sel_both for a symbol */
> +struct pexpr *sym_get_fexpr_sel_both(struct symbol *sym, struct cfdata *data);
> +
> +/* create the fexpr of a non-boolean symbol for a specific value */
> +struct fexpr *sym_create_nonbool_fexpr(struct symbol *sym, char *value,
> + struct cfdata *data);
> +
> +/*
> + * return the fexpr of a non-boolean symbol for a specific value, NULL if
> + * non-existent
> + */
> +struct fexpr *sym_get_nonbool_fexpr(struct symbol *sym, char *value);
> +
> +/*
> + * return the fexpr of a non-boolean symbol for a specific value, if it exists
> + * otherwise create it
> + */
> +struct fexpr *sym_get_or_create_nonbool_fexpr(struct symbol *sym, char *value, struct cfdata *data);
> +
> +/* macro to construct a pexpr for "A implies B" */
> +struct pexpr *pexpr_implies_share(struct pexpr *a, struct pexpr *b, struct cfdata *data);
> +struct pexpr *pexpr_implies(struct pexpr *a, struct pexpr *b, struct cfdata *data,
> + enum pexpr_move move);
> +
> +/* check, if the fexpr is a symbol, a True/False-constant, a literal symbolising a non-boolean or
> + * a choice symbol
> + */
> +bool fexpr_is_symbol(struct fexpr *e);
> +
> +/* check whether a pexpr is a symbol or a negated symbol */
> +bool pexpr_is_symbol(struct pexpr *e);
> +
> +/* check whether the fexpr is a constant (true/false) */
> +bool fexpr_is_constant(struct fexpr *e, struct cfdata *data);
> +
> +/* add a fexpr to the satmap */
> +void fexpr_add_to_satmap(struct fexpr *e, struct cfdata *data);
> +
> +/* print an fexpr */
> +void fexpr_print(char *tag, struct fexpr *e);
> +
> +/* write an fexpr into a string (format needed for testing) */
> +void fexpr_as_char(struct fexpr *e, struct gstr *s);
> +
> +/* write pn pexpr into a string */
> +void pexpr_as_char_short(struct pexpr *e, struct gstr *s, int parent);
> +
> +/* write an fexpr into a string (format needed for testing) */
> +void pexpr_as_char(struct pexpr *e, struct gstr *s, int parent, struct cfdata *data);
> +
> +/* check whether a pexpr contains a specific fexpr */
> +bool pexpr_contains_fexpr(struct pexpr *e, struct fexpr *fe);
> +
> +/* init list of fexpr */
> +struct fexpr_list *fexpr_list_init(void);
> +
> +/* init list of fexpr_list */
> +struct fexl_list *fexl_list_init(void);
> +
> +/* init list of pexpr */
> +struct pexpr_list *pexpr_list_init(void);
> +
> +/* init list of symbol_fix */
> +struct sfix_list *sfix_list_init(void);
> +
> +/* init list of sfix_list */
> +struct sfl_list *sfl_list_init(void);
> +
> +/* init list of symbol_dvalue */
> +struct sdv_list *sdv_list_init(void);
> +
> +/* init list of symbols */
> +struct sym_list *sym_list_init(void);
> +
> +/* init list of default_maps */
> +struct defm_list *defm_list_init(void);
> +
> +/* init list of properties */
> +struct prop_list *prop_list_init(void);
> +
> +/* add element to tail of a fexpr_list */
> +void fexpr_list_add(struct fexpr_list *list, struct fexpr *fe);
> +
> +/* add element to tail of a fexl_list */
> +void fexl_list_add(struct fexl_list *list, struct fexpr_list *elem);
> +
> +/* add element to tail of a pexpr_list */
> +void pexpr_list_add(struct pexpr_list *list, struct pexpr *e);
> +
> +/* add element to tail of a sfix_list */
> +void sfix_list_add(struct sfix_list *list, struct symbol_fix *fix);
> +
> +/* add element to tail of a sfl_list */
> +void sfl_list_add(struct sfl_list *list, struct sfix_list *elem);
> +
> +/* add element to tail of a sdv_list */
> +void sdv_list_add(struct sdv_list *list, struct symbol_dvalue *sdv);
> +
> +/* add element to tail of a sym_list */
> +void sym_list_add(struct sym_list *list, struct symbol *sym);
> +
> +/* add element to tail of a defm_list */
> +void defm_list_add(struct defm_list *list, struct default_map *map);
> +
> +/* add element to tail of a prop_list */
> +void prop_list_add(struct prop_list *list, struct property *prop);
> +
> +/* delete an element from a fexpr_list */
> +void fexpr_list_delete(struct fexpr_list *list, struct fexpr_node *node);
> +
> +/* delete an element from a fexpr_list */
> +void fexl_list_delete(struct fexl_list *list, struct fexl_node *node);
> +
> +/* delete the first occurrence of elem in an fexl_list */
> +void fexl_list_delete_elem(struct fexl_list *list, struct fexpr_list *elem);
> +
> +/* delete an element from a pexpr_list */
> +void pexpr_list_delete(struct pexpr_list *list, struct pexpr_node *node);
> +
> +/* delete an element from a sfix_list */
> +void sfix_list_delete(struct sfix_list *list, struct sfix_node *node);
> +
> +/* make a shallow copy of a fexpr_list */
> +struct fexpr_list *fexpr_list_copy(struct fexpr_list *list);
> +
> +/* make a shallow copy of a fexpr_list */
> +struct fexl_list *fexl_list_copy(struct fexl_list *list);
> +
> +/* make a shallow copy of a sdv_list */
> +struct sdv_list *sdv_list_copy(struct sdv_list *list);
> +
> +/* make a shallow copy of a sfix_list */
> +struct sfix_list *sfix_list_copy(struct sfix_list *list);
> +
> +/* print a fexpr_list */
> +void fexpr_list_print(char *title, struct fexpr_list *list);
> +
> +/* print a fexl_list */
> +void fexl_list_print(char *title, struct fexl_list *list);
> +
> +/* print a pexpr_list */
> +void pexpr_list_print(char *title, struct pexpr_list *list);
> +
> +/* free an fexpr_list */
> +void fexpr_list_free(struct fexpr_list *list);
> +
> +/* free an pexpr_list (and pexpr_put the elements) */
> +void pexpr_list_free_put(struct pexpr_list *list);
> +
> +/* free an fexl_list */
> +void fexl_list_free(struct fexl_list *list);
> +
> +/* free a sdv_list */
> +void sdv_list_free(struct sdv_list *list);
> +
> +/* free a prop_list */
> +void prop_list_free(struct prop_list *list);
> +
> +/* free a defm_list (and pexpr_put the conditions of the maps) */
> +void defm_list_destruct(struct defm_list *list);
> +
> +/* free a sym_list */
> +void sym_list_free(struct sym_list *list);
> +
> +/* check whether 2 pexpr are equal */
> +bool pexpr_eq(struct pexpr *e1, struct pexpr *e2, struct cfdata *data);
> +
> +/* copy a pexpr */
> +struct pexpr *pexpr_deep_copy(const struct pexpr *org);
> +
> +void pexpr_construct_sym(struct pexpr *e, struct fexpr *left,
> + unsigned int ref_count);
> +void pexpr_construct_not(struct pexpr *e, struct pexpr *left,
> + unsigned int ref_count);
> +void pexpr_construct_and(struct pexpr *e, struct pexpr *left,
> + struct pexpr *right, unsigned int ref_count);
> +void pexpr_construct_or(struct pexpr *e, struct pexpr *left,
> + struct pexpr *right, unsigned int ref_count);
> +
> +/* free a pexpr */
> +void pexpr_free_depr(struct pexpr *e);
> +
> +/* give up a reference to e. Also see struct pexpr. */
> +void pexpr_put(struct pexpr *e);
> +/* Used by PEXPR_PUT(). Not to be used directly. */
> +void _pexpr_put_list(struct pexpr **es);
> +
> +/* acquire a reference to e. Also see struct pexpr. */
> +struct pexpr *pexpr_get(struct pexpr *e);
> +
> +/* print a pexpr */
> +void pexpr_print(char *tag, struct pexpr *e, int prevtoken);
> +
> +/* convert a fexpr to a pexpr */
> +struct pexpr *pexf(struct fexpr *fe);
> +
> +/* eliminate duplicate and redundant operands */
> +struct pexpr *pexpr_eliminate_dups(struct pexpr *e, struct cfdata *data);
> +
> +#endif
> --
> 2.39.2
>
--
Best Regards
Masahiro Yamada
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v4 07/12] kconfig: Add files for handling expressions
2024-08-12 8:46 ` Masahiro Yamada
@ 2024-08-16 10:23 ` Ole Schuerks
0 siblings, 0 replies; 25+ messages in thread
From: Ole Schuerks @ 2024-08-16 10:23 UTC (permalink / raw)
To: masahiroy
Cc: deltaone, jan.sollmann, jude.gyimah, linux-kbuild, linux-kernel,
mcgrof, ole0811sch, thorsten.berger
On 8/12/24 10:46, Masahiro Yamada wrote:
> On Wed, Jul 10, 2024 at 3:54 PM Ole Schuerks <ole0811sch@gmail.com> wrote:
>>
>> To translate the Kconfig-model into propositional logic and resolve
>> conflicts, we need to handle propostional formulas.
>> These files contain many functions and macros to deal with
>> propositional formulas.
>>
>> Co-developed-by: Patrick Franz <deltaone@debian.org>
>> Signed-off-by: Patrick Franz <deltaone@debian.org>
>> Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
>> Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
>> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
>> Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
>> Suggested-by: Sarah Nadi <nadi@ualberta.ca>
>> Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
>> Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
>> Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
>> ---
>> scripts/kconfig/cf_expr.c | 2594 +++++++++++++++++++++++++++++++++++++
>> scripts/kconfig/cf_expr.h | 296 +++++
>> 2 files changed, 2890 insertions(+)
>> create mode 100644 scripts/kconfig/cf_expr.c
>> create mode 100644 scripts/kconfig/cf_expr.h
>> +/*
>> + * convert a fexpr to a pexpr
>> + */
>> +struct pexpr *pexf(struct fexpr *fe)
>
>
>
>
> Not only this one, but more descriptive function name please.
>
> "pexf", so what?
> I do not understand what it is doing from the name.
>
>
Could you give us one or two examples of what other functions have bad
names? I didn't find anything as bad as pexf() so I'm not sure what the
expectations are. Thank you, that would be helpful.
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v4 08/12] kconfig: Add files for RangeFix
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
` (6 preceding siblings ...)
2024-07-10 6:52 ` [PATCH v4 07/12] kconfig: Add files for handling expressions Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 09/12] kconfig: Add files with utility functions Ole Schuerks
` (3 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
The algorithm RangeFix is used to resolve the conflicts. This is the
implementation of the algorithm.
Co-developed-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Patrick Franz <deltaone@debian.org>
Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
Suggested-by: Sarah Nadi <nadi@ualberta.ca>
Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/cf_rangefix.c | 1066 +++++++++++++++++++++++++++++++++
scripts/kconfig/cf_rangefix.h | 21 +
2 files changed, 1087 insertions(+)
create mode 100644 scripts/kconfig/cf_rangefix.c
create mode 100644 scripts/kconfig/cf_rangefix.h
diff --git a/scripts/kconfig/cf_rangefix.c b/scripts/kconfig/cf_rangefix.c
new file mode 100644
index 000000000000..bd5229b1d656
--- /dev/null
+++ b/scripts/kconfig/cf_rangefix.c
@@ -0,0 +1,1066 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#include "cf_defs.h"
+#include "cf_expr.h"
+#define _GNU_SOURCE
+#include <assert.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "cf_rangefix.h"
+#include "internal.h"
+#include "cf_utils.h"
+
+#define MAX_DIAGNOSES 3
+#define MAX_SECONDS 120
+#define PRINT_UNSAT_CORE true
+#define PRINT_DIAGNOSES false
+#define PRINT_DIAGNOSIS_FOUND true
+#define MINIMISE_DIAGNOSES false
+#define MINIMISE_UNSAT_CORE true
+
+static struct sfl_list *diagnoses_symbol;
+
+static struct fexl_list *generate_diagnoses(PicoSAT *pico, struct cfdata *data);
+
+static void add_fexpr_to_constraint_set(struct fexpr_list *C, struct cfdata *data);
+static void set_assumptions(PicoSAT *pico, struct fexpr_list *c, struct cfdata *data);
+static void fexpr_add_assumption(PicoSAT *pico, struct fexpr *e, int satval);
+static struct fexpr_list *get_unsat_core_soft(PicoSAT *pico, struct cfdata *data);
+static void minimise_unsat_core(PicoSAT *pico, struct fexpr_list *C, struct cfdata *data);
+
+
+static struct fexpr_list *get_difference(struct fexpr_list *C, struct fexpr_list *E0);
+static bool has_intersection(struct fexpr_list *e, struct fexpr_list *X);
+static struct fexpr_list *fexpr_list_union(struct fexpr_list *A, struct fexpr_list *B);
+static struct fexl_list *fexl_list_union(struct fexl_list *A, struct fexl_list *B);
+static bool is_subset_of(struct fexpr_list *A, struct fexpr_list *B);
+static void print_unsat_core(struct fexpr_list *list);
+static bool diagnosis_contains_fexpr(struct fexpr_list *diagnosis, struct fexpr *e);
+static bool diagnosis_contains_symbol(struct sfix_list *diagnosis, struct symbol *sym);
+
+static void print_diagnoses(struct fexl_list *diag);
+static void print_diagnoses_symbol(struct sfl_list *diag_sym);
+
+static struct sfl_list *convert_diagnoses(struct fexl_list *diagnoses, struct cfdata *data);
+static struct sfix_list *convert_diagnosis(struct fexpr_list *diagnosis, struct cfdata *data);
+static struct symbol_fix *symbol_fix_create(struct fexpr *e, enum symbolfix_type type,
+ struct fexpr_list *diagnosis);
+static struct sfl_list *minimise_diagnoses(PicoSAT *pico, struct fexl_list *diagnoses,
+ struct cfdata *data);
+
+static tristate calculate_new_tri_val(struct fexpr *e, struct fexpr_list *diagnosis);
+static const char *calculate_new_string_value(struct fexpr *e, struct fexpr_list *diagnosis);
+
+/* count assumptions, only used for debugging */
+static unsigned int nr_of_assumptions = 0, nr_of_assumptions_true;
+
+/* -------------------------------------- */
+
+struct sfl_list *rangefix_run(PicoSAT *pico, struct cfdata *data)
+{
+ clock_t start, end;
+ double time;
+ struct fexl_list *diagnoses;
+ struct fexl_node *node;
+
+ printd("Starting RangeFix...\n");
+ printd("Generating diagnoses...");
+
+ /* generate the diagnoses */
+ start = clock();
+ diagnoses = generate_diagnoses(pico, data);
+ end = clock();
+
+ time = ((double) (end - start)) / CLOCKS_PER_SEC;
+ printd("Generating diagnoses...done. (%.6f secs.)\n", time);
+
+ if (PRINT_DIAGNOSES) {
+ printd("Diagnoses (only for debugging):\n");
+ print_diagnoses(diagnoses);
+ printd("\n");
+ }
+
+ /* convert diagnoses of fexpr to diagnoses of symbols */
+ if (MINIMISE_DIAGNOSES)
+ diagnoses_symbol = minimise_diagnoses(pico, diagnoses, data);
+ else
+ diagnoses_symbol = convert_diagnoses(diagnoses, data);
+
+ printd("\n");
+
+ fexpr_list_for_each(node, diagnoses)
+ fexpr_list_free(node->elem);
+ fexl_list_free(diagnoses);
+
+ return diagnoses_symbol;
+}
+
+/*
+ * generate the diagnoses
+ */
+static struct fexl_list *generate_diagnoses(PicoSAT *pico, struct cfdata *data)
+{
+ struct fexpr_list *C = fexpr_list_init();
+ struct fexl_list *E = fexl_list_init();
+ struct fexl_list *R = fexl_list_init();
+ struct fexpr_list *X, *e, *x_set, *E1, *E2;
+ struct fexl_list *E_R_Union;
+ struct fexpr_list *empty_diagnosis;
+ clock_t start_t, end_t;
+ double time_t;
+
+ /* create constraint set C */
+ add_fexpr_to_constraint_set(C, data);
+
+ if (PRINT_UNSAT_CORE)
+ printd("\n");
+
+ /* init E with an empty diagnosis */
+ empty_diagnosis = fexpr_list_init();
+ fexl_list_add(E, empty_diagnosis);
+
+ /* start the clock */
+ start_t = clock();
+
+ while (E->size > 0) {
+ /* get random diagnosis */
+ struct fexpr_list *E0 = E->head->elem;
+
+ /* calculate C\E0 */
+ struct fexpr_list *c = get_difference(C, E0);
+
+ struct fexl_node *node, *tmp;
+ int res;
+
+ /* set assumptions */
+ nr_of_assumptions = 0;
+ nr_of_assumptions_true = 0;
+ set_assumptions(pico, c, data);
+ fexpr_list_free(c);
+
+ res = picosat_sat(pico, -1);
+
+ if (res == PICOSAT_SATISFIABLE) {
+ if (PRINT_DIAGNOSIS_FOUND && CFDEBUG)
+ fexpr_list_print("DIAGNOSIS FOUND", E0);
+
+ fexl_list_delete(E, E->head);
+ if (E0->size > 0)
+ fexl_list_add(R, E0);
+ else
+ fexpr_list_free(E0);
+
+ if (R->size >= MAX_DIAGNOSES)
+ goto DIAGNOSES_FOUND;
+
+ continue;
+
+ } else if (res == PICOSAT_UNSATISFIABLE) {
+
+ } else if (res == PICOSAT_UNKNOWN) {
+ printd("UNKNOWN\n");
+ } else {
+ perror("Doh.");
+ }
+
+ /* check elapsed time */
+ end_t = clock();
+ time_t = ((double) (end_t - start_t)) / CLOCKS_PER_SEC;
+ if (time_t > (double) MAX_SECONDS)
+ goto DIAGNOSES_FOUND;
+
+ /* abort and return results if cancelled by user */
+ if (stop_rangefix) {
+ stop_rangefix = false;
+ goto DIAGNOSES_FOUND;
+ }
+
+ /* get unsat core from SAT solver */
+ X = get_unsat_core_soft(pico, data);
+
+ /* minimise the unsat core */
+ if (MINIMISE_UNSAT_CORE)
+ minimise_unsat_core(pico, X, data);
+
+ if (PRINT_UNSAT_CORE)
+ print_unsat_core(X);
+
+ for (node = E->head; node != NULL;) {
+ struct fexpr_node *fnode;
+
+ /* get partial diagnosis */
+ e = node->elem;
+
+ /* check, if there is an intersection between e and X
+ * if there is, go to the next partial diagnosis
+ */
+ if (has_intersection(e, X)) {
+ node = node->next;
+ continue;
+ }
+
+ /* for each fexpr in the core */
+ fexpr_list_for_each(fnode, X) {
+ struct fexpr *x = fnode->elem;
+ bool E2_subset_of_E1;
+ struct fexl_node *lnode;
+ struct fexl_list *E_without_e;
+
+ /* create {x} */
+ x_set = fexpr_list_init();
+ fexpr_list_add(x_set, x);
+
+ /* create E' = e U {x} */
+ E1 = fexpr_list_union(e, x_set);
+
+ /* create (E\e) U R */
+ E_without_e = fexl_list_copy(E);
+ fexl_list_delete_elem(E_without_e, e);
+ E_R_Union = fexl_list_union(E_without_e, R);
+
+ E2_subset_of_E1 = false;
+
+ /* E" in (E\e) U R */
+ fexl_list_for_each(lnode, E_R_Union) {
+ E2 = lnode->elem;
+
+ /* E" subset of E' ? */
+ if (is_subset_of(E2, E1)) {
+ E2_subset_of_E1 = true;
+ break;
+ }
+ }
+
+ fexl_list_free(E_without_e);
+ fexl_list_free(E_R_Union);
+ fexpr_list_free(x_set);
+
+ /* there exists no E" that is a subset of E' */
+ if (!E2_subset_of_E1)
+ fexl_list_add(E, E1);
+ else
+ fexpr_list_free(E1);
+ }
+
+ fexpr_list_free(e);
+
+ tmp = node->next;
+ fexl_list_delete(E, node);
+ node = tmp;
+ }
+ fexpr_list_free(X);
+ }
+
+ struct fexl_node *node;
+DIAGNOSES_FOUND:
+ fexpr_list_free(C);
+ fexl_list_for_each(node, E)
+ fexpr_list_free(node->elem);
+ fexl_list_free(E);
+
+ return R;
+}
+
+/*
+ * add the fexpr to the constraint set C
+ */
+static void add_fexpr_to_constraint_set(struct fexpr_list *C, struct cfdata *data)
+{
+ struct symbol *sym;
+
+ for_all_symbols(sym) {
+ /* must be a proper symbol */
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ /* don't need the conflict symbols they are handled separately */
+ if (sym_is_sdv(data->sdv_symbols, sym))
+ continue;
+
+ /* must have a prompt and a name */
+ if (!sym->name || !sym_has_prompt(sym))
+ continue;
+
+ if (sym->type == S_BOOLEAN)
+ fexpr_list_add(C, sym->fexpr_y);
+ else if (sym->type == S_TRISTATE) {
+ fexpr_list_add(C, sym->fexpr_y);
+ fexpr_list_add(C, sym->fexpr_m);
+ } else if (sym->type == S_INT || sym->type == S_HEX || sym->type == S_STRING) {
+ struct fexpr_node *node;
+
+ fexpr_list_for_each(node, sym->nb_vals)
+ fexpr_list_add(C, node->elem);
+ } else {
+ perror("Error adding variables to constraint set C.");
+ }
+ }
+}
+
+/*
+ * check whether the fexpr symbolises the no-value-set fexpr for a non-boolean symbol
+ */
+static bool fexpr_is_novalue(struct fexpr *e)
+{
+ if (!sym_is_nonboolean(e->sym))
+ return false;
+
+ return e == e->sym->nb_vals->head->elem;
+}
+
+static void set_assumptions_sdv(PicoSAT *pico, struct sdv_list *arr)
+{
+ struct symbol_dvalue *sdv;
+ struct sdv_node *node;
+ struct symbol *sym;
+
+ sdv_list_for_each(node, arr) {
+ int lit_y;
+
+ sdv = node->elem;
+ sym = sdv->sym;
+
+ lit_y = sym->fexpr_y->satval;
+
+ if (sym->type == S_BOOLEAN) {
+ switch (sdv->tri) {
+ case yes:
+ picosat_assume(pico, lit_y);
+ sym->fexpr_y->assumption = true;
+ nr_of_assumptions_true++;
+ break;
+ case no:
+ picosat_assume(pico, -lit_y);
+ sym->fexpr_y->assumption = false;
+ break;
+ case mod:
+ perror("Should not happen.\n");
+ }
+ nr_of_assumptions++;
+ } else if (sym->type == S_TRISTATE) {
+ int lit_m = sym->fexpr_m->satval;
+
+ switch (sdv->tri) {
+ case yes:
+ picosat_assume(pico, lit_y);
+ sym->fexpr_y->assumption = true;
+ picosat_assume(pico, -lit_m);
+ sym->fexpr_m->assumption = false;
+ nr_of_assumptions_true++;
+ break;
+ case mod:
+ picosat_assume(pico, -lit_y);
+ sym->fexpr_y->assumption = false;
+ picosat_assume(pico, lit_m);
+ sym->fexpr_m->assumption = true;
+ nr_of_assumptions_true++;
+ break;
+ case no:
+ picosat_assume(pico, -lit_y);
+ sym->fexpr_y->assumption = false;
+ picosat_assume(pico, -lit_m);
+ sym->fexpr_y->assumption = false;
+ }
+ nr_of_assumptions += 2;
+ }
+ }
+}
+
+/*
+ * set the assumptions for the next run of Picosat
+ */
+static void set_assumptions(PicoSAT *pico, struct fexpr_list *c, struct cfdata *data)
+{
+ struct fexpr_node *node;
+
+ fexpr_list_for_each(node, c)
+ fexpr_add_assumption(pico, node->elem, node->elem->satval);
+
+ /* set assumptions for the conflict-symbols */
+ set_assumptions_sdv(pico, data->sdv_symbols);
+}
+
+/*
+ * set the assumtption for a fexpr for the next run of Picosat
+ */
+static void fexpr_add_assumption(PicoSAT *pico, struct fexpr *e, int satval)
+{
+ struct symbol *sym = e->sym;
+
+ if (sym->type == S_BOOLEAN) {
+ int tri_val = sym_get_tristate_value(sym);
+
+ if (tri_val == yes) {
+ picosat_assume(pico, satval);
+ e->assumption = true;
+ nr_of_assumptions_true++;
+ } else {
+ picosat_assume(pico, -satval);
+ e->assumption = false;
+ }
+ nr_of_assumptions++;
+ }
+
+ if (sym->type == S_TRISTATE) {
+ int tri_val = sym_get_tristate_value(sym);
+
+ if (e->tri == yes) {
+ if (tri_val == yes) {
+ picosat_assume(pico, satval);
+ e->assumption = true;
+ nr_of_assumptions_true++;
+ } else {
+ picosat_assume(pico, -satval);
+ e->assumption = false;
+ }
+ } else if (e->tri == mod) {
+ if (tri_val == mod) {
+ picosat_assume(pico, satval);
+ e->assumption = true;
+ nr_of_assumptions_true++;
+ } else {
+ picosat_assume(pico, -satval);
+ e->assumption = false;
+ }
+ }
+ nr_of_assumptions++;
+ }
+
+ if (sym->type == S_INT || sym->type == S_HEX || sym->type == S_STRING) {
+
+ char *string_val = (char *) sym_get_string_value(sym);
+
+ if (sym->type == S_STRING && !strcmp(string_val, ""))
+ return;
+
+ /* check, if e symbolises the no-value-set fexpr */
+ if (fexpr_is_novalue(e)) {
+ if (!sym_nonbool_has_value_set(sym)) {
+ picosat_assume(pico, satval);
+ e->assumption = true;
+ nr_of_assumptions_true++;
+ } else {
+ picosat_assume(pico, -satval);
+ e->assumption = false;
+ }
+ }
+ /* check whena string-symbol has value "" */
+ else if (sym->type == S_STRING && !strcmp(string_val, "")) {
+ if (sym_nonbool_has_value_set(sym)) {
+ picosat_assume(pico, satval);
+ e->assumption = true;
+ nr_of_assumptions_true++;
+ } else {
+ picosat_assume(pico, -satval);
+ e->assumption = false;
+ }
+ } else {
+ if (!strcmp(str_get(&e->nb_val), string_val) &&
+ sym_nonbool_has_value_set(sym)) {
+ picosat_assume(pico, satval);
+ e->assumption = true;
+ nr_of_assumptions_true++;
+ } else {
+ picosat_assume(pico, -satval);
+ e->assumption = false;
+ }
+ }
+ nr_of_assumptions++;
+ }
+}
+
+/*
+ * get the unsatisfiable soft constraints from the last run of Picosat
+ */
+static struct fexpr_list *get_unsat_core_soft(PicoSAT *pico, struct cfdata *data)
+{
+ struct fexpr_list *ret = fexpr_list_init();
+ struct fexpr *e;
+
+ int lit;
+ const int *i = picosat_failed_assumptions(pico);
+
+ lit = abs(*i++);
+
+ while (lit != 0) {
+ e = data->satmap[lit];
+
+ if (!sym_is_sdv(data->sdv_symbols, e->sym))
+ fexpr_list_add(ret, e);
+
+ lit = abs(*i++);
+ }
+
+ return ret;
+}
+
+/*
+ * minimise the unsat core C
+ */
+static void minimise_unsat_core(PicoSAT *pico, struct fexpr_list *C, struct cfdata *data)
+{
+ struct fexpr_list *c_set;
+ struct fexpr_node *node, *tmp;
+
+ /* no need to check further */
+ if (C->size == 1)
+ return;
+
+ for (node = C->head; node != NULL;) {
+ struct fexpr_list *t;
+ int res;
+
+ if (C->size == 1)
+ return;
+
+ /* create C\c */
+ c_set = fexpr_list_init();
+ fexpr_list_add(c_set, node->elem);
+ t = get_difference(C, c_set);
+
+ /* invoke PicoSAT */
+ set_assumptions(pico, t, data);
+
+ res = picosat_sat(pico, -1);
+
+ tmp = node->next;
+
+ if (res == PICOSAT_UNSATISFIABLE)
+ fexpr_list_delete(C, node);
+
+ node = tmp;
+
+ fexpr_list_free(c_set);
+ fexpr_list_free(t);
+ }
+}
+
+
+/*
+ * Calculate C\E0
+ */
+static struct fexpr_list *get_difference(struct fexpr_list *C, struct fexpr_list *E0)
+{
+ struct fexpr_list *ret = fexpr_list_init();
+ struct fexpr_node *node1, *node2;
+ bool found;
+
+ fexpr_list_for_each(node1, C) {
+ found = false;
+ fexpr_list_for_each(node2, E0) {
+ if (node1->elem->satval == node2->elem->satval) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ fexpr_list_add(ret, node1->elem);
+ }
+
+ return ret;
+}
+
+/*
+ * check, if there is an intersection between e and X
+ */
+static bool has_intersection(struct fexpr_list *e, struct fexpr_list *X)
+{
+ struct fexpr_node *node1, *node2;
+
+ fexpr_list_for_each(node1, e)
+ fexpr_list_for_each(node2, X)
+ if (node1->elem->satval == node2->elem->satval)
+ return true;
+
+ return false;
+}
+
+/*
+ * get the union of 2 fexpr_list
+ */
+static struct fexpr_list *fexpr_list_union(struct fexpr_list *A, struct fexpr_list *B)
+{
+ struct fexpr_list *ret = fexpr_list_copy(A);
+ struct fexpr_node *node1, *node2;
+ bool found;
+
+ fexpr_list_for_each(node2, B) {
+ found = false;
+ fexpr_list_for_each(node1, A) {
+ if (node2->elem->satval == node1->elem->satval) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ fexpr_list_add(ret, node2->elem);
+ }
+
+ return ret;
+}
+
+/*
+ * get the union of 2 fexl_list
+ */
+static struct fexl_list *fexl_list_union(struct fexl_list *A, struct fexl_list *B)
+{
+ struct fexl_list *ret = fexl_list_copy(A);
+ struct fexl_node *node1, *node2;
+ bool found;
+
+ fexl_list_for_each(node2, B) {
+ found = false;
+ fexl_list_for_each(node1, A) {
+ if (node2->elem == node1->elem) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ fexl_list_add(ret, node2->elem);
+ }
+
+ return ret;
+}
+
+/*
+ * check, whether A is a subset of B
+ */
+static bool is_subset_of(struct fexpr_list *A, struct fexpr_list *B)
+{
+ struct fexpr_node *node1, *node2;
+ bool found;
+
+ fexpr_list_for_each(node1, A) {
+ found = false;
+ fexpr_list_for_each(node2, B) {
+ if (node1->elem->satval == node2->elem->satval) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * print an unsat core
+ */
+static void print_unsat_core(struct fexpr_list *list)
+{
+ struct fexpr_node *node;
+
+ printd("Unsat core: [");
+
+ fexpr_list_for_each(node, list) {
+ printd("%s", str_get(&node->elem->name));
+ printd(" <%s>", node->elem->assumption == true ? "T" : "F");
+ if (node->next != NULL)
+ printd(", ");
+ }
+
+ printd("]\n");
+}
+
+
+/*
+ * check if a diagnosis contains a fexpr
+ */
+static bool diagnosis_contains_fexpr(struct fexpr_list *diagnosis, struct fexpr *e)
+{
+ struct fexpr_node *node;
+
+ fexpr_list_for_each(node, diagnosis)
+ if (node->elem->satval == e->satval)
+ return true;
+
+ return false;
+}
+
+/*
+ * check if a diagnosis contains a symbol
+ */
+static bool diagnosis_contains_symbol(struct sfix_list *diagnosis, struct symbol *sym)
+{
+ struct sfix_node *node;
+
+ sfix_list_for_each(node, diagnosis)
+ if (sym == node->elem->sym)
+ return true;
+
+ return false;
+}
+
+/*
+ * print the diagnoses of type fexpr_list
+ */
+static void print_diagnoses(struct fexl_list *diag)
+{
+ struct fexl_node *lnode;
+ unsigned int i = 1;
+
+ fexl_list_for_each(lnode, diag) {
+ struct fexpr_node *node;
+
+ printd("%d: [", i++);
+ fexpr_list_for_each(node, lnode->elem) {
+ char *new_val = node->elem->assumption ? "false" : "true";
+
+ printd("%s => %s", str_get(&node->elem->name), new_val);
+ if (node->next != NULL)
+ printd(", ");
+ }
+ printd("]\n");
+ }
+}
+
+/*
+ * print a single diagnosis of type symbol_fix
+ */
+void print_diagnosis_symbol(struct sfix_list *diag_sym)
+{
+ struct symbol_fix *fix;
+ struct sfix_node *node;
+
+ printd("[");
+
+ sfix_list_for_each(node, diag_sym) {
+ fix = node->elem;
+
+ if (fix->type == SF_BOOLEAN)
+ printd("%s => %s", fix->sym->name, tristate_get_char(fix->tri));
+ else if (fix->type == SF_NONBOOLEAN)
+ printd("%s => %s", fix->sym->name, str_get(&fix->nb_val));
+ else
+ perror("NB not yet implemented.");
+
+ if (node->next != NULL)
+ printd(", ");
+ }
+ printd("]\n");
+}
+
+/*
+ * print the diagnoses of type symbol_fix
+ */
+static void print_diagnoses_symbol(struct sfl_list *diag_sym)
+{
+ struct sfl_node *arr;
+ unsigned int i = 1;
+
+ sfl_list_for_each(arr, diag_sym) {
+ printd("%d: ", i++);
+ print_diagnosis_symbol(arr->elem);
+ }
+}
+
+/*
+ * convert a single diagnosis of fexpr into a diagnosis of symbols
+ */
+static struct sfix_list *convert_diagnosis(struct fexpr_list *diagnosis, struct cfdata *data)
+{
+ struct sfix_list *diagnosis_symbol = sfix_list_init();
+ struct fexpr *e;
+ struct symbol_fix *fix;
+ struct symbol_dvalue *sdv;
+ struct sdv_node *snode;
+ struct fexpr_node *fnode;
+
+ /* set the values for the conflict symbols */
+ sdv_list_for_each(snode, data->sdv_symbols) {
+ sdv = snode->elem;
+ fix = xcalloc(1, sizeof(*fix));
+ fix->sym = sdv->sym;
+ fix->type = SF_BOOLEAN;
+ fix->tri = sdv->tri;
+ sfix_list_add(diagnosis_symbol, fix);
+ }
+
+ fexpr_list_for_each(fnode, diagnosis) {
+ enum symbolfix_type type;
+
+ e = fnode->elem;
+
+ /* diagnosis already contains symbol, so continue */
+ if (diagnosis_contains_symbol(diagnosis_symbol, e->sym))
+ continue;
+
+ if (sym_is_boolean(e->sym))
+ type = SF_BOOLEAN;
+ else if (sym_is_nonboolean(e->sym))
+ type = SF_NONBOOLEAN;
+ else
+ type = SF_DISALLOWED;
+ fix = symbol_fix_create(e, type, diagnosis);
+
+ sfix_list_add(diagnosis_symbol, fix);
+ }
+
+ return diagnosis_symbol;
+}
+
+/*
+ * convert the diagnoses of fexpr into diagnoses of symbols
+ * it is easier to handle symbols when applying fixes
+ */
+static struct sfl_list *convert_diagnoses(struct fexl_list *diag_arr, struct cfdata *data)
+{
+ struct fexl_node *lnode;
+
+ diagnoses_symbol = sfl_list_init();
+
+ fexl_list_for_each(lnode, diag_arr) {
+ struct sfix_list *fix = convert_diagnosis(lnode->elem, data);
+
+ sfl_list_add(diagnoses_symbol, fix);
+ }
+
+ return diagnoses_symbol;
+}
+
+/*
+ * create a symbol_fix given a fexpr
+ */
+static struct symbol_fix *symbol_fix_create(struct fexpr *e, enum symbolfix_type type,
+ struct fexpr_list *diagnosis)
+{
+ struct symbol_fix *fix = malloc(sizeof(struct symbol_fix));
+
+ fix->sym = e->sym;
+ fix->type = type;
+
+ switch (type) {
+ case SF_BOOLEAN:
+ fix->tri = calculate_new_tri_val(e, diagnosis);
+ break;
+ case SF_NONBOOLEAN:
+ fix->nb_val = str_new();
+ str_append(&fix->nb_val, calculate_new_string_value(e, diagnosis));
+ break;
+ default:
+ perror("Illegal symbolfix_type.\n");
+ }
+
+ return fix;
+}
+
+/*
+ * remove symbols from the diagnosis, which will be set automatically:
+ * 1. symbol gets selected
+ * 2. choice symbol gets enabled/disabled automatically
+ * 3. symbol uses a default value
+ */
+static struct sfl_list *minimise_diagnoses(PicoSAT *pico, struct fexl_list *diagnoses,
+ struct cfdata *data)
+{
+ clock_t start, end;
+ double time;
+ struct fexpr_list *d;
+ struct sfix_list *diagnosis_symbol;
+ struct sfl_list *diagnoses_symbol = sfl_list_init();
+ struct fexpr *e;
+ int satval, deref = 0;
+ struct symbol_fix *fix;
+ struct fexl_node *flnode;
+ struct fexpr_list *C;
+
+ printd("Minimising diagnoses...");
+
+ start = clock();
+
+ /* create soft constraint set C */
+ C = fexpr_list_init();
+ add_fexpr_to_constraint_set(C, data);
+
+ fexl_list_for_each(flnode, diagnoses) {
+ struct fexpr_node *fnode;
+ struct sfix_node *snode;
+ struct fexpr_list *C_without_d;
+ int res;
+
+ d = flnode->elem;
+
+ /* set assumptions for those symbols that don't need to be changed */
+ C_without_d = get_difference(C, d);
+ set_assumptions(pico, C_without_d, data);
+ fexpr_list_free(C_without_d);
+ fexpr_list_free(C);
+
+
+ /* flip the assumptions from the diagnosis */
+ fexpr_list_for_each(fnode, d) {
+ e = fnode->elem;
+ satval = e->assumption ? -(e->satval) : e->satval;
+ picosat_assume(pico, satval);
+ }
+
+ res = picosat_sat(pico, -1);
+ if (res != PICOSAT_SATISFIABLE)
+ perror("Diagnosis not satisfiable (minimise).");
+
+ diagnosis_symbol = convert_diagnosis(d, data);
+
+ /* check if symbol gets selected */
+ for (snode = diagnosis_symbol->head; snode != NULL;) {
+ fix = snode->elem;
+
+ /* symbol is never selected, continue */
+ if (!fix->sym->fexpr_sel_y) {
+ snode = snode->next;
+ continue;
+ }
+
+ /* check, whether the symbol was selected anyway */
+ if (fix->sym->type == S_BOOLEAN && fix->tri == yes)
+ deref = picosat_deref(pico, fix->sym->fexpr_sel_y->satval);
+ else if (fix->sym->type == S_TRISTATE && fix->tri == yes)
+ deref = picosat_deref(pico, fix->sym->fexpr_sel_y->satval);
+ else if (fix->sym->type == S_TRISTATE && fix->tri == mod)
+ deref = picosat_deref(pico, fix->sym->fexpr_sel_m->satval);
+
+ if (deref == 1) {
+ struct sfix_node *tmp = snode->next;
+
+ sfix_list_delete(diagnosis_symbol, snode);
+ snode = tmp;
+ } else {
+ deref = 0;
+ snode = snode->next;
+ }
+ }
+ sfl_list_add(diagnoses_symbol, diagnosis_symbol);
+ }
+
+ end = clock();
+ time = ((double) (end - start)) / CLOCKS_PER_SEC;
+
+ printd("done. (%.6f secs.)\n", time);
+
+ return diagnoses_symbol;
+}
+
+/*
+ * list the diagnoses and let user choose a diagnosis to be applied
+ */
+struct sfix_list *choose_fix(struct sfl_list *diag)
+{
+ int choice;
+ struct sfl_node *node;
+
+ printd("=== GENERATED DIAGNOSES ===\n");
+ printd("0: No changes wanted\n");
+ print_diagnoses_symbol(diag);
+
+ printd("\n> Choose option: ");
+ scanf("%d", &choice);
+
+ /* no changes wanted */
+ if (choice == 0)
+ return NULL;
+
+ /* invalid choice */
+ if (choice > diag->size)
+ return NULL;
+
+ node = diag->head;
+ for (int counter = 1; counter < choice; counter++)
+ node = node->next;
+
+ return node->elem;
+}
+
+
+/*
+ * calculate the new value for a boolean symbol given a diagnosis and an fexpr
+ */
+static tristate calculate_new_tri_val(struct fexpr *e, struct fexpr_list *diagnosis)
+{
+ assert(sym_is_boolean(e->sym));
+
+ /* return the opposite of the last assumption for booleans */
+ if (e->sym->type == S_BOOLEAN)
+ return e->assumption ? no : yes;
+
+ /* new values for tristate must be deduced from the diagnosis */
+ if (e->sym->type == S_TRISTATE) {
+ /* fexpr_y */
+ if (e->tri == yes) {
+ if (e->assumption == true)
+ /*
+ * if diagnosis contains fexpr_m, fexpr_m was false
+ * => new value is mod
+ */
+ return diagnosis_contains_fexpr(
+ diagnosis, e->sym->fexpr_m) ? mod : no;
+ else if (e->assumption == false)
+ /*
+ * if fexpr_y is set to true, the new value must be yes
+ */
+ return yes;
+ }
+ /* fexpr_m */
+ if (e->tri == mod) {
+ if (e->assumption == true)
+ /*
+ * if diagnosis contains fexpr_y, fexpr_y was false
+ * => new value is yes
+ */
+ return diagnosis_contains_fexpr(
+ diagnosis, e->sym->fexpr_m) ? yes : no;
+ else if (e->assumption == false)
+ /* if diagnosis contains fexpr_m, the new value must be mod */
+ return mod;
+ }
+ perror("Should not get here.\n");
+ }
+
+ perror("Error calculating new tristate value.\n");
+ return no;
+}
+
+/*
+ * calculate the new value for a non-boolean symbol given a diagnosis and an fexpr
+ */
+static const char *calculate_new_string_value(struct fexpr *e, struct fexpr_list *diagnosis)
+{
+ struct fexpr_node *node;
+ struct fexpr *e2;
+
+ assert(sym_is_nonboolean(e->sym));
+
+ /* if assumption was false before, this is the new value because only 1
+ * variable can be true
+ */
+ if (e->assumption == false)
+ return str_get(&e->nb_val);
+
+ /* a diagnosis always contains 2 variables for the same non-boolean symbol
+ * one is set to true, the other to false
+ * otherwise you'd set 2 variables to true, which is not allowed
+ */
+ fexpr_list_for_each(node, diagnosis) {
+ e2 = node->elem;
+
+ /* not interested in other symbols or the same fexpr */
+ if (e->sym != e2->sym || e->satval == e2->satval)
+ continue;
+
+ return str_get(&e2->nb_val);
+ }
+
+ perror("Error calculating new string value.\n");
+ return "";
+}
diff --git a/scripts/kconfig/cf_rangefix.h b/scripts/kconfig/cf_rangefix.h
new file mode 100644
index 000000000000..3de0b7811487
--- /dev/null
+++ b/scripts/kconfig/cf_rangefix.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CF_RANGEFIX_H
+#define CF_RANGEFIX_H
+
+#include "picosat.h"
+#include "cf_defs.h"
+
+/* initialize RangeFix and return the diagnoses */
+struct sfl_list *rangefix_run(PicoSAT *pico, struct cfdata *data);
+
+/* ask user which fix to apply */
+struct sfix_list *choose_fix(struct sfl_list *diag);
+
+/* print a single diagnosis of type symbol_fix */
+void print_diagnosis_symbol(struct sfix_list *diag_sym);
+
+#endif
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v4 09/12] kconfig: Add files with utility functions
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
` (7 preceding siblings ...)
2024-07-10 6:52 ` [PATCH v4 08/12] kconfig: Add files for RangeFix Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-08-12 8:48 ` Masahiro Yamada
2024-07-10 6:52 ` [PATCH v4 10/12] kconfig: Add tools Ole Schuerks
` (2 subsequent siblings)
11 siblings, 1 reply; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
This commit contains various helper functions used in the project.
Co-developed-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Patrick Franz <deltaone@debian.org>
Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
Suggested-by: Sarah Nadi <nadi@ualberta.ca>
Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/cf_utils.c | 1031 ++++++++++++++++++++++++++++++++++++
scripts/kconfig/cf_utils.h | 115 ++++
2 files changed, 1146 insertions(+)
create mode 100644 scripts/kconfig/cf_utils.c
create mode 100644 scripts/kconfig/cf_utils.h
diff --git a/scripts/kconfig/cf_utils.c b/scripts/kconfig/cf_utils.c
new file mode 100644
index 000000000000..bcffd0a4fc1b
--- /dev/null
+++ b/scripts/kconfig/cf_utils.c
@@ -0,0 +1,1031 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "configfix.h"
+#include "internal.h"
+
+#define SATMAP_INIT_SIZE 2
+
+static PicoSAT *pico;
+
+static void unfold_cnf_clause(struct pexpr *e);
+static void build_cnf_tseytin(struct pexpr *e, struct cfdata *data);
+
+static void build_cnf_tseytin_top_and(struct pexpr *e, struct cfdata *data);
+static void build_cnf_tseytin_top_or(struct pexpr *e, struct cfdata *data);
+
+static void build_cnf_tseytin_tmp(struct pexpr *e, struct fexpr *t, struct cfdata *data);
+static void build_cnf_tseytin_and(struct pexpr *e, struct fexpr *t, struct cfdata *data);
+static void build_cnf_tseytin_or(struct pexpr *e, struct fexpr *t, struct cfdata *data);
+static int pexpr_satval(struct pexpr *e);
+
+/*
+ * parse Kconfig-file and read .config
+ */
+void init_config(const char *Kconfig_file)
+{
+ conf_parse(Kconfig_file);
+ conf_read(NULL);
+}
+
+/*
+ * initialize satmap
+ */
+void init_data(struct cfdata *data)
+{
+ /* create hashtable with all fexpr */
+ data->satmap = xcalloc(SATMAP_INIT_SIZE, sizeof(**data->satmap));
+ data->satmap_size = SATMAP_INIT_SIZE;
+
+ printd("done.\n");
+}
+
+/*
+ * create SAT-variables for all fexpr
+ */
+void create_sat_variables(struct cfdata *data)
+{
+ struct symbol *sym;
+
+ printd("Creating SAT-variables...");
+
+ for_all_symbols(sym) {
+ sym->constraints = pexpr_list_init();
+ sym_create_fexpr(sym, data);
+ }
+
+ printd("done.\n");
+}
+
+/*
+ * create various constants
+ */
+void create_constants(struct cfdata *data)
+{
+ printd("Creating constants...");
+
+ /* create TRUE and FALSE constants */
+ data->constants->const_false = fexpr_create(data->sat_variable_nr++, FE_FALSE, "False");
+ // const_false = fexpr_create(sat_variable_nr++, FE_FALSE, "False");
+ fexpr_add_to_satmap(data->constants->const_false, data);
+
+ data->constants->const_true = fexpr_create(data->sat_variable_nr++, FE_TRUE, "True");
+ fexpr_add_to_satmap(data->constants->const_true, data);
+
+ /* add fexpr of constants to tristate constants */
+ symbol_yes.fexpr_y = data->constants->const_true;
+ symbol_yes.fexpr_m = data->constants->const_false;
+
+ symbol_mod.fexpr_y = data->constants->const_false;
+ symbol_mod.fexpr_m = data->constants->const_true;
+
+ symbol_no.fexpr_y = data->constants->const_false;
+ symbol_no.fexpr_m = data->constants->const_false;
+
+ /* create symbols yes/mod/no as fexpr */
+ data->constants->symbol_yes_fexpr = fexpr_create(0, FE_SYMBOL, "y");
+ data->constants->symbol_yes_fexpr->sym = &symbol_yes;
+ data->constants->symbol_yes_fexpr->tri = yes;
+
+ data->constants->symbol_mod_fexpr = fexpr_create(0, FE_SYMBOL, "m");
+ data->constants->symbol_mod_fexpr->sym = &symbol_mod;
+ data->constants->symbol_mod_fexpr->tri = mod;
+
+ data->constants->symbol_no_fexpr = fexpr_create(0, FE_SYMBOL, "n");
+ data->constants->symbol_no_fexpr->sym = &symbol_no;
+ data->constants->symbol_no_fexpr->tri = no;
+
+ printd("done.\n");
+}
+
+/*
+ * create a temporary SAT-variable
+ */
+struct fexpr *create_tmpsatvar(struct cfdata *data)
+{
+ char *name = get_tmp_var_as_char(data->tmp_variable_nr);
+ struct fexpr *t = fexpr_create(data->sat_variable_nr++, FE_TMPSATVAR, name);
+
+ ++data->tmp_variable_nr;
+ fexpr_add_to_satmap(t, data);
+
+ free(name);
+ return t;
+}
+
+/*
+ * return a temporary SAT variable as string
+ */
+char *get_tmp_var_as_char(int i)
+{
+ char *val = malloc(sizeof(char) * 18);
+
+ snprintf(val, 18, "T_%d", i);
+ return val;
+}
+
+/*
+ * return a tristate value as a char *
+ */
+char *tristate_get_char(tristate val)
+{
+ switch (val) {
+ case yes:
+ return "yes";
+ case mod:
+ return "mod";
+ case no:
+ return "no";
+ default:
+ return "";
+ }
+}
+
+/*
+ *check whether an expr can evaluate to mod
+ */
+bool expr_can_evaluate_to_mod(struct expr *e)
+{
+ if (!e)
+ return false;
+
+ switch (e->type) {
+ case E_SYMBOL:
+ return e->left.sym == &symbol_mod || e->left.sym->type == S_TRISTATE ? true : false;
+ case E_AND:
+ case E_OR:
+ return expr_can_evaluate_to_mod(e->left.expr) ||
+ expr_can_evaluate_to_mod(e->right.expr);
+ case E_NOT:
+ return expr_can_evaluate_to_mod(e->left.expr);
+ default:
+ return false;
+ }
+}
+
+/*
+ * check whether an expr is a non-Boolean constant
+ */
+bool expr_is_nonbool_constant(struct expr *e)
+{
+ if (e->type != E_SYMBOL)
+ return false;
+ if (e->left.sym->type != S_UNKNOWN)
+ return false;
+
+ if (e->left.sym->flags & SYMBOL_CONST)
+ return true;
+
+ return string_is_number(e->left.sym->name) || string_is_hex(e->left.sym->name);
+}
+
+/*
+ * check whether a symbol is a non-Boolean constant
+ */
+bool sym_is_nonbool_constant(struct symbol *sym)
+{
+ if (sym->type != S_UNKNOWN)
+ return false;
+
+ if (sym->flags & SYMBOL_CONST)
+ return true;
+
+ return string_is_number(sym->name) || string_is_hex(sym->name);
+}
+
+/*
+ * print an expr
+ */
+static void print_expr_util(struct expr *e, int prevtoken)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case E_SYMBOL:
+ if (sym_get_name(e->left.sym) != NULL)
+ printf("%s", sym_get_name(e->left.sym));
+ else
+ printf("left was null\n");
+ break;
+ case E_NOT:
+ printf("!");
+ print_expr_util(e->left.expr, E_NOT);
+ break;
+ case E_AND:
+ if (prevtoken != E_AND && prevtoken != 0)
+ printf("(");
+ print_expr_util(e->left.expr, E_AND);
+ printf(" && ");
+ print_expr_util(e->right.expr, E_AND);
+ if (prevtoken != E_AND && prevtoken != 0)
+ printf(")");
+ break;
+ case E_OR:
+ if (prevtoken != E_OR && prevtoken != 0)
+ printf("(");
+ print_expr_util(e->left.expr, E_OR);
+ printf(" || ");
+ print_expr_util(e->right.expr, E_OR);
+ if (prevtoken != E_OR && prevtoken != 0)
+ printf(")");
+ break;
+ case E_EQUAL:
+ case E_UNEQUAL:
+ if (e->left.sym->name)
+ printf("%s", e->left.sym->name);
+ else
+ printf("left was null\n");
+ printf("%s", e->type == E_EQUAL ? "=" : "!=");
+ printf("%s", e->right.sym->name);
+ break;
+ case E_LEQ:
+ case E_LTH:
+ if (e->left.sym->name)
+ printf("%s", e->left.sym->name);
+ else
+ printf("left was null\n");
+ printf("%s", e->type == E_LEQ ? "<=" : "<");
+ printf("%s", e->right.sym->name);
+ break;
+ case E_GEQ:
+ case E_GTH:
+ if (e->left.sym->name)
+ printf("%s", e->left.sym->name);
+ else
+ printf("left was null\n");
+ printf("%s", e->type == E_GEQ ? ">=" : ">");
+ printf("%s", e->right.sym->name);
+ break;
+ case E_RANGE:
+ printf("[");
+ printf("%s", e->left.sym->name);
+ printf(" ");
+ printf("%s", e->right.sym->name);
+ printf("]");
+ break;
+ default:
+ break;
+ }
+}
+void print_expr(char *tag, struct expr *e, int prevtoken)
+{
+ printf("%s ", tag);
+ print_expr_util(e, prevtoken);
+ printf("\n");
+}
+
+/*
+ * check, if the symbol is a tristate-constant
+ */
+bool sym_is_tristate_constant(struct symbol *sym)
+{
+ return sym == &symbol_yes || sym == &symbol_mod || sym == &symbol_no;
+}
+
+/*
+ * check, if a symbol is of type boolean or tristate
+ */
+bool sym_is_boolean(struct symbol *sym)
+{
+ return sym->type == S_BOOLEAN || sym->type == S_TRISTATE;
+}
+
+/*
+ * check, if a symbol is a boolean/tristate or a tristate constant
+ */
+bool sym_is_bool_or_triconst(struct symbol *sym)
+{
+ return sym_is_tristate_constant(sym) || sym_is_boolean(sym);
+}
+
+/*
+ * check, if a symbol is of type int, hex, or string
+ */
+bool sym_is_nonboolean(struct symbol *sym)
+{
+ return sym->type == S_INT || sym->type == S_HEX || sym->type == S_STRING;
+}
+
+/*
+ * check, if a symbol has a prompt
+ */
+bool sym_has_prompt(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_prompts(sym, prop)
+ return true;
+
+ return false;
+}
+
+/*
+ * return the prompt of the symbol if there is one, NULL otherwise
+ */
+struct property *sym_get_prompt(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_prompts(sym, prop)
+ return prop;
+
+ return NULL;
+}
+
+/*
+ * return the condition for the property, NULL if there is none. To be pexpr_put
+ * by caller.
+ */
+struct pexpr *prop_get_condition(struct property *prop, struct cfdata *data)
+{
+ if (prop == NULL)
+ return NULL;
+
+ /* if there is no condition, return True */
+ if (!prop->visible.expr)
+ return pexf(data->constants->const_true);
+
+ return expr_calculate_pexpr_both(prop->visible.expr, data);
+}
+
+/*
+ * return the default property, NULL if none exists or can be satisfied
+ */
+struct property *sym_get_default_prop(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_defaults(sym, prop) {
+ prop->visible.tri = expr_calc_value(prop->visible.expr);
+ if (prop->visible.tri != no)
+ return prop;
+ }
+ return NULL;
+}
+
+/*
+ * check whether a non-boolean symbol has a value set
+ */
+bool sym_nonbool_has_value_set(struct symbol *sym)
+{
+ /*
+ * The built constraints make the following constraints:
+ *
+ * visible -> not 'n'
+ * sym->dir_dep not fulfilled -> 'n'
+ * invisible -> (no default's condition is fulfilled <-> 'n')
+ */
+ struct property *prompt;
+ struct property *p;
+
+ if (!sym_is_nonboolean(sym))
+ return false;
+
+ /* cannot have a value with unmet dependencies */
+ if (sym->dir_dep.expr && sym->dir_dep.tri == no)
+ return false;
+
+ /* visible prompt => value set */
+ prompt = sym_get_prompt(sym);
+ if (prompt != NULL && prompt->visible.tri != no)
+ return true;
+
+ /* invisible prompt => must get value from default value */
+ p = sym_get_default_prop(sym);
+ return p != NULL;
+}
+
+/*
+ * return pointer to the name of the symbol or the current prompt-text, if it
+ * is a choice symbol
+ */
+const char *sym_get_name(struct symbol *sym)
+{
+ if (sym_is_choice(sym)) {
+ struct property *prompt = sym_get_prompt(sym);
+
+ if (prompt == NULL)
+ return "";
+
+ return prompt->text;
+ } else {
+ return sym->name;
+ }
+}
+
+/*
+ * check whether symbol is to be changed
+ */
+bool sym_is_sdv(struct sdv_list *list, struct symbol *sym)
+{
+ struct sdv_node *node;
+
+ sdv_list_for_each(node, list)
+ if (sym == node->elem->sym)
+ return true;
+
+ return false;
+}
+
+/*
+ * print a symbol's name
+ */
+void print_sym_name(struct symbol *sym)
+{
+ printf("Symbol: ");
+ if (sym_is_choice(sym)) {
+ struct property *prompt = sym_get_prompt(sym);
+
+ printf("(Choice) %s", prompt->text);
+ } else {
+ printf("%s", sym->name);
+ }
+ printf("\n");
+}
+
+/*
+ * print all constraints for a symbol
+ */
+void print_sym_constraint(struct symbol *sym)
+{
+ struct pexpr_node *node;
+
+ pexpr_list_for_each(node, sym->constraints)
+ pexpr_print("::", node->elem, -1);
+}
+
+/*
+ * print a default map
+ */
+void print_default_map(struct defm_list *map)
+{
+ struct default_map *entry;
+ struct defm_node *node;
+
+ defm_list_for_each(node, map) {
+ struct gstr s = str_new();
+
+ entry = node->elem;
+
+ str_append(&s, "\t");
+ str_append(&s, str_get(&entry->val->name));
+ str_append(&s, " ->");
+ pexpr_print(strdup(str_get(&s)), entry->e, -1);
+ str_free(&s);
+ }
+}
+
+/*
+ * check whether a string is a number
+ */
+bool string_is_number(char *s)
+{
+ int len = strlen(s);
+ int i = 0;
+
+ while (i < len) {
+ if (!isdigit(s[i]))
+ return false;
+ i++;
+ }
+
+ return true;
+}
+
+/*
+ * check whether a string is a hexadecimal number
+ */
+bool string_is_hex(char *s)
+{
+ int len = strlen(s);
+ int i = 2;
+
+ if (len >= 3 && s[0] == '0' && s[1] == 'x') {
+ while (i < len) {
+ if (!isxdigit(s[i]))
+ return false;
+ i++;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*
+ * initialize PicoSAT
+ */
+PicoSAT *initialize_picosat(void)
+{
+ PicoSAT *pico;
+
+ printd("\nInitializing PicoSAT...");
+ pico = picosat_init();
+ picosat_enable_trace_generation(pico);
+ printd("done.\n");
+
+ return pico;
+}
+
+/*
+ * construct the CNF-clauses from the constraints
+ */
+void construct_cnf_clauses(PicoSAT *p, struct cfdata *data)
+{
+ struct symbol *sym;
+
+ pico = p;
+
+ /* adding unit-clauses for constants */
+ sat_add_clause(2, pico, -(data->constants->const_false->satval));
+ sat_add_clause(2, pico, data->constants->const_true->satval);
+
+ for_all_symbols(sym) {
+ struct pexpr_node *node;
+
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ pexpr_list_for_each(node, sym->constraints) {
+ if (pexpr_is_cnf(node->elem)) {
+ unfold_cnf_clause(node->elem);
+ picosat_add(pico, 0);
+ } else {
+ build_cnf_tseytin(node->elem, data);
+ }
+
+ }
+ }
+}
+
+/*
+ * helper function to add an expression to a CNF-clause
+ */
+static void unfold_cnf_clause(struct pexpr *e)
+{
+ switch (e->type) {
+ case PE_SYMBOL:
+ picosat_add(pico, e->left.fexpr->satval);
+ break;
+ case PE_OR:
+ unfold_cnf_clause(e->left.pexpr);
+ unfold_cnf_clause(e->right.pexpr);
+ break;
+ case PE_NOT:
+ picosat_add(pico, -(e->left.pexpr->left.fexpr->satval));
+ break;
+ default:
+ perror("Not in CNF, FE_EQUALS.");
+ }
+}
+
+/*
+ * build CNF-clauses for a pexpr not in CNF
+ */
+static void build_cnf_tseytin(struct pexpr *e, struct cfdata *data)
+{
+ switch (e->type) {
+ case PE_AND:
+ build_cnf_tseytin_top_and(e, data);
+ break;
+ case PE_OR:
+ build_cnf_tseytin_top_or(e, data);
+ break;
+ default:
+ perror("Expression not a propositional logic formula. root.");
+ }
+}
+
+/*
+ * split up a pexpr of type AND as both sides must be satisfied
+ */
+static void build_cnf_tseytin_top_and(struct pexpr *e, struct cfdata *data)
+{
+ if (pexpr_is_cnf(e->left.pexpr))
+ unfold_cnf_clause(e->left.pexpr);
+ else
+ build_cnf_tseytin(e->left.pexpr, data);
+
+ if (pexpr_is_cnf(e->right.pexpr))
+ unfold_cnf_clause(e->right.pexpr);
+ else
+ build_cnf_tseytin(e->right.pexpr, data);
+
+}
+
+static void build_cnf_tseytin_top_or(struct pexpr *e, struct cfdata *data)
+{
+ struct fexpr *t1 = NULL, *t2 = NULL;
+ int a, b;
+
+ /* set left side */
+ if (pexpr_is_symbol(e->left.pexpr)) {
+ a = pexpr_satval(e->left.pexpr);
+ } else {
+ t1 = create_tmpsatvar(data);
+ a = t1->satval;
+ }
+
+ /* set right side */
+ if (pexpr_is_symbol(e->right.pexpr)) {
+ b = pexpr_satval(e->right.pexpr);
+ } else {
+ t2 = create_tmpsatvar(data);
+ b = t2->satval;
+ }
+
+ /* A v B */
+ sat_add_clause(3, pico, a, b);
+
+ /* traverse down the tree to build more constraints if needed */
+ if (!pexpr_is_symbol(e->left.pexpr)) {
+ if (t1 == NULL)
+ perror("t1 is NULL.");
+
+ build_cnf_tseytin_tmp(e->left.pexpr, t1, data);
+ }
+
+ if (!pexpr_is_symbol(e->right.pexpr)) {
+ if (t2 == NULL)
+ perror("t2 is NULL.");
+
+ build_cnf_tseytin_tmp(e->right.pexpr, t2, data);
+ }
+}
+
+/*
+ * build the sub-expressions
+ */
+static void build_cnf_tseytin_tmp(struct pexpr *e, struct fexpr *t, struct cfdata *data)
+{
+ switch (e->type) {
+ case PE_AND:
+ build_cnf_tseytin_and(e, t, data);
+ break;
+ case PE_OR:
+ build_cnf_tseytin_or(e, t, data);
+ break;
+ default:
+ perror("Expression not a propositional logic formula. root.");
+ }
+}
+
+/*
+ * build the Tseytin sub-expressions for a pexpr of type AND
+ */
+static void build_cnf_tseytin_and(struct pexpr *e, struct fexpr *t, struct cfdata *data)
+{
+ struct fexpr *t1 = NULL, *t2 = NULL;
+ int a, b, c;
+
+ assert(t != NULL);
+
+ /* set left side */
+ if (pexpr_is_symbol(e->left.pexpr)) {
+ a = pexpr_satval(e->left.pexpr);
+ } else {
+ t1 = create_tmpsatvar(data);
+ a = t1->satval;
+ }
+
+ /* set right side */
+ if (pexpr_is_symbol(e->right.pexpr)) {
+ b = pexpr_satval(e->right.pexpr);
+ } else {
+ t2 = create_tmpsatvar(data);
+ b = t2->satval;
+ }
+
+ c = t->satval;
+
+ /* -A v -B v C */
+ sat_add_clause(4, pico, -a, -b, c);
+ /* A v -C */
+ sat_add_clause(3, pico, a, -c);
+ /* B v -C */
+ sat_add_clause(3, pico, b, -c);
+
+ /* traverse down the tree to build more constraints if needed */
+ if (!pexpr_is_symbol(e->left.pexpr)) {
+ if (t1 == NULL)
+ perror("t1 is NULL.");
+
+ build_cnf_tseytin_tmp(e->left.pexpr, t1, data);
+ }
+ if (!pexpr_is_symbol(e->right.pexpr)) {
+ if (t2 == NULL)
+ perror("t2 is NULL.");
+
+ build_cnf_tseytin_tmp(e->right.pexpr, t2, data);
+ }
+}
+
+/*
+ * build the Tseytin sub-expressions for a pexpr of type
+ */
+static void build_cnf_tseytin_or(struct pexpr *e, struct fexpr *t, struct cfdata *data)
+{
+ struct fexpr *t1 = NULL, *t2 = NULL;
+ int a, b, c;
+
+ assert(t != NULL);
+
+ /* set left side */
+ if (pexpr_is_symbol(e->left.pexpr)) {
+ a = pexpr_satval(e->left.pexpr);
+ } else {
+ t1 = create_tmpsatvar(data);
+ a = t1->satval;
+ }
+
+ /* set right side */
+ if (pexpr_is_symbol(e->right.pexpr)) {
+ b = pexpr_satval(e->right.pexpr);
+ } else {
+ t2 = create_tmpsatvar(data);
+ b = t2->satval;
+ }
+
+ c = t->satval;
+
+ /* A v B v -C */
+ sat_add_clause(4, pico, a, b, -c);
+ /* -A v C */;
+ sat_add_clause(3, pico, -a, c);
+ /* -B v C */
+ sat_add_clause(3, pico, -b, c);
+
+ /* traverse down the tree to build more constraints if needed */
+ if (!pexpr_is_symbol(e->left.pexpr)) {
+ if (t1 == NULL)
+ perror("t1 is NULL.");
+
+ build_cnf_tseytin_tmp(e->left.pexpr, t1, data);
+ }
+ if (!pexpr_is_symbol(e->right.pexpr)) {
+ if (t2 == NULL)
+ perror("t2 is NULL.");
+
+ build_cnf_tseytin_tmp(e->right.pexpr, t2, data);
+ }
+}
+
+/*
+ * add a clause to PicoSAT
+ * First argument must be the SAT solver
+ */
+void sat_add_clause(int num, ...)
+{
+ va_list valist;
+ int lit;
+ PicoSAT *pico;
+
+ if (num <= 1)
+ return;
+
+ va_start(valist, num);
+
+ pico = va_arg(valist, PicoSAT *);
+
+ /* access all the arguments assigned to valist */
+ for (int i = 1; i < num; i++) {
+ lit = va_arg(valist, int);
+ picosat_add(pico, lit);
+ }
+ picosat_add(pico, 0);
+
+ va_end(valist);
+}
+
+/*
+ * return the SAT-variable for a pexpr that is a symbol
+ */
+static int pexpr_satval(struct pexpr *e)
+{
+ if (!pexpr_is_symbol(e)) {
+ perror("pexpr is not a symbol.");
+ return -1;
+ }
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ return e->left.fexpr->satval;
+ case PE_NOT:
+ return -(e->left.pexpr->left.fexpr->satval);
+ default:
+ perror("Not a symbol.");
+ }
+
+ return -1;
+}
+
+/*
+ * start PicoSAT
+ */
+void picosat_solve(PicoSAT *pico, struct cfdata *data)
+{
+ clock_t start, end;
+ double time;
+ int res;
+
+ printd("Solving SAT-problem...");
+
+ start = clock();
+ res = picosat_sat(pico, -1);
+ end = clock();
+
+ time = ((double) (end - start)) / CLOCKS_PER_SEC;
+ printd("done. (%.6f secs.)\n\n", time);
+
+ if (res == PICOSAT_SATISFIABLE) {
+ printd("===> PROBLEM IS SATISFIABLE <===\n");
+
+ } else if (res == PICOSAT_UNSATISFIABLE) {
+ struct fexpr *e;
+ int lit;
+ const int *i;
+
+ printd("===> PROBLEM IS UNSATISFIABLE <===\n");
+
+ /* print unsat core */
+ printd("\nPrinting unsatisfiable core:\n");
+
+ i = picosat_failed_assumptions(pico);
+ lit = abs(*i++);
+
+ while (lit != 0) {
+ e = data->satmap[lit];
+
+ printd("(%d) %s <%d>\n", lit, str_get(&e->name), e->assumption);
+ lit = abs(*i++);
+ }
+ } else {
+ printd("Unknown if satisfiable.\n");
+ }
+}
+
+/*
+ * add assumption for a symbol to the SAT-solver
+ */
+void sym_add_assumption(PicoSAT *pico, struct symbol *sym)
+{
+ if (sym_is_boolean(sym)) {
+ int tri_val = sym_get_tristate_value(sym);
+
+ sym_add_assumption_tri(pico, sym, tri_val);
+ return;
+ }
+
+ if (sym_is_nonboolean(sym)) {
+ struct fexpr *e = sym->nb_vals->head->elem;
+ struct fexpr_node *node;
+
+ const char *string_val = sym_get_string_value(sym);
+
+ if (sym->type == S_STRING && !strcmp(string_val, ""))
+ return;
+
+ /* symbol does not have a value */
+ if (!sym_nonbool_has_value_set(sym)) {
+ /* set value for sym=n */
+ picosat_assume(pico, e->satval);
+ e->assumption = true;
+
+ for (node = sym->nb_vals->head->next; node != NULL; node = node->next) {
+ picosat_assume(pico, -(node->elem->satval));
+ node->elem->assumption = false;
+ }
+
+ return;
+ }
+
+ /* symbol does have a value set */
+
+ /* set value for sym=n */
+ picosat_assume(pico, -(e->satval));
+ e->assumption = false;
+
+ /* set value for all other fexpr */
+ fexpr_list_for_each(node, sym->nb_vals) {
+ if (node->prev == NULL)
+ continue;
+
+ if (strcmp(str_get(&node->elem->nb_val), string_val) == 0) {
+ picosat_assume(pico, node->elem->satval);
+ node->elem->assumption = true;
+ } else {
+ picosat_assume(pico, -(node->elem->satval));
+ node->elem->assumption = false;
+ }
+ }
+ }
+}
+
+/*
+ * add assumption for a boolean symbol to the SAT-solver
+ */
+void sym_add_assumption_tri(PicoSAT *pico, struct symbol *sym, tristate tri_val)
+{
+ if (sym->type == S_BOOLEAN) {
+ int a = sym->fexpr_y->satval;
+
+ switch (tri_val) {
+ case no:
+ picosat_assume(pico, -a);
+ sym->fexpr_y->assumption = false;
+ break;
+ case mod:
+ perror("Should not happen. Boolean symbol is set to mod.\n");
+ break;
+ case yes:
+
+ picosat_assume(pico, a);
+ sym->fexpr_y->assumption = true;
+ break;
+ }
+ }
+ if (sym->type == S_TRISTATE) {
+ int a = sym->fexpr_y->satval;
+ int a_m = sym->fexpr_m->satval;
+
+ switch (tri_val) {
+ case no:
+ picosat_assume(pico, -a);
+ picosat_assume(pico, -a_m);
+ sym->fexpr_y->assumption = false;
+ sym->fexpr_m->assumption = false;
+ break;
+ case mod:
+ picosat_assume(pico, -a);
+ picosat_assume(pico, a_m);
+ sym->fexpr_y->assumption = false;
+ sym->fexpr_m->assumption = true;
+ break;
+ case yes:
+ picosat_assume(pico, a);
+ picosat_assume(pico, -a_m);
+ sym->fexpr_y->assumption = true;
+ sym->fexpr_m->assumption = false;
+ break;
+ }
+ }
+}
+
+/*
+ * add assumptions for the symbols to be changed to the SAT solver
+ */
+void sym_add_assumption_sdv(PicoSAT *pico, struct sdv_list *list)
+{
+ struct symbol_dvalue *sdv;
+ struct sdv_node *node;
+ int lit_y, lit_m;
+
+ sdv_list_for_each(node, list) {
+ sdv = node->elem;
+ lit_y = sdv->sym->fexpr_y->satval;
+
+ if (sdv->sym->type == S_BOOLEAN) {
+ switch (sdv->tri) {
+ case yes:
+ picosat_assume(pico, lit_y);
+ break;
+ case no:
+ picosat_assume(pico, -lit_y);
+ break;
+ case mod:
+ perror("Should not happen.\n");
+ }
+ } else if (sdv->sym->type == S_TRISTATE) {
+ lit_m = sdv->sym->fexpr_m->satval;
+
+ switch (sdv->tri) {
+ case yes:
+ picosat_assume(pico, lit_y);
+ picosat_assume(pico, -lit_m);
+ break;
+ case mod:
+ picosat_assume(pico, -lit_y);
+ picosat_assume(pico, lit_m);
+ break;
+ case no:
+ picosat_assume(pico, -lit_y);
+ picosat_assume(pico, -lit_m);
+ }
+ }
+ }
+}
diff --git a/scripts/kconfig/cf_utils.h b/scripts/kconfig/cf_utils.h
new file mode 100644
index 000000000000..b71c8731a8ff
--- /dev/null
+++ b/scripts/kconfig/cf_utils.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CF_UTILS_H
+#define CF_UTILS_H
+
+#include "expr.h"
+#include "cf_defs.h"
+#include "picosat.h"
+
+/* parse Kconfig-file and read .config */
+void init_config(const char *Kconfig_file);
+
+/* initialize satmap and cnf_clauses */
+void init_data(struct cfdata *data);
+
+/* assign SAT-variables to all fexpr and create the sat_map */
+void create_sat_variables(struct cfdata *data);
+
+/* create True/False constants */
+void create_constants(struct cfdata *data);
+
+/* create a temporary SAT-variable */
+struct fexpr *create_tmpsatvar(struct cfdata *data);
+
+/* return a temporary SAT variable as string */
+char *get_tmp_var_as_char(int i);
+
+/* return a tristate value as a char * */
+char *tristate_get_char(tristate val);
+
+/* check whether an expr can evaluate to mod */
+bool expr_can_evaluate_to_mod(struct expr *e);
+
+/* check whether an expr is a non-Boolean constant */
+bool expr_is_nonbool_constant(struct expr *e);
+
+/* check whether a symbol is a non-Boolean constant */
+bool sym_is_nonbool_constant(struct symbol *sym);
+
+/* print an expr */
+void print_expr(char *tag, struct expr *e, int prevtoken);
+
+/* check, if the symbol is a tristate-constant */
+bool sym_is_tristate_constant(struct symbol *sym);
+
+/* check, if a symbol is of type boolean or tristate */
+bool sym_is_boolean(struct symbol *sym);
+
+/* check, if a symbol is a boolean/tristate or a tristate constant */
+bool sym_is_bool_or_triconst(struct symbol *sym);
+
+/* check, if a symbol is of type int, hex, or string */
+bool sym_is_nonboolean(struct symbol *sym);
+
+/* check, if a symbol has a prompt */
+bool sym_has_prompt(struct symbol *sym);
+
+/* return the prompt of the symbol, if there is one */
+struct property *sym_get_prompt(struct symbol *sym);
+
+/* return the condition for the property, True if there is none */
+struct pexpr *prop_get_condition(struct property *prop, struct cfdata *data);
+
+/* return the default property, NULL if none exists or can be satisfied */
+struct property *sym_get_default_prop(struct symbol *sym);
+
+/* check whether a non-boolean symbol has a value set */
+bool sym_nonbool_has_value_set(struct symbol *sym);
+
+/* return the name of the symbol */
+const char *sym_get_name(struct symbol *sym);
+
+/* check whether symbol is to be changed */
+bool sym_is_sdv(struct sdv_list *list, struct symbol *sym);
+
+/* print a symbol's name */
+void print_sym_name(struct symbol *sym);
+
+/* print all constraints for a symbol */
+void print_sym_constraint(struct symbol *sym);
+
+/* print a default map */
+void print_default_map(struct defm_list *map);
+
+/* check whether a string is a number */
+bool string_is_number(char *s);
+
+/* check whether a string is a hexadecimal number */
+bool string_is_hex(char *s);
+
+/* initialize PicoSAT */
+PicoSAT *initialize_picosat(void);
+
+/* construct the CNF-clauses from the constraints */
+void construct_cnf_clauses(PicoSAT *pico, struct cfdata *data);
+
+/* add a clause to PicoSAT */
+void sat_add_clause(int num, ...);
+
+/* start PicoSAT */
+void picosat_solve(PicoSAT *pico, struct cfdata *data);
+
+/* add assumption for a symbol to the SAT-solver */
+void sym_add_assumption(PicoSAT *pico, struct symbol *sym);
+
+/* add assumption for a boolean symbol to the SAT-solver */
+void sym_add_assumption_tri(PicoSAT *pico, struct symbol *sym, tristate tri_val);
+
+/* add assumptions for the symbols to be changed to the SAT solver */
+void sym_add_assumption_sdv(PicoSAT *pico, struct sdv_list *list);
+
+#endif
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH v4 09/12] kconfig: Add files with utility functions
2024-07-10 6:52 ` [PATCH v4 09/12] kconfig: Add files with utility functions Ole Schuerks
@ 2024-08-12 8:48 ` Masahiro Yamada
0 siblings, 0 replies; 25+ messages in thread
From: Masahiro Yamada @ 2024-08-12 8:48 UTC (permalink / raw)
To: Ole Schuerks
Cc: linux-kbuild, jude.gyimah, thorsten.berger, deltaone,
jan.sollmann, mcgrof, linux-kernel
On Wed, Jul 10, 2024 at 3:54 PM Ole Schuerks <ole0811sch@gmail.com> wrote:
>
> This commit contains various helper functions used in the project.
>
> Co-developed-by: Patrick Franz <deltaone@debian.org>
> Signed-off-by: Patrick Franz <deltaone@debian.org>
> Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
> Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
> Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
> Suggested-by: Sarah Nadi <nadi@ualberta.ca>
> Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
> Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
> Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
> ---
> scripts/kconfig/cf_utils.c | 1031 ++++++++++++++++++++++++++++++++++++
> scripts/kconfig/cf_utils.h | 115 ++++
> 2 files changed, 1146 insertions(+)
> create mode 100644 scripts/kconfig/cf_utils.c
> create mode 100644 scripts/kconfig/cf_utils.h
>
> diff --git a/scripts/kconfig/cf_utils.c b/scripts/kconfig/cf_utils.c
> new file mode 100644
> index 000000000000..bcffd0a4fc1b
> --- /dev/null
> +++ b/scripts/kconfig/cf_utils.c
> @@ -0,0 +1,1031 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
> + */
> +
> +#define _GNU_SOURCE
> +#include <assert.h>
> +#include <locale.h>
> +#include <stdarg.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <time.h>
> +#include <unistd.h>
> +#include <ctype.h>
> +
> +#include "configfix.h"
> +#include "internal.h"
> +
> +#define SATMAP_INIT_SIZE 2
> +
> +static PicoSAT *pico;
> +
> +static void unfold_cnf_clause(struct pexpr *e);
> +static void build_cnf_tseytin(struct pexpr *e, struct cfdata *data);
> +
> +static void build_cnf_tseytin_top_and(struct pexpr *e, struct cfdata *data);
> +static void build_cnf_tseytin_top_or(struct pexpr *e, struct cfdata *data);
> +
> +static void build_cnf_tseytin_tmp(struct pexpr *e, struct fexpr *t, struct cfdata *data);
> +static void build_cnf_tseytin_and(struct pexpr *e, struct fexpr *t, struct cfdata *data);
> +static void build_cnf_tseytin_or(struct pexpr *e, struct fexpr *t, struct cfdata *data);
> +static int pexpr_satval(struct pexpr *e);
> +
> +/*
> + * parse Kconfig-file and read .config
> + */
> +void init_config(const char *Kconfig_file)
> +{
> + conf_parse(Kconfig_file);
> + conf_read(NULL);
> +}
> +
> +/*
> + * initialize satmap
> + */
> +void init_data(struct cfdata *data)
> +{
> + /* create hashtable with all fexpr */
> + data->satmap = xcalloc(SATMAP_INIT_SIZE, sizeof(**data->satmap));
> + data->satmap_size = SATMAP_INIT_SIZE;
This is a bug.
xcalloc() allocated much bigger memory than used.
> + printd("done.\n");
> +}
> +
> +/*
> + * create SAT-variables for all fexpr
> + */
> +void create_sat_variables(struct cfdata *data)
> +{
> + struct symbol *sym;
> +
> + printd("Creating SAT-variables...");
> +
> + for_all_symbols(sym) {
> + sym->constraints = pexpr_list_init();
> + sym_create_fexpr(sym, data);
> + }
> +
> + printd("done.\n");
> +}
> +
> +/*
> + * create various constants
> + */
> +void create_constants(struct cfdata *data)
> +{
> + printd("Creating constants...");
> +
> + /* create TRUE and FALSE constants */
> + data->constants->const_false = fexpr_create(data->sat_variable_nr++, FE_FALSE, "False");
> + // const_false = fexpr_create(sat_variable_nr++, FE_FALSE, "False");
> + fexpr_add_to_satmap(data->constants->const_false, data);
> +
> + data->constants->const_true = fexpr_create(data->sat_variable_nr++, FE_TRUE, "True");
> + fexpr_add_to_satmap(data->constants->const_true, data);
> +
> + /* add fexpr of constants to tristate constants */
> + symbol_yes.fexpr_y = data->constants->const_true;
> + symbol_yes.fexpr_m = data->constants->const_false;
> +
> + symbol_mod.fexpr_y = data->constants->const_false;
> + symbol_mod.fexpr_m = data->constants->const_true;
> +
> + symbol_no.fexpr_y = data->constants->const_false;
> + symbol_no.fexpr_m = data->constants->const_false;
> +
> + /* create symbols yes/mod/no as fexpr */
> + data->constants->symbol_yes_fexpr = fexpr_create(0, FE_SYMBOL, "y");
> + data->constants->symbol_yes_fexpr->sym = &symbol_yes;
> + data->constants->symbol_yes_fexpr->tri = yes;
> +
> + data->constants->symbol_mod_fexpr = fexpr_create(0, FE_SYMBOL, "m");
> + data->constants->symbol_mod_fexpr->sym = &symbol_mod;
> + data->constants->symbol_mod_fexpr->tri = mod;
> +
> + data->constants->symbol_no_fexpr = fexpr_create(0, FE_SYMBOL, "n");
> + data->constants->symbol_no_fexpr->sym = &symbol_no;
> + data->constants->symbol_no_fexpr->tri = no;
> +
> + printd("done.\n");
> +}
> +
> +/*
> + * create a temporary SAT-variable
> + */
> +struct fexpr *create_tmpsatvar(struct cfdata *data)
> +{
> + char *name = get_tmp_var_as_char(data->tmp_variable_nr);
> + struct fexpr *t = fexpr_create(data->sat_variable_nr++, FE_TMPSATVAR, name);
> +
> + ++data->tmp_variable_nr;
> + fexpr_add_to_satmap(t, data);
> +
> + free(name);
> + return t;
> +}
> +
> +/*
> + * return a temporary SAT variable as string
> + */
> +char *get_tmp_var_as_char(int i)
> +{
> + char *val = malloc(sizeof(char) * 18);
> +
> + snprintf(val, 18, "T_%d", i);
> + return val;
> +}
> +
> +/*
> + * return a tristate value as a char *
> + */
> +char *tristate_get_char(tristate val)
> +{
> + switch (val) {
> + case yes:
> + return "yes";
> + case mod:
> + return "mod";
> + case no:
> + return "no";
> + default:
> + return "";
> + }
> +}
> +
> +/*
> + *check whether an expr can evaluate to mod
> + */
> +bool expr_can_evaluate_to_mod(struct expr *e)
> +{
> + if (!e)
> + return false;
> +
> + switch (e->type) {
> + case E_SYMBOL:
> + return e->left.sym == &symbol_mod || e->left.sym->type == S_TRISTATE ? true : false;
> + case E_AND:
> + case E_OR:
> + return expr_can_evaluate_to_mod(e->left.expr) ||
> + expr_can_evaluate_to_mod(e->right.expr);
> + case E_NOT:
> + return expr_can_evaluate_to_mod(e->left.expr);
> + default:
> + return false;
> + }
> +}
> +
> +/*
> + * check whether an expr is a non-Boolean constant
> + */
> +bool expr_is_nonbool_constant(struct expr *e)
> +{
> + if (e->type != E_SYMBOL)
> + return false;
> + if (e->left.sym->type != S_UNKNOWN)
> + return false;
> +
> + if (e->left.sym->flags & SYMBOL_CONST)
> + return true;
> +
> + return string_is_number(e->left.sym->name) || string_is_hex(e->left.sym->name);
> +}
> +
> +/*
> + * check whether a symbol is a non-Boolean constant
> + */
> +bool sym_is_nonbool_constant(struct symbol *sym)
> +{
> + if (sym->type != S_UNKNOWN)
> + return false;
> +
> + if (sym->flags & SYMBOL_CONST)
> + return true;
> +
> + return string_is_number(sym->name) || string_is_hex(sym->name);
> +}
> +
> +/*
> + * print an expr
> + */
> +static void print_expr_util(struct expr *e, int prevtoken)
> +{
> + if (!e)
> + return;
> +
> + switch (e->type) {
> + case E_SYMBOL:
> + if (sym_get_name(e->left.sym) != NULL)
> + printf("%s", sym_get_name(e->left.sym));
> + else
> + printf("left was null\n");
> + break;
> + case E_NOT:
> + printf("!");
> + print_expr_util(e->left.expr, E_NOT);
> + break;
> + case E_AND:
> + if (prevtoken != E_AND && prevtoken != 0)
> + printf("(");
> + print_expr_util(e->left.expr, E_AND);
> + printf(" && ");
> + print_expr_util(e->right.expr, E_AND);
> + if (prevtoken != E_AND && prevtoken != 0)
> + printf(")");
> + break;
> + case E_OR:
> + if (prevtoken != E_OR && prevtoken != 0)
> + printf("(");
> + print_expr_util(e->left.expr, E_OR);
> + printf(" || ");
> + print_expr_util(e->right.expr, E_OR);
> + if (prevtoken != E_OR && prevtoken != 0)
> + printf(")");
> + break;
> + case E_EQUAL:
> + case E_UNEQUAL:
> + if (e->left.sym->name)
> + printf("%s", e->left.sym->name);
> + else
> + printf("left was null\n");
> + printf("%s", e->type == E_EQUAL ? "=" : "!=");
> + printf("%s", e->right.sym->name);
> + break;
> + case E_LEQ:
> + case E_LTH:
> + if (e->left.sym->name)
> + printf("%s", e->left.sym->name);
> + else
> + printf("left was null\n");
> + printf("%s", e->type == E_LEQ ? "<=" : "<");
> + printf("%s", e->right.sym->name);
> + break;
> + case E_GEQ:
> + case E_GTH:
> + if (e->left.sym->name)
> + printf("%s", e->left.sym->name);
> + else
> + printf("left was null\n");
> + printf("%s", e->type == E_GEQ ? ">=" : ">");
> + printf("%s", e->right.sym->name);
> + break;
> + case E_RANGE:
> + printf("[");
> + printf("%s", e->left.sym->name);
> + printf(" ");
> + printf("%s", e->right.sym->name);
> + printf("]");
> + break;
> + default:
> + break;
> + }
> +}
> +void print_expr(char *tag, struct expr *e, int prevtoken)
> +{
> + printf("%s ", tag);
> + print_expr_util(e, prevtoken);
> + printf("\n");
> +}
> +
This is the same as the existing expr_fprint() except that
it prints the 'tag'.
If you need this, presumably you need to do code refactoring.
> +/*
> + * check, if the symbol is a tristate-constant
> + */
> +bool sym_is_tristate_constant(struct symbol *sym)
> +{
> + return sym == &symbol_yes || sym == &symbol_mod || sym == &symbol_no;
> +}
> +
> +/*
> + * check, if a symbol is of type boolean or tristate
> + */
> +bool sym_is_boolean(struct symbol *sym)
> +{
> + return sym->type == S_BOOLEAN || sym->type == S_TRISTATE;
> +}
> +
> +/*
> + * check, if a symbol is a boolean/tristate or a tristate constant
> + */
> +bool sym_is_bool_or_triconst(struct symbol *sym)
> +{
> + return sym_is_tristate_constant(sym) || sym_is_boolean(sym);
> +}
> +
> +/*
> + * check, if a symbol is of type int, hex, or string
> + */
> +bool sym_is_nonboolean(struct symbol *sym)
> +{
> + return sym->type == S_INT || sym->type == S_HEX || sym->type == S_STRING;
> +}
> +
> +/*
> + * check, if a symbol has a prompt
> + */
> +bool sym_has_prompt(struct symbol *sym)
> +{
> + struct property *prop;
> +
> + for_all_prompts(sym, prop)
> + return true;
> +
> + return false;
> +}
> +
> +/*
> + * return the prompt of the symbol if there is one, NULL otherwise
> + */
> +struct property *sym_get_prompt(struct symbol *sym)
> +{
> + struct property *prop;
> +
> + for_all_prompts(sym, prop)
> + return prop;
> +
> + return NULL;
> +}
> +
> +/*
> + * return the condition for the property, NULL if there is none. To be pexpr_put
> + * by caller.
> + */
> +struct pexpr *prop_get_condition(struct property *prop, struct cfdata *data)
> +{
> + if (prop == NULL)
> + return NULL;
> +
> + /* if there is no condition, return True */
> + if (!prop->visible.expr)
> + return pexf(data->constants->const_true);
> +
> + return expr_calculate_pexpr_both(prop->visible.expr, data);
> +}
> +
> +/*
> + * return the default property, NULL if none exists or can be satisfied
> + */
> +struct property *sym_get_default_prop(struct symbol *sym)
> +{
> + struct property *prop;
> +
> + for_all_defaults(sym, prop) {
> + prop->visible.tri = expr_calc_value(prop->visible.expr);
> + if (prop->visible.tri != no)
> + return prop;
> + }
> + return NULL;
> +}
> +
> +/*
> + * check whether a non-boolean symbol has a value set
> + */
> +bool sym_nonbool_has_value_set(struct symbol *sym)
> +{
> + /*
> + * The built constraints make the following constraints:
> + *
> + * visible -> not 'n'
> + * sym->dir_dep not fulfilled -> 'n'
> + * invisible -> (no default's condition is fulfilled <-> 'n')
> + */
> + struct property *prompt;
> + struct property *p;
> +
> + if (!sym_is_nonboolean(sym))
> + return false;
> +
> + /* cannot have a value with unmet dependencies */
> + if (sym->dir_dep.expr && sym->dir_dep.tri == no)
> + return false;
> +
> + /* visible prompt => value set */
> + prompt = sym_get_prompt(sym);
> + if (prompt != NULL && prompt->visible.tri != no)
> + return true;
> +
> + /* invisible prompt => must get value from default value */
> + p = sym_get_default_prop(sym);
> + return p != NULL;
> +}
> +
> +/*
> + * return pointer to the name of the symbol or the current prompt-text, if it
> + * is a choice symbol
> + */
> +const char *sym_get_name(struct symbol *sym)
> +{
> + if (sym_is_choice(sym)) {
> + struct property *prompt = sym_get_prompt(sym);
> +
> + if (prompt == NULL)
> + return "";
> +
> + return prompt->text;
> + } else {
> + return sym->name;
> + }
> +}
> +
> +/*
> + * check whether symbol is to be changed
> + */
> +bool sym_is_sdv(struct sdv_list *list, struct symbol *sym)
> +{
> + struct sdv_node *node;
> +
> + sdv_list_for_each(node, list)
> + if (sym == node->elem->sym)
> + return true;
> +
> + return false;
> +}
> +
> +/*
> + * print a symbol's name
> + */
> +void print_sym_name(struct symbol *sym)
> +{
> + printf("Symbol: ");
> + if (sym_is_choice(sym)) {
> + struct property *prompt = sym_get_prompt(sym);
> +
> + printf("(Choice) %s", prompt->text);
> + } else {
> + printf("%s", sym->name);
> + }
> + printf("\n");
> +}
> +
> +/*
> + * print all constraints for a symbol
> + */
> +void print_sym_constraint(struct symbol *sym)
> +{
> + struct pexpr_node *node;
> +
> + pexpr_list_for_each(node, sym->constraints)
> + pexpr_print("::", node->elem, -1);
> +}
> +
> +/*
> + * print a default map
> + */
> +void print_default_map(struct defm_list *map)
> +{
> + struct default_map *entry;
> + struct defm_node *node;
> +
> + defm_list_for_each(node, map) {
> + struct gstr s = str_new();
> +
> + entry = node->elem;
> +
> + str_append(&s, "\t");
> + str_append(&s, str_get(&entry->val->name));
> + str_append(&s, " ->");
> + pexpr_print(strdup(str_get(&s)), entry->e, -1);
> + str_free(&s);
> + }
> +}
> +
> +/*
> + * check whether a string is a number
> + */
> +bool string_is_number(char *s)
> +{
> + int len = strlen(s);
> + int i = 0;
> +
> + while (i < len) {
> + if (!isdigit(s[i]))
> + return false;
> + i++;
> + }
> +
> + return true;
> +}
> +
> +/*
> + * check whether a string is a hexadecimal number
> + */
> +bool string_is_hex(char *s)
> +{
> + int len = strlen(s);
> + int i = 2;
> +
> + if (len >= 3 && s[0] == '0' && s[1] == 'x') {
> + while (i < len) {
> + if (!isxdigit(s[i]))
> + return false;
> + i++;
> + }
> + return true;
> + } else {
> + return false;
> + }
> +}
> +
> +/*
> + * initialize PicoSAT
> + */
> +PicoSAT *initialize_picosat(void)
> +{
> + PicoSAT *pico;
> +
> + printd("\nInitializing PicoSAT...");
> + pico = picosat_init();
> + picosat_enable_trace_generation(pico);
> + printd("done.\n");
> +
> + return pico;
> +}
> +
> +/*
> + * construct the CNF-clauses from the constraints
> + */
> +void construct_cnf_clauses(PicoSAT *p, struct cfdata *data)
> +{
> + struct symbol *sym;
> +
> + pico = p;
> +
> + /* adding unit-clauses for constants */
> + sat_add_clause(2, pico, -(data->constants->const_false->satval));
> + sat_add_clause(2, pico, data->constants->const_true->satval);
> +
> + for_all_symbols(sym) {
> + struct pexpr_node *node;
> +
> + if (sym->type == S_UNKNOWN)
> + continue;
> +
> + pexpr_list_for_each(node, sym->constraints) {
> + if (pexpr_is_cnf(node->elem)) {
> + unfold_cnf_clause(node->elem);
> + picosat_add(pico, 0);
> + } else {
> + build_cnf_tseytin(node->elem, data);
> + }
> +
> + }
> + }
> +}
> +
> +/*
> + * helper function to add an expression to a CNF-clause
> + */
> +static void unfold_cnf_clause(struct pexpr *e)
> +{
> + switch (e->type) {
> + case PE_SYMBOL:
> + picosat_add(pico, e->left.fexpr->satval);
> + break;
> + case PE_OR:
> + unfold_cnf_clause(e->left.pexpr);
> + unfold_cnf_clause(e->right.pexpr);
> + break;
> + case PE_NOT:
> + picosat_add(pico, -(e->left.pexpr->left.fexpr->satval));
> + break;
> + default:
> + perror("Not in CNF, FE_EQUALS.");
> + }
> +}
> +
> +/*
> + * build CNF-clauses for a pexpr not in CNF
> + */
> +static void build_cnf_tseytin(struct pexpr *e, struct cfdata *data)
> +{
> + switch (e->type) {
> + case PE_AND:
> + build_cnf_tseytin_top_and(e, data);
> + break;
> + case PE_OR:
> + build_cnf_tseytin_top_or(e, data);
> + break;
> + default:
> + perror("Expression not a propositional logic formula. root.");
> + }
> +}
> +
> +/*
> + * split up a pexpr of type AND as both sides must be satisfied
> + */
> +static void build_cnf_tseytin_top_and(struct pexpr *e, struct cfdata *data)
> +{
> + if (pexpr_is_cnf(e->left.pexpr))
> + unfold_cnf_clause(e->left.pexpr);
> + else
> + build_cnf_tseytin(e->left.pexpr, data);
> +
> + if (pexpr_is_cnf(e->right.pexpr))
> + unfold_cnf_clause(e->right.pexpr);
> + else
> + build_cnf_tseytin(e->right.pexpr, data);
> +
> +}
> +
> +static void build_cnf_tseytin_top_or(struct pexpr *e, struct cfdata *data)
> +{
> + struct fexpr *t1 = NULL, *t2 = NULL;
> + int a, b;
> +
> + /* set left side */
> + if (pexpr_is_symbol(e->left.pexpr)) {
> + a = pexpr_satval(e->left.pexpr);
> + } else {
> + t1 = create_tmpsatvar(data);
> + a = t1->satval;
> + }
> +
> + /* set right side */
> + if (pexpr_is_symbol(e->right.pexpr)) {
> + b = pexpr_satval(e->right.pexpr);
> + } else {
> + t2 = create_tmpsatvar(data);
> + b = t2->satval;
> + }
> +
> + /* A v B */
> + sat_add_clause(3, pico, a, b);
> +
> + /* traverse down the tree to build more constraints if needed */
> + if (!pexpr_is_symbol(e->left.pexpr)) {
> + if (t1 == NULL)
> + perror("t1 is NULL.");
> +
> + build_cnf_tseytin_tmp(e->left.pexpr, t1, data);
> + }
> +
> + if (!pexpr_is_symbol(e->right.pexpr)) {
> + if (t2 == NULL)
> + perror("t2 is NULL.");
> +
> + build_cnf_tseytin_tmp(e->right.pexpr, t2, data);
> + }
> +}
> +
> +/*
> + * build the sub-expressions
> + */
> +static void build_cnf_tseytin_tmp(struct pexpr *e, struct fexpr *t, struct cfdata *data)
> +{
> + switch (e->type) {
> + case PE_AND:
> + build_cnf_tseytin_and(e, t, data);
> + break;
> + case PE_OR:
> + build_cnf_tseytin_or(e, t, data);
> + break;
> + default:
> + perror("Expression not a propositional logic formula. root.");
> + }
> +}
> +
> +/*
> + * build the Tseytin sub-expressions for a pexpr of type AND
> + */
> +static void build_cnf_tseytin_and(struct pexpr *e, struct fexpr *t, struct cfdata *data)
> +{
> + struct fexpr *t1 = NULL, *t2 = NULL;
> + int a, b, c;
> +
> + assert(t != NULL);
> +
> + /* set left side */
> + if (pexpr_is_symbol(e->left.pexpr)) {
> + a = pexpr_satval(e->left.pexpr);
> + } else {
> + t1 = create_tmpsatvar(data);
> + a = t1->satval;
> + }
> +
> + /* set right side */
> + if (pexpr_is_symbol(e->right.pexpr)) {
> + b = pexpr_satval(e->right.pexpr);
> + } else {
> + t2 = create_tmpsatvar(data);
> + b = t2->satval;
> + }
> +
> + c = t->satval;
> +
> + /* -A v -B v C */
> + sat_add_clause(4, pico, -a, -b, c);
> + /* A v -C */
> + sat_add_clause(3, pico, a, -c);
> + /* B v -C */
> + sat_add_clause(3, pico, b, -c);
> +
> + /* traverse down the tree to build more constraints if needed */
> + if (!pexpr_is_symbol(e->left.pexpr)) {
> + if (t1 == NULL)
> + perror("t1 is NULL.");
> +
> + build_cnf_tseytin_tmp(e->left.pexpr, t1, data);
> + }
> + if (!pexpr_is_symbol(e->right.pexpr)) {
> + if (t2 == NULL)
> + perror("t2 is NULL.");
> +
> + build_cnf_tseytin_tmp(e->right.pexpr, t2, data);
> + }
> +}
> +
> +/*
> + * build the Tseytin sub-expressions for a pexpr of type
> + */
> +static void build_cnf_tseytin_or(struct pexpr *e, struct fexpr *t, struct cfdata *data)
> +{
> + struct fexpr *t1 = NULL, *t2 = NULL;
> + int a, b, c;
> +
> + assert(t != NULL);
> +
> + /* set left side */
> + if (pexpr_is_symbol(e->left.pexpr)) {
> + a = pexpr_satval(e->left.pexpr);
> + } else {
> + t1 = create_tmpsatvar(data);
> + a = t1->satval;
> + }
> +
> + /* set right side */
> + if (pexpr_is_symbol(e->right.pexpr)) {
> + b = pexpr_satval(e->right.pexpr);
> + } else {
> + t2 = create_tmpsatvar(data);
> + b = t2->satval;
> + }
> +
> + c = t->satval;
> +
> + /* A v B v -C */
> + sat_add_clause(4, pico, a, b, -c);
> + /* -A v C */;
> + sat_add_clause(3, pico, -a, c);
> + /* -B v C */
> + sat_add_clause(3, pico, -b, c);
> +
> + /* traverse down the tree to build more constraints if needed */
> + if (!pexpr_is_symbol(e->left.pexpr)) {
> + if (t1 == NULL)
> + perror("t1 is NULL.");
> +
> + build_cnf_tseytin_tmp(e->left.pexpr, t1, data);
> + }
> + if (!pexpr_is_symbol(e->right.pexpr)) {
> + if (t2 == NULL)
> + perror("t2 is NULL.");
> +
> + build_cnf_tseytin_tmp(e->right.pexpr, t2, data);
> + }
> +}
> +
> +/*
> + * add a clause to PicoSAT
> + * First argument must be the SAT solver
> + */
> +void sat_add_clause(int num, ...)
> +{
> + va_list valist;
> + int lit;
> + PicoSAT *pico;
> +
> + if (num <= 1)
> + return;
> +
> + va_start(valist, num);
> +
> + pico = va_arg(valist, PicoSAT *);
> +
> + /* access all the arguments assigned to valist */
> + for (int i = 1; i < num; i++) {
> + lit = va_arg(valist, int);
> + picosat_add(pico, lit);
> + }
> + picosat_add(pico, 0);
> +
> + va_end(valist);
> +}
> +
> +/*
> + * return the SAT-variable for a pexpr that is a symbol
> + */
> +static int pexpr_satval(struct pexpr *e)
> +{
> + if (!pexpr_is_symbol(e)) {
> + perror("pexpr is not a symbol.");
> + return -1;
> + }
> +
> + switch (e->type) {
> + case PE_SYMBOL:
> + return e->left.fexpr->satval;
> + case PE_NOT:
> + return -(e->left.pexpr->left.fexpr->satval);
> + default:
> + perror("Not a symbol.");
> + }
> +
> + return -1;
> +}
> +
> +/*
> + * start PicoSAT
> + */
> +void picosat_solve(PicoSAT *pico, struct cfdata *data)
> +{
> + clock_t start, end;
> + double time;
> + int res;
> +
> + printd("Solving SAT-problem...");
> +
> + start = clock();
> + res = picosat_sat(pico, -1);
> + end = clock();
> +
> + time = ((double) (end - start)) / CLOCKS_PER_SEC;
> + printd("done. (%.6f secs.)\n\n", time);
> +
> + if (res == PICOSAT_SATISFIABLE) {
> + printd("===> PROBLEM IS SATISFIABLE <===\n");
> +
> + } else if (res == PICOSAT_UNSATISFIABLE) {
> + struct fexpr *e;
> + int lit;
> + const int *i;
> +
> + printd("===> PROBLEM IS UNSATISFIABLE <===\n");
> +
> + /* print unsat core */
> + printd("\nPrinting unsatisfiable core:\n");
> +
> + i = picosat_failed_assumptions(pico);
> + lit = abs(*i++);
> +
> + while (lit != 0) {
> + e = data->satmap[lit];
> +
> + printd("(%d) %s <%d>\n", lit, str_get(&e->name), e->assumption);
> + lit = abs(*i++);
> + }
> + } else {
> + printd("Unknown if satisfiable.\n");
> + }
> +}
> +
> +/*
> + * add assumption for a symbol to the SAT-solver
> + */
> +void sym_add_assumption(PicoSAT *pico, struct symbol *sym)
> +{
> + if (sym_is_boolean(sym)) {
> + int tri_val = sym_get_tristate_value(sym);
> +
> + sym_add_assumption_tri(pico, sym, tri_val);
> + return;
> + }
> +
> + if (sym_is_nonboolean(sym)) {
> + struct fexpr *e = sym->nb_vals->head->elem;
> + struct fexpr_node *node;
> +
> + const char *string_val = sym_get_string_value(sym);
> +
> + if (sym->type == S_STRING && !strcmp(string_val, ""))
> + return;
> +
> + /* symbol does not have a value */
> + if (!sym_nonbool_has_value_set(sym)) {
> + /* set value for sym=n */
> + picosat_assume(pico, e->satval);
> + e->assumption = true;
> +
> + for (node = sym->nb_vals->head->next; node != NULL; node = node->next) {
> + picosat_assume(pico, -(node->elem->satval));
> + node->elem->assumption = false;
> + }
> +
> + return;
> + }
> +
> + /* symbol does have a value set */
> +
> + /* set value for sym=n */
> + picosat_assume(pico, -(e->satval));
> + e->assumption = false;
> +
> + /* set value for all other fexpr */
> + fexpr_list_for_each(node, sym->nb_vals) {
> + if (node->prev == NULL)
> + continue;
> +
> + if (strcmp(str_get(&node->elem->nb_val), string_val) == 0) {
> + picosat_assume(pico, node->elem->satval);
> + node->elem->assumption = true;
> + } else {
> + picosat_assume(pico, -(node->elem->satval));
> + node->elem->assumption = false;
> + }
> + }
> + }
> +}
> +
> +/*
> + * add assumption for a boolean symbol to the SAT-solver
> + */
> +void sym_add_assumption_tri(PicoSAT *pico, struct symbol *sym, tristate tri_val)
> +{
> + if (sym->type == S_BOOLEAN) {
> + int a = sym->fexpr_y->satval;
> +
> + switch (tri_val) {
> + case no:
> + picosat_assume(pico, -a);
> + sym->fexpr_y->assumption = false;
> + break;
> + case mod:
> + perror("Should not happen. Boolean symbol is set to mod.\n");
> + break;
> + case yes:
> +
> + picosat_assume(pico, a);
> + sym->fexpr_y->assumption = true;
> + break;
> + }
> + }
> + if (sym->type == S_TRISTATE) {
> + int a = sym->fexpr_y->satval;
> + int a_m = sym->fexpr_m->satval;
> +
> + switch (tri_val) {
> + case no:
> + picosat_assume(pico, -a);
> + picosat_assume(pico, -a_m);
> + sym->fexpr_y->assumption = false;
> + sym->fexpr_m->assumption = false;
> + break;
> + case mod:
> + picosat_assume(pico, -a);
> + picosat_assume(pico, a_m);
> + sym->fexpr_y->assumption = false;
> + sym->fexpr_m->assumption = true;
> + break;
> + case yes:
> + picosat_assume(pico, a);
> + picosat_assume(pico, -a_m);
> + sym->fexpr_y->assumption = true;
> + sym->fexpr_m->assumption = false;
> + break;
> + }
> + }
> +}
> +
> +/*
> + * add assumptions for the symbols to be changed to the SAT solver
> + */
> +void sym_add_assumption_sdv(PicoSAT *pico, struct sdv_list *list)
> +{
> + struct symbol_dvalue *sdv;
> + struct sdv_node *node;
> + int lit_y, lit_m;
> +
> + sdv_list_for_each(node, list) {
> + sdv = node->elem;
> + lit_y = sdv->sym->fexpr_y->satval;
> +
> + if (sdv->sym->type == S_BOOLEAN) {
> + switch (sdv->tri) {
> + case yes:
> + picosat_assume(pico, lit_y);
> + break;
> + case no:
> + picosat_assume(pico, -lit_y);
> + break;
> + case mod:
> + perror("Should not happen.\n");
> + }
> + } else if (sdv->sym->type == S_TRISTATE) {
> + lit_m = sdv->sym->fexpr_m->satval;
> +
> + switch (sdv->tri) {
> + case yes:
> + picosat_assume(pico, lit_y);
> + picosat_assume(pico, -lit_m);
> + break;
> + case mod:
> + picosat_assume(pico, -lit_y);
> + picosat_assume(pico, lit_m);
> + break;
> + case no:
> + picosat_assume(pico, -lit_y);
> + picosat_assume(pico, -lit_m);
> + }
> + }
> + }
> +}
> diff --git a/scripts/kconfig/cf_utils.h b/scripts/kconfig/cf_utils.h
> new file mode 100644
> index 000000000000..b71c8731a8ff
> --- /dev/null
> +++ b/scripts/kconfig/cf_utils.h
> @@ -0,0 +1,115 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
> + */
> +
> +#ifndef CF_UTILS_H
> +#define CF_UTILS_H
> +
> +#include "expr.h"
> +#include "cf_defs.h"
> +#include "picosat.h"
> +
> +/* parse Kconfig-file and read .config */
> +void init_config(const char *Kconfig_file);
> +
> +/* initialize satmap and cnf_clauses */
> +void init_data(struct cfdata *data);
> +
> +/* assign SAT-variables to all fexpr and create the sat_map */
> +void create_sat_variables(struct cfdata *data);
> +
> +/* create True/False constants */
> +void create_constants(struct cfdata *data);
> +
> +/* create a temporary SAT-variable */
> +struct fexpr *create_tmpsatvar(struct cfdata *data);
> +
> +/* return a temporary SAT variable as string */
> +char *get_tmp_var_as_char(int i);
> +
> +/* return a tristate value as a char * */
> +char *tristate_get_char(tristate val);
> +
> +/* check whether an expr can evaluate to mod */
> +bool expr_can_evaluate_to_mod(struct expr *e);
> +
> +/* check whether an expr is a non-Boolean constant */
> +bool expr_is_nonbool_constant(struct expr *e);
> +
> +/* check whether a symbol is a non-Boolean constant */
> +bool sym_is_nonbool_constant(struct symbol *sym);
> +
> +/* print an expr */
> +void print_expr(char *tag, struct expr *e, int prevtoken);
> +
> +/* check, if the symbol is a tristate-constant */
> +bool sym_is_tristate_constant(struct symbol *sym);
> +
> +/* check, if a symbol is of type boolean or tristate */
> +bool sym_is_boolean(struct symbol *sym);
> +
> +/* check, if a symbol is a boolean/tristate or a tristate constant */
> +bool sym_is_bool_or_triconst(struct symbol *sym);
> +
> +/* check, if a symbol is of type int, hex, or string */
> +bool sym_is_nonboolean(struct symbol *sym);
> +
> +/* check, if a symbol has a prompt */
> +bool sym_has_prompt(struct symbol *sym);
> +
> +/* return the prompt of the symbol, if there is one */
> +struct property *sym_get_prompt(struct symbol *sym);
> +
> +/* return the condition for the property, True if there is none */
> +struct pexpr *prop_get_condition(struct property *prop, struct cfdata *data);
> +
> +/* return the default property, NULL if none exists or can be satisfied */
> +struct property *sym_get_default_prop(struct symbol *sym);
> +
> +/* check whether a non-boolean symbol has a value set */
> +bool sym_nonbool_has_value_set(struct symbol *sym);
> +
> +/* return the name of the symbol */
> +const char *sym_get_name(struct symbol *sym);
> +
> +/* check whether symbol is to be changed */
> +bool sym_is_sdv(struct sdv_list *list, struct symbol *sym);
> +
> +/* print a symbol's name */
> +void print_sym_name(struct symbol *sym);
> +
> +/* print all constraints for a symbol */
> +void print_sym_constraint(struct symbol *sym);
> +
> +/* print a default map */
> +void print_default_map(struct defm_list *map);
> +
> +/* check whether a string is a number */
> +bool string_is_number(char *s);
> +
> +/* check whether a string is a hexadecimal number */
> +bool string_is_hex(char *s);
> +
> +/* initialize PicoSAT */
> +PicoSAT *initialize_picosat(void);
> +
> +/* construct the CNF-clauses from the constraints */
> +void construct_cnf_clauses(PicoSAT *pico, struct cfdata *data);
> +
> +/* add a clause to PicoSAT */
> +void sat_add_clause(int num, ...);
> +
> +/* start PicoSAT */
> +void picosat_solve(PicoSAT *pico, struct cfdata *data);
> +
> +/* add assumption for a symbol to the SAT-solver */
> +void sym_add_assumption(PicoSAT *pico, struct symbol *sym);
> +
> +/* add assumption for a boolean symbol to the SAT-solver */
> +void sym_add_assumption_tri(PicoSAT *pico, struct symbol *sym, tristate tri_val);
> +
> +/* add assumptions for the symbols to be changed to the SAT solver */
> +void sym_add_assumption_sdv(PicoSAT *pico, struct sdv_list *list);
> +
> +#endif
> --
> 2.39.2
>
--
Best Regards
Masahiro Yamada
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v4 10/12] kconfig: Add tools
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
` (8 preceding siblings ...)
2024-07-10 6:52 ` [PATCH v4 09/12] kconfig: Add files with utility functions Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 11/12] kconfig: Add xconfig-modifications Ole Schuerks
2024-07-10 6:52 ` [PATCH v4 12/12] kconfig: Add loader.gif Ole Schuerks
11 siblings, 0 replies; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
This commit contains the actual API to be used from a configurator.
Furthermore, it contains a tool to print all constraints into a file.
Co-developed-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Patrick Franz <deltaone@debian.org>
Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
Suggested-by: Sarah Nadi <nadi@ualberta.ca>
Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/.gitignore | 1 +
scripts/kconfig/cfoutconfig.c | 142 ++++++++++++++
scripts/kconfig/configfix.c | 337 ++++++++++++++++++++++++++++++++++
scripts/kconfig/configfix.h | 40 ++++
4 files changed, 520 insertions(+)
create mode 100644 scripts/kconfig/cfoutconfig.c
create mode 100644 scripts/kconfig/configfix.c
create mode 100644 scripts/kconfig/configfix.h
diff --git a/scripts/kconfig/.gitignore b/scripts/kconfig/.gitignore
index 0b2ff775b2e3..23446f70083e 100644
--- a/scripts/kconfig/.gitignore
+++ b/scripts/kconfig/.gitignore
@@ -5,3 +5,4 @@
/[gmnq]conf-cflags
/[gmnq]conf-libs
/qconf-moc.cc
+/cfoutconfig
diff --git a/scripts/kconfig/cfoutconfig.c b/scripts/kconfig/cfoutconfig.c
new file mode 100644
index 000000000000..c0879e6ebaa1
--- /dev/null
+++ b/scripts/kconfig/cfoutconfig.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "configfix.h"
+#include "internal.h"
+
+#define OUTFILE_CONSTRAINTS "./scripts/kconfig/cfout_constraints.txt"
+#define OUTFILE_DIMACS "./scripts/kconfig/cfout_constraints.dimacs"
+
+static void write_constraints_to_file(struct cfdata *data);
+static void write_dimacs_to_file(PicoSAT *pico, struct cfdata *data);
+
+/* -------------------------------------- */
+
+int main(int argc, char *argv[])
+{
+ clock_t start, end;
+ double time;
+ PicoSAT *pico;
+
+ static struct constants constants = {NULL, NULL, NULL, NULL, NULL};
+ static struct cfdata data = {
+ 1, // unsigned int sat_variable_nr
+ 1, // unsigned int tmp_variable_nr
+ NULL, // struct fexpr *satmap
+ 0, // size_t satmap_size
+ &constants // struct constants *constants
+ };
+
+ printf("\nCreating constraints and CNF clauses...");
+ /* measure time for constructing constraints and clauses */
+ start = clock();
+
+ /* parse Kconfig-file and read .config */
+ init_config(argv[1]);
+
+ /* initialize satmap and cnf_clauses */
+ init_data(&data);
+
+ /* creating constants */
+ create_constants(&data);
+
+ /* assign SAT variables & create sat_map */
+ create_sat_variables(&data);
+
+ /* get the constraints */
+ get_constraints(&data);
+
+ end = clock();
+ time = ((double) (end - start)) / CLOCKS_PER_SEC;
+
+ printd("done. (%.6f secs.)\n", time);
+
+ /* start PicoSAT */
+ pico = picosat_init();
+ picosat_enable_trace_generation(pico);
+ printd("Building CNF-clauses...");
+ start = clock();
+
+ /* construct the CNF clauses */
+ construct_cnf_clauses(pico, &data);
+
+ end = clock();
+ time = ((double) (end - start)) / CLOCKS_PER_SEC;
+ printf("done. (%.6f secs.)\n", time);
+
+ printf("\n");
+
+ /* write constraints into file */
+ start = clock();
+ printf("Writing constraints...");
+ write_constraints_to_file(&data);
+ end = clock();
+ time = ((double) (end - start)) / CLOCKS_PER_SEC;
+ printf("done. (%.6f secs.)\n", time);
+
+ /* write SAT problem in DIMACS into file */
+ start = clock();
+ printf("Writing SAT problem in DIMACS...");
+ write_dimacs_to_file(pico, &data);
+ end = clock();
+ time = ((double) (end - start)) / CLOCKS_PER_SEC;
+ printf("done. (%.6f secs.)\n", time);
+
+ printf("\nConstraints have been written into %s\n", OUTFILE_CONSTRAINTS);
+ printf("DIMACS-output has been written into %s\n", OUTFILE_DIMACS);
+
+ return 0;
+}
+
+static void write_constraints_to_file(struct cfdata *data)
+{
+ FILE *fd = fopen(OUTFILE_CONSTRAINTS, "w");
+ struct symbol *sym;
+
+ for_all_symbols(sym) {
+ struct pexpr_node *node;
+
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ pexpr_list_for_each(node, sym->constraints) {
+ struct gstr s = str_new();
+
+ pexpr_as_char(node->elem, &s, 0, data);
+ fprintf(fd, "%s\n", str_get(&s));
+ str_free(&s);
+ }
+ }
+ fclose(fd);
+}
+
+static void add_comment(FILE *fd, struct fexpr *e)
+{
+ fprintf(fd, "c %d %s\n", e->satval, str_get(&e->name));
+}
+
+static void write_dimacs_to_file(PicoSAT *pico, struct cfdata *data)
+{
+ FILE *fd = fopen(OUTFILE_DIMACS, "w");
+
+ unsigned int i;
+
+ for (i = 1; i < data->sat_variable_nr; i++)
+ add_comment(fd, data->satmap[i]);
+
+ picosat_print(pico, fd);
+ fclose(fd);
+}
diff --git a/scripts/kconfig/configfix.c b/scripts/kconfig/configfix.c
new file mode 100644
index 000000000000..e161424149c4
--- /dev/null
+++ b/scripts/kconfig/configfix.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "configfix.h"
+#include "internal.h"
+#include "cf_expr.h"
+
+bool CFDEBUG;
+bool stop_rangefix;
+
+static PicoSAT *pico;
+static bool init_done;
+static struct sym_list *conflict_syms;
+
+static bool sdv_within_range(struct sdv_list *symbols);
+
+/* -------------------------------------- */
+
+
+struct sfl_list *run_satconf(struct sdv_list *symbols)
+{
+ clock_t start, end;
+ double time;
+ struct symbol *sym;
+ struct sdv_node *node;
+ int res;
+ struct sfl_list *ret;
+
+ static struct constants constants = {NULL, NULL, NULL, NULL, NULL};
+ static struct cfdata data = {
+ 1, // unsigned int sat_variable_nr
+ 1, // unsigned int tmp_variable_nr
+ NULL, // struct fexpr *satmap
+ 0, // size_t satmap_size
+ &constants, // struct constants *constants
+ NULL // array with conflict-symbols
+ };
+
+
+ /* check whether all values can be applied -> no need to run */
+ if (sdv_within_range(symbols)) {
+ printd("\nAll symbols are already within range.\n\n");
+ return sfl_list_init();
+ }
+
+ if (!init_done) {
+ printd("\n");
+ printd("Init...");
+
+ /* measure time for constructing constraints and clauses */
+ start = clock();
+
+ /* initialize satmap and cnf_clauses */
+ init_data(&data);
+
+ /* creating constants */
+ create_constants(&data);
+
+ /* assign SAT variables & create sat_map */
+ create_sat_variables(&data);
+
+ /* get the constraints */
+ get_constraints(&data);
+
+ end = clock();
+ time = ((double)(end - start)) / CLOCKS_PER_SEC;
+
+ printd("done. (%.6f secs.)\n", time);
+
+ /* start PicoSAT */
+ pico = initialize_picosat();
+ printd("Building CNF-clauses...");
+ start = clock();
+
+ /* construct the CNF clauses */
+ construct_cnf_clauses(pico, &data);
+
+ end = clock();
+ time = ((double)(end - start)) / CLOCKS_PER_SEC;
+
+ printd("done. (%.6f secs.)\n", time);
+
+ printd("CNF-clauses added: %d\n",
+ picosat_added_original_clauses(pico));
+
+ init_done = true;
+ }
+
+ /* copy array with symbols to change */
+ data.sdv_symbols = sdv_list_copy(symbols);
+
+ /* add assumptions for conflict-symbols */
+ sym_add_assumption_sdv(pico, data.sdv_symbols);
+
+ /* add assumptions for all other symbols */
+ for_all_symbols(sym) {
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ if (!sym_is_sdv(data.sdv_symbols, sym))
+ sym_add_assumption(pico, sym);
+ }
+
+ /* store the conflict symbols */
+ conflict_syms = sym_list_init();
+ sdv_list_for_each(node, data.sdv_symbols)
+ sym_list_add(conflict_syms, node->elem->sym);
+
+ printd("Solving SAT-problem...");
+ start = clock();
+
+ res = picosat_sat(pico, -1);
+
+ end = clock();
+ time = ((double)(end - start)) / CLOCKS_PER_SEC;
+ printd("done. (%.6f secs.)\n\n", time);
+
+ if (res == PICOSAT_SATISFIABLE) {
+ printd("===> PROBLEM IS SATISFIABLE <===\n");
+
+ ret = sfl_list_init();
+
+ } else if (res == PICOSAT_UNSATISFIABLE) {
+ printd("===> PROBLEM IS UNSATISFIABLE <===\n");
+ printd("\n");
+
+ ret = rangefix_run(pico, &data);
+ } else {
+ printd("Unknown if satisfiable.\n");
+
+ ret = sfl_list_init();
+ }
+
+ sdv_list_free(data.sdv_symbols);
+ return ret;
+}
+
+/*
+ * check whether a symbol is a conflict symbol
+ */
+static bool sym_is_conflict_sym(struct symbol *sym)
+{
+ struct sym_node *node;
+
+ sym_list_for_each(node, conflict_syms)
+ if (sym == node->elem)
+ return true;
+
+ return false;
+}
+
+/*
+ * check whether all conflict symbols are set to their target values
+ */
+static bool syms_have_target_value(struct sfix_list *list)
+{
+ struct symbol_fix *fix;
+ struct sfix_node *node;
+
+ sfix_list_for_each(node, list) {
+ fix = node->elem;
+
+ if (!sym_is_conflict_sym(fix->sym))
+ continue;
+
+ sym_calc_value(fix->sym);
+
+ if (sym_is_boolean(fix->sym)) {
+ if (fix->tri != sym_get_tristate_value(fix->sym))
+ return false;
+ } else {
+ if (strcmp(str_get(&fix->nb_val),
+ sym_get_string_value(fix->sym)) != 0)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ *
+ * apply the fixes from a diagnosis
+ */
+int apply_fix(struct sfix_list *fix)
+{
+ struct symbol_fix *sfix;
+ struct sfix_node *node, *next;
+ unsigned int no_symbols_set = 0, iterations = 0, manually_changed = 0;
+
+ struct sfix_list *tmp = sfix_list_copy(fix);
+
+ printd("Trying to apply fixes now...\n");
+
+ while (no_symbols_set < fix->size && !syms_have_target_value(fix)) {
+ if (iterations > fix->size * 2) {
+ printd("\nCould not apply all values :-(.\n");
+ return manually_changed;
+ }
+
+ for (node = tmp->head; node != NULL;) {
+ sfix = node->elem;
+
+ /* update symbol's current value */
+ sym_calc_value(sfix->sym);
+
+ /* value already set? */
+ if (sfix->type == SF_BOOLEAN) {
+ if (sfix->tri == sym_get_tristate_value(sfix->sym)) {
+ next = node->next;
+ sfix_list_delete(tmp, node);
+ node = next;
+ no_symbols_set++;
+ continue;
+ }
+ } else if (sfix->type == SF_NONBOOLEAN) {
+ if (strcmp(str_get(&sfix->nb_val),
+ sym_get_string_value(sfix->sym)) == 0) {
+ next = node->next;
+ sfix_list_delete(tmp, node);
+ node = next;
+ no_symbols_set++;
+ continue;
+ }
+ } else {
+ perror("Error applying fix. Value set for disallowed.");
+ }
+
+ /* could not set value, try next */
+ if (sfix->type == SF_BOOLEAN) {
+ if (!sym_set_tristate_value(sfix->sym,
+ sfix->tri)) {
+ node = node->next;
+ continue;
+ }
+ } else if (sfix->type == SF_NONBOOLEAN) {
+ if (!sym_set_string_value(
+ sfix->sym,
+ str_get(&sfix->nb_val))) {
+ node = node->next;
+ continue;
+ }
+ } else {
+ perror("Error applying fix. Value set for disallowed.");
+ }
+
+ /* could set value, remove from tmp */
+ manually_changed++;
+ if (sfix->type == SF_BOOLEAN) {
+ printd("%s set to %s.\n",
+ sym_get_name(sfix->sym),
+ tristate_get_char(sfix->tri));
+ } else if (sfix->type == SF_NONBOOLEAN) {
+ printd("%s set to %s.\n",
+ sym_get_name(sfix->sym),
+ str_get(&sfix->nb_val));
+ }
+
+ next = node->next;
+ sfix_list_delete(tmp, node);
+ node = next;
+ no_symbols_set++;
+ }
+
+ iterations++;
+ }
+
+ printd("Fixes successfully applied.\n");
+
+ return manually_changed;
+}
+
+/*
+ * stop RangeFix after the next iteration
+ */
+void interrupt_rangefix(void)
+{
+ stop_rangefix = true;
+}
+
+/*
+ * check whether all symbols are already within range
+ */
+static bool sdv_within_range(struct sdv_list *symbols)
+{
+ struct symbol_dvalue *sdv;
+ struct sdv_node *node;
+
+ sdv_list_for_each(node, symbols) {
+ sdv = node->elem;
+
+ assert(sym_is_boolean(sdv->sym));
+
+ if (sdv->tri == sym_get_tristate_value(sdv->sym))
+ continue;
+
+ if (!sym_tristate_within_range(sdv->sym, sdv->tri))
+ return false;
+ }
+
+ return true;
+}
+
+struct sfix_list *select_solution(struct sfl_list *solutions, int index)
+{
+ struct sfl_node *node = solutions->head;
+ unsigned int counter;
+
+ for (counter = 0; counter < index; counter++)
+ node = node->next;
+
+ return node->elem;
+}
+
+struct symbol_fix *select_symbol(struct sfix_list *solution, int index)
+{
+ struct sfix_node *node = solution->head;
+ unsigned int counter;
+
+ for (counter = 0; counter < index; counter++)
+ node = node->next;
+
+ return node->elem;
+}
diff --git a/scripts/kconfig/configfix.h b/scripts/kconfig/configfix.h
new file mode 100644
index 000000000000..8ebcc807da9d
--- /dev/null
+++ b/scripts/kconfig/configfix.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CONFIGFIX_H
+#define CONFIGFIX_H
+
+/* make functions accessible from xconfig */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* include internal definitions */
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+/* include own definitions */
+#include "cf_defs.h"
+
+/* include other header files needed */
+#include "picosat.h"
+#include "cf_constraints.h"
+#include "cf_expr.h"
+#include "cf_rangefix.h"
+#include "cf_utils.h"
+
+/* external functions */
+struct sfl_list *run_satconf(struct sdv_list *symbols);
+int apply_fix(struct sfix_list *fix);
+int run_satconf_cli(const char *Kconfig_file);
+void interrupt_rangefix(void);
+struct sfix_list *select_solution(struct sfl_list *solutions, int index);
+struct symbol_fix *select_symbol(struct sfix_list *solution, int index);
+
+/* make functions accessible from xconfig */
+#ifdef __cplusplus
+}
+#endif
+#endif
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v4 11/12] kconfig: Add xconfig-modifications
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
` (9 preceding siblings ...)
2024-07-10 6:52 ` [PATCH v4 10/12] kconfig: Add tools Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
2024-08-12 9:28 ` Masahiro Yamada
2024-07-10 6:52 ` [PATCH v4 12/12] kconfig: Add loader.gif Ole Schuerks
11 siblings, 1 reply; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
The tool can be called from any configurator. We chose to modify xconfig
for this purpose. These files contain the necessary modifications to
xconfig in order to resolve conflicts.
Co-developed-by: Patrick Franz <deltaone@debian.org>
Signed-off-by: Patrick Franz <deltaone@debian.org>
Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
Suggested-by: Sarah Nadi <nadi@ualberta.ca>
Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/qconf.cc | 515 ++++++++++++++++++++++++++++++++++++++-
scripts/kconfig/qconf.h | 103 ++++++++
2 files changed, 616 insertions(+), 2 deletions(-)
diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc
index 7d239c032b3d..20ca9936d592 100644
--- a/scripts/kconfig/qconf.cc
+++ b/scripts/kconfig/qconf.cc
@@ -3,7 +3,7 @@
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
* Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>
*/
-
+#include "qnamespace.h"
#include <QAction>
#include <QActionGroup>
#include <QApplication>
@@ -19,14 +19,27 @@
#include <QRegularExpression>
#include <QScreen>
#include <QToolBar>
+#include <QListWidget>
+#include <QComboBox>
+#include <QTableWidget>
+#include <QHBoxLayout>
+#include <QMovie>
+#include <QMessageBox>
#include <stdlib.h>
+#include <QAbstractItemView>
+#include <QMimeData>
+#include <QBrush>
+#include <QColor>
#include "lkc.h"
#include "qconf.h"
+#include "configfix.h"
#include "images.h"
+static QString tristate_value_to_string(tristate val);
+static tristate string_value_to_tristate(QString s);
static QApplication *configApp;
static ConfigSettings *configSettings;
@@ -178,6 +191,7 @@ void ConfigItem::updateMenu(void)
prompt += " (NEW)";
set_prompt:
setText(promptColIdx, prompt);
+
}
void ConfigItem::testUpdateMenu(bool v)
@@ -225,6 +239,7 @@ void ConfigItem::init(void)
}
}
updateMenu();
+
}
/*
@@ -405,7 +420,7 @@ void ConfigList::updateSelection(void)
ConfigItem* item = (ConfigItem*)selectedItems().first();
if (!item)
return;
-
+ emit selectionChanged(selectedItems());
menu = item->menu;
emit menuChanged(menu);
if (!menu)
@@ -471,6 +486,7 @@ void ConfigList::updateListForAll()
list->updateList();
}
+
}
void ConfigList::updateListAllForAll()
@@ -539,6 +555,7 @@ void ConfigList::changeValue(ConfigItem* item)
}
if (oldexpr != newexpr)
ConfigList::updateListForAll();
+ emit updateConflictsViewColorization();
break;
default:
break;
@@ -898,6 +915,7 @@ void ConfigList::contextMenuEvent(QContextMenuEvent *e)
action, &QAction::setChecked);
action->setChecked(showName);
headerPopup->addAction(action);
+ headerPopup->addAction(addSymbolFromContextMenu);
}
headerPopup->exec(e->globalPos());
@@ -918,6 +936,7 @@ QList<ConfigList *> ConfigList::allLists;
QAction *ConfigList::showNormalAction;
QAction *ConfigList::showAllAction;
QAction *ConfigList::showPromptAction;
+QAction *ConfigList::addSymbolFromContextMenu;
void ConfigList::setAllOpen(bool open)
{
@@ -1249,7 +1268,10 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
info, &ConfigInfoView::setInfo);
connect(list, &ConfigList::menuChanged,
parent, &ConfigMainWindow::setMenuLink);
+ connect(list, &ConfigList::menuChanged,
+ parent, &ConfigMainWindow::conflictSelected);
+ connect(list,&ConfigList::updateConflictsViewColorization,this,&ConfigSearchWindow::updateConflictsViewColorizationFowarder);
layout1->addWidget(split);
QVariant x, y;
@@ -1272,6 +1294,10 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
this, &ConfigSearchWindow::saveSettings);
}
+void ConfigSearchWindow::updateConflictsViewColorizationFowarder(void){
+ emit updateConflictsViewColorization();
+}
+
void ConfigSearchWindow::saveSettings(void)
{
if (!objectName().isEmpty()) {
@@ -1364,6 +1390,17 @@ ConfigMainWindow::ConfigMainWindow(void)
split1->addWidget(configList);
split1->addWidget(menuList);
split2->addWidget(helpText);
+ split3 = new QSplitter(split2);
+ split3->setOrientation(Qt::Vertical);
+ conflictsView = new ConflictsView(split3, "help");
+ /* conflictsSelected signal in conflictsview triggers when a conflict is selected
+ in the view. this line connects that event to conflictselected event in mainwindow
+ which updates the selection to match (in the configlist) the symbol that was selected.
+ */
+ connect(conflictsView,SIGNAL(conflictSelected(struct menu *)),SLOT(conflictSelected(struct menu *)));
+ connect(conflictsView,SIGNAL(refreshMenu()),SLOT(refreshMenu()));
+ connect(menuList,SIGNAL(updateConflictsViewColorization()),conflictsView,SLOT(updateConflictsViewColorization()));
+ connect(configList,SIGNAL(updateConflictsViewColorization()),conflictsView,SLOT(updateConflictsViewColorization()));
setTabOrder(configList, helpText);
configList->setFocus();
@@ -1430,6 +1467,8 @@ ConfigMainWindow::ConfigMainWindow(void)
ConfigList::showAllAction->setCheckable(true);
ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup);
ConfigList::showPromptAction->setCheckable(true);
+ ConfigList::addSymbolFromContextMenu = new QAction("Add symbol from context menu");
+ connect(ConfigList::addSymbolFromContextMenu, &QAction::triggered, conflictsView, &ConflictsView::addSymbol);
QAction *showDebugAction = new QAction("Show Debug Info", this);
showDebugAction->setCheckable(true);
@@ -1485,6 +1524,8 @@ ConfigMainWindow::ConfigMainWindow(void)
connect(configList, &ConfigList::menuChanged,
helpText, &ConfigInfoView::setInfo);
+ connect(configList, &ConfigList::menuChanged,
+ conflictsView, &ConflictsView::menuChanged);
connect(configList, &ConfigList::menuSelected,
this, &ConfigMainWindow::changeMenu);
connect(configList, &ConfigList::itemSelected,
@@ -1493,6 +1534,8 @@ ConfigMainWindow::ConfigMainWindow(void)
this, &ConfigMainWindow::goBack);
connect(menuList, &ConfigList::menuChanged,
helpText, &ConfigInfoView::setInfo);
+ connect(menuList, &ConfigList::menuChanged,
+ conflictsView, &ConflictsView::menuChanged);
connect(menuList, &ConfigList::menuSelected,
this, &ConfigMainWindow::changeMenu);
@@ -1712,6 +1755,13 @@ void ConfigMainWindow::showSplitView(void)
menuList->setFocus();
}
+void ConfigMainWindow::conflictSelected(struct menu * men)
+{
+ configList->clearSelection();
+ menuList->clearSelection();
+ emit(setMenuLink(men));
+}
+
void ConfigMainWindow::showFullView(void)
{
singleViewAction->setEnabled(true);
@@ -1847,6 +1897,432 @@ void ConfigMainWindow::conf_changed(bool dirty)
saveAction->setEnabled(dirty);
}
+void ConfigMainWindow::refreshMenu(void)
+{
+ configList->updateListAll();
+}
+
+void QTableWidget::dropEvent(QDropEvent *event)
+{
+}
+
+ConflictsView::ConflictsView(QWidget *parent, const char *name)
+ : Parent(parent)
+{
+ currentSelectedMenu = nullptr;
+ setObjectName(name);
+ QHBoxLayout *horizontalLayout = new QHBoxLayout(this);
+ QVBoxLayout *verticalLayout = new QVBoxLayout();
+ verticalLayout->setContentsMargins(0, 0, 0, 0);
+ conflictsToolBar = new QToolBar("ConflictTools", this);
+ // toolbar buttons [n] [m] [y] [calculate fixes] [remove]
+ QAction *addSymbol = new QAction("Add Symbol");
+ QAction *setConfigSymbolAsNo = new QAction("N");
+ QAction *setConfigSymbolAsModule = new QAction("M");
+ QAction *setConfigSymbolAsYes = new QAction("Y");
+ fixConflictsAction_ = new QAction("Calculate Fixes");
+ QAction *removeSymbol = new QAction("Remove Symbol");
+ QMovie *loadingGif = new QMovie("scripts/kconfig/loader.gif");
+ auto loadingLabel = new QLabel;
+
+ if (loadingGif->isValid()) {
+ loadingGif->setScaledSize(loadingGif->scaledSize().scaled(
+ 20, 20, Qt::KeepAspectRatioByExpanding));
+ loadingGif->start();
+ loadingLabel->setMovie(loadingGif);
+ } else {
+ loadingLabel->setText("Calculating...");
+ }
+
+ //if you change the order of buttons here, change the code where
+ //module button was disabled if symbol is boolean, selecting module button
+ //depends on a specific index in list of action
+ fixConflictsAction_->setCheckable(false);
+ conflictsToolBar->addAction(addSymbol);
+ conflictsToolBar->addAction(setConfigSymbolAsNo);
+ conflictsToolBar->addAction(setConfigSymbolAsModule);
+ conflictsToolBar->addAction(setConfigSymbolAsYes);
+ conflictsToolBar->addAction(fixConflictsAction_);
+ conflictsToolBar->addAction(removeSymbol);
+ // loadingLabel->setMargin(5);
+ loadingLabel->setContentsMargins(5, 5, 5, 5);
+ loadingAction = conflictsToolBar->addWidget(loadingLabel);
+ loadingAction->setVisible(false);
+
+
+ verticalLayout->addWidget(conflictsToolBar);
+
+ connect(addSymbol, &QAction::triggered, this, &ConflictsView::addSymbol);
+ connect(setConfigSymbolAsNo, &QAction::triggered,this, &ConflictsView::changeToNo);
+ connect(setConfigSymbolAsModule, &QAction::triggered,this, &ConflictsView::changeToModule);
+ connect(setConfigSymbolAsYes, &QAction::triggered,this, &ConflictsView::changeToYes);
+ connect(removeSymbol, &QAction::triggered,this, &ConflictsView::removeSymbol);
+ connect(this, SIGNAL(resultsReady()), SLOT(updateResults()));
+ //connect clicking 'calculate fixes' to 'change all symbol values to fix all conflicts'
+ // no longer used anymore for now.
+ connect(fixConflictsAction_, &QAction::triggered,this, &ConflictsView::calculateFixes);
+
+ conflictsTable = (QTableWidget *) new dropAbleView(this);
+ conflictsTable->setRowCount(0);
+ conflictsTable->setColumnCount(3);
+ conflictsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+
+ conflictsTable->setHorizontalHeaderLabels(QStringList() << "Option" << "Wanted value" << "Current value" );
+ verticalLayout->addWidget(conflictsTable);
+
+ conflictsTable->setDragDropMode(QAbstractItemView::DropOnly);
+ setAcceptDrops(true);
+
+ connect(conflictsTable, SIGNAL(cellClicked(int, int)), SLOT(cellClicked(int,int)));
+ horizontalLayout->addLayout(verticalLayout);
+
+ // populate the solution view on the right hand side:
+ QVBoxLayout *solutionLayout = new QVBoxLayout();
+ solutionLayout->setContentsMargins(0, 0, 0, 0);
+ solutionSelector = new QComboBox();
+ connect(solutionSelector, QOverload<int>::of(&QComboBox::currentIndexChanged),
+ [=](int index){ changeSolutionTable(index); });
+ solutionTable = new QTableWidget();
+ solutionTable->setRowCount(0);
+ solutionTable->setColumnCount(2);
+ solutionTable->setHorizontalHeaderLabels(QStringList() << "Symbol" << "New Value" );
+
+ applyFixButton = new QPushButton("Apply Selected solution");
+ connect(applyFixButton, SIGNAL(clicked(bool)), SLOT(applyFixButtonClick()));
+
+ numSolutionLabel = new QLabel("Solutions:");
+ solutionLayout->addWidget(numSolutionLabel);
+ solutionLayout->addWidget(solutionSelector);
+ solutionLayout->addWidget(solutionTable);
+ solutionLayout->addWidget(applyFixButton);
+
+ horizontalLayout->addLayout(solutionLayout);
+}
+
+
+void ConflictsView::changeToNo(){
+ QItemSelectionModel *select = conflictsTable->selectionModel();
+ if (select->hasSelection()){
+ QModelIndexList rows = select->selectedRows();
+ for (int i = 0;i < rows.count(); i++)
+ {
+ conflictsTable->item(rows[i].row(),1)->setText("NO");
+ }
+ }
+}
+
+void ConflictsView::applyFixButtonClick(){
+ signed int solution_number = solutionSelector->currentIndex();
+
+ if (solution_number == -1 || solution_output == NULL) {
+ return;
+ }
+
+ struct sfix_list * selected_solution = select_solution(solution_output, solution_number);
+ apply_fix(selected_solution);
+
+
+ ConfigList::updateListForAll();
+ for (int i = 0;i < conflictsTable->rowCount(); i++)
+ {
+ conflictsTable->item(i,2)->setText(conflictsTable->item(i,1)->text());
+ }
+ updateConflictsViewColorization();
+ QMessageBox msgBox;
+ msgBox.setText("The solution has been applied.");
+ msgBox.exec();
+}
+
+void ConflictsView::changeToYes(){
+ QItemSelectionModel *select = conflictsTable->selectionModel();
+ if (select->hasSelection()){
+ QModelIndexList rows = select->selectedRows();
+ for (int i = 0;i < rows.count(); i++)
+ {
+ conflictsTable->item(rows[i].row(),1)->setText("YES");
+ }
+ }
+
+}
+
+void ConflictsView::changeToModule() {
+ QItemSelectionModel *select = conflictsTable->selectionModel();
+ if (select->hasSelection()){
+ QModelIndexList rows = select->selectedRows();
+ for (int i = 0;i < rows.count(); i++)
+ {
+ conflictsTable->item(rows[i].row(),1)->setText("MODULE");
+ }
+ }
+
+}
+
+void ConflictsView::menuChanged(struct menu *m)
+{
+ currentSelectedMenu = m;
+}
+
+void ConflictsView::addSymbol()
+{
+ addSymbolFromMenu(currentSelectedMenu);
+}
+
+void ConflictsView::selectionChanged(QList<QTreeWidgetItem *> selection)
+{
+ currentSelection = selection;
+
+}
+
+void ConflictsView::addSymbolFromMenu(struct menu *m)
+{
+ // adds a symbol to the conflict resolver list
+ if (m != nullptr){
+ if (m->sym != nullptr){
+ struct symbol *sym = m->sym;
+ tristate currentval = sym_get_tristate_value(sym);
+ //if symbol is not added yet:
+ QAbstractItemModel *tableModel = conflictsTable->model();
+ QModelIndexList matches = tableModel->match(tableModel->index(0,0), Qt::DisplayRole, QString(sym->name));
+ if (matches.isEmpty()){
+ conflictsTable->insertRow(conflictsTable->rowCount());
+ conflictsTable->setItem(conflictsTable->rowCount()-1,0,new QTableWidgetItem(sym->name));
+ conflictsTable->setItem(conflictsTable->rowCount()-1,1,new QTableWidgetItem(tristate_value_to_string(currentval)));
+ conflictsTable->setItem(conflictsTable->rowCount()-1,2,new QTableWidgetItem(tristate_value_to_string(currentval)));
+ }else{
+ conflictsTable->item(matches[0].row(),2)->setText(tristate_value_to_string(currentval));
+ }
+ }
+ }
+}
+
+void ConflictsView::addSymbolFromContextMenu() {
+ struct menu *menu;
+
+ if (currentSelection.count() < 0){
+ return;
+ }
+ for (auto el: currentSelection){
+ ConfigItem *item = (ConfigItem *)el;
+ if (!item)
+ {
+ continue;
+ }
+ menu = item->menu;
+ addSymbolFromMenu(menu);
+ }
+
+}
+
+void ConflictsView::removeSymbol()
+{
+ QItemSelectionModel *select = conflictsTable->selectionModel();
+ QAbstractItemModel *itemModel = select->model();
+ if (select->hasSelection()){
+ QModelIndexList rows = select->selectedRows();
+ itemModel->removeRows(rows[0].row(),rows.size());
+ }
+}
+
+void ConflictsView::cellClicked(int row, int column)
+{
+ auto itemText = conflictsTable->item(row,0)->text().toUtf8().data();
+ struct property *prop;
+ struct menu *men;
+ struct symbol *sym = sym_find(itemText);
+
+ if (sym == NULL)
+ return;
+ prop = sym->prop;
+ men = prop->menu;
+ // uncommenting following like somehow disables click signal of 'apply selected solution'
+ if (sym->type == symbol_type::S_BOOLEAN) {
+ //disable module button
+ conflictsToolBar->actions()[2]->setDisabled(true);
+ } else {
+ //enable module button
+ conflictsToolBar->actions()[2]->setDisabled(false);
+ }
+ emit(conflictSelected(men));
+}
+
+void ConflictsView::changeSolutionTable(int solution_number){
+ if (solution_output == nullptr || solution_number < 0){
+ return;
+ }
+ struct sfix_list *selected_solution = select_solution(solution_output, solution_number);
+ current_solution_number = solution_number;
+ solutionTable->setRowCount(0);
+ for (unsigned int i = 0; i <selected_solution->size; i++)
+ {
+ solutionTable->insertRow(solutionTable->rowCount());
+ struct symbol_fix *cur_symbol = select_symbol(selected_solution,i);
+
+ QTableWidgetItem *symbol_name = new QTableWidgetItem(cur_symbol->sym->name);
+
+ solutionTable->setItem(solutionTable->rowCount()-1,0,symbol_name);
+
+ if (cur_symbol->type == symbolfix_type::SF_BOOLEAN){
+ QTableWidgetItem *symbol_value = new QTableWidgetItem(tristate_value_to_string(cur_symbol->tri));
+ solutionTable->setItem(solutionTable->rowCount()-1,1,symbol_value);
+ } else if(cur_symbol->type == symbolfix_type::SF_NONBOOLEAN){
+ QTableWidgetItem *symbol_value = new QTableWidgetItem(cur_symbol->nb_val.s);
+ solutionTable->setItem(solutionTable->rowCount()-1,1,symbol_value);
+ } else {
+ QTableWidgetItem *symbol_value = new QTableWidgetItem(cur_symbol->disallowed.s);
+ solutionTable->setItem(solutionTable->rowCount()-1,1,symbol_value);
+ }
+ }
+ updateConflictsViewColorization();
+}
+
+void ConflictsView::updateConflictsViewColorization(void)
+{
+ auto green = QColor(0,170,0);
+ auto red = QColor(255,0,0);
+ auto grey = QColor(180,180,180);
+
+ if (solutionTable->rowCount() == 0 || current_solution_number < 0)
+ return;
+
+ for (int i=0; i< solutionTable->rowCount(); i++) {
+ QTableWidgetItem *symbol = solutionTable->item(i,0);
+ //symbol from solution list
+ struct sfix_list *selected_solution = select_solution(solution_output, current_solution_number);
+ struct symbol_fix *cur_symbol = select_symbol(selected_solution,i);
+
+ // if symbol is editable but the value is not the target value from solution we got, the color is red
+ // if symbol is editable but the value is the target value from solution we got, the color is green
+ // if symbol is not editable , the value is not the target value, the color is grey
+ // if symbol is not editable , the value is the target value, the color is green
+ auto editable = sym_string_within_range(cur_symbol->sym, tristate_value_to_string(cur_symbol->tri).toStdString().c_str());
+ auto _symbol = solutionTable->item(i,0)->text().toUtf8().data();
+ struct symbol *sym_ = sym_find(_symbol);
+
+ tristate current_value_of_symbol = sym_get_tristate_value(sym_);
+ tristate target_value_of_symbol = string_value_to_tristate(solutionTable->item(i,1)->text());
+ bool symbol_value_same_as_target = current_value_of_symbol == target_value_of_symbol;
+
+ if (editable && !symbol_value_same_as_target){
+ symbol->setForeground(red);
+ } else if (editable && symbol_value_same_as_target){
+ symbol->setForeground(green);
+ } else if (!editable && !symbol_value_same_as_target){
+ symbol->setForeground(grey);
+ } else if (!editable && symbol_value_same_as_target){
+ symbol->setForeground(green);
+ }
+ }
+
+}
+
+void ConflictsView::runSatConfAsync()
+{
+ //loop through the rows in conflicts table adding each row into the array:
+ struct symbol_dvalue *p = nullptr;
+ p = static_cast<struct symbol_dvalue *>(calloc(conflictsTable->rowCount(),sizeof(struct symbol_dvalue)));
+ if (!p)
+ {
+ printf("memory allocation error\n");
+ return;
+ }
+
+ struct sdv_list *wanted_symbols = sdv_list_init();
+
+ for (int i = 0; i < conflictsTable->rowCount(); i++)
+ {
+
+ struct symbol_dvalue *tmp = (p+i);
+ auto _symbol = conflictsTable->item(i,0)->text().toUtf8().data();
+ struct symbol *sym = sym_find(_symbol);
+
+ tmp->sym = sym;
+ tmp->type = static_cast<symboldv_type>(sym->type == symbol_type::S_BOOLEAN?0:1);
+ tmp->tri = string_value_to_tristate(conflictsTable->item(i,1)->text());
+ sdv_list_add(wanted_symbols,tmp);
+ }
+ fixConflictsAction_->setText("Cancel");
+ loadingAction->setVisible(true);
+
+ struct sfl_list *ret = run_satconf(wanted_symbols);
+ solution_output = ret;
+
+ free(p);
+ emit resultsReady();
+ {
+ std::lock_guard<std::mutex> lk{satconf_mutex};
+ satconf_cancelled = true;
+ }
+ satconf_cancellation_cv.notify_one();
+
+}
+
+void ConflictsView::updateResults(void)
+{
+ fixConflictsAction_->setText("Calculate Fixes");
+ loadingAction->setVisible(false);
+ if (!(solution_output == nullptr || solution_output->size == 0))
+ {
+ solutionSelector->clear();
+ for (unsigned int i = 0; i < solution_output->size; i++)
+ {
+ solutionSelector->addItem(QString::number(i+1));
+ }
+ // populate the solution table from the first solution gotten
+ numSolutionLabel->setText(QString("Solutions: (%1) found").arg(solution_output->size));
+ changeSolutionTable(0);
+ }
+ else {
+ QMessageBox msgBox;
+ msgBox.setText("All symbols are already within range.");
+ msgBox.exec();
+ }
+ if (runSatConfAsyncThread->joinable()){
+ runSatConfAsyncThread->join();
+ delete runSatConfAsyncThread;
+ runSatConfAsyncThread = nullptr;
+ }
+
+}
+
+void ConflictsView::calculateFixes()
+{
+ if(conflictsTable->rowCount() == 0)
+ {
+ printd("table is empty\n");
+ return;
+ }
+
+ if (runSatConfAsyncThread == nullptr)
+ {
+ // fire away asynchronous call
+ std::unique_lock<std::mutex> lk{satconf_mutex};
+
+ numSolutionLabel->setText(QString("Solutions: "));
+ solutionSelector->clear();
+ solutionTable->setRowCount(0);
+ satconf_cancelled = false;
+ runSatConfAsyncThread = new std::thread(&ConflictsView::runSatConfAsync,this);
+ }else{
+ printd("Interrupting rangefix\n");
+ interrupt_rangefix();
+ std::unique_lock<std::mutex> lk{satconf_mutex};
+ satconf_cancellation_cv.wait(lk,[this] {return satconf_cancelled == true;});
+ }
+
+}
+
+void ConflictsView::changeAll(void)
+{
+ // not implemented for now
+ return;
+}
+
+ConflictsView::~ConflictsView(void)
+{
+
+}
+
+
void fixup_rootmenu(struct menu *menu)
{
struct menu *child;
@@ -1918,3 +2394,38 @@ int main(int ac, char** av)
return 0;
}
+
+dropAbleView::dropAbleView(QWidget *parent) :
+ QTableWidget(parent) {}
+
+dropAbleView::~dropAbleView() {}
+void dropAbleView::dropEvent(QDropEvent *event)
+{
+ event->acceptProposedAction();
+}
+
+static QString tristate_value_to_string(tristate val)
+{
+ switch ( val ) {
+ case yes:
+ return QString::fromStdString("YES");
+ case mod:
+ return QString::fromStdString("MODULE");
+ case no:
+ return QString::fromStdString("NO");
+ default:
+ return QString::fromStdString("");
+ }
+
+}
+
+static tristate string_value_to_tristate(QString s){
+ if (s == "YES")
+ return tristate::yes;
+ else if (s == "MODULE")
+ return tristate::mod;
+ else if (s == "NO")
+ return tristate::no;
+ else
+ return tristate::no;
+}
diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h
index 53373064d90a..1ace9f673fd0 100644
--- a/scripts/kconfig/qconf.h
+++ b/scripts/kconfig/qconf.h
@@ -14,9 +14,16 @@
#include <QStyledItemDelegate>
#include <QTextBrowser>
#include <QTreeWidget>
+#include <QTableWidget>
+#include <QList>
+#include <QComboBox>
+#include <QLabel>
+#include <thread>
+#include <condition_variable>
#include "expr.h"
+
class ConfigList;
class ConfigItem;
class ConfigMainWindow;
@@ -80,6 +87,8 @@ public slots:
void parentSelected(void);
void gotFocus(struct menu *);
void showNameChanged(bool on);
+ void selectionChanged(QList<QTreeWidgetItem *> selection);
+ void updateConflictsViewColorization();
public:
void updateListAll(void)
@@ -111,6 +120,82 @@ public slots:
static void updateListAllForAll();
static QAction *showNormalAction, *showAllAction, *showPromptAction;
+ static QAction *addSymbolFromContextMenu;
+
+};
+
+class ConflictsView : public QWidget {
+ Q_OBJECT
+ typedef class QWidget Parent;
+private:
+ QAction *loadingAction;
+public:
+ ConflictsView(QWidget *parent, const char *name = 0);
+ ~ConflictsView(void);
+ void addSymbolFromMenu(struct menu *m);
+ int current_solution_number = -1;
+
+public slots:
+ void cellClicked(int, int);
+ void changeAll();
+ // triggered by Qactions on the tool bar that adds/remove symbol
+ void addSymbol();
+ // triggered from config list right click -> add symbols
+ void addSymbolFromContextMenu();
+ void removeSymbol();
+ void menuChanged(struct menu *);
+ void changeToNo();
+ void changeToYes();
+ void changeToModule();
+ void selectionChanged(QList<QTreeWidgetItem *> selection);
+
+
+ void applyFixButtonClick();
+ void updateConflictsViewColorization();
+ void updateResults();
+
+
+
+ // switches the solution table with selected solution index from solution_output
+ void changeSolutionTable(int solution_number);
+
+ // calls satconfig to solve to get wanted value to current value
+ void calculateFixes();
+signals:
+ void showNameChanged(bool);
+ void showRangeChanged(bool);
+ void showDataChanged(bool);
+ void conflictSelected(struct menu *);
+ void refreshMenu();
+ void resultsReady();
+public:
+ QTableWidget *conflictsTable;
+
+ // the comobox on the right hand side. used to select a solution after
+ // getting solution from satconfig
+ QComboBox *solutionSelector{nullptr};
+
+ // the table which shows the selected solution showing variable = New value changes
+ QTableWidget *solutionTable{nullptr};
+
+ // Apply fixes button on the solution view
+ QPushButton *applyFixButton{nullptr};
+
+ struct sfl_list *solution_output{nullptr};
+
+ QToolBar *conflictsToolBar;
+ struct menu *currentSelectedMenu;
+ QLabel *numSolutionLabel{nullptr};
+ // currently selected config items in configlist.
+ QList<QTreeWidgetItem *> currentSelection;
+ QAction *fixConflictsAction_{nullptr};
+ void runSatConfAsync();
+ std::thread *runSatConfAsyncThread{nullptr};
+
+ std::mutex satconf_mutex;
+ std::condition_variable satconf_cancellation_cv;
+ bool satconf_cancelled{false};
+
};
class ConfigItem : public QTreeWidgetItem {
@@ -223,6 +308,9 @@ class ConfigSearchWindow : public QDialog {
public slots:
void saveSettings(void);
void search(void);
+ void updateConflictsViewColorizationFowarder();
+signals:
+ void updateConflictsViewColorization();
protected:
QLineEdit* editField;
@@ -258,6 +346,8 @@ public slots:
void showIntro(void);
void showAbout(void);
void saveSettings(void);
+ void conflictSelected(struct menu *);
+ void refreshMenu();
protected:
void closeEvent(QCloseEvent *e);
@@ -266,10 +356,23 @@ public slots:
ConfigList *menuList;
ConfigList *configList;
ConfigInfoView *helpText;
+ ConflictsView *conflictsView;
+ QToolBar *conflictsToolBar;
QAction *backAction;
QAction *singleViewAction;
QAction *splitViewAction;
QAction *fullViewAction;
QSplitter *split1;
QSplitter *split2;
+ QSplitter *split3;
+};
+
+class dropAbleView : public QTableWidget
+{
+public:
+ dropAbleView(QWidget *parent = nullptr);
+ ~dropAbleView();
+
+protected:
+ void dropEvent(QDropEvent *event);
};
--
2.39.2
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH v4 11/12] kconfig: Add xconfig-modifications
2024-07-10 6:52 ` [PATCH v4 11/12] kconfig: Add xconfig-modifications Ole Schuerks
@ 2024-08-12 9:28 ` Masahiro Yamada
0 siblings, 0 replies; 25+ messages in thread
From: Masahiro Yamada @ 2024-08-12 9:28 UTC (permalink / raw)
To: Ole Schuerks
Cc: linux-kbuild, jude.gyimah, thorsten.berger, deltaone,
jan.sollmann, mcgrof, linux-kernel
On Wed, Jul 10, 2024 at 3:54 PM Ole Schuerks <ole0811sch@gmail.com> wrote:
>
> The tool can be called from any configurator. We chose to modify xconfig
> for this purpose. These files contain the necessary modifications to
> xconfig in order to resolve conflicts.
>
> Co-developed-by: Patrick Franz <deltaone@debian.org>
> Signed-off-by: Patrick Franz <deltaone@debian.org>
> Co-developed-by: Ibrahim Fayaz <phayax@gmail.com>
> Signed-off-by: Ibrahim Fayaz <phayax@gmail.com>
> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
> Tested-by: Evgeny Groshev <eugene.groshev@gmail.com>
> Suggested-by: Sarah Nadi <nadi@ualberta.ca>
> Suggested-by: Thorsten Berger <thorsten.berger@rub.de>
> Signed-off-by: Thorsten Berger <thorsten.berger@rub.de>
> Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
> ---
> scripts/kconfig/qconf.cc | 515 ++++++++++++++++++++++++++++++++++++++-
> scripts/kconfig/qconf.h | 103 ++++++++
> 2 files changed, 616 insertions(+), 2 deletions(-)
>
> diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc
> index 7d239c032b3d..20ca9936d592 100644
> --- a/scripts/kconfig/qconf.cc
> +++ b/scripts/kconfig/qconf.cc
> @@ -3,7 +3,7 @@
> * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
> * Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>
> */
> -
> +#include "qnamespace.h"
> #include <QAction>
> #include <QActionGroup>
> #include <QApplication>
> @@ -19,14 +19,27 @@
> #include <QRegularExpression>
> #include <QScreen>
> #include <QToolBar>
> +#include <QListWidget>
> +#include <QComboBox>
> +#include <QTableWidget>
> +#include <QHBoxLayout>
> +#include <QMovie>
> +#include <QMessageBox>
>
> #include <stdlib.h>
> +#include <QAbstractItemView>
> +#include <QMimeData>
> +#include <QBrush>
> +#include <QColor>
>
> #include "lkc.h"
> #include "qconf.h"
> +#include "configfix.h"
>
> #include "images.h"
>
> +static QString tristate_value_to_string(tristate val);
> +static tristate string_value_to_tristate(QString s);
>
> static QApplication *configApp;
> static ConfigSettings *configSettings;
> @@ -178,6 +191,7 @@ void ConfigItem::updateMenu(void)
> prompt += " (NEW)";
> set_prompt:
> setText(promptColIdx, prompt);
> +
> }
Unrelated change.
(Same in many places)
> void ConfigItem::testUpdateMenu(bool v)
> @@ -225,6 +239,7 @@ void ConfigItem::init(void)
> }
> }
> updateMenu();
> +
> }
>
> /*
> @@ -405,7 +420,7 @@ void ConfigList::updateSelection(void)
> ConfigItem* item = (ConfigItem*)selectedItems().first();
> if (!item)
> return;
> -
> + emit selectionChanged(selectedItems());
> menu = item->menu;
> emit menuChanged(menu);
> if (!menu)
> @@ -471,6 +486,7 @@ void ConfigList::updateListForAll()
>
> list->updateList();
> }
> +
> }
>
> void ConfigList::updateListAllForAll()
> @@ -539,6 +555,7 @@ void ConfigList::changeValue(ConfigItem* item)
> }
> if (oldexpr != newexpr)
> ConfigList::updateListForAll();
> + emit updateConflictsViewColorization();
> break;
> default:
> break;
> @@ -898,6 +915,7 @@ void ConfigList::contextMenuEvent(QContextMenuEvent *e)
> action, &QAction::setChecked);
> action->setChecked(showName);
> headerPopup->addAction(action);
> + headerPopup->addAction(addSymbolFromContextMenu);
> }
>
> headerPopup->exec(e->globalPos());
> @@ -918,6 +936,7 @@ QList<ConfigList *> ConfigList::allLists;
> QAction *ConfigList::showNormalAction;
> QAction *ConfigList::showAllAction;
> QAction *ConfigList::showPromptAction;
> +QAction *ConfigList::addSymbolFromContextMenu;
>
> void ConfigList::setAllOpen(bool open)
> {
> @@ -1249,7 +1268,10 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
> info, &ConfigInfoView::setInfo);
> connect(list, &ConfigList::menuChanged,
> parent, &ConfigMainWindow::setMenuLink);
> + connect(list, &ConfigList::menuChanged,
> + parent, &ConfigMainWindow::conflictSelected);
>
> + connect(list,&ConfigList::updateConflictsViewColorization,this,&ConfigSearchWindow::updateConflictsViewColorizationFowarder);
> layout1->addWidget(split);
>
> QVariant x, y;
> @@ -1272,6 +1294,10 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
> this, &ConfigSearchWindow::saveSettings);
> }
>
> +void ConfigSearchWindow::updateConflictsViewColorizationFowarder(void){
> + emit updateConflictsViewColorization();
> +}
> +
> void ConfigSearchWindow::saveSettings(void)
> {
> if (!objectName().isEmpty()) {
> @@ -1364,6 +1390,17 @@ ConfigMainWindow::ConfigMainWindow(void)
> split1->addWidget(configList);
> split1->addWidget(menuList);
> split2->addWidget(helpText);
> + split3 = new QSplitter(split2);
> + split3->setOrientation(Qt::Vertical);
> + conflictsView = new ConflictsView(split3, "help");
> + /* conflictsSelected signal in conflictsview triggers when a conflict is selected
> + in the view. this line connects that event to conflictselected event in mainwindow
> + which updates the selection to match (in the configlist) the symbol that was selected.
> + */
> + connect(conflictsView,SIGNAL(conflictSelected(struct menu *)),SLOT(conflictSelected(struct menu *)));
> + connect(conflictsView,SIGNAL(refreshMenu()),SLOT(refreshMenu()));
> + connect(menuList,SIGNAL(updateConflictsViewColorization()),conflictsView,SLOT(updateConflictsViewColorization()));
> + connect(configList,SIGNAL(updateConflictsViewColorization()),conflictsView,SLOT(updateConflictsViewColorization()));
>
> setTabOrder(configList, helpText);
> configList->setFocus();
> @@ -1430,6 +1467,8 @@ ConfigMainWindow::ConfigMainWindow(void)
> ConfigList::showAllAction->setCheckable(true);
> ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup);
> ConfigList::showPromptAction->setCheckable(true);
> + ConfigList::addSymbolFromContextMenu = new QAction("Add symbol from context menu");
> + connect(ConfigList::addSymbolFromContextMenu, &QAction::triggered, conflictsView, &ConflictsView::addSymbol);
>
> QAction *showDebugAction = new QAction("Show Debug Info", this);
> showDebugAction->setCheckable(true);
> @@ -1485,6 +1524,8 @@ ConfigMainWindow::ConfigMainWindow(void)
>
> connect(configList, &ConfigList::menuChanged,
> helpText, &ConfigInfoView::setInfo);
> + connect(configList, &ConfigList::menuChanged,
> + conflictsView, &ConflictsView::menuChanged);
> connect(configList, &ConfigList::menuSelected,
> this, &ConfigMainWindow::changeMenu);
> connect(configList, &ConfigList::itemSelected,
> @@ -1493,6 +1534,8 @@ ConfigMainWindow::ConfigMainWindow(void)
> this, &ConfigMainWindow::goBack);
> connect(menuList, &ConfigList::menuChanged,
> helpText, &ConfigInfoView::setInfo);
> + connect(menuList, &ConfigList::menuChanged,
> + conflictsView, &ConflictsView::menuChanged);
> connect(menuList, &ConfigList::menuSelected,
> this, &ConfigMainWindow::changeMenu);
>
> @@ -1712,6 +1755,13 @@ void ConfigMainWindow::showSplitView(void)
> menuList->setFocus();
> }
>
> +void ConfigMainWindow::conflictSelected(struct menu * men)
> +{
> + configList->clearSelection();
> + menuList->clearSelection();
> + emit(setMenuLink(men));
> +}
> +
> void ConfigMainWindow::showFullView(void)
> {
> singleViewAction->setEnabled(true);
> @@ -1847,6 +1897,432 @@ void ConfigMainWindow::conf_changed(bool dirty)
> saveAction->setEnabled(dirty);
> }
>
> +void ConfigMainWindow::refreshMenu(void)
> +{
> + configList->updateListAll();
> +}
> +
> +void QTableWidget::dropEvent(QDropEvent *event)
> +{
> +}
> +
> +ConflictsView::ConflictsView(QWidget *parent, const char *name)
> + : Parent(parent)
> +{
> + currentSelectedMenu = nullptr;
> + setObjectName(name);
> + QHBoxLayout *horizontalLayout = new QHBoxLayout(this);
> + QVBoxLayout *verticalLayout = new QVBoxLayout();
> + verticalLayout->setContentsMargins(0, 0, 0, 0);
> + conflictsToolBar = new QToolBar("ConflictTools", this);
> + // toolbar buttons [n] [m] [y] [calculate fixes] [remove]
> + QAction *addSymbol = new QAction("Add Symbol");
> + QAction *setConfigSymbolAsNo = new QAction("N");
> + QAction *setConfigSymbolAsModule = new QAction("M");
> + QAction *setConfigSymbolAsYes = new QAction("Y");
> + fixConflictsAction_ = new QAction("Calculate Fixes");
> + QAction *removeSymbol = new QAction("Remove Symbol");
> + QMovie *loadingGif = new QMovie("scripts/kconfig/loader.gif");
> + auto loadingLabel = new QLabel;
> +
> + if (loadingGif->isValid()) {
> + loadingGif->setScaledSize(loadingGif->scaledSize().scaled(
> + 20, 20, Qt::KeepAspectRatioByExpanding));
> + loadingGif->start();
> + loadingLabel->setMovie(loadingGif);
> + } else {
> + loadingLabel->setText("Calculating...");
> + }
> +
> + //if you change the order of buttons here, change the code where
> + //module button was disabled if symbol is boolean, selecting module button
> + //depends on a specific index in list of action
> + fixConflictsAction_->setCheckable(false);
> + conflictsToolBar->addAction(addSymbol);
> + conflictsToolBar->addAction(setConfigSymbolAsNo);
> + conflictsToolBar->addAction(setConfigSymbolAsModule);
> + conflictsToolBar->addAction(setConfigSymbolAsYes);
> + conflictsToolBar->addAction(fixConflictsAction_);
> + conflictsToolBar->addAction(removeSymbol);
> + // loadingLabel->setMargin(5);
> + loadingLabel->setContentsMargins(5, 5, 5, 5);
> + loadingAction = conflictsToolBar->addWidget(loadingLabel);
> + loadingAction->setVisible(false);
> +
> +
> + verticalLayout->addWidget(conflictsToolBar);
> +
> + connect(addSymbol, &QAction::triggered, this, &ConflictsView::addSymbol);
> + connect(setConfigSymbolAsNo, &QAction::triggered,this, &ConflictsView::changeToNo);
> + connect(setConfigSymbolAsModule, &QAction::triggered,this, &ConflictsView::changeToModule);
> + connect(setConfigSymbolAsYes, &QAction::triggered,this, &ConflictsView::changeToYes);
> + connect(removeSymbol, &QAction::triggered,this, &ConflictsView::removeSymbol);
> + connect(this, SIGNAL(resultsReady()), SLOT(updateResults()));
> + //connect clicking 'calculate fixes' to 'change all symbol values to fix all conflicts'
> + // no longer used anymore for now.
> + connect(fixConflictsAction_, &QAction::triggered,this, &ConflictsView::calculateFixes);
> +
> + conflictsTable = (QTableWidget *) new dropAbleView(this);
> + conflictsTable->setRowCount(0);
> + conflictsTable->setColumnCount(3);
> + conflictsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
> +
> + conflictsTable->setHorizontalHeaderLabels(QStringList() << "Option" << "Wanted value" << "Current value" );
> + verticalLayout->addWidget(conflictsTable);
> +
> + conflictsTable->setDragDropMode(QAbstractItemView::DropOnly);
> + setAcceptDrops(true);
> +
> + connect(conflictsTable, SIGNAL(cellClicked(int, int)), SLOT(cellClicked(int,int)));
> + horizontalLayout->addLayout(verticalLayout);
> +
> + // populate the solution view on the right hand side:
> + QVBoxLayout *solutionLayout = new QVBoxLayout();
> + solutionLayout->setContentsMargins(0, 0, 0, 0);
> + solutionSelector = new QComboBox();
> + connect(solutionSelector, QOverload<int>::of(&QComboBox::currentIndexChanged),
> + [=](int index){ changeSolutionTable(index); });
> + solutionTable = new QTableWidget();
> + solutionTable->setRowCount(0);
> + solutionTable->setColumnCount(2);
> + solutionTable->setHorizontalHeaderLabels(QStringList() << "Symbol" << "New Value" );
> +
> + applyFixButton = new QPushButton("Apply Selected solution");
> + connect(applyFixButton, SIGNAL(clicked(bool)), SLOT(applyFixButtonClick()));
> +
> + numSolutionLabel = new QLabel("Solutions:");
> + solutionLayout->addWidget(numSolutionLabel);
> + solutionLayout->addWidget(solutionSelector);
> + solutionLayout->addWidget(solutionTable);
> + solutionLayout->addWidget(applyFixButton);
> +
> + horizontalLayout->addLayout(solutionLayout);
> +}
The "Current value" does not reflect the current value.
1. "Add Symbol" button.
2. Edit the "Option" column
-> The "Current value" stay the same
(it shows the value of the previous symbol)
The "Wanted value" should toggle when you click on the cell.
See how the existing window works.
The values should be "Y", "M", "N" instead of
"YES", "MODULE", "NO", for consistency.
> +
> +void ConflictsView::changeToNo(){
> + QItemSelectionModel *select = conflictsTable->selectionModel();
> + if (select->hasSelection()){
> + QModelIndexList rows = select->selectedRows();
> + for (int i = 0;i < rows.count(); i++)
> + {
> + conflictsTable->item(rows[i].row(),1)->setText("NO");
> + }
> + }
> +}
> +
> +void ConflictsView::applyFixButtonClick(){
> + signed int solution_number = solutionSelector->currentIndex();
> +
> + if (solution_number == -1 || solution_output == NULL) {
> + return;
> + }
> +
> + struct sfix_list * selected_solution = select_solution(solution_output, solution_number);
> + apply_fix(selected_solution);
> +
> +
> + ConfigList::updateListForAll();
> + for (int i = 0;i < conflictsTable->rowCount(); i++)
> + {
> + conflictsTable->item(i,2)->setText(conflictsTable->item(i,1)->text());
> + }
> + updateConflictsViewColorization();
> + QMessageBox msgBox;
> + msgBox.setText("The solution has been applied.");
> + msgBox.exec();
> +}
> +
> +void ConflictsView::changeToYes(){
> + QItemSelectionModel *select = conflictsTable->selectionModel();
> + if (select->hasSelection()){
> + QModelIndexList rows = select->selectedRows();
> + for (int i = 0;i < rows.count(); i++)
> + {
> + conflictsTable->item(rows[i].row(),1)->setText("YES");
> + }
> + }
> +
> +}
> +
> +void ConflictsView::changeToModule() {
> + QItemSelectionModel *select = conflictsTable->selectionModel();
> + if (select->hasSelection()){
> + QModelIndexList rows = select->selectedRows();
> + for (int i = 0;i < rows.count(); i++)
> + {
> + conflictsTable->item(rows[i].row(),1)->setText("MODULE");
> + }
> + }
> +
> +}
> +
> +void ConflictsView::menuChanged(struct menu *m)
> +{
> + currentSelectedMenu = m;
> +}
> +
> +void ConflictsView::addSymbol()
> +{
> + addSymbolFromMenu(currentSelectedMenu);
> +}
> +
> +void ConflictsView::selectionChanged(QList<QTreeWidgetItem *> selection)
> +{
> + currentSelection = selection;
> +
> +}
> +
> +void ConflictsView::addSymbolFromMenu(struct menu *m)
> +{
> + // adds a symbol to the conflict resolver list
> + if (m != nullptr){
> + if (m->sym != nullptr){
> + struct symbol *sym = m->sym;
> + tristate currentval = sym_get_tristate_value(sym);
> + //if symbol is not added yet:
> + QAbstractItemModel *tableModel = conflictsTable->model();
> + QModelIndexList matches = tableModel->match(tableModel->index(0,0), Qt::DisplayRole, QString(sym->name));
> + if (matches.isEmpty()){
> + conflictsTable->insertRow(conflictsTable->rowCount());
> + conflictsTable->setItem(conflictsTable->rowCount()-1,0,new QTableWidgetItem(sym->name));
> + conflictsTable->setItem(conflictsTable->rowCount()-1,1,new QTableWidgetItem(tristate_value_to_string(currentval)));
> + conflictsTable->setItem(conflictsTable->rowCount()-1,2,new QTableWidgetItem(tristate_value_to_string(currentval)));
> + }else{
> + conflictsTable->item(matches[0].row(),2)->setText(tristate_value_to_string(currentval));
> + }
> + }
> + }
> +}
> +
> +void ConflictsView::addSymbolFromContextMenu() {
> + struct menu *menu;
> +
> + if (currentSelection.count() < 0){
> + return;
> + }
> + for (auto el: currentSelection){
> + ConfigItem *item = (ConfigItem *)el;
> + if (!item)
> + {
> + continue;
> + }
> + menu = item->menu;
> + addSymbolFromMenu(menu);
> + }
> +
> +}
> +
> +void ConflictsView::removeSymbol()
> +{
> + QItemSelectionModel *select = conflictsTable->selectionModel();
> + QAbstractItemModel *itemModel = select->model();
> + if (select->hasSelection()){
> + QModelIndexList rows = select->selectedRows();
> + itemModel->removeRows(rows[0].row(),rows.size());
> + }
> +}
> +
> +void ConflictsView::cellClicked(int row, int column)
> +{
> + auto itemText = conflictsTable->item(row,0)->text().toUtf8().data();
> + struct property *prop;
> + struct menu *men;
> + struct symbol *sym = sym_find(itemText);
> +
> + if (sym == NULL)
> + return;
> + prop = sym->prop;
> + men = prop->menu;
> + // uncommenting following like somehow disables click signal of 'apply selected solution'
> + if (sym->type == symbol_type::S_BOOLEAN) {
> + //disable module button
> + conflictsToolBar->actions()[2]->setDisabled(true);
> + } else {
> + //enable module button
> + conflictsToolBar->actions()[2]->setDisabled(false);
> + }
> + emit(conflictSelected(men));
> +}
> +
> +void ConflictsView::changeSolutionTable(int solution_number){
> + if (solution_output == nullptr || solution_number < 0){
> + return;
> + }
> + struct sfix_list *selected_solution = select_solution(solution_output, solution_number);
> + current_solution_number = solution_number;
> + solutionTable->setRowCount(0);
> + for (unsigned int i = 0; i <selected_solution->size; i++)
> + {
> + solutionTable->insertRow(solutionTable->rowCount());
> + struct symbol_fix *cur_symbol = select_symbol(selected_solution,i);
> +
> + QTableWidgetItem *symbol_name = new QTableWidgetItem(cur_symbol->sym->name);
> +
> + solutionTable->setItem(solutionTable->rowCount()-1,0,symbol_name);
> +
> + if (cur_symbol->type == symbolfix_type::SF_BOOLEAN){
> + QTableWidgetItem *symbol_value = new QTableWidgetItem(tristate_value_to_string(cur_symbol->tri));
> + solutionTable->setItem(solutionTable->rowCount()-1,1,symbol_value);
> + } else if(cur_symbol->type == symbolfix_type::SF_NONBOOLEAN){
> + QTableWidgetItem *symbol_value = new QTableWidgetItem(cur_symbol->nb_val.s);
> + solutionTable->setItem(solutionTable->rowCount()-1,1,symbol_value);
> + } else {
> + QTableWidgetItem *symbol_value = new QTableWidgetItem(cur_symbol->disallowed.s);
> + solutionTable->setItem(solutionTable->rowCount()-1,1,symbol_value);
> + }
> + }
> + updateConflictsViewColorization();
> +}
> +
> +void ConflictsView::updateConflictsViewColorization(void)
> +{
> + auto green = QColor(0,170,0);
> + auto red = QColor(255,0,0);
> + auto grey = QColor(180,180,180);
> +
> + if (solutionTable->rowCount() == 0 || current_solution_number < 0)
> + return;
> +
> + for (int i=0; i< solutionTable->rowCount(); i++) {
> + QTableWidgetItem *symbol = solutionTable->item(i,0);
> + //symbol from solution list
> + struct sfix_list *selected_solution = select_solution(solution_output, current_solution_number);
> + struct symbol_fix *cur_symbol = select_symbol(selected_solution,i);
> +
> + // if symbol is editable but the value is not the target value from solution we got, the color is red
> + // if symbol is editable but the value is the target value from solution we got, the color is green
> + // if symbol is not editable , the value is not the target value, the color is grey
> + // if symbol is not editable , the value is the target value, the color is green
> + auto editable = sym_string_within_range(cur_symbol->sym, tristate_value_to_string(cur_symbol->tri).toStdString().c_str());
> + auto _symbol = solutionTable->item(i,0)->text().toUtf8().data();
> + struct symbol *sym_ = sym_find(_symbol);
> +
> + tristate current_value_of_symbol = sym_get_tristate_value(sym_);
> + tristate target_value_of_symbol = string_value_to_tristate(solutionTable->item(i,1)->text());
> + bool symbol_value_same_as_target = current_value_of_symbol == target_value_of_symbol;
> +
> + if (editable && !symbol_value_same_as_target){
> + symbol->setForeground(red);
> + } else if (editable && symbol_value_same_as_target){
> + symbol->setForeground(green);
> + } else if (!editable && !symbol_value_same_as_target){
> + symbol->setForeground(grey);
> + } else if (!editable && symbol_value_same_as_target){
> + symbol->setForeground(green);
> + }
> + }
> +
> +}
> +
> +void ConflictsView::runSatConfAsync()
> +{
> + //loop through the rows in conflicts table adding each row into the array:
> + struct symbol_dvalue *p = nullptr;
> + p = static_cast<struct symbol_dvalue *>(calloc(conflictsTable->rowCount(),sizeof(struct symbol_dvalue)));
> + if (!p)
> + {
> + printf("memory allocation error\n");
> + return;
> + }
> +
> + struct sdv_list *wanted_symbols = sdv_list_init();
> +
> + for (int i = 0; i < conflictsTable->rowCount(); i++)
> + {
> +
> + struct symbol_dvalue *tmp = (p+i);
> + auto _symbol = conflictsTable->item(i,0)->text().toUtf8().data();
> + struct symbol *sym = sym_find(_symbol);
> +
> + tmp->sym = sym;
> + tmp->type = static_cast<symboldv_type>(sym->type == symbol_type::S_BOOLEAN?0:1);
> + tmp->tri = string_value_to_tristate(conflictsTable->item(i,1)->text());
> + sdv_list_add(wanted_symbols,tmp);
> + }
> + fixConflictsAction_->setText("Cancel");
> + loadingAction->setVisible(true);
> +
> + struct sfl_list *ret = run_satconf(wanted_symbols);
> + solution_output = ret;
> +
> + free(p);
> + emit resultsReady();
> + {
> + std::lock_guard<std::mutex> lk{satconf_mutex};
> + satconf_cancelled = true;
> + }
> + satconf_cancellation_cv.notify_one();
> +
> +}
> +
> +void ConflictsView::updateResults(void)
> +{
> + fixConflictsAction_->setText("Calculate Fixes");
> + loadingAction->setVisible(false);
> + if (!(solution_output == nullptr || solution_output->size == 0))
> + {
> + solutionSelector->clear();
> + for (unsigned int i = 0; i < solution_output->size; i++)
> + {
> + solutionSelector->addItem(QString::number(i+1));
> + }
> + // populate the solution table from the first solution gotten
> + numSolutionLabel->setText(QString("Solutions: (%1) found").arg(solution_output->size));
> + changeSolutionTable(0);
> + }
> + else {
> + QMessageBox msgBox;
> + msgBox.setText("All symbols are already within range.");
> + msgBox.exec();
> + }
> + if (runSatConfAsyncThread->joinable()){
> + runSatConfAsyncThread->join();
> + delete runSatConfAsyncThread;
> + runSatConfAsyncThread = nullptr;
> + }
> +
> +}
> +
> +void ConflictsView::calculateFixes()
> +{
> + if(conflictsTable->rowCount() == 0)
> + {
> + printd("table is empty\n");
> + return;
> + }
> +
> + if (runSatConfAsyncThread == nullptr)
> + {
> + // fire away asynchronous call
> + std::unique_lock<std::mutex> lk{satconf_mutex};
> +
> + numSolutionLabel->setText(QString("Solutions: "));
> + solutionSelector->clear();
> + solutionTable->setRowCount(0);
> + satconf_cancelled = false;
> + runSatConfAsyncThread = new std::thread(&ConflictsView::runSatConfAsync,this);
> + }else{
> + printd("Interrupting rangefix\n");
> + interrupt_rangefix();
> + std::unique_lock<std::mutex> lk{satconf_mutex};
> + satconf_cancellation_cv.wait(lk,[this] {return satconf_cancelled == true;});
> + }
> +
> +}
> +
> +void ConflictsView::changeAll(void)
> +{
> + // not implemented for now
> + return;
> +}
> +
> +ConflictsView::~ConflictsView(void)
> +{
> +
> +}
> +
> +
> void fixup_rootmenu(struct menu *menu)
> {
> struct menu *child;
> @@ -1918,3 +2394,38 @@ int main(int ac, char** av)
>
> return 0;
> }
> +
> +dropAbleView::dropAbleView(QWidget *parent) :
> + QTableWidget(parent) {}
> +
> +dropAbleView::~dropAbleView() {}
> +void dropAbleView::dropEvent(QDropEvent *event)
> +{
> + event->acceptProposedAction();
> +}
> +
> +static QString tristate_value_to_string(tristate val)
> +{
> + switch ( val ) {
> + case yes:
> + return QString::fromStdString("YES");
> + case mod:
> + return QString::fromStdString("MODULE");
> + case no:
> + return QString::fromStdString("NO");
> + default:
> + return QString::fromStdString("");
> + }
> +
> +}
> +
> +static tristate string_value_to_tristate(QString s){
> + if (s == "YES")
> + return tristate::yes;
> + else if (s == "MODULE")
> + return tristate::mod;
> + else if (s == "NO")
> + return tristate::no;
> + else
> + return tristate::no;
> +}
> diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h
> index 53373064d90a..1ace9f673fd0 100644
> --- a/scripts/kconfig/qconf.h
> +++ b/scripts/kconfig/qconf.h
> @@ -14,9 +14,16 @@
> #include <QStyledItemDelegate>
> #include <QTextBrowser>
> #include <QTreeWidget>
> +#include <QTableWidget>
> +#include <QList>
> +#include <QComboBox>
> +#include <QLabel>
> +#include <thread>
> +#include <condition_variable>
>
> #include "expr.h"
>
> +
Unrelated change.
(Do not add excessive brank line)
> class ConfigList;
> class ConfigItem;
> class ConfigMainWindow;
> @@ -80,6 +87,8 @@ public slots:
> void parentSelected(void);
> void gotFocus(struct menu *);
> void showNameChanged(bool on);
> + void selectionChanged(QList<QTreeWidgetItem *> selection);
> + void updateConflictsViewColorization();
>
> public:
> void updateListAll(void)
> @@ -111,6 +120,82 @@ public slots:
> static void updateListAllForAll();
>
> static QAction *showNormalAction, *showAllAction, *showPromptAction;
> + static QAction *addSymbolFromContextMenu;
> +
> +};
> +
> +class ConflictsView : public QWidget {
> + Q_OBJECT
> + typedef class QWidget Parent;
> +private:
> + QAction *loadingAction;
> +public:
> + ConflictsView(QWidget *parent, const char *name = 0);
> + ~ConflictsView(void);
> + void addSymbolFromMenu(struct menu *m);
> + int current_solution_number = -1;
> +
> +public slots:
> + void cellClicked(int, int);
> + void changeAll();
> + // triggered by Qactions on the tool bar that adds/remove symbol
> + void addSymbol();
> + // triggered from config list right click -> add symbols
> + void addSymbolFromContextMenu();
> + void removeSymbol();
> + void menuChanged(struct menu *);
> + void changeToNo();
> + void changeToYes();
> + void changeToModule();
> + void selectionChanged(QList<QTreeWidgetItem *> selection);
> +
> +
> + void applyFixButtonClick();
> + void updateConflictsViewColorization();
> + void updateResults();
> +
> +
> +
> + // switches the solution table with selected solution index from solution_output
> + void changeSolutionTable(int solution_number);
> +
> + // calls satconfig to solve to get wanted value to current value
> + void calculateFixes();
> +signals:
> + void showNameChanged(bool);
> + void showRangeChanged(bool);
> + void showDataChanged(bool);
> + void conflictSelected(struct menu *);
> + void refreshMenu();
> + void resultsReady();
> +public:
> + QTableWidget *conflictsTable;
> +
> + // the comobox on the right hand side. used to select a solution after
> + // getting solution from satconfig
> + QComboBox *solutionSelector{nullptr};
> +
> + // the table which shows the selected solution showing variable = New value changes
> + QTableWidget *solutionTable{nullptr};
> +
> + // Apply fixes button on the solution view
> + QPushButton *applyFixButton{nullptr};
> +
> + struct sfl_list *solution_output{nullptr};
> +
> + QToolBar *conflictsToolBar;
> + struct menu *currentSelectedMenu;
> + QLabel *numSolutionLabel{nullptr};
> + // currently selected config items in configlist.
> + QList<QTreeWidgetItem *> currentSelection;
> + QAction *fixConflictsAction_{nullptr};
> + void runSatConfAsync();
> + std::thread *runSatConfAsyncThread{nullptr};
> +
> + std::mutex satconf_mutex;
> + std::condition_variable satconf_cancellation_cv;
> + bool satconf_cancelled{false};
> +
> };
>
> class ConfigItem : public QTreeWidgetItem {
> @@ -223,6 +308,9 @@ class ConfigSearchWindow : public QDialog {
> public slots:
> void saveSettings(void);
> void search(void);
> + void updateConflictsViewColorizationFowarder();
> +signals:
> + void updateConflictsViewColorization();
>
> protected:
> QLineEdit* editField;
> @@ -258,6 +346,8 @@ public slots:
> void showIntro(void);
> void showAbout(void);
> void saveSettings(void);
> + void conflictSelected(struct menu *);
> + void refreshMenu();
>
> protected:
> void closeEvent(QCloseEvent *e);
> @@ -266,10 +356,23 @@ public slots:
> ConfigList *menuList;
> ConfigList *configList;
> ConfigInfoView *helpText;
> + ConflictsView *conflictsView;
> + QToolBar *conflictsToolBar;
> QAction *backAction;
> QAction *singleViewAction;
> QAction *splitViewAction;
> QAction *fullViewAction;
> QSplitter *split1;
> QSplitter *split2;
> + QSplitter *split3;
> +};
> +
> +class dropAbleView : public QTableWidget
> +{
> +public:
> + dropAbleView(QWidget *parent = nullptr);
> + ~dropAbleView();
> +
> +protected:
> + void dropEvent(QDropEvent *event);
> };
> --
> 2.39.2
>
>
--
Best Regards
Masahiro Yamada
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v4 12/12] kconfig: Add loader.gif
2024-07-10 6:52 [PATCH v4 00/12] kconfig: Add support for conflict resolution Ole Schuerks
` (10 preceding siblings ...)
2024-07-10 6:52 ` [PATCH v4 11/12] kconfig: Add xconfig-modifications Ole Schuerks
@ 2024-07-10 6:52 ` Ole Schuerks
11 siblings, 0 replies; 25+ messages in thread
From: Ole Schuerks @ 2024-07-10 6:52 UTC (permalink / raw)
To: linux-kbuild
Cc: ole0811sch, jude.gyimah, thorsten.berger, deltaone, jan.sollmann,
mcgrof, masahiroy, linux-kernel
Add the GIF used as the loading indicator.
We created the file from scratch using GIMP. It's also available at
https://github.com/delta-one/linux/blob/configfix_20240607-patches-5/scripts/kconfig/loader.gif
Signed-off-by: Ole Schuerks <ole0811sch@gmail.com>
---
scripts/kconfig/loader.gif | Bin 0 -> 4177 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 scripts/kconfig/loader.gif
diff --git a/scripts/kconfig/loader.gif b/scripts/kconfig/loader.gif
new file mode 100644
index 0000000000000000000000000000000000000000..54c8bc76cc037ab04dc1d37ddc34384f9714284e
GIT binary patch
literal 4177
zcmcJSdo<Ju8^`IwY&S)@)w(u}ORX(LCe^46a*rAJjLQreA;!ohR4xhYK61^3an0RG
zB!ihk%4LX{jF@3UvBQ*X@2K7P-FJ6-&+ggt{{H-(^ZT98`F@}0`#g`n!8t8$XAmEV
zFPxu``%Fws$bQ~#a`u9y-f6S5M-HoP-gqth%N{*HH)nr0SD8Q$f0T^Afw38%?1wGi
zZRX?WQ{DJ(;}3kB{B9>owDoS2_pjR&n>ennze`g}IK4nH%)TK$4p_fnSMn@-@K!=f
z&NbG2m_V+Xp1M@hY5I}j9lCcF8FkoN$Lg9xyJyywTS}_topYfBh=z}E+zX~&X@~Z7
zWwC?N6Et$;(6sf6m>*Q!9Y@3n4qI~B{*2*&JMp{j93dCfR}8&fnA|<-J%Dpc4e=?!
z$CcKh?Aw&4n8k!^b0@s-<@ulvN=3vWd}4Pj<@aPH8BSe9S5!b)ZYHF%Dof<y;>R<!
zH96?^M}19eh{UO7>Vx&2(VB|lP$JbTW~4|<YlT5RR&B)-B<OacM_YxEOIB)UR%sD@
z;bedW)E2>~?9o^)xsr>T_FaOMeTW~UJY#8xwZ7(ut4rxYQj#g>b+ZO%QJ@^Z8fqF_
zat%VFE5O#268USLUt%=dGZ#@{=%emoFZO08k}5{L7wyg&gn)=}=HQ4Xph?ZfBgl*=
zO~8N4qQC3Nzuigs>dtwLX}rpV71p%3K&}tA0GLQQn?(6JC(=Bh0<V8sV<Fxcx2{a0
zrNWJ7?)15M2CZX=We<Ytyu~C3PN+T-8WI(JFD6FOjDj-n5l1)x2_+?Ko+OyBb>iZm
z`ZTMw0lTp1F9Fq+E6E=ou1;2vsv>uGfuk_C-Rog%R^dP0y?^>%rsyMAvtfT-WAl<u
zP%pa1P*sfo<5B~nstn9dw_=r5#=CY+4l3`tB)BqYpmB|y5h$#|_&HF1CCa+4j=4di
z{SB1=mqni%n!9f;{II)lrI>o|7<A+q)E$I_qlg_VoZU(`9!fSmg}L&75T>B_y>^SY
zDPytw;pi`ds{-PjfGA819Mk}4%vb{PF5F)^{&^#-iFg&A=qHKTeTpLYWoQSUkltuy
zr;lBYLuu!r4NFKZ0v3dwCdTmYWNyj+(?EGQ8E-%$i8SrdwH)wNXf_Tmt6%;x+r&DT
z9sDpuT7Wn-q)1HlbCU~nO%}5NSRymbc{=ke$=@v5Rexpvet50zL&TNt`N8JM$RLVu
zgV&7306>22W$2~H-c2|MQwIBrY64Ah8nTzH4u$(g<A@3rW*R^o$GeTW-u#tN!ISjf
zhMjgGfR<$)$iRU&H#G1Xp!#h@$JEihdX?-*p;zyBpcX=Vr+u4sT;s+UFC93LGK~K)
zFiI3OZ}78R+T%K6k*XDqB1B^>cq;O1*#Ak95o)N4;$f??ju`6b^YYb@`)m9zRg)#C
zkfCHU&~i4!@TdL@gySuUz|nco+zGRm_NpCTQkT1(HPTx!ut_DE1OY(*BNS_W6P?_D
zfnEUPFs_dj!~4@mzG=+w!=9UJ6C&xuD7bWV(e?-RDF?bSZVaqRAo%TCJO*M~6{9T&
zOK_{1b!{!^7fIXFB9}U9(7@Ukq2Y9BPme~%eI)yMy;n*FI0#;DM59@Osk_YO`TNO@
zZWjI50k~>{;0@^67~X<#Q|&in%1U#I(K|Qg?)8>`b8zy>VcW6RcS?PZXcsHpg%JG@
z{MXyJSI5w0Qc9+$m}gGmD;4TJo1}^^R1U0Jd7h+K5SS#nyR-IL_E9a9zN6XR^W)V)
z;}f3*0Y#gV%?U6I;S?;03}@g*GyuFs`Qpv^HoSdhP3+xqnc$~3(xTo|Ho3~hl?a_X
z?^d5VqEk%5U?DSW#*yuSN}P;bN^>D(R{a#-5*bkP7H$cB5g_`hy&bW)`oJZ_7pIsn
z3Uw*ASI*?Dq|<=q&J@nD^$||EKE(|~=_)1wZ{T=i_}os+Y|9tIdk#KPxzrirF?usk
zG+o}Z$i*|vYkMnQA<#8`oKv{CXMU|Sa<|NTXRutd;pW<)8W6shZ{O8Y?_3Q@*v$6=
zBt&+lpk{#+Q=>fM(-N^Pb8MDAcr(~EPkLVrLJ?PNfkj>iX}D8(L!fUbmI$(hL~w^}
zjb_JHkh`u*QDUWS?w~-GvrI>{D#_oK)>poWT%_;=i=ON@SC`uS<Y{%2s{;PH8(X{H
zG5FBr&Y8jpl8Tf>+EnLYzthXItI<>CSA4HTdnMV}FE7pr5@AQN)>>L;IJ`TSn;j+H
z^Fny=#nG*PE%r$7AW2ZHyhP^yj|9~>tqDqvay8Vhmb~NNzg`EQaEez13#aH=RZ0x(
z!&@-)k8ed>?h#9dZw<6@syeSD0IUaL)Ewq4^cLQx?aCW_y%J2yL=;|RtLtHzVle<h
z1B%)Z;}*zU3+`t0g(SOfbU$C4_2t7sl7LvL4>%{k0%etg%-DzbPLOMiyU)><U-;;x
zJzG7R@RI?s)+1!5z;rs<!$G#TznrQ87PWi)J{iz{_lJ&vVsJ~%q?vZ-_m^)Hwj(=e
zZ9_;l!wr?;2f!9X$pqqA5N{Cde?a1Dx7eFb1o1x)&ks~~$5oV$wUZ4CmQc5%&a<$;
zxXfNWmQ=>r-tTGP=1biBvA_aV*Mvvpm>S>TZxr!khws*s>`k>SA5ZtF{7H0owqv5I
z(JDFjGb0}e?Ytqzgc0CrL~OvMcrhb}{S6py<9=0T=ku)aE_kqRkTf7x-X>G@&Yg06
zEBSb{pAVgmW;GvJMtiqg32}5<?G5jFLRB?0OhMJ2)6RH-8B?7*$zF)vO(EZjKDK7V
zXBX7-X0XGK?X(x$j`w^n&ZyRa?~A<20Vd(#WCC-90;KRR72ge%-q!_Z@}I4hcD-WK
z9BAT(^6+ztsijC(LDljzV>HsPa`7SL<M*l&GKn|3D_kD|&6%#Jb09+Xk`<!0#s>#P
z)s@W+#7(^1(~YJzkn>$cnC#WQW7V`rajbYFesPhrBd6gMOiVg9rtlbVcerID!8a$g
zC+pBCbaq_%(z755VyuYGEfk@0mf`65DYp(h0k6~x`OM+hsd9#(z%nUu^yeKHHH|G$
zK#vMYeKGJ5BR(SM!H+gQS!vhi?8e?Xc1}8XXNRrU9~d09sp%9$A+0!Ku3FG!BBNd0
zZIH|x<jWqiu|OZTg&rjyn0@7&Cu%!ojd8NA^a8Z10sAb`FrvXCY;kHf61Trv3#<_S
zw6J>iJVdOoB*LRp*wpe4W_XR>J|xow7U*j033%`}tn)r1X(UZg`rXOo=l9#(0wEwa
zOshB&H&PSh*YUqNdbhdw4?Za?4HI7+56TlYaS=!bm6Uqfwfew%(dcNbiCxIznga+T
zQ;gas2O|TIPT$y#H}xF4g+uIYuAr8wYSz$bl|5^iD=dwk`PRE)zAv)vOru1kr`FXr
zouw9wpg8VOtrQL;8M?KA_lV&p$!{a(%R#|ioB_9{ihO1w5L8K&nrfdrI?gJrEQGP<
zfPoC(1AyhCpH9m&!7T+P3j6q5y2?k<gl@WLE$m1dV8!GylLmXxkmP&x>PSz=SUjh*
zb#&%MB)b~o2QZ7)A`rzhYPG1VybE=sM7XEkAE$r#c%vUqL8{lL6L2be#e47}H`?vn
z(<@QxvMI#rIOdI0_!4<5&z1ytTh+#9t4KFXpQd1{0J-HYwl^wkK(PqRNox!Lxw9*b
z!4_)}g!jV&;1oP_1+S&}jY#7*?;qwr&#NXbbWRCzd@=?Jo=AQwBXT`NILG33;Wh_^
zZ0EcQtJj<qb(p&%>i5-2+odSnA94>GmlEn@SG^npUZ)EX+v|_H2{SWBhDq2)95ap1
z+nwJn0Dtk^i{_qt1Q%WByCp`Rp$BWt6=8Pkk=1ly6Ich|$Wj}98(KVsz(|0|jq@~I
zhzWUUsS&b8&5Djpr6`ibnK({SH1EdwgDQXf1U@-*h&30lNm5qR*3nhznJXFn5JLLF
mPrb!EzI*)gbndmHWkD#_qOspS`CK89f)W-#!ra)Lb^ZkcGe|A~
literal 0
HcmV?d00001
--
2.39.2
^ permalink raw reply [flat|nested] 25+ messages in thread