* [RFC 01/12] Add picosat.h
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
@ 2021-10-20 9:35 ` Thorsten Berger
2021-10-20 15:54 ` Luis Chamberlain
2021-10-20 9:36 ` [RFC 02/12] Add picosat.c (1/3) Thorsten Berger
` (10 subsequent siblings)
11 siblings, 1 reply; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:35 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
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..668bc00bcbc0
--- /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.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 02/12] Add picosat.c (1/3)
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
2021-10-20 9:35 ` [RFC 01/12] Add picosat.h Thorsten Berger
@ 2021-10-20 9:36 ` Thorsten Berger
2021-10-20 9:37 ` [RFC 03/12] Add picosat.c (2/3) Thorsten Berger
` (9 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:36 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
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..653bf0f0b99f
--- /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.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 03/12] Add picosat.c (2/3)
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
2021-10-20 9:35 ` [RFC 01/12] Add picosat.h Thorsten Berger
2021-10-20 9:36 ` [RFC 02/12] Add picosat.c (1/3) Thorsten Berger
@ 2021-10-20 9:37 ` Thorsten Berger
2021-10-20 9:38 ` [RFC 04/12] Add picosat.c (3/3) Thorsten Berger
` (8 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:37 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
scripts/kconfig/picosat.c | 3000 +++++++++++++++++++++++++++++++++++++
1 file changed, 3000 insertions(+)
diff --git a/scripts/kconfig/picosat.c b/scripts/kconfig/picosat.c
index 653bf0f0b99f..0a6eb3d5a45d 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.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 04/12] Add picosat.c (3/3)
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
` (2 preceding siblings ...)
2021-10-20 9:37 ` [RFC 03/12] Add picosat.c (2/3) Thorsten Berger
@ 2021-10-20 9:38 ` Thorsten Berger
2021-10-20 9:40 ` [RFC 05/12] Add definitions Thorsten Berger
` (7 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:38 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
scripts/kconfig/picosat.c | 2502 +++++++++++++++++++++++++++++++++++++
1 file changed, 2502 insertions(+)
diff --git a/scripts/kconfig/picosat.c b/scripts/kconfig/picosat.c
index 0a6eb3d5a45d..76db7ae0028a 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.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 05/12] Add definitions
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
` (3 preceding siblings ...)
2021-10-20 9:38 ` [RFC 04/12] Add picosat.c (3/3) Thorsten Berger
@ 2021-10-20 9:40 ` Thorsten Berger
2021-10-20 9:41 ` [RFC 06/12] Add files for building constraints Thorsten Berger
` (6 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:40 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
scripts/kconfig/Makefile | 19 +++-
scripts/kconfig/cf_defs.h | 233 ++++++++++++++++++++++++++++++++++++++
scripts/kconfig/expr.h | 13 +++
3 files changed, 262 insertions(+), 3 deletions(-)
create mode 100644 scripts/kconfig/cf_defs.h
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 5a215880b268..75caf1b755b0 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -35,6 +35,8 @@ menuconfig-prog := mconf
nconfig-prog := nconf
gconfig-prog := gconf
xconfig-prog := qconf
+cfconfig-prog := cfconfig
+cfoutconfig-prog := cfoutconfig
define config_rule
PHONY += $(1)
@@ -45,7 +47,8 @@ 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 cfconfig \
+cfoutconfig, $(eval $(call config_rule,$(c))))
PHONY += localmodconfig localyesconfig
localyesconfig localmodconfig: $(obj)/conf
@@ -140,6 +143,8 @@ help:
@echo ' default value without prompting'
@echo ' tinyconfig - Configure the tiniest possible kernel'
@echo ' testconfig - Run Kconfig unit tests (requires python3 and pytest)'
+ @echo ' cfconfig - CLI tool for debugging ConfigFix'
+ @echo ' cfoutconfig - Print constraints and DIMACS-output into files'
# ===========================================================================
# object files used by all kconfig flavours
@@ -176,12 +181,20 @@ $(foreach f, mconf.o $(lxdialog), \
$(addprefix $(obj)/, mconf.o $(lxdialog)): $(obj)/mconf-cfg
+# configfix: Used for the xconfig target as well as for its debugging tools
+hostprogs += cfconfig cfoutconfig
+cfconf-objs := configfix.o cf_constraints.o cf_expr.o cf_rangefix.o cf_satutils.o cf_utils.o picosat.o
+cfconfig-objs := cfconfig.o $(cfconf-objs) $(common-objs)
+cfoutconfig-objs := cfoutconfig.o $(cfconf-objs) $(common-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 = $(shell . $(obj)/qconf-cfg && echo $$libs)
+HOSTLDLIBS_qconf = $(shell . $(obj)/qconf-cfg && echo $$libs && echo -lpthread)
HOSTCXXFLAGS_qconf.o = $(shell . $(obj)/qconf-cfg && echo $$cflags)
HOSTCXXFLAGS_qconf-moc.o = $(shell . $(obj)/qconf-cfg && echo $$cflags)
diff --git a/scripts/kconfig/cf_defs.h b/scripts/kconfig/cf_defs.h
new file mode 100644
index 000000000000..342327a31dc2
--- /dev/null
+++ b/scripts/kconfig/cf_defs.h
@@ -0,0 +1,233 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef DEFS_H
+#define DEFS_H
+
+/* external variables */
+extern unsigned int sat_variable_nr;
+extern unsigned int tmp_variable_nr;
+extern struct fexpr *satmap; // map SAT variables to fexpr
+extern size_t satmap_size;
+
+extern struct sdv_list *sdv_symbols; /* array with conflict-symbols */
+extern bool CFDEBUG;
+extern bool stop_rangefix;
+extern struct fexpr *const_false;
+extern struct fexpr *const_true;
+extern struct fexpr *symbol_yes_fexpr;
+extern struct fexpr *symbol_mod_fexpr;
+extern struct fexpr *symbol_no_fexpr;
+
+#define printd(fmt...) if (CFDEBUG) printf(fmt)
+
+/* 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;
+ };
+ /* AND, OR, NOT */
+ struct {
+ struct fexpr *left;
+ struct fexpr *right; /* not used for NOT */
+ };
+ /* 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 {
+ enum pexpr_type type;
+ union pexpr_data left, right;
+};
+
+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 {
+ struct fexpr *val;
+
+ struct pexpr *e;
+};
+
+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;
+};
+
+#endif
diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index 9c9caca5bd5f..9c5327dd6be8 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -129,6 +129,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 for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next)
--
2.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 06/12] Add files for building constraints
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
` (4 preceding siblings ...)
2021-10-20 9:40 ` [RFC 05/12] Add definitions Thorsten Berger
@ 2021-10-20 9:41 ` Thorsten Berger
2021-10-20 9:43 ` [RFC 07/12] Add files for handling expressions Thorsten Berger
` (5 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:41 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
scripts/kconfig/cf_constraints.c | 1219 ++++++++++++++++++++++++++++++
scripts/kconfig/cf_constraints.h | 23 +
2 files changed, 1242 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..d1f3b2bd9945
--- /dev/null
+++ b/scripts/kconfig/cf_constraints.c
@@ -0,0 +1,1219 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 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"
+
+#define KCR_CMP false
+#define NPC_OPTIMISATION true
+
+static void init_constraints(void);
+static void get_constraints_bool(void);
+static void get_constraints_select(void);
+static void get_constraints_nonbool(void);
+
+static void build_tristate_constraint_clause(struct symbol *sym);
+
+static void add_selects_kcr(struct symbol *sym);
+static void add_selects(struct symbol *sym);
+
+static void add_dependencies_bool(struct symbol* sym);
+static void add_dependencies_bool_kcr(struct symbol* sym);
+static void add_dependencies_nonbool(struct symbol *sym);
+
+static void add_choice_prompt_cond(struct symbol *sym);
+static void add_choice_dependencies(struct symbol *sym);
+static void add_choice_constraints(struct symbol *sym);
+static void add_invisible_constraints(struct symbol *sym);
+static void sym_nonbool_at_least_1(struct symbol *sym);
+static void sym_nonbool_at_most_1(struct symbol *sym);
+static void sym_add_nonbool_values_from_default_range(struct symbol *sym);
+static void sym_add_range_constraints(struct symbol *sym);
+static void sym_add_nonbool_prompt_constraint(struct symbol *sym);
+
+static struct default_map * create_default_map_entry(struct fexpr *val, struct pexpr *e);
+static struct defm_list * get_defaults(struct symbol *sym);
+static struct pexpr * get_default_y(struct defm_list *list);
+static struct pexpr * get_default_m(struct defm_list *list);
+static struct pexpr *get_default_any(struct symbol *sym);
+static long sym_get_range_val(struct symbol *sym, int base);
+
+/* -------------------------------------- */
+
+/*
+ * build the constraints for each symbol
+ */
+void get_constraints(void)
+{
+ printd("Building constraints...");
+
+ init_constraints();
+ get_constraints_bool();
+ get_constraints_select();
+ get_constraints_nonbool();
+}
+
+/*
+ * need to go through the constraints once to find all "known values"
+ * for the non-Boolean symbols
+ */
+static void init_constraints(void)
+{
+ unsigned int i;
+ struct symbol *sym;
+ struct property *p;
+ for_all_symbols(i, sym) {
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ if (sym_is_boolean(sym)) {
+ for_all_properties(sym, p, P_SELECT)
+ expr_calculate_pexpr_both(p->visible.expr);
+
+ for_all_properties(sym, p, P_IMPLY)
+ expr_calculate_pexpr_both(p->visible.expr);
+ }
+
+ if (sym->dir_dep.expr)
+ expr_calculate_pexpr_both(sym->dir_dep.expr);
+
+ struct property *prompt = sym_get_prompt(sym);
+ if (prompt != NULL && prompt->visible.expr) {
+ expr_calculate_pexpr_both(prompt->visible.expr);
+ get_defaults(sym);
+ }
+
+ if (sym_is_nonboolean(sym)) {
+ for_all_defaults(sym, p) {
+ if (p == NULL)
+ continue;
+
+ sym_create_nonbool_fexpr(sym, p->expr->left.sym->name);
+ }
+ for_all_properties(sym, p, P_RANGE) {
+ if (p == NULL)
+ continue;
+
+ sym_create_nonbool_fexpr(sym, p->expr->left.sym->name);
+ sym_create_nonbool_fexpr(sym, p->expr->right.sym->name);
+ }
+ const char *curr = sym_get_string_value(sym);
+ if (strcmp(curr, "") != 0)
+ sym_create_nonbool_fexpr(sym, (char *) curr);
+ }
+
+ if (sym->type == S_HEX || sym->type == S_INT)
+ sym_add_nonbool_values_from_default_range(sym);
+ }
+}
+
+
+/*
+ * build constraints for boolean symbols
+ */
+static void get_constraints_bool(void)
+{
+ unsigned int i;
+ struct symbol *sym;
+ for_all_symbols(i, sym) {
+
+ if (!sym_is_boolean(sym))
+ continue;
+
+ /* build tristate constraints */
+ if (sym->type == S_TRISTATE)
+ build_tristate_constraint_clause(sym);
+
+ /* build constraints for select statements
+ * need to treat choice symbols seperately */
+ if (!KCR_CMP) {
+ add_selects(sym);
+ } else {
+ if (sym->rev_dep.expr && !sym_is_choice(sym) && !sym_is_choice_value(sym))
+ add_selects_kcr(sym);
+ }
+
+ /* 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);
+ else
+ add_dependencies_bool_kcr(sym);
+ }
+
+ /* build constraints for choice prompts */
+ if (sym_is_choice(sym))
+ add_choice_prompt_cond(sym);
+
+ /* build constraints for dependencies (choice symbols and options) */
+ if (sym_is_choice(sym) || sym_is_choice_value(sym))
+ add_choice_dependencies(sym);
+
+ /* build constraints for the choice groups */
+ if (sym_is_choice(sym))
+ add_choice_constraints(sym);
+
+
+ /* build invisible constraints */
+ add_invisible_constraints(sym);
+ }
+}
+
+/*
+* build the constraints for select-variables
+* skip non-Booleans, choice symbols/options och symbols without rev_dir
+*/
+static void get_constraints_select(void)
+{
+ unsigned int i;
+ struct symbol *sym;
+ for_all_symbols(i, sym) {
+ 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;
+
+ struct pexpr *sel_y = pexpr_implies(
+ pexf(sym->fexpr_sel_y),
+ pexf(sym->fexpr_y));
+ sym_add_constraint(sym, sel_y);
+
+ struct pexpr *c1 = pexpr_implies(
+ pexf(sym->fexpr_sel_y),
+ sym->list_sel_y);
+ sym_add_constraint(sym, c1);
+
+ /* only continue for tristates */
+ if (sym->type == S_BOOLEAN)
+ continue;
+
+ struct pexpr *sel_m = pexpr_implies(
+ pexf(sym->fexpr_sel_m),
+ sym_get_fexpr_both(sym));
+ sym_add_constraint(sym, sel_m);
+
+ struct pexpr *c2 = pexpr_implies(
+ pexf(sym->fexpr_sel_m),
+ sym->list_sel_m);
+ sym_add_constraint(sym, c2);
+ }
+}
+
+/*
+ * build constraints for non-booleans
+ */
+static void get_constraints_nonbool(void)
+{
+ unsigned int i;
+ struct symbol *sym;
+ for_all_symbols(i, 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);
+
+ /* build the range constraints for int/hex */
+ if (sym->type == S_HEX || sym->type == S_INT)
+ sym_add_range_constraints(sym);
+
+ /* build constraints for dependencies for non-booleans */
+ if (sym->dir_dep.expr)
+ add_dependencies_nonbool(sym);
+
+ /* build invisible constraints */
+ add_invisible_constraints(sym);
+
+ /* exactly one of the symbols must be true */
+ sym_nonbool_at_least_1(sym);
+ sym_nonbool_at_most_1(sym);
+ }
+}
+
+/*
+ * enforce tristate constraints
+ */
+static void build_tristate_constraint_clause(struct symbol *sym)
+{
+ if (sym->type != S_TRISTATE)
+ return;
+
+ struct pexpr *X = pexf(sym->fexpr_y), *X_m = pexf(sym->fexpr_m), *modules = pexf(modules_sym->fexpr_y);
+
+ /* -X v -X_m */
+ struct pexpr *c = pexpr_or(pexpr_not(X), pexpr_not(X_m));
+ sym_add_constraint(sym, c);
+
+ /* X_m -> MODULES */
+ if (modules_sym->fexpr_y != NULL) {
+ struct pexpr *c2 = pexpr_implies(X_m, modules);
+ sym_add_constraint(sym, c2);
+ }
+}
+
+/*
+ * build the select constraints
+ * - RDep(X) implies X
+ */
+static void add_selects_kcr(struct symbol *sym)
+{
+ struct pexpr *rdep_y = expr_calculate_pexpr_y(sym->rev_dep.expr);
+ struct pexpr *c1 = pexpr_implies(rdep_y, pexf(sym->fexpr_y));
+ sym_add_constraint(sym, c1);
+
+ struct pexpr *rdep_both = expr_calculate_pexpr_both(sym->rev_dep.expr);
+ struct pexpr *c2 = pexpr_implies(rdep_both, sym_get_fexpr_both(sym));
+ sym_add_constraint(sym, c2);
+}
+
+/*
+ * build the select constraints simplified
+ * - RDep(X) implies X
+ */
+static void add_selects(struct symbol *sym)
+{
+ if (!sym_is_boolean(sym))
+ return;
+
+ struct property *p;
+ for_all_properties(sym, p, P_SELECT) {
+ struct symbol *selected = p->expr->left.sym;
+
+ if (selected->type == S_UNKNOWN)
+ continue;
+
+ if (!selected->rev_dep.expr)
+ continue;
+
+ struct pexpr *cond_y = pexf(const_true);
+ struct pexpr *cond_both = pexf(const_true);
+ if (p->visible.expr) {
+ cond_y = expr_calculate_pexpr_y(p->visible.expr);
+ cond_both = expr_calculate_pexpr_both(p->visible.expr);
+ }
+
+ if (selected->type == S_BOOLEAN) {
+ /* imply that symbol is selected to y */
+ struct pexpr *e1 = pexpr_and(cond_both, sym_get_fexpr_both(sym));
+ struct pexpr *c1 = pexpr_implies(e1, pexf(selected->fexpr_sel_y));
+ sym_add_constraint(selected, c1);
+
+ if (selected->list_sel_y == NULL)
+ selected->list_sel_y = e1;
+ else
+ selected->list_sel_y = pexpr_or(selected->list_sel_y, e1);
+ }
+
+ if (selected->type == S_TRISTATE) {
+ /* imply that symbol is selected to y */
+ struct pexpr *e2 = pexpr_and(cond_y, pexf(sym->fexpr_y));
+ struct pexpr *c2 = pexpr_implies(e2, pexf(selected->fexpr_sel_y));
+ sym_add_constraint(selected, c2);
+
+ if (selected->list_sel_y == NULL)
+ selected->list_sel_y = e2;
+ else
+ selected->list_sel_y = pexpr_or(selected->list_sel_y, e2);
+
+ /* imply that symbol is selected to m */
+ struct pexpr *e3 = pexpr_and(cond_both, sym_get_fexpr_both(sym));
+ struct pexpr *c3 = pexpr_implies(e3, pexf(selected->fexpr_sel_m));
+ sym_add_constraint(selected, c3);
+
+ if (selected->list_sel_m == NULL)
+ selected->list_sel_m = e3;
+ else
+ selected->list_sel_m = pexpr_or(selected->list_sel_m, e3);
+ }
+ }
+}
+
+/*
+ * build the dependency constraints for booleans
+ * - X implies Dep(X) or RDep(X)
+ */
+static void add_dependencies_bool(struct symbol *sym)
+{
+ if (!sym_is_boolean(sym) || !sym->dir_dep.expr)
+ return;
+
+ struct pexpr *dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr);
+
+ if (sym->type == S_TRISTATE) {
+ struct pexpr *dep_y = expr_calculate_pexpr_y(sym->dir_dep.expr);
+ struct pexpr *sel_y = sym->rev_dep.expr ? pexf(sym->fexpr_sel_y) : pexf(const_false);
+
+ struct pexpr *c1 = pexpr_implies(pexf(sym->fexpr_y), pexpr_or(dep_y, sel_y));
+
+ sym_add_constraint(sym, c1);
+
+ struct pexpr *c2 = pexpr_implies(pexf(sym->fexpr_m), pexpr_or(dep_both, sym_get_fexpr_sel_both(sym)));
+
+ sym_add_constraint(sym, c2);
+ } else if (sym->type == S_BOOLEAN) {
+ struct pexpr *c = pexpr_implies(pexf(sym->fexpr_y), pexpr_or(dep_both, sym_get_fexpr_sel_both(sym)));
+ sym_add_constraint(sym, c);
+ }
+}
+
+/*
+ * build the dependency constraints for booleans (KCR)
+ * - X implies Dep(X) or RDep(X)
+ */
+static void add_dependencies_bool_kcr(struct symbol *sym)
+{
+ if (!sym_is_boolean(sym) || !sym->dir_dep.expr)
+ return;
+
+ struct pexpr *dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr);
+
+ struct pexpr *sel_both = sym->rev_dep.expr ? expr_calculate_pexpr_both(sym->rev_dep.expr) : pexf(const_false);
+
+ if (sym->type == S_TRISTATE) {
+ struct pexpr *dep_y = expr_calculate_pexpr_y(sym->dir_dep.expr);
+ struct pexpr *sel_y = sym->rev_dep.expr ? expr_calculate_pexpr_y(sym->rev_dep.expr) : pexf(const_false);
+ struct pexpr *c1 = pexpr_implies(pexf(sym->fexpr_y), pexpr_or(dep_y, sel_y));
+ sym_add_constraint(sym, c1);
+
+ struct pexpr *c2 = pexpr_implies(pexf(sym->fexpr_m), pexpr_or(dep_both, sel_both));
+ sym_add_constraint(sym, c2);
+ } else if (sym->type == S_BOOLEAN) {
+ struct pexpr *c = pexpr_implies(pexf(sym->fexpr_y), pexpr_or(dep_both, sel_both));
+ sym_add_constraint(sym, c);
+ }
+}
+
+/*
+ * build the dependency constraints for non-booleans
+ * X_i implies Dep(X)
+ */
+static void add_dependencies_nonbool(struct symbol *sym)
+{
+ if (!sym_is_nonboolean(sym) || !sym->dir_dep.expr || sym->rev_dep.expr)
+ return;
+
+ struct pexpr *dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr);
+
+ struct pexpr *nb_vals = pexf(const_false);
+ struct fexpr_node *node;
+ /* 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));
+ }
+
+ struct pexpr *c = pexpr_implies(nb_vals, dep_both);
+ sym_add_constraint(sym, c);
+}
+
+/*
+ * build the constraints for the choice prompt
+ */
+static void add_choice_prompt_cond(struct symbol* sym)
+{
+ if (!sym_is_boolean(sym))
+ return;
+
+ struct property *prompt = sym_get_prompt(sym);
+ if (prompt == NULL)
+ return;
+
+ struct pexpr *promptCondition = prompt->visible.expr ? expr_calculate_pexpr_both(prompt->visible.expr) : pexf(const_true);
+
+ struct pexpr *fe_both = sym_get_fexpr_both(sym);
+
+ if (!sym_is_optional(sym)) {
+ struct pexpr *req_cond = pexpr_implies(promptCondition, fe_both);
+ sym_add_constraint(sym, req_cond);
+ }
+
+ struct pexpr *pr_cond = pexpr_implies(fe_both, promptCondition);
+ sym_add_constraint(sym, pr_cond);
+}
+
+/*
+ * build constraints for dependencies (choice symbols and options)
+ */
+static void add_choice_dependencies(struct symbol *sym)
+{
+ if (!sym_is_choice(sym) || !sym_is_choice_value(sym))
+ return;
+
+ struct property *prompt = sym_get_prompt(sym);
+ if (prompt == NULL)
+ return;
+
+ struct expr *to_parse;
+ 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;
+ }
+
+ struct pexpr *dep_both = expr_calculate_pexpr_both(to_parse);
+
+ if (sym->type == S_TRISTATE) {
+ struct pexpr *dep_y = expr_calculate_pexpr_y(to_parse);
+ struct pexpr *c1 = pexpr_implies(pexf(sym->fexpr_y), dep_y);
+ sym_add_constraint_eq(sym, c1);
+
+ struct pexpr *c2 = pexpr_implies(pexf(sym->fexpr_m), dep_both);
+ sym_add_constraint_eq(sym, c2);
+ } else if (sym->type == S_BOOLEAN) {
+ struct pexpr *c = pexpr_implies(pexf(sym->fexpr_y), dep_both);
+ sym_add_constraint_eq(sym, c);
+ }
+}
+
+/*
+ * build constraints for the choice groups
+ */
+static void add_choice_constraints(struct symbol *sym)
+{
+ if (!sym_is_boolean(sym))
+ return;
+
+ struct property *prompt = sym_get_prompt(sym);
+ if (prompt == NULL)
+ return;
+
+ struct symbol *choice, *choice2;
+ struct sym_node *node, *node2;
+
+ /* create list of all choice options */
+ struct sym_list *items = sym_list_init();
+ /* create list of choice options with a prompt */
+ struct sym_list *promptItems = sym_list_init();
+
+ struct property *prop;
+ for_all_choices(sym, prop) {
+ struct expr *expr;
+ expr_list_for_each_sym(prop->expr, expr, choice) {
+ 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 */
+ struct pexpr *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));
+ }
+ if (c1 != NULL) {
+ struct pexpr *c2 = pexpr_implies(pexf(sym->fexpr_y), c1);
+ sym_add_constraint(sym, 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), sym_get_fexpr_both(sym));
+ sym_add_constraint(sym, 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));
+ sym_add_constraint(sym, 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)
+ sym_add_constraint(sym, pexpr_not(pexf(choice->fexpr_m)));
+ }
+ }
+
+ /* all choice options are mutually exclusive for yes */
+ sym_list_for_each(node, promptItems) {
+ choice = node->elem;
+ for (node2 = node->next; node2 != NULL; node2 = node2->next) {
+ choice2 = node2->elem;
+ c1 = pexpr_or(pexpr_not(pexf(choice->fexpr_y)), pexpr_not(pexf(choice2->fexpr_y)));
+ sym_add_constraint(sym, 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) {
+ choice = node->elem;
+
+ struct sym_list *tmp = sym_list_init();
+ for (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));
+ else
+ c1 = pexpr_and(c1, pexpr_not(pexf(choice2->fexpr_m)));
+ }
+ c1 = pexpr_implies(pexf(choice->fexpr_y), c1);
+ sym_add_constraint(sym, c1);
+ }
+ }
+}
+
+/*
+ * build the constraints for invisible options such as defaults
+ */
+static void add_invisible_constraints(struct symbol *sym)
+{
+ struct property *prompt = sym_get_prompt(sym);
+
+ /* no constraints for the prompt, nothing to do here */
+ if (prompt != NULL && !prompt->visible.expr)
+ return;
+
+ struct pexpr *promptCondition_both, *promptCondition_yes, *noPromptCond;
+ if (prompt == NULL) {
+ promptCondition_both = pexf(const_false);
+ promptCondition_yes = pexf(const_false);
+ noPromptCond = pexf(const_true);
+ } else {
+ promptCondition_both = pexf(const_false);
+ promptCondition_yes = pexf(const_false);
+
+ /* some symbols have multiple prompts */
+ struct property *p;
+ for_all_prompts(sym, p) {
+ promptCondition_both = pexpr_or(promptCondition_both,
+ expr_calculate_pexpr_both(p->visible.expr));
+ promptCondition_yes = pexpr_or(promptCondition_yes,
+ expr_calculate_pexpr_y(p->visible.expr));
+ }
+ noPromptCond = pexpr_not(promptCondition_both);
+ }
+
+ struct pexpr *npc;
+ if (NPC_OPTIMISATION) {
+ struct fexpr * npc_fe = fexpr_create(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);
+
+ npc = pexf(npc_fe);
+
+ struct pexpr *c = pexpr_implies(noPromptCond, npc);
+
+ if (!sym_is_choice_value(sym) && !sym_is_choice(sym))
+ sym_add_constraint(sym, c);
+ } else {
+ npc = noPromptCond;
+ }
+
+ struct defm_list *defaults = get_defaults(sym);
+ struct pexpr *default_y = get_default_y(defaults);
+ struct pexpr *default_m = get_default_m(defaults);
+ struct pexpr *default_both = pexpr_or(default_y, default_m);
+
+ /* 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));
+ sym_add_constraint(sym, 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;
+ if (sym->fexpr_sel_y != NULL) {
+ sel_y = pexpr_implies(pexf(sym->fexpr_y), pexf(sym->fexpr_sel_y));
+ sel_m = pexpr_implies(pexf(sym->fexpr_m), pexf(sym->fexpr_sel_m));
+ sel_both = pexpr_implies(pexf(sym->fexpr_y), pexpr_or(pexf(sym->fexpr_sel_m), pexf(sym->fexpr_sel_y)));
+ } else {
+ sel_y = pexpr_not(pexf(sym->fexpr_y));
+ sel_m = pexpr_not(pexf(sym->fexpr_m));
+ sel_both = sel_y;
+ }
+
+ struct pexpr *c1 = pexpr_implies(pexpr_not(default_y), sel_y);
+ struct pexpr *c2 = pexpr_implies(pexf(modules_sym->fexpr_y), c1);
+ struct pexpr *c3 = pexpr_implies(npc, c2);
+ sym_add_constraint(sym, c3);
+
+ struct pexpr *d1 = pexpr_implies(pexpr_not(default_m), sel_m);
+ struct pexpr *d2 = pexpr_implies(pexf(modules_sym->fexpr_y), d1);
+ struct pexpr *d3 = pexpr_implies(npc, d2);
+ sym_add_constraint(sym, d3);
+
+ struct pexpr *e1 = pexpr_implies(pexpr_not(default_both), sel_both);
+ struct pexpr *e2 = pexpr_implies(pexpr_not(pexf(modules_sym->fexpr_y)), e1);
+ struct pexpr *e3 = pexpr_implies(npc, e2);
+ sym_add_constraint(sym, e3);
+ } else if (sym->type == S_BOOLEAN) {
+ struct pexpr *sel_y;
+ if (sym->fexpr_sel_y != NULL)
+ sel_y = pexpr_implies(pexf(sym->fexpr_y), pexf(sym->fexpr_sel_y)); //sym->fexpr_sel_y;
+ else
+ sel_y = pexpr_not(pexf(sym->fexpr_y));
+
+ struct pexpr *e1 = pexpr_implies(pexpr_not(default_both), sel_y);
+ struct pexpr *e2 = pexpr_implies(npc, e1);
+
+ sym_add_constraint_eq(sym, e2);
+ } else {
+ struct pexpr *default_any = get_default_any(sym);
+ struct pexpr *e1 = pexf(const_true);
+ struct fexpr_node *node;
+ for (node = sym->nb_vals->head->next; node != NULL; node = node->next)
+ e1 = pexpr_and(e1, pexpr_not(pexf(node->elem)));
+
+ struct pexpr *e2 = pexpr_implies(pexpr_not(default_any), e1);
+ struct pexpr *e3 = pexpr_implies(npc, e2);
+
+ sym_add_constraint(sym, e3);
+ }
+
+ /* if invisible and on by default, then a symbol can only be deactivated by its dependencies */
+ if (sym->type == S_TRISTATE) {
+ if (defaults->size == 0)
+ return;
+
+ struct pexpr *e1 = pexpr_implies(npc, pexpr_implies(default_y, pexf(sym->fexpr_y)));
+ sym_add_constraint(sym, e1);
+
+ struct pexpr *e2 = pexpr_implies(npc, pexpr_implies(default_m, sym_get_fexpr_both(sym)));
+ sym_add_constraint(sym, e2);
+ } else if (sym->type == S_BOOLEAN) {
+ if (defaults->size == 0)
+ return;
+
+ struct pexpr *c = pexpr_implies(default_both, pexf(sym->fexpr_y));
+
+ // TODO tristate choice hack
+
+ struct pexpr *c2 = pexpr_implies(npc, c);
+ sym_add_constraint(sym, c2);
+ } else {
+ 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)));
+ sym_add_constraint(sym, c);
+ }
+ }
+}
+
+/*
+ * add the known values from the default and range properties
+ */
+static void sym_add_nonbool_values_from_default_range(struct symbol *sym)
+{
+ 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);
+ }
+
+ 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);
+ sym_create_nonbool_fexpr(sym, p->expr->right.sym->name);
+ }
+}
+
+/*
+ * build the range constraints for int/hex
+ */
+static void sym_add_range_constraints(struct symbol *sym)
+{
+ struct property *prop;
+ struct pexpr *prevs, *propCond;
+ struct pexpr_list *prevCond = pexpr_list_init();
+ for_all_properties(sym, prop, P_RANGE) {
+ if (prop == NULL)
+ continue;
+
+ prevs = pexf(const_true);
+ propCond = prop_get_condition(prop);
+
+ if (prevCond->size == 0) {
+ prevs = propCond;
+ } else {
+ struct pexpr_node *node;
+ pexpr_list_for_each(node, prevCond)
+ prevs = pexpr_and(pexpr_not(node->elem), prevs);
+
+ prevs = pexpr_and(propCond, prevs);
+ }
+ pexpr_list_add(prevCond, propCond);
+
+ int base;
+ long long range_min, range_max, tmp;
+
+ 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' */
+ struct fexpr_node *node;
+ fexpr_list_for_each(node, sym->nb_vals) {
+ 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;
+
+ struct pexpr *not_nb_val = pexpr_not(pexf(node->elem));
+ if (tmp < range_min) {
+ struct pexpr *c = pexpr_implies(prevs, not_nb_val);
+ sym_add_constraint(sym, c);
+ }
+
+ if (tmp > range_max) {
+ struct pexpr *c = pexpr_implies(prevs, not_nb_val);
+ sym_add_constraint(sym, c);
+ }
+ }
+ }
+}
+
+/*
+ * 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)
+{
+ if (!sym_is_nonboolean(sym))
+ return;
+
+ struct pexpr *e = NULL;
+ struct fexpr_node *node;
+ fexpr_list_for_each(node, sym->nb_vals) {
+ if (node->prev == NULL)
+ e = pexf(node->elem);
+ else
+ e = pexpr_or(e, pexf(node->elem));
+ }
+ sym_add_constraint(sym, 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)
+{
+ if (!sym_is_nonboolean(sym))
+ return;
+
+ struct pexpr *e1, *e2;
+ struct fexpr_node *node1, *node2;
+ fexpr_list_for_each(node1, sym->nb_vals) {
+ e1 = pexf(node1->elem);
+ for (node2 = node1->next; node2 != NULL; node2 = node2->next) {
+ e2 = pexf(node2->elem);
+ struct pexpr *e = pexpr_or(pexpr_not(e1), pexpr_not(e2));
+ sym_add_constraint(sym, e);
+ }
+ }
+}
+
+/*
+ * a visible prompt for a non-boolean implies a value for the symbol
+ */
+static void sym_add_nonbool_prompt_constraint(struct symbol *sym)
+{
+ struct property *prompt = sym_get_prompt(sym);
+ if (prompt == NULL)
+ return;
+
+ struct pexpr *promptCondition = prop_get_condition(prompt);
+ struct pexpr *n = pexf(sym_get_nonbool_fexpr(sym, "n"));
+
+ if (n->type != PE_SYMBOL)
+ return;
+ if (n->left.fexpr == NULL)
+ return;
+
+ struct pexpr *c = pexpr_implies(promptCondition, pexpr_not(n));
+
+ sym_add_constraint(sym, c);
+}
+
+static struct default_map * create_default_map_entry(struct fexpr *val, struct pexpr *e)
+{
+ struct default_map *map = malloc(sizeof(struct default_map));
+ map->val = val;
+ map->e = e;
+
+ return map;
+}
+
+static struct pexpr * findDefaultEntry(struct fexpr *val, struct defm_list *defaults)
+{
+ struct defm_node *node;
+ defm_list_for_each(node, defaults)
+ if (val == node->elem->val)
+ return node->elem->e;
+
+ return pexf(const_false);
+}
+
+/* add a default value to the list */
+
+/*
+ * return all defaults for 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");
+}
+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) {
+ map->e = entry->e;
+ 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) {
+ map->e = entry->e;
+ return;
+ }
+ }
+ defm_list_add(defaults, entry);
+ }
+}
+static void updateDefaultList(struct fexpr *val, struct pexpr *newCond, struct defm_list *result, struct symbol *sym)
+{
+ struct pexpr *prevCond = findDefaultEntry(val, result);
+ struct pexpr *cond = pexpr_or(prevCond, pexpr_and(newCond, pexpr_not(covered)));
+ struct default_map *entry = create_default_map_entry(val, cond);
+ add_to_default_map(result, entry, sym);
+ covered = pexpr_or(covered, newCond);
+}
+static void add_defaults(struct prop_list *defaults, struct expr *ctx, struct defm_list *result, struct symbol *sym)
+{
+ struct prop_node *node;
+ struct property *p;
+ struct expr *expr;
+
+ prop_list_for_each(node, defaults) {
+ p = node->elem;
+ if (p->visible.expr) {
+ if (ctx == NULL)
+ expr = p->visible.expr;
+ else
+ expr = expr_alloc_and(p->visible.expr, ctx);
+ } else {
+ if (ctx == NULL)
+ expr = expr_alloc_symbol(&symbol_yes);
+ else
+ expr = expr_alloc_and(expr_alloc_symbol(&symbol_yes), 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);
+ struct pexpr *expr_m = expr_calculate_pexpr_m(expr);
+
+ updateDefaultList(symbol_yes_fexpr, expr_y, result, sym);
+ updateDefaultList(symbol_mod_fexpr, expr_m, result, sym);
+ }
+ /* if def.value = n/m/y */
+ else if (p->expr->type == E_SYMBOL && sym_is_tristate_constant(p->expr->left.sym)) {
+ struct fexpr *s;
+ if (p->expr->left.sym == &symbol_yes)
+ s = symbol_yes_fexpr;
+ else if (p->expr->left.sym == &symbol_mod)
+ s = symbol_mod_fexpr;
+ else
+ s = symbol_no_fexpr;
+
+ updateDefaultList(s, expr_calculate_pexpr_both(expr), result, sym);
+ }
+ /* 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;
+ if (!strcmp(p->expr->left.sym->name, "0"))
+ s = symbol_no_fexpr;
+ else if (!strcmp(p->expr->left.sym->name, "1"))
+ s = symbol_mod_fexpr;
+ else
+ s = symbol_yes_fexpr;
+
+ updateDefaultList(s, expr_calculate_pexpr_both(expr), result, sym);
+ }
+ /* 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);
+ updateDefaultList(s, expr_calculate_pexpr_both(expr), result, sym);
+ }
+ /* any expression which evaluates to n/m/y for a tristate */
+ else if (sym->type == S_TRISTATE) {
+ struct expr *e_tmp = expr_alloc_and(p->expr, expr);
+ struct pexpr *expr_y = expr_calculate_pexpr_y(e_tmp);
+ struct pexpr *expr_m = expr_calculate_pexpr_m(e_tmp);
+ updateDefaultList(symbol_yes_fexpr, expr_y, result, sym);
+ updateDefaultList(symbol_mod_fexpr, expr_m, result, sym);
+ }
+ /* 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;
+ 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);
+ }
+ /* any expression which evaluates to n/m/y */
+ else {
+ struct expr *e_tmp = expr_alloc_and(p->expr, expr);
+ struct pexpr *expr_both = expr_calculate_pexpr_both(e_tmp);
+ updateDefaultList(symbol_yes_fexpr, expr_both, result, sym);
+ }
+ }
+}
+static struct defm_list * get_defaults(struct symbol *sym)
+{
+ struct defm_list *result = defm_list_init();
+ covered = pexf(const_false);
+
+ struct prop_list *defaults = prop_list_init();
+ struct property *p;
+ for_all_defaults(sym, p)
+ prop_list_add(defaults, p);
+
+ add_defaults(defaults, NULL, result, sym);
+
+ return result;
+}
+
+/*
+ * return the default_map for "y", False if it doesn't exist
+ */
+static struct pexpr * get_default_y(struct defm_list *list)
+{
+ 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)
+ return entry->e;
+ }
+
+ return pexf(const_false);
+}
+
+/*
+ * return the default map for "m", False if it doesn't exist
+ */
+static struct pexpr *get_default_m(struct defm_list *list)
+{
+ 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)
+ return entry->e;
+ }
+
+ return pexf(const_false);
+}
+
+/*
+ * return the constraint when _some_ default value will be applied
+ */
+static struct pexpr *get_default_any(struct symbol *sym)
+{
+ if (!sym_is_nonboolean(sym))
+ return NULL;
+
+ struct property *prop;
+ struct expr *e;
+ struct pexpr *p = pexf(const_false);
+ for_all_defaults(sym, prop) {
+ if (prop->visible.expr)
+ e = 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));
+
+ p = pexpr_or(p, expr_calculate_pexpr_y(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 i, c = 0;
+ struct symbol *sym;
+ for_all_symbols(i, 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)
+{
+ if (!constraint)
+ return;
+
+ /* no need to add that */
+ if (constraint->type == PE_SYMBOL && constraint->left.fexpr == const_true)
+ return;
+
+ /* this should never happen */
+ if (constraint->type == PE_SYMBOL && constraint->left.fexpr == const_false)
+ perror("Adding const_false.");
+
+ pexpr_list_add(sym->constraints, 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)
+{
+ if (!constraint)
+ return;
+
+ /* no need to add that */
+ if (constraint->type == PE_SYMBOL && constraint->left.fexpr == const_true)
+ return;
+
+ /* this should never happen */
+ if (constraint->type == PE_SYMBOL && constraint->left.fexpr == const_false)
+ perror("Adding const_false.");
+
+ /* check the constraints for the same symbol */
+ struct pexpr_node *node;
+ pexpr_list_for_each(node, sym->constraints)
+ if (pexpr_eq(constraint, node->elem))
+ return;
+
+ pexpr_list_add(sym->constraints, 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..dca89ca1640e
--- /dev/null
+++ b/scripts/kconfig/cf_constraints.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CF_CONSTRAINTS_H
+#define CF_CONSTRAINTS_H
+
+/* build the constraints for each symbol */
+void get_constraints(void);
+
+/* 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);
+
+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);
+
+#endif
--
2.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 07/12] Add files for handling expressions
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
` (5 preceding siblings ...)
2021-10-20 9:41 ` [RFC 06/12] Add files for building constraints Thorsten Berger
@ 2021-10-20 9:43 ` Thorsten Berger
2021-10-20 9:44 ` [RFC 08/12] Add files for RangeFix Thorsten Berger
` (4 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:43 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
scripts/kconfig/cf_expr.c | 2146 +++++++++++++++++++++++++++++++++++++
scripts/kconfig/cf_expr.h | 237 ++++
2 files changed, 2383 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..7ca0ff4bc6ad
--- /dev/null
+++ b/scripts/kconfig/cf_expr.c
@@ -0,0 +1,2146 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 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"
+
+static void create_fexpr_bool(struct symbol *sym);
+static void create_fexpr_nonbool(struct symbol *sym);
+static void create_fexpr_unknown(struct symbol *sym);
+static void create_fexpr_choice(struct symbol *sym);
+
+static void pexpr_print_util(struct pexpr *e, int prevtoken);
+
+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)
+{
+ if (sym_is_choice(sym))
+ create_fexpr_choice(sym);
+ else if (sym_is_boolean(sym))
+ create_fexpr_bool(sym);
+ else if (sym_is_nonboolean(sym))
+ create_fexpr_nonbool(sym);
+ else
+ create_fexpr_unknown(sym);
+}
+
+/*
+ * create the fexpr for symbols with reverse dependencies
+ */
+static void create_fexpr_selected(struct symbol *sym)
+{
+ /* fexpr_sel_y */
+ struct fexpr *fexpr_sel_y = fexpr_create(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);
+
+ sym->fexpr_sel_y = fexpr_sel_y;
+
+ /* fexpr_sel_m */
+ if (sym->type == S_BOOLEAN)
+ return;
+ struct fexpr *fexpr_sel_m = fexpr_create(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);
+
+ sym->fexpr_sel_m = fexpr_sel_m;
+}
+
+/*
+ * create the fexpr for a boolean/tristate symbol
+ */
+static void create_fexpr_bool(struct symbol *sym)
+{
+ struct fexpr *fexpr_y = fexpr_create(sat_variable_nr++, FE_SYMBOL, sym->name);
+ fexpr_y->sym = sym;
+ fexpr_y->tri = yes;
+ fexpr_add_to_satmap(fexpr_y);
+
+ sym->fexpr_y = fexpr_y;
+
+ struct fexpr *fexpr_m;
+ if (sym->type == S_TRISTATE) {
+ fexpr_m = fexpr_create(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);
+ } else {
+ fexpr_m = const_false;
+ }
+
+ sym->fexpr_m = fexpr_m;
+
+ if (sym->rev_dep.expr)
+ create_fexpr_selected(sym);
+}
+
+/*
+ * create the fexpr for a non-boolean symbol
+ */
+static void create_fexpr_nonbool(struct symbol *sym)
+{
+ sym->fexpr_y = const_false;
+ sym->fexpr_m = const_false;
+ sym->nb_vals = fexpr_list_init();
+
+ /* default values */
+ char int_values[][2] = {"n", "0", "1"};
+ char hex_values[][4] = {"n", "0x0", "0x1"};
+ char string_values[][9] = {"n", "", "nonempty"};
+
+ int i;
+ for (i = 0; i < 3; i++) {
+ struct fexpr *e = fexpr_create(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);
+ }
+}
+
+
+/*
+ * set fexpr_y and fexpr_m simply to False
+ */
+static void create_fexpr_unknown(struct symbol *sym)
+{
+ sym->fexpr_y = const_false;
+ sym->fexpr_m = const_false;
+}
+
+
+/*
+ * create the fexpr for a choice symbol
+ */
+static void create_fexpr_choice(struct symbol *sym)
+{
+ if (!sym_is_boolean(sym))
+ return;
+
+ struct property *prompt = sym_get_prompt(sym);
+ if (prompt == NULL) {
+ perror("Choice symbol should have a prompt.");
+ return;
+ }
+
+ char *name = strdup(prompt->text);
+
+ /* remove spaces */
+ char *write = name, *read = name;
+ do {
+ if (*read != ' ')
+ *write++ = *read;
+ } while (*read++);
+
+ struct fexpr *fexpr_y = fexpr_create(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);
+
+ sym->fexpr_y = fexpr_y;
+
+ struct fexpr *fexpr_m;
+ if (sym->type == S_TRISTATE) {
+ fexpr_m = fexpr_create(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);
+ } else {
+ fexpr_m = const_false;
+ }
+ sym->fexpr_m = fexpr_m;
+}
+
+/*
+ * 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)
+{
+ if (!sym || !compval)
+ return pexf(const_false);
+
+ int base = 0;
+ switch (sym->type) {
+ case S_INT:
+ base = 10;
+ break;
+ case S_HEX:
+ base = 16;
+ break;
+ default:
+ break;
+ }
+
+ struct pexpr *c = pexf(const_false);
+ long val = strtol(compval->name, NULL, base);
+
+ struct fexpr_node *node;
+ struct fexpr *fe;
+ for (node = sym->nb_vals->head->next; node != NULL; node = node->next) {
+ fe = node->elem;
+ long symval = strtol(str_get(&fe->nb_val), NULL, base);
+
+ switch (type) {
+ case E_LTH:
+ if (symval < val)
+ c = pexpr_or(c, pexf(fe));
+ break;
+ case E_LEQ:
+ if (symval <= val)
+ c = pexpr_or(c, pexf(fe));
+ break;
+ case E_GTH:
+ if (symval > val)
+ c = pexpr_or(c, pexf(fe));
+ break;
+ case E_GEQ:
+ if (symval >= val)
+ c = pexpr_or(c, pexf(fe));
+ 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)
+{
+ if (!left || !right)
+ return pexf(const_false);
+
+ if (!sym_is_boolean(left) || !sym_is_boolean(right)) {
+ perror("Comparing 2 symbols that should be boolean.");
+ return pexf(const_false);
+ }
+
+ struct pexpr *c = pexf(const_false);
+ switch (type) {
+ case E_LTH:
+ c = pexpr_and(
+ pexpr_not(sym_get_fexpr_both(left)),
+ sym_get_fexpr_both(right));
+ if (left->type == S_TRISTATE)
+ c = pexpr_or(c,
+ pexpr_and
+ (pexf(left->fexpr_m),
+ pexf(right->fexpr_y)));
+ break;
+ case E_LEQ:
+ c = pexpr_and(pexf(left->fexpr_y), pexf(right->fexpr_y));
+ if (left->type == S_TRISTATE)
+ c = pexpr_or(c,
+ pexpr_and(
+ pexf(left->fexpr_m),
+ sym_get_fexpr_both(right)));
+ c = pexpr_or(c, pexpr_not(sym_get_fexpr_both(left)));
+ break;
+ case E_GTH:
+ c = pexpr_and(
+ sym_get_fexpr_both(left),
+ pexpr_not(sym_get_fexpr_both(right)));
+ if (right->type == S_TRISTATE)
+ c = pexpr_or(c,
+ pexpr_and
+ (pexf(left->fexpr_y),
+ pexf(right->fexpr_m)));
+ break;
+ case E_GEQ:
+ c = pexpr_and(pexf(left->fexpr_y), pexf(right->fexpr_y));
+ if (right->type == S_TRISTATE)
+ c = pexpr_or(c,
+ pexpr_and(
+ sym_get_fexpr_both(left),
+ pexf(right->fexpr_m)));
+ c = pexpr_or(c, pexpr_not(sym_get_fexpr_both(right)));
+ break;
+ default:
+ perror("Wrong type in expr_eval_unequal_bool.");
+ }
+
+ return c;
+}
+/*
+ * calculate, when expr will evaluate to yes or mod
+ */
+struct pexpr * expr_calculate_pexpr_both(struct expr *e)
+{
+ if (!e)
+ return pexf(const_false);
+
+ if (!expr_can_evaluate_to_mod(e))
+ return expr_calculate_pexpr_y(e);
+
+ switch (e->type) {
+ case E_SYMBOL:
+ return pexpr_or(expr_calculate_pexpr_m(e), expr_calculate_pexpr_y(e));
+ case E_AND:
+ return expr_calculate_pexpr_both_and(e->left.expr, e->right.expr);
+ case E_OR:
+ return expr_calculate_pexpr_both_or(e->left.expr, e->right.expr);
+ case E_NOT:
+ return pexpr_or(expr_calculate_pexpr_m(e), expr_calculate_pexpr_y(e));
+ case E_EQUAL:
+ return expr_calculate_pexpr_y_equals(e);
+ case E_UNEQUAL:
+ return expr_calculate_pexpr_y_unequals(e);
+ case E_LTH:
+ case E_LEQ:
+ case E_GTH:
+ case E_GEQ:
+ return expr_calculate_pexpr_y_comp(e);
+ default:
+ // TODO
+ perror("Unhandled type - expr_calculate_pexpr_both");
+ return NULL;
+ }
+}
+
+/*
+ * calculate, when expr will evaluate to yes
+ */
+struct pexpr * expr_calculate_pexpr_y(struct expr *e){
+ 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);
+ case E_OR:
+ return expr_calculate_pexpr_y_or(e->left.expr, e->right.expr);
+ case E_NOT:
+ return expr_calculate_pexpr_y_not(e->left.expr);
+ case E_EQUAL:
+ return expr_calculate_pexpr_y_equals(e);
+ case E_UNEQUAL:
+ return expr_calculate_pexpr_y_unequals(e);
+ case E_LTH:
+ case E_LEQ:
+ case E_GTH:
+ case E_GEQ:
+ return expr_calculate_pexpr_y_comp(e);
+ default:
+ perror("Unhandled type - expr_calculate_pexpr_y");
+ return NULL;
+ }
+}
+
+/*
+ * calculate, when expr will evaluate to mod
+ */
+struct pexpr * expr_calculate_pexpr_m(struct expr *e){
+ if (!e)
+ return NULL;
+
+ if (!expr_can_evaluate_to_mod(e))
+ return pexf(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);
+ case E_OR:
+ return expr_calculate_pexpr_m_or(e->left.expr, e->right.expr);
+ case E_NOT:
+ return expr_calculate_pexpr_m_not(e->left.expr);
+ 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)
+{
+ return pexpr_and(expr_calculate_pexpr_y(a), expr_calculate_pexpr_y(b));
+}
+
+/*
+ * 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 pexpr *topright = pexpr_not(pexpr_and(expr_calculate_pexpr_y(a), expr_calculate_pexpr_y(b)));
+ struct pexpr *ll_left = pexpr_or(expr_calculate_pexpr_y(a), expr_calculate_pexpr_m(a));
+ struct pexpr *ll_right = pexpr_or(expr_calculate_pexpr_y(b), expr_calculate_pexpr_m(b));
+ struct pexpr *topleft = pexpr_and(ll_left, ll_right);
+
+ return pexpr_and(topleft, topright);
+}
+
+/*
+ * 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 pexpr *left = pexpr_or(expr_calculate_pexpr_y(a), expr_calculate_pexpr_m(a));
+ struct pexpr *right = pexpr_or(expr_calculate_pexpr_y(b), expr_calculate_pexpr_m(b));
+ return pexpr_and(left, right);
+}
+
+/*
+ * 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)
+{
+ return pexpr_or(expr_calculate_pexpr_y(a), expr_calculate_pexpr_y(b));
+}
+
+/*
+ * 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 pexpr *topright = pexpr_not(expr_calculate_pexpr_y(b));
+ struct pexpr *lowerleft = pexpr_or(expr_calculate_pexpr_m(a), expr_calculate_pexpr_m(b));
+ struct pexpr *topleft = pexpr_and(lowerleft, pexpr_not(expr_calculate_pexpr_y(a)));
+
+ return pexpr_and(topleft, topright);
+}
+
+/*
+ * 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 pexpr *left = pexpr_or(expr_calculate_pexpr_y(a), expr_calculate_pexpr_m(a));
+ struct pexpr *right = pexpr_or(expr_calculate_pexpr_y(b), expr_calculate_pexpr_m(b));
+ return pexpr_or(left, right);
+}
+
+/*
+ * calculate, when expr of type NOT will evaluate to yes
+ * !(A || A_m)
+ */
+struct pexpr * expr_calculate_pexpr_y_not(struct expr * e)
+{
+ return pexpr_not(pexpr_or(expr_calculate_pexpr_y(e), expr_calculate_pexpr_m(e)));
+}
+
+/*
+ * calculate, when expr of type NOT will evaluate to mod
+ * A_m
+ */
+struct pexpr * expr_calculate_pexpr_m_not(struct expr * e)
+{
+ return expr_calculate_pexpr_m(e);
+}
+
+static struct pexpr * equiv_pexpr(struct pexpr *a, struct pexpr *b)
+{
+ struct pexpr *yes = pexpr_and(a, b);
+ struct pexpr *not = pexpr_and(pexpr_not(a), pexpr_not(b));
+
+ return pexpr_or(yes, not);
+}
+
+/*
+ * create the fexpr of a non-boolean symbol for a specific value
+ */
+struct fexpr * sym_create_nonbool_fexpr(struct symbol *sym, char *value)
+{
+
+ if (!strcmp(value, "")) {
+ if (sym->type == S_STRING)
+ return sym->nb_vals->head->next->elem;
+ else
+ return sym->nb_vals->head->elem;
+ }
+
+ struct fexpr *e = sym_get_nonbool_fexpr(sym, value);
+
+ /* fexpr already exists */
+ if (e != NULL)
+ return e;
+
+ char *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(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);
+
+ 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 fexpr *e = sym_get_nonbool_fexpr(sym, value);
+
+ if (e != NULL)
+ return e;
+ else
+ return sym_create_nonbool_fexpr(sym, value);
+}
+
+/*
+ * calculate, when expr of type EQUAL will evaluate to yes
+ */
+struct pexpr * expr_calculate_pexpr_y_equals(struct expr *e)
+{
+ /* 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(const_true) : pexf(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(const_true) : pexf(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(pexf(e->left.sym->fexpr_y), pexf(e->right.sym->fexpr_y));
+ struct pexpr *mod = equiv_pexpr(pexf(e->left.sym->fexpr_m), pexf(e->right.sym->fexpr_m));
+
+ return pexpr_and(yes, mod);
+ }
+
+ /* 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));
+ }
+ 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));
+
+ /* 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(const_false);
+ if (sym_is_tristate_constant(e->left.sym) && sym_is_nonboolean(e->right.sym))
+ return pexf(const_false);
+
+ /* comparing 2 nonboolean symbols */
+ if (sym_is_nonboolean(e->left.sym) && sym_is_nonboolean(e->right.sym)) {
+ struct pexpr *c = pexf(const_false);
+ struct fexpr_node *node1, *node2;
+ struct fexpr *e1, *e2;
+ for (node1 = e->left.sym->nb_vals->head->next; node1 != NULL; node1 = node1->next) {
+ e1 = node1->elem;
+ for (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)));
+ 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(const_false);
+ if (sym_is_nonbool_constant(e->left.sym) && sym_is_tristate_constant(e->right.sym))
+ return pexf(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(const_false);
+ if (sym_is_tristate_constant(e->left.sym) && e->right.sym->type == S_UNKNOWN)
+ return pexf(const_false);
+
+ /* any other comparison is not supported and should not be executed */
+ perror("Unsupported equality.");
+ print_expr(":", e, 0);
+
+ return pexf(const_false);
+}
+
+/*
+ * transform an UNEQUAL into a Not(EQUAL)
+ */
+struct pexpr * expr_calculate_pexpr_y_unequals(struct expr *e)
+{
+ return pexpr_not(expr_calculate_pexpr_y_equals(e));
+}
+
+struct pexpr * expr_calculate_pexpr_y_comp(struct expr *e)
+{
+ 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);
+ }
+ 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);
+ }
+
+ /* 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);
+
+ return pexf(const_false);
+ default:
+ perror("Unhandled type - expr_calculate_pexpr_y_comp");
+ return NULL;
+ }
+}
+
+/*
+ * macro to create a pexpr of type AND
+ */
+struct pexpr * pexpr_and(struct pexpr *a, struct pexpr *b)
+{
+ /* simplifications:
+ * expr && False -> False
+ * expr && True -> expr
+ * expr && expr -> expr
+ */
+ if (a->type == PE_SYMBOL && a->left.fexpr == const_false)
+ return a;
+
+ if (b->type == PE_SYMBOL && b->left.fexpr == const_false)
+ return b;
+
+ if (a->type == PE_SYMBOL && a->left.fexpr == const_true)
+ return b;
+
+ if (b->type == PE_SYMBOL && b->left.fexpr == const_true)
+ return a;
+
+ /* A && A -> A */
+ if (pexpr_eq(a,b))
+ return a;
+
+ /* (A && B) && C -> A && B if B == C */
+ if (a->type == PE_AND && pexpr_eq(a->right.pexpr, b))
+ return a;
+ /* A && (B && C) -> B && C if A == B */
+ if (b->type == PE_AND && pexpr_eq(a, b->left.pexpr))
+ return b;
+
+ /* (A || B) && (C || D) -> A || (B && D) if A == C */
+ if (a->type == PE_OR && b->type == PE_OR && (
+ pexpr_eq(a->left.pexpr, b->left.pexpr)
+ ))
+ return pexpr_or(a->left.pexpr,
+ pexpr_and(a->right.pexpr, b->right.pexpr));
+ /* (A || B) && (C || D) -> B || (A && C) if B == D */
+ if (a->type == PE_OR && b->type == PE_OR && (
+ pexpr_eq(a->right.pexpr, b->right.pexpr)
+ ))
+ return pexpr_or(a->right.pexpr,
+ pexpr_and(a->left.pexpr, b->left.pexpr));
+ /* (A || B) && (C || D) -> A || (B && C) if A == D */
+ if (a->type == PE_OR && b->type == PE_OR && (
+ pexpr_eq(a->left.pexpr, b->right.pexpr)
+ ))
+ return pexpr_or(a->left.pexpr,
+ pexpr_and(a->right.pexpr, b->left.pexpr));
+ /* (A || B) && (C || D) -> B || (A && D) if B == C */
+ if (a->type == PE_OR && b->type == PE_OR && (
+ pexpr_eq(a->right.pexpr, b->left.pexpr)
+ ))
+ return pexpr_or(a->right.pexpr,
+ pexpr_and(a->left.pexpr, b->right.pexpr));
+
+ struct pexpr *e = xcalloc(1, sizeof(*e));
+ e->type = PE_AND;
+ e->left.pexpr = a;
+ e->right.pexpr = b;
+
+ return e;
+}
+
+/*
+ * macro to create a pexpr of type OR
+ */
+struct pexpr * pexpr_or(struct pexpr *a, struct pexpr *b)
+{
+ /* simplifications:
+ * expr || False -> expr
+ * expr || True -> True
+ * expr || expr -> expr
+ */
+ if (a->type == PE_SYMBOL && a->left.fexpr == const_false)
+ return b;
+
+ if (b->type == PE_SYMBOL && b->left.fexpr == const_false)
+ return a;
+
+ if (a->type == PE_SYMBOL && a->left.fexpr == const_true)
+ return a;
+
+ if (b->type == PE_SYMBOL && b->left.fexpr == const_true)
+ return b;
+
+ /* A || A -> A */
+ if (pexpr_eq(a,b))
+ return a;
+
+ /* A || (B && C) -> A if (A == B || A == C) */
+ if (b->type == PE_AND && (
+ pexpr_eq(a, b->left.pexpr) || pexpr_eq(a, b->right.pexpr)
+ ))
+ return a;
+ /* (A && B) || C -> C if (A == C || B == C) */
+ if (a->type == PE_AND && (
+ pexpr_eq(a->left.pexpr, b) || pexpr_eq(a->right.pexpr, b)
+ ))
+ return b;
+
+ /* -A || B -> True if A == B */
+ if (a->type == PE_NOT && pexpr_eq(a->left.pexpr, b))
+ return pexf(const_true);
+ /* A || -B -> True if A == B */
+ if (b->type == PE_NOT && pexpr_eq(a, b->left.pexpr))
+ return pexf(const_true);
+
+ /* (A && B) || (C && D) -> A && (B || D) if (A == C) */
+ if (a->type == PE_AND && b->type == PE_AND &&
+ pexpr_eq(a->left.pexpr, b->left.pexpr)
+ )
+ return pexpr_and(a->left.pexpr,
+ pexpr_or(a->right.pexpr, b->right.pexpr));
+ /* (A && B) || (C && D) -> B && (A || C) if (B == D) */
+ if (a->type == PE_AND && b->type == PE_AND &&
+ pexpr_eq(a->right.pexpr, b->right.pexpr)
+ )
+ return pexpr_and(a->right.pexpr,
+ pexpr_or(a->left.pexpr, b->left.pexpr));
+ /* (A && B) || (C && D) -> A && (B || C) if (A == D) */
+ if (a->type == PE_AND && b->type == PE_AND &&
+ pexpr_eq(a->left.pexpr, b->right.pexpr)
+ )
+ return pexpr_and(a->left.pexpr,
+ pexpr_or(a->right.pexpr, b->left.pexpr));
+ /* (A && B) || (C && D) -> B && (A || D) if (B == C) */
+ if (a->type == PE_AND && b->type == PE_AND &&
+ pexpr_eq(a->right.pexpr, b->left.pexpr)
+ )
+ return pexpr_and(a->right.pexpr,
+ pexpr_or(a->left.pexpr, b->right.pexpr));
+
+ /* (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) ||
+ pexpr_eq(a->left.pexpr, b->right.pexpr) ||
+ pexpr_eq(a->right.pexpr, b->left.pexpr) ||
+ pexpr_eq(a->right.pexpr, b->right.pexpr)
+ ))
+ 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) ||
+ pexpr_eq(a->left.pexpr, b->right.pexpr) ||
+ pexpr_eq(a->right.pexpr, b->left.pexpr) ||
+ pexpr_eq(a->right.pexpr, b->right.pexpr)
+ ))
+ return a;
+
+ struct pexpr *e = xcalloc(1, sizeof(*e));
+ e->type = PE_OR;
+ e->left.pexpr = a;
+ e->right.pexpr = b;
+
+ return e;
+}
+
+/*
+ * macro to create a pexpr of type NOT
+ */
+struct pexpr * pexpr_not(struct pexpr *a)
+{
+ if (a->type == PE_SYMBOL && a->left.fexpr == const_false)
+ return pexf(const_true);
+ if (a->type == PE_SYMBOL && a->left.fexpr == const_true)
+ return pexf(const_false);
+
+ /* eliminate double negation */
+ if (a->type == PE_NOT)
+ return a->left.pexpr;
+
+ /* De Morgan */
+ if (a->type == PE_AND) {
+ struct pexpr *e = xcalloc(1, sizeof(*e));
+ e->type = PE_OR;
+ e->left.pexpr = pexpr_not(a->left.pexpr);
+ e->right.pexpr = pexpr_not(a->right.pexpr);
+ return e;
+ }
+ if (a->type == PE_OR) {
+ struct pexpr *e = xcalloc(1, sizeof(*e));
+ e->type = PE_AND;
+ e->left.pexpr = pexpr_not(a->left.pexpr);
+ e->right.pexpr = pexpr_not(a->right.pexpr);
+ return e;
+ }
+
+ struct pexpr *e = xcalloc(1, sizeof(*e));
+ e->type = PE_NOT;
+ e->left.pexpr = a;
+ return e;
+}
+
+/*
+ * macro to construct a pexpr for "A implies B"
+ */
+struct pexpr * pexpr_implies(struct pexpr *a, struct pexpr *b)
+{
+ /* A => B -> True if A == B */
+ if (pexpr_eq(a, b))
+ return pexf(const_true);
+
+ /* (A => B && C) -> (A => C) if A == B */
+ if (b->type == PE_AND && pexpr_eq(a, b->left.pexpr))
+ return pexpr_implies(a, b->right.pexpr);
+ /* (A => B && C) -> (A => B) if A == C */
+ if (b->type == PE_AND && pexpr_eq(a, b->right.pexpr))
+ return pexpr_implies(a, b->left.pexpr);
+
+ /* (A => B || C) -> True if (A == B || A == C) */
+ if (b->type == PE_OR && (
+ pexpr_eq(a, b->left.pexpr) || pexpr_eq(a, b->right.pexpr)
+ ))
+ return pexf(const_true);
+
+ /* (A && B => C) -> True if (A == C || B == C) */
+ if (a->type == PE_AND && (
+ pexpr_eq(a->left.pexpr, b) || pexpr_eq(a->right.pexpr, b)
+ ))
+ return pexf(const_true);
+
+ return pexpr_or(pexpr_not(a), b);
+}
+
+/*
+ * 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)
+{
+ return sym->type == S_TRISTATE ? pexpr_or(pexf(sym->fexpr_m), pexf(sym->fexpr_y)) : pexf(sym->fexpr_y);
+}
+
+/*
+ * return fexpr_sel_both for a symbol
+ */
+struct pexpr * sym_get_fexpr_sel_both(struct symbol *sym)
+{
+ if (!sym->rev_dep.expr)
+ return pexf(const_false);
+
+ return sym->type == S_TRISTATE ? pexpr_or(pexf(sym->fexpr_sel_m), pexf(sym->fexpr_sel_y)) : 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)
+{
+ return e == const_true || e == const_false;
+}
+
+/*
+ * add a fexpr to the satmap
+ */
+void fexpr_add_to_satmap(struct fexpr *e)
+{
+ if (e->satval >= satmap_size) {
+ satmap = xrealloc(satmap, satmap_size * 2 * sizeof(*satmap));
+ satmap_size *= 2;
+ }
+
+ 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)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ if (e->left.fexpr == const_false) {
+ str_append(s, "0");
+ return;
+ }
+ if (e->left.fexpr == 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:
+ /* need this hack for the FeatureExpr parser */
+ if (parent != PE_AND)
+ str_append(s, "(");
+ pexpr_as_char(e->left.pexpr, s, PE_AND);
+ str_append(s, " && ");
+ pexpr_as_char(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(e->left.pexpr, s, PE_OR);
+ str_append(s, " || ");
+ pexpr_as_char(e->right.pexpr, s, PE_OR);
+ if (parent != PE_OR)
+ str_append(s, ")");
+ return;
+ case PE_NOT:
+ str_append(s, "!");
+ pexpr_as_char(e->left.pexpr, s, PE_NOT);
+ 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:
+ /* need this hack for the FeatureExpr parser */
+ 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()
+{
+ 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()
+{
+ 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()
+{
+ 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 occurence 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 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);
+}
+
+/*
+ * simplify a pexpr in-place
+ * pexpr && False -> False
+ * pexpr && True -> pexpr
+ * pexpr || False -> pexpr
+ * pexpr || True -> True
+ */
+static struct pexpr * pexpr_eliminate_yn(struct pexpr *e)
+{
+ struct pexpr *tmp;
+
+ if (e) switch (e->type) {
+ case PE_AND:
+ e->left.pexpr = pexpr_eliminate_yn(e->left.pexpr);
+ e->right.pexpr = pexpr_eliminate_yn(e->right.pexpr);
+ if (e->left.pexpr->type == PE_SYMBOL) {
+ if (e->left.pexpr->left.fexpr == const_false) {
+ pexpr_free(e->left.pexpr);
+ pexpr_free(e->right.pexpr);
+ e->type = PE_SYMBOL;
+ e->left.fexpr = const_false;
+ e->right.pexpr = NULL;
+ return e;
+ } else if (e->left.pexpr->left.fexpr == const_true) {
+ free(e->left.pexpr);
+ tmp = e->right.pexpr;
+ *e = *(e->right.pexpr);
+ free(tmp);
+ return e;
+ }
+ }
+ if (e->right.pexpr->type == PE_SYMBOL) {
+ if (e->right.pexpr->left.fexpr == const_false) {
+ pexpr_free(e->left.pexpr);
+ pexpr_free(e->right.pexpr);
+ e->type = PE_SYMBOL;
+ e->left.fexpr = const_false;
+ e->right.fexpr = NULL;
+ return e;
+ } else if (e->right.pexpr->left.fexpr == const_true) {
+ free(e->right.pexpr);
+ tmp = e->left.pexpr;
+ *e = *(e->left.pexpr);
+ free(tmp);
+ return e;
+ }
+ }
+ break;
+ case PE_OR:
+ e->left.pexpr = pexpr_eliminate_yn(e->left.pexpr);
+ e->right.pexpr = pexpr_eliminate_yn(e->right.pexpr);
+ if (e->left.pexpr->type == PE_SYMBOL) {
+ if (e->left.pexpr->left.fexpr == const_false) {
+ free(e->left.pexpr);
+ tmp = e->right.pexpr;
+ *e = *(e->right.pexpr);
+ free(tmp);
+ return e;
+ } else if (e->left.pexpr->left.fexpr == const_true) {
+ pexpr_free(e->left.pexpr);
+ pexpr_free(e->right.pexpr);
+ e->type = PE_SYMBOL;
+ e->left.fexpr = const_true;
+ e->right.pexpr = NULL;
+ }
+ }
+ if (e->right.pexpr->type == PE_SYMBOL) {
+ if (e->right.pexpr->left.fexpr == const_false) {
+ free(e->right.pexpr);
+ tmp = e->left.pexpr;
+ *e = *(e->left.pexpr);
+ free(tmp);
+ return e;
+ } else if (e->right.pexpr->left.fexpr == const_true) {
+ pexpr_free(e->left.pexpr);
+ pexpr_free(e->right.pexpr);
+ e->type = PE_SYMBOL;
+ e->left.fexpr = const_true;
+ e->right.pexpr = NULL;
+ return e;
+ }
+ }
+ default:
+ ;
+ }
+
+ return e;
+}
+
+/*
+ * copy a pexpr
+ */
+struct pexpr * pexpr_copy(const struct pexpr *org)
+{
+ struct pexpr *e;
+
+ if (!org)
+ return NULL;
+
+ e = xmalloc(sizeof(*org));
+ memcpy(e, org, sizeof(*org));
+ switch (org->type) {
+ case PE_SYMBOL:
+ e->left = org->left;
+ break;
+ case PE_AND:
+ case PE_OR:
+ e->left.pexpr = pexpr_copy(org->left.pexpr);
+ e->right.pexpr = pexpr_copy(org->right.pexpr);
+ break;
+ case PE_NOT:
+ e->left.pexpr = pexpr_copy(org->left.pexpr);
+ break;
+ }
+
+ return e;
+}
+
+/*
+ * free a pexpr
+ */
+void pexpr_free(struct pexpr *e)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case PE_SYMBOL:
+ break;
+ case PE_AND:
+ case PE_OR:
+ pexpr_free(e->left.pexpr);
+ pexpr_free(e->right.pexpr);
+ break;
+ case PE_NOT:
+ pexpr_free(e->left.pexpr);
+ break;
+ }
+
+ free(e);
+}
+
+#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)
+{
+ /* recurse down to the leaves */
+ if (e1->type == type) {
+ __pexpr_eliminate_eq(type, &e1->left.pexpr, &e2);
+ __pexpr_eliminate_eq(type, &e1->right.pexpr, &e2);
+ return;
+ }
+ if (e2->type == type) {
+ __pexpr_eliminate_eq(type, &e1, &e2->left.pexpr);
+ __pexpr_eliminate_eq(type, &e1, &e2->right.pexpr);
+ 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 == const_true || e2->left.fexpr == const_false))
+ return;
+ if (!pexpr_eq(e1, e2))
+ return;
+
+ /* e1 and e2 are equal leaves. Prepare them for elimination. */
+ trans_count++;
+ pexpr_free(e1);
+ pexpr_free(e2);
+ switch (type) {
+ case PE_AND:
+ e1 = pexf(const_true);
+ e2 = pexf(const_true);
+ break;
+ case PE_OR:
+ e1 = pexf(const_false);
+ e2 = pexf(const_false);
+ break;
+ default:
+ ;
+ }
+}
+
+/*
+ * rewrite pexpr ep1 and ep2 to remove operands common to both
+ */
+static void pexpr_eliminate_eq(struct pexpr **ep1, struct pexpr **ep2)
+{
+ if (!e1 || !e2)
+ return;
+
+ switch (e1->type) {
+ case PE_AND:
+ case PE_OR:
+ __pexpr_eliminate_eq(e1->type, ep1, ep2);
+ default:
+ ;
+ }
+ if (e1->type != e2->type) switch (e2->type) {
+ case PE_AND:
+ case PE_OR:
+ __pexpr_eliminate_eq(e2->type, ep1, ep2);
+ default:
+ ;
+ }
+ e1 = pexpr_eliminate_yn(e1);
+ e2 = pexpr_eliminate_yn(e2);
+}
+#undef e1
+#undef e2
+
+/*
+ * check whether 2 pexpr are equal
+ */
+bool pexpr_eq(struct pexpr *e1, struct pexpr *e2)
+{
+ 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_copy(e1);
+ e2 = pexpr_copy(e2);
+ old_count = trans_count;
+ pexpr_eliminate_eq(&e1, &e2);
+ res = (e1->type == PE_SYMBOL && e2->type == PE_SYMBOL &&
+ e1->left.fexpr->satval == e2->left.fexpr->satval);
+ pexpr_free(e1);
+ pexpr_free(e2);
+ trans_count = old_count;
+ return res;
+ case PE_NOT:
+ return pexpr_eq(e1->left.pexpr, e2->left.pexpr);
+ }
+
+ 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));
+ pe->type = PE_SYMBOL;
+ pe->left.fexpr = fe;
+ return pe;
+}
+
+static struct pexpr * pexpr_join_or(struct pexpr *e1, struct pexpr *e2)
+{
+ if (pexpr_eq(e1, e2))
+ return pexpr_copy(e1);
+ else
+ return NULL;
+}
+
+static struct pexpr * pexpr_join_and(struct pexpr *e1, struct pexpr *e2)
+{
+ if (pexpr_eq(e1, e2))
+ return pexpr_copy(e1);
+ else
+ return NULL;
+}
+
+/*
+ * pexpr_eliminate_dups() helper.
+ */
+static void pexpr_eliminate_dups1(enum pexpr_type type, struct pexpr **ep1, struct pexpr **ep2)
+{
+#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);
+ pexpr_eliminate_dups1(type, &e1->right.pexpr, &e2);
+ return;
+ }
+ if (e2->type == type) {
+ pexpr_eliminate_dups1(type, &e1, &e2->left.pexpr);
+ pexpr_eliminate_dups1(type, &e1, &e2->right.pexpr);
+ 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);
+ default:
+ ;
+ }
+
+ switch (type) {
+ case PE_AND:
+ tmp = pexpr_join_and(e1, e2);
+ if (tmp) {
+ pexpr_free(e1);
+ pexpr_free(e2);
+ e1 = pexf(const_true);
+ e2 = tmp;
+ trans_count++;
+ }
+ break;
+ case PE_OR:
+ tmp = pexpr_join_or(e1, e2);
+ if (tmp) {
+ pexpr_free(e1);
+ pexpr_free(e2);
+ e1 = pexf(const_false);
+ e2 = tmp;
+ trans_count++;
+ }
+ break;
+ default:
+ ;
+ }
+
+#undef e1
+#undef e2
+}
+
+/*
+ * eliminate duplicate and redundant operands
+ */
+struct pexpr * pexpr_eliminate_dups(struct pexpr *e)
+{
+ if (!e)
+ return e;
+
+ int oldcount = trans_count;
+ while (true) {
+ trans_count = 0;
+ switch (e->type) {
+ case PE_AND:
+ case PE_OR:
+ pexpr_eliminate_dups1(e->type, &e, &e);
+ default:
+ ;
+ }
+ if (!trans_count)
+ /* no simplification done in this pass. We're done. */
+ break;
+ e = pexpr_eliminate_yn(e);
+ }
+ 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..cc612866fed4
--- /dev/null
+++ b/scripts/kconfig/cf_expr.h
@@ -0,0 +1,237 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CF_EXPR_H
+#define CF_EXPR_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)
+
+/* 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 pexpr * expr_calculate_pexpr_both(struct expr *e);
+struct pexpr * expr_calculate_pexpr_y(struct expr *e);
+struct pexpr * expr_calculate_pexpr_m(struct expr *e);
+struct pexpr * expr_calculate_pexpr_y_and(struct expr *a, struct expr *b);
+struct pexpr * expr_calculate_pexpr_m_and(struct expr *a, struct expr *b);
+struct pexpr * expr_calculate_pexpr_both_and(struct expr *a, struct expr *b);
+struct pexpr * expr_calculate_pexpr_y_or(struct expr *a, struct expr *b);
+struct pexpr * expr_calculate_pexpr_m_or(struct expr *a, struct expr *b);
+struct pexpr * expr_calculate_pexpr_both_or(struct expr *a, struct expr *b);
+struct pexpr * expr_calculate_pexpr_y_not(struct expr * e);
+struct pexpr * expr_calculate_pexpr_m_not(struct expr *e);
+struct pexpr * expr_calculate_pexpr_y_equals(struct expr *e);
+struct pexpr * expr_calculate_pexpr_y_unequals(struct expr *e);
+struct pexpr * expr_calculate_pexpr_y_comp(struct expr *e);
+
+/* macro to create a pexpr of type AND */
+struct pexpr * pexpr_and(struct pexpr *a, struct pexpr *b);
+
+/* macro to create a pexpr of type OR */
+struct pexpr * pexpr_or(struct pexpr *a, struct pexpr *b);
+
+/* macro to create a pexpr of type NOT */
+struct pexpr * pexpr_not(struct pexpr *a);
+
+/* 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);
+
+/* return fexpr_sel_both for a symbol */
+struct pexpr * sym_get_fexpr_sel_both(struct symbol *sym);
+
+/* create the fexpr of a non-boolean symbol for a specific value */
+struct fexpr * sym_create_nonbool_fexpr(struct symbol *sym, char *value);
+
+/* 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);
+
+/* macro to construct a pexpr for "A implies B" */
+struct pexpr * pexpr_implies(struct pexpr *a, struct pexpr *b);
+
+/* 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);
+
+/* add a fexpr to the satmap */
+void fexpr_add_to_satmap(struct fexpr *e);
+
+/* 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);
+
+/* 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 occurence 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 fexl_list */
+void fexl_list_free(struct fexl_list *list);
+
+/* free a sdv_list */
+void sdv_list_free(struct sdv_list *list);
+
+/* check whether 2 pexpr are equal */
+bool pexpr_eq(struct pexpr *e1, struct pexpr *e2);
+
+/* copy a pexpr */
+struct pexpr * pexpr_copy(const struct pexpr *org);
+
+/* free a pexpr */
+void pexpr_free(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);
+
+#endif
--
2.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 08/12] Add files for RangeFix
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
` (6 preceding siblings ...)
2021-10-20 9:43 ` [RFC 07/12] Add files for handling expressions Thorsten Berger
@ 2021-10-20 9:44 ` Thorsten Berger
2021-10-20 9:45 ` [RFC 09/12] Add files with utility functions Thorsten Berger
` (3 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:44 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
scripts/kconfig/cf_rangefix.c | 1017 +++++++++++++++++++++++++++++++++
scripts/kconfig/cf_rangefix.h | 18 +
2 files changed, 1035 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..9b5773188175
--- /dev/null
+++ b/scripts/kconfig/cf_rangefix.c
@@ -0,0 +1,1017 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 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"
+
+#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 fexl_list *diagnoses;
+static struct sfl_list *diagnoses_symbol;
+
+static struct fexl_list * generate_diagnoses(PicoSAT *pico);
+
+static void add_fexpr_to_constraint_set(struct fexpr_list *C);
+static void set_assumptions(PicoSAT *pico, struct fexpr_list *c);
+static void fexpr_add_assumption(PicoSAT *pico, struct fexpr *e, int satval);
+static struct fexpr_list * get_unsat_core_soft(PicoSAT *pico);
+static struct fexpr_list * minimise_unsat_core(PicoSAT *pico, struct fexpr_list *C);
+
+
+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);
+static struct sfix_list * convert_diagnosis(struct fexpr_list *diagnosis);
+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);
+
+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 = 0;
+
+/* -------------------------------------- */
+
+struct sfl_list * rangefix_run(PicoSAT *pico)
+{
+ printd("Starting RangeFix...\n");
+ printd("Generating diagnoses...");
+ clock_t start, end;
+ double time;
+ start = clock();
+
+ /* generate the diagnoses */
+ diagnoses = generate_diagnoses(pico);
+
+ 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);
+ else
+ diagnoses_symbol = convert_diagnoses(diagnoses);
+
+ printd("\n");
+
+ return diagnoses_symbol;
+}
+
+/*
+ * generate the diagnoses
+ */
+static struct fexl_list * generate_diagnoses(PicoSAT *pico)
+{
+ 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;
+
+ /* create constraint set C */
+ add_fexpr_to_constraint_set(C);
+
+ if (PRINT_UNSAT_CORE)
+ printd("\n");
+
+ /* init E with an empty diagnosis */
+ struct fexpr_list *empty_diagnosis = fexpr_list_init();
+ fexl_list_add(E, empty_diagnosis);
+
+ /* start the clock */
+ clock_t start_t, end_t;
+ double time_t;
+ 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);
+
+ /* set assumptions */
+ nr_of_assumptions = 0;
+ nr_of_assumptions_true = 0;
+ set_assumptions(pico, c);
+
+ int 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);
+
+ fexpr_list_free(c);
+
+ 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;
+
+ /* get unsat core from SAT solver */
+ X = get_unsat_core_soft(pico);
+
+ /* minimise the unsat core */
+ if (MINIMISE_UNSAT_CORE)
+ X = minimise_unsat_core(pico, X);
+
+ if (PRINT_UNSAT_CORE)
+ print_unsat_core(X);
+
+ struct fexl_node *node, *tmp;
+ for (node = E->head; node != NULL;) {
+ /* 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 */
+ struct fexpr_node *fnode;
+ fexpr_list_for_each(fnode, X) {
+ struct fexpr *x = fnode->elem;
+
+ /* create {x} */
+ x_set = fexpr_list_init();
+ fexpr_list_add(x_set, x);
+
+ /* create E' = e ∪ {x} */
+ E1 = fexpr_list_union(e, x_set);
+
+ /* create (E\e) ∪ R */
+ E_R_Union = fexl_list_copy(E);
+ fexl_list_delete_elem(E_R_Union, e);
+ E_R_Union = fexl_list_union(E_R_Union, R);
+
+ bool E2_subset_of_E1 = false;
+
+ /* E" ∈ (E\e) ∪ R */
+ struct fexl_node *lnode;
+ fexl_list_for_each(lnode, E_R_Union) {
+ E2 = lnode->elem;
+
+ /* E" ⊆ E' ? */
+ if (is_subset_of(E2, E1)) {
+ E2_subset_of_E1 = true;
+ break;
+ }
+ }
+
+ fexl_list_free(E_R_Union);
+
+ /* ∄ E" ⊆ 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);
+ fexpr_list_free(c);
+ }
+
+DIAGNOSES_FOUND:
+ fexpr_list_free(C);
+ 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)
+{
+ unsigned int i, nr_sym = 0, nr_fexpr = 0;
+ struct symbol *sym;
+ for_all_symbols(i, sym) {
+ /* must be a proper symbol */
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ /* don't need the conflict symbols
+ * they are handled seperately */
+ if (sym_is_sdv(sdv_symbols, sym))
+ continue;
+
+ /* must have a prompt and a name */
+ if (!sym->name || !sym_has_prompt(sym))
+ continue;
+
+ nr_sym++;
+
+ if (sym->type == S_BOOLEAN) {
+ fexpr_list_add(C, sym->fexpr_y);
+ nr_fexpr++;
+ } else if (sym->type == S_TRISTATE) {
+ fexpr_list_add(C, sym->fexpr_y);
+ fexpr_list_add(C, sym->fexpr_m);
+ nr_fexpr += 2;
+ } 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);
+ nr_fexpr++;
+ }
+ } 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) {
+ sdv = node->elem;
+ sym = sdv->sym;
+
+ int 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 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, 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)) {
+ 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 fexpr_list *ret = fexpr_list_init();
+ struct fexpr *e;
+
+ int *lit = malloc(sizeof(int));
+ const int *i = picosat_failed_assumptions(pico);
+ *lit = abs(*i++);
+
+ while (*lit != 0) {
+ e = &satmap[*lit];
+
+ if (!sym_is_sdv(sdv_symbols, e->sym))
+ fexpr_list_add(ret, e);
+
+ *lit = abs(*i++);
+ }
+
+ return ret;
+}
+
+/*
+ * minimise the unsat core C
+ */
+static struct fexpr_list * minimise_unsat_core(PicoSAT *pico, struct fexpr_list *C)
+{
+ /* no need to check further */
+ if (C->size == 1)
+ return C;
+
+ struct fexpr_list *c_set;
+ struct fexpr_node *node, *tmp;
+
+ for (node = C->head; node != NULL;) {
+ if (C->size == 1)
+ return C;
+
+ /* create C\c */
+ c_set = fexpr_list_init();
+ fexpr_list_add(c_set, node->elem);
+ struct fexpr_list *t = get_difference(C, c_set);
+
+ /* invoke PicoSAT */
+ set_assumptions(pico, t);
+
+ int 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);
+ }
+
+ return C;
+}
+
+
+/*
+ * 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) {
+ printd("%d: [", i++);
+ struct fexpr_node *node;
+ 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 sfix_list *diagnosis_symbol = sfix_list_init();
+ struct fexpr *e;
+ struct symbol_fix *fix;
+ struct symbol_dvalue *sdv;
+
+ /* set the values for the conflict symbols */
+ struct sdv_node *snode;
+ sdv_list_for_each(snode, 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);
+ }
+
+ struct fexpr_node * fnode;
+ fexpr_list_for_each(fnode, diagnosis) {
+ e = fnode->elem;
+
+ /* diagnosis already contains symbol, so continue */
+ if (diagnosis_contains_symbol(diagnosis_symbol, e->sym))
+ continue;
+
+ enum symbolfix_type type;
+ 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)
+{
+ diagnoses_symbol = sfl_list_init();
+
+ struct fexl_node *lnode;
+ fexl_list_for_each(lnode, diag_arr) {
+ struct sfix_list *fix = convert_diagnosis(lnode->elem);
+ 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)
+{
+ clock_t start, end;
+ double time;
+
+ printd("Minimising diagnoses...");
+
+ start = clock();
+
+ 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;
+
+ /* create soft constraint set C */
+ struct fexpr_list *C = fexpr_list_init();
+ add_fexpr_to_constraint_set(C);
+
+ struct fexl_node *flnode;
+ fexl_list_for_each(flnode, diagnoses) {
+ d = flnode->elem;
+
+ /* set assumptions for those symbols that don't need to be changed */
+ set_assumptions(pico, get_difference(C, d));
+
+ /* flip the assumptions from the diagnosis */
+ struct fexpr_node *fnode;
+ fexpr_list_for_each(fnode, d) {
+ e = fnode->elem;
+ satval = e->assumption ? -(e->satval) : e->satval;
+ picosat_assume(pico, satval);
+ }
+
+ int res = picosat_sat(pico, -1);
+ if (res != PICOSAT_SATISFIABLE)
+ perror("Diagnosis not satisfiable (minimise).");
+
+ diagnosis_symbol = convert_diagnosis(d);
+
+ /* check if symbol gets selected */
+ struct sfix_node *snode;
+ 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)
+{
+ printd("=== GENERATED DIAGNOSES ===\n");
+ printd("0: No changes wanted\n");
+ print_diagnoses_symbol(diag);
+
+ int choice;
+ printd("\n> Choose option: ");
+ scanf("%d", &choice);
+
+ /* no changes wanted */
+ if (choice == 0)
+ return NULL;
+
+ /* invalid choice */
+ if (choice > diag->size)
+ return NULL;
+
+ unsigned int counter;
+ struct sfl_node *node = diag->head;
+ for (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)
+{
+ 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 */
+ struct fexpr_node *node;
+ struct fexpr *e2;
+ 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..0daf29cb70b9
--- /dev/null
+++ b/scripts/kconfig/cf_rangefix.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CF_RANGEFIX_H
+#define CF_RANGEFIX_H
+
+/* initialize RangeFix and return the diagnoses */
+struct sfl_list * rangefix_run(PicoSAT *pico);
+
+/* 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.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 09/12] Add files with utility functions
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
` (7 preceding siblings ...)
2021-10-20 9:44 ` [RFC 08/12] Add files for RangeFix Thorsten Berger
@ 2021-10-20 9:45 ` Thorsten Berger
2021-10-20 9:46 ` [RFC 10/12] Add tools Thorsten Berger
` (2 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:45 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
scripts/kconfig/cf_satutils.c | 536 ++++++++++++++++++++++++++++++++++
scripts/kconfig/cf_satutils.h | 30 ++
scripts/kconfig/cf_utils.c | 510 ++++++++++++++++++++++++++++++++
scripts/kconfig/cf_utils.h | 90 ++++++
4 files changed, 1166 insertions(+)
create mode 100644 scripts/kconfig/cf_satutils.c
create mode 100644 scripts/kconfig/cf_satutils.h
create mode 100644 scripts/kconfig/cf_utils.c
create mode 100644 scripts/kconfig/cf_utils.h
diff --git a/scripts/kconfig/cf_satutils.c b/scripts/kconfig/cf_satutils.c
new file mode 100644
index 000000000000..84d8c46ad686
--- /dev/null
+++ b/scripts/kconfig/cf_satutils.c
@@ -0,0 +1,536 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 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"
+
+static void unfold_cnf_clause(struct pexpr *e);
+static void build_cnf_tseytin(struct pexpr *e);
+
+static void build_cnf_tseytin_top_and(struct pexpr *e);
+static void build_cnf_tseytin_top_or(struct pexpr *e);
+
+static void build_cnf_tseytin_tmp(struct pexpr *e, struct fexpr *t);
+static void build_cnf_tseytin_and(struct pexpr *e, struct fexpr *t);
+static void build_cnf_tseytin_or(struct pexpr *e, struct fexpr *t);
+static int pexpr_satval(struct pexpr *e);
+
+static PicoSAT *pico;
+
+/* -------------------------------------- */
+
+/*
+ * initialize PicoSAT
+ */
+PicoSAT * initialize_picosat(void)
+{
+ printd("\nInitializing PicoSAT...");
+ 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)
+{
+ pico = p;
+ unsigned int i;
+ struct symbol *sym;
+
+ /* adding unit-clauses for constants */
+ sat_add_clause(2, pico, -(const_false->satval));
+ sat_add_clause(2, pico, const_true->satval);
+
+ for_all_symbols(i, sym) {
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ struct pexpr_node *node;
+ pexpr_list_for_each(node, sym->constraints) {
+ if (pexpr_is_cnf(node->elem))
+ unfold_cnf_clause(node->elem);
+ else
+ build_cnf_tseytin(node->elem);
+ }
+ }
+}
+
+/*
+ * helper function to add an expression to a CNF-clause
+ */
+static void unfold_cnf_clause_util(struct pexpr *e)
+{
+ switch (e->type) {
+ case PE_SYMBOL:
+ picosat_add(pico, e->left.fexpr->satval);
+ break;
+ case PE_OR:
+ unfold_cnf_clause_util(e->left.pexpr);
+ unfold_cnf_clause_util(e->right.pexpr);
+ break;
+ case PE_NOT:
+ picosat_add(pico, -(e->left.pexpr->left.fexpr->satval));
+ break;
+ default:
+ perror("Not in CNF, FE_EQUALS.");
+ }
+}
+
+/*
+ * extract the variables from a pexpr in CNF
+ */
+static void unfold_cnf_clause(struct pexpr *e)
+{
+ if (!pexpr_is_cnf(e))
+ return;
+
+ unfold_cnf_clause_util(e);
+
+ picosat_add(pico, 0);
+}
+
+/*
+ * build CNF-clauses for a pexpr not in CNF
+ */
+static void build_cnf_tseytin(struct pexpr *e)
+{
+ switch (e->type) {
+ case PE_AND:
+ build_cnf_tseytin_top_and(e);
+ break;
+ case PE_OR:
+ build_cnf_tseytin_top_or(e);
+ 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)
+{
+ if (pexpr_is_cnf(e->left.pexpr))
+ unfold_cnf_clause(e->left.pexpr);
+ else
+ build_cnf_tseytin(e->left.pexpr);
+
+ if (pexpr_is_cnf(e->right.pexpr))
+ unfold_cnf_clause(e->right.pexpr);
+ else
+ build_cnf_tseytin(e->right.pexpr);
+
+}
+
+static void build_cnf_tseytin_top_or(struct pexpr *e)
+{
+ 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();
+ a = t1->satval;
+ }
+
+ /* set right side */
+ if (pexpr_is_symbol(e->right.pexpr)) {
+ b = pexpr_satval(e->right.pexpr);
+ } else {
+ t2 = create_tmpsatvar();
+ 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);
+
+ }
+
+ if (!pexpr_is_symbol(e->right.pexpr)) {
+ if (t2 == NULL)
+ perror("t2 is NULL.");
+
+ build_cnf_tseytin_tmp(e->right.pexpr, t2);
+ }
+}
+
+/*
+ * build the sub-expressions
+ */
+static void build_cnf_tseytin_tmp(struct pexpr *e, struct fexpr *t)
+{
+ switch (e->type) {
+ case PE_AND:
+ build_cnf_tseytin_and(e, t);
+ break;
+ case PE_OR:
+ build_cnf_tseytin_or(e, t);
+ 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 fexpr *t1 = NULL, *t2 = NULL;
+ int a, b, c;
+
+ /* set left side */
+ if (pexpr_is_symbol(e->left.pexpr)) {
+ a = pexpr_satval(e->left.pexpr);
+ } else {
+ t1 = create_tmpsatvar();
+ a = t1->satval;
+ }
+
+ /* set right side */
+ if (pexpr_is_symbol(e->right.pexpr)) {
+ b = pexpr_satval(e->right.pexpr);
+ } else {
+ t2 = create_tmpsatvar();
+ 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);
+ }
+ if (!pexpr_is_symbol(e->right.pexpr)) {
+ if (t2 == NULL)
+ perror("t2 is NULL.");
+
+ build_cnf_tseytin_tmp(e->right.pexpr, t2);
+ }
+}
+
+/*
+ * build the Tseytin sub-expressions for a pexpr of type
+ */
+static void build_cnf_tseytin_or(struct pexpr *e, struct fexpr *t)
+{
+ struct fexpr *t1 = NULL, *t2 = NULL;
+ int a, b, c;
+
+ /* set left side */
+ if (pexpr_is_symbol(e->left.pexpr)) {
+ a = pexpr_satval(e->left.pexpr);
+ } else {
+ t1 = create_tmpsatvar();
+ a = t1->satval;
+ }
+
+ /* set right side */
+ if (pexpr_is_symbol(e->right.pexpr)) {
+ b = pexpr_satval(e->right.pexpr);
+ } else {
+ t2 = create_tmpsatvar();
+ 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);
+ }
+ if (!pexpr_is_symbol(e->right.pexpr)) {
+ if (t2 == NULL)
+ perror("t2 is NULL.");
+ build_cnf_tseytin_tmp(e->right.pexpr, t2);
+ }
+}
+
+/*
+ * add a clause to PicoSAT
+ * First argument must be the SAT solver
+ */
+void sat_add_clause(int num, ...)
+{
+ if (num <= 1)
+ return;
+
+ va_list valist;
+ int i, *lit;
+ PicoSAT *pico;
+
+
+ va_start(valist, num);
+
+ pico = va_arg(valist, PicoSAT *);
+
+ /* access all the arguments assigned to valist */
+ for (i = 1; i < num; i++) {
+ lit = xmalloc(sizeof(int));
+ *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)
+{
+ printd("Solving SAT-problem...");
+
+ clock_t start, end;
+ double time;
+ start = clock();
+
+ int 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) {
+ printd("===> PROBLEM IS UNSATISFIABLE <===\n");
+
+ /* print unsat core */
+ printd("\nPrinting unsatisfiable core:\n");
+ struct fexpr *e;
+
+ int *lit = malloc(sizeof(int));
+ const int *i = picosat_failed_assumptions(pico);
+ *lit = abs(*i++);
+
+ while (*lit != 0) {
+ e = &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;
+
+ struct fexpr_node *node;
+ 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;
+ sdv_list_for_each(node, list) {
+ sdv = node->elem;
+
+ int 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) {
+ int 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_satutils.h b/scripts/kconfig/cf_satutils.h
new file mode 100644
index 000000000000..d5caf87e3427
--- /dev/null
+++ b/scripts/kconfig/cf_satutils.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CF_SATUTILS_H
+#define CF_SATUTILS_H
+
+/* initialize PicoSAT */
+PicoSAT * initialize_picosat(void);
+
+/* construct the CNF-clauses from the constraints */
+void construct_cnf_clauses(PicoSAT *pico);
+
+/* add a clause to to PicoSAT */
+void sat_add_clause(int num, ...);
+
+/* start PicoSAT */
+void picosat_solve(PicoSAT *pico);
+
+/* 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
diff --git a/scripts/kconfig/cf_utils.c b/scripts/kconfig/cf_utils.c
new file mode 100644
index 000000000000..36d7ab374f6d
--- /dev/null
+++ b/scripts/kconfig/cf_utils.c
@@ -0,0 +1,510 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 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"
+
+#define SATMAP_INIT_SIZE 2
+
+/*
+ * parse Kconfig-file and read .config
+ */
+void init_config(const char *Kconfig_file)
+{
+ conf_parse(Kconfig_file);
+ conf_read(NULL);
+}
+
+/*
+ * initialize satmap and cnf_clauses_map
+ */
+void init_data(void)
+{
+ /* create hashtable with all fexpr */
+ satmap = xcalloc(SATMAP_INIT_SIZE, sizeof(*satmap));
+ satmap_size = SATMAP_INIT_SIZE;
+
+ printd("done.\n");
+}
+
+/*
+ * bool-symbols have 1 variable (X), tristate-symbols have 2 variables (X, X_m)
+ */
+static void create_sat_variables(struct symbol *sym)
+{
+ sym->constraints = pexpr_list_init();
+ sym_create_fexpr(sym);
+}
+
+/*
+ * assign SAT-variables to all fexpr and create the sat_map
+ */
+void assign_sat_variables(void)
+{
+ unsigned int i;
+ struct symbol *sym;
+
+ printd("Creating SAT-variables...");
+
+ for_all_symbols(i, sym)
+ create_sat_variables(sym);
+
+ printd("done.\n");
+}
+
+/*
+ * create True/False constants
+ */
+void create_constants(void)
+{
+ printd("Creating constants...");
+
+ /* create TRUE and FALSE constants */
+ const_false = fexpr_create(sat_variable_nr++, FE_FALSE, "False");
+ fexpr_add_to_satmap(const_false);
+
+ const_true = fexpr_create(sat_variable_nr++, FE_TRUE, "True");
+ fexpr_add_to_satmap(const_true);
+
+ /* add fexpr of constants to tristate constants */
+ symbol_yes.fexpr_y = const_true;
+ symbol_yes.fexpr_m = const_false;
+
+ symbol_mod.fexpr_y = const_false;
+ symbol_mod.fexpr_m = const_true;
+
+ symbol_no.fexpr_y = const_false;
+ symbol_no.fexpr_m = const_false;
+
+ /* create symbols yes/mod/no as fexpr */
+ symbol_yes_fexpr = fexpr_create(0, FE_SYMBOL, "y");
+ symbol_yes_fexpr->sym = &symbol_yes;
+ symbol_yes_fexpr->tri = yes;
+
+ symbol_mod_fexpr = fexpr_create(0, FE_SYMBOL, "m");
+ symbol_mod_fexpr->sym = &symbol_mod;
+ symbol_mod_fexpr->tri = mod;
+
+ symbol_no_fexpr = fexpr_create(0, FE_SYMBOL, "n");
+ symbol_no_fexpr->sym = &symbol_no;
+ symbol_no_fexpr->tri = no;
+
+ printd("done.\n");
+}
+
+/*
+ * create a temporary SAT-variable
+ */
+struct fexpr * create_tmpsatvar(void)
+{
+ struct fexpr *t = fexpr_create(sat_variable_nr++, FE_TMPSATVAR, "");
+ str_append(&t->name, get_tmp_var_as_char(tmp_variable_nr++));
+ fexpr_add_to_satmap(t);
+
+ 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, True if there is none
+ */
+struct pexpr * prop_get_condition(struct property *prop)
+{
+ if (prop == NULL)
+ return NULL;
+
+ /* if there is no condition, return True */
+ if (!prop->visible.expr)
+ return pexf(const_true);
+
+ return expr_calculate_pexpr_both(prop->visible.expr);
+}
+
+/*
+ * 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)
+{
+ if (!sym_is_nonboolean(sym))
+ return false;
+
+ const char *string_val = sym_get_string_value(sym);
+
+ if (strcmp(string_val, "") != 0)
+ return true;
+
+ /* a HEX/INT symbol cannot have value "" */
+ if (sym->type == S_HEX || sym->type == S_INT)
+ 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 */
+ struct property *prompt = sym_get_prompt(sym);
+ if (prompt != NULL && prompt->visible.tri != no)
+ return true;
+
+ /* invisible prompt => must get value from default value */
+ struct property *p = sym_get_default_prop(sym);
+ if (p == NULL)
+ return false;
+
+ if (!strcmp(sym_get_string_default(sym), ""))
+ return true;
+
+ return false;
+}
+
+/*
+ * return the name of the symbol or the prompt-text, if it is a choice symbol
+ */
+char * sym_get_name(struct symbol *sym)
+{
+ if (sym_is_choice(sym)) {
+ struct property *prompt = sym_get_prompt(sym);
+ if (prompt == NULL)
+ return "";
+
+ return strdup(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) {
+ entry = node->elem;
+ struct gstr s = str_new();
+ 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;
+ }
+}
diff --git a/scripts/kconfig/cf_utils.h b/scripts/kconfig/cf_utils.h
new file mode 100644
index 000000000000..91f9bbf26191
--- /dev/null
+++ b/scripts/kconfig/cf_utils.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Patrick Franz <deltaone@debian.org>
+ */
+
+#ifndef CF_UTILS_H
+#define CF_UTILS_H
+
+/* parse Kconfig-file and read .config */
+void init_config (const char *Kconfig_file);
+
+/* initialize satmap and cnf_clauses */
+void init_data(void);
+
+/* assign SAT-variables to all fexpr and create the sat_map */
+void assign_sat_variables(void);
+
+/* create True/False constants */
+void create_constants(void);
+
+/* create a temporary SAT-variable */
+struct fexpr * create_tmpsatvar(void);
+
+/* 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);
+
+/* 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 */
+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);
+
+#endif
--
2.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 10/12] Add tools
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
` (8 preceding siblings ...)
2021-10-20 9:45 ` [RFC 09/12] Add files with utility functions Thorsten Berger
@ 2021-10-20 9:46 ` Thorsten Berger
2021-10-20 9:48 ` [RFC 11/12] Add xconfig-modifications Thorsten Berger
2021-10-20 9:49 ` [RFC 12/12] Simplify dependencies for MODULE_SIG_KEY_TYPE_RSA & MODULE_SIG_KEY_TYPE_ECDSA Thorsten Berger
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:46 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
scripts/kconfig/cfconfig.c | 176 ++++++++++++++
scripts/kconfig/cfoutconfig.c | 128 +++++++++++
scripts/kconfig/configfix.c | 420 ++++++++++++++++++++++++++++++++++
scripts/kconfig/configfix.h | 41 ++++
4 files changed, 765 insertions(+)
create mode 100644 scripts/kconfig/cfconfig.c
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/cfconfig.c b/scripts/kconfig/cfconfig.c
new file mode 100644
index 000000000000..105bdf5d3f4e
--- /dev/null
+++ b/scripts/kconfig/cfconfig.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 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"
+
+static struct symbol * read_symbol_from_stdin(void);
+static struct symbol_dvalue * sym_create_sdv(struct symbol *sym, char *input);
+static void handle_fixes(struct sfl_list *diag);
+
+/* -------------------------------------- */
+
+int main(int argc, char *argv[])
+{
+ CFDEBUG = true;
+
+ if (argc > 1 && !strcmp(argv[1], "-s")) {
+ printd("\nHello configfix!\n\n");
+
+ run_satconf_cli(argv[2]);
+ return EXIT_SUCCESS;
+ }
+
+ printd("\nCLI for configfix!\n");
+
+ init_config(argv[1]);
+
+ struct sfl_list *diagnoses;
+ struct sdv_list *symbols;
+
+ while(1) {
+ /* create the array */
+ symbols = sdv_list_init();
+
+ /* ask for user input */
+ struct symbol *sym = read_symbol_from_stdin();
+
+ printd("Found symbol %s, type %s\n\n", sym->name, sym_type_name(sym->type));
+ printd("Current value: %s\n", sym_get_string_value(sym));
+ printd("Desired value: ");
+
+ char input[100];
+ fgets(input, 100, stdin);
+ strtok(input, "\n");
+
+ struct symbol_dvalue *sdv = sym_create_sdv(sym, input);
+ sdv_list_add(symbols, sdv);
+
+ diagnoses = run_satconf(symbols);
+ handle_fixes(diagnoses);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/*
+ * read a symbol name from stdin
+ */
+static struct symbol * read_symbol_from_stdin(void)
+{
+ char input[100];
+ struct symbol *sym = NULL;
+
+ printd("\n");
+ while (sym == NULL) {
+ printd("Enter symbol name: ");
+ fgets(input, 100, stdin);
+ strtok(input, "\n");
+ sym = sym_find(input);
+ }
+
+ return sym;
+}
+
+/*
+ * create a symbol_dvalue struct containing the symbol and the desired value
+ */
+static struct symbol_dvalue * sym_create_sdv(struct symbol *sym, char *input)
+{
+ struct symbol_dvalue *sdv = malloc(sizeof(struct symbol_dvalue));
+ sdv->sym = sym;
+ sdv->type = sym_is_boolean(sym) ? SDV_BOOLEAN : SDV_NONBOOLEAN;
+
+ if (sym_is_boolean(sym)) {
+ if (strcmp(input, "y") == 0)
+ sdv->tri = yes;
+ else if (strcmp(input, "m") == 0)
+ sdv->tri = mod;
+ else if (strcmp(input, "n") == 0)
+ sdv->tri = no;
+ else
+ perror("Not a valid tristate value.");
+
+ /* sanitize input for booleans */
+ if (sym->type == S_BOOLEAN && sdv->tri == mod)
+ sdv->tri = yes;
+ } else if (sym_is_nonboolean(sym)) {
+ sdv->nb_val = str_new();
+ str_append(&sdv->nb_val, input);
+ }
+
+ return sdv;
+}
+
+/*
+ * 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);
+ }
+}
+
+static void apply_all_adiagnoses(struct sfl_list *diag) {
+ printd("Applying all diagnoses now...\n");
+
+ unsigned int counter = 1;
+ struct sfl_node *node;
+ sfl_list_for_each(node, diag) {
+ printd("\nDiagnosis %d:\n", counter++);
+ apply_fix(node->elem);
+
+ printd("\nResetting config.\n");
+ conf_read(NULL);
+ }
+}
+
+/*
+ * print all void print_fixes()
+ */
+static void handle_fixes(struct sfl_list *diag)
+{
+ printd("=== GENERATED DIAGNOSES ===\n");
+ printd("-1: No changes wanted\n");
+ printd(" 0: Apply all diagnoses\n");
+ print_diagnoses_symbol(diag);
+
+ int choice;
+ printd("\n> Choose option: ");
+ scanf("%d", &choice);
+
+ if (choice == -1 || choice > diag->size)
+ return;
+
+ if (choice == 0) {
+ apply_all_adiagnoses(diag);
+ return;
+ }
+
+ unsigned int counter;
+ struct sfl_node *node = diag->head;
+ for (counter = 1; counter < choice; counter++)
+ node = node->next;
+
+ apply_fix(node->elem);
+
+ printd("\nResetting config.\n");
+ conf_read(NULL);
+}
diff --git a/scripts/kconfig/cfoutconfig.c b/scripts/kconfig/cfoutconfig.c
new file mode 100644
index 000000000000..4164e25e66aa
--- /dev/null
+++ b/scripts/kconfig/cfoutconfig.c
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 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"
+
+#define OUTFILE_CONSTRAINTS "./scripts/kconfig/cfout_constraints.txt"
+#define OUTFILE_DIMACS "./scripts/kconfig/cfout_constraints.dimacs"
+
+static void write_constraints_to_file(void);
+static void write_dimacs_to_file(PicoSAT *pico);
+
+/* -------------------------------------- */
+
+int main(int argc, char *argv[])
+{
+ clock_t start, end;
+ double time;
+
+ 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();
+
+ /* creating constants */
+ create_constants();
+
+ /* assign SAT variables & create sat_map */
+ assign_sat_variables();
+
+ /* get the constraints */
+ get_constraints();
+
+ end = clock();
+ time = ((double) (end - start)) / CLOCKS_PER_SEC;
+
+ printd("done. (%.6f secs.)\n", time);
+
+ /* start PicoSAT */
+ PicoSAT *pico = picosat_init();
+ picosat_enable_trace_generation(pico);
+ printd("Building CNF-clauses...");
+ start = clock();
+
+ /* construct the CNF clauses */
+ construct_cnf_clauses(pico);
+
+ 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();
+ 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);
+ 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(void)
+{
+ FILE *fd = fopen(OUTFILE_CONSTRAINTS, "w");
+ unsigned int i;
+ struct symbol *sym;
+
+ for_all_symbols(i, sym) {
+ if (sym->type == S_UNKNOWN) continue;
+
+ struct pexpr_node *node;
+ pexpr_list_for_each(node, sym->constraints) {
+ struct gstr s = str_new();
+ pexpr_as_char(node->elem, &s, 0);
+ 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)
+{
+ FILE *fd = fopen(OUTFILE_DIMACS, "w");
+
+ unsigned int i;
+ for (i = 1; i < sat_variable_nr; i++)
+ add_comment(fd, &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..e226541e10f9
--- /dev/null
+++ b/scripts/kconfig/configfix.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 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"
+
+unsigned int sat_variable_nr = 1;
+unsigned int tmp_variable_nr = 1;
+
+struct fexpr *satmap;
+size_t satmap_size;
+
+struct sdv_list *sdv_symbols; /* array with conflict-symbols */
+
+bool CFDEBUG = false;
+bool stop_rangefix = false;
+
+struct fexpr *const_false; /* constant False */
+struct fexpr *const_true; /* constant True */
+struct fexpr *symbol_yes_fexpr; /* symbol_yes as fexpr */
+struct fexpr *symbol_mod_fexpr; /* symbol_mod as fexpr */
+struct fexpr *symbol_no_fexpr; /* symbol_no_as fexpr */
+
+static PicoSAT *pico;
+static bool init_done = false;
+static struct sym_list *conflict_syms;
+
+static bool sdv_within_range(struct sdv_list *symbols);
+
+/* -------------------------------------- */
+
+int run_satconf_cli(const char *Kconfig_file)
+{
+ clock_t start, end;
+ double time;
+
+ if (!init_done) {
+ printd("Init...");
+ /* measure time for constructing constraints and clauses */
+ start = clock();
+
+ /* parse Kconfig-file and read .config */
+ init_config(Kconfig_file);
+
+ /* initialize satmap and cnf_clauses */
+ init_data();
+
+ /* creating constants */
+ create_constants();
+
+ /* assign SAT variables & create sat_map */
+ assign_sat_variables();
+
+ /* get the constraints */
+ get_constraints();
+
+ /* print all symbols and its constraints */
+ // print_all_symbols();
+
+ end = clock();
+ time = ((double)(end - start)) / CLOCKS_PER_SEC;
+
+ printd("done. (%.6f secs.)\n", time);
+
+ init_done = true;
+ }
+
+ /* start PicoSAT */
+ PicoSAT *pico = initialize_picosat();
+ printd("Building CNF-clauses...");
+ start = clock();
+
+ /* construct the CNF clauses */
+ construct_cnf_clauses(pico);
+
+ end = clock();
+ time = ((double)(end - start)) / CLOCKS_PER_SEC;
+
+ printd("done. (%.6f secs.)\n", time);
+
+ /* add assumptions for all other symbols */
+ printd("Adding assumptions...");
+ start = clock();
+
+ unsigned int i;
+ struct symbol *sym;
+ for_all_symbols(i, sym) {
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ if (!sym->name || !sym_has_prompt(sym))
+ continue;
+
+ sym_add_assumption(pico, sym);
+
+ }
+
+ end = clock();
+ time = ((double)(end - start)) / CLOCKS_PER_SEC;
+
+ printd("done. (%.6f secs.)\n", time);
+
+ picosat_solve(pico);
+
+ printd("\n===> STATISTICS <===\n");
+ printd("Constraints : %d\n", count_counstraints());
+ printd("CNF-clauses : %d\n", picosat_added_original_clauses(pico));
+ printd("SAT-variables: %d\n", picosat_variables(pico));
+ printd("Temp vars : %d\n", tmp_variable_nr - 1);
+ printd("PicoSAT time : %.6f secs.\n", picosat_seconds(pico));
+
+ return EXIT_SUCCESS;
+}
+
+/*
+ * called from satdvconfig
+ */
+struct sfl_list *run_satconf(struct sdv_list *symbols)
+{
+ clock_t start, end;
+ double time;
+
+ /* 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();
+
+ /* creating constants */
+ create_constants();
+
+ /* assign SAT variables & create sat_map */
+ assign_sat_variables();
+
+ /* get the constraints */
+ get_constraints();
+
+ 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);
+
+ 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 */
+ sdv_symbols = sdv_list_copy(symbols);
+
+ /* add assumptions for conflict-symbols */
+ sym_add_assumption_sdv(pico, sdv_symbols);
+
+ /* add assumptions for all other symbols */
+ struct symbol *sym;
+ unsigned int i;
+ for_all_symbols(i, sym) {
+ if (sym->type == S_UNKNOWN)
+ continue;
+
+ if (!sym_is_sdv(sdv_symbols, sym))
+ sym_add_assumption(pico, sym);
+ }
+
+ /* store the conflict symbols */
+ conflict_syms = sym_list_init();
+ struct sdv_node *node;
+ sdv_list_for_each(node, sdv_symbols)
+ sym_list_add(conflict_syms, node->elem->sym);
+
+ printd("Solving SAT-problem...");
+ start = clock();
+
+ int res = picosat_sat(pico, -1);
+
+ end = clock();
+ time = ((double)(end - start)) / CLOCKS_PER_SEC;
+ printd("done. (%.6f secs.)\n\n", time);
+
+ struct sfl_list *ret;
+ 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);
+ } else {
+ printd("Unknown if satisfiable.\n");
+
+ ret = sfl_list_init();
+ }
+
+ sdv_list_free(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..0abaf433f41c
--- /dev/null
+++ b/scripts/kconfig/configfix.h
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 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_satutils.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.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 11/12] Add xconfig-modifications
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
` (9 preceding siblings ...)
2021-10-20 9:46 ` [RFC 10/12] Add tools Thorsten Berger
@ 2021-10-20 9:48 ` Thorsten Berger
2021-10-20 9:49 ` [RFC 12/12] Simplify dependencies for MODULE_SIG_KEY_TYPE_RSA & MODULE_SIG_KEY_TYPE_ECDSA Thorsten Berger
11 siblings, 0 replies; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:48 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
scripts/kconfig/qconf.cc | 1003 +++++++++++++++++++++++++++++---------
scripts/kconfig/qconf.h | 179 ++++++-
2 files changed, 929 insertions(+), 253 deletions(-)
diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc
index 78087b2d9ac6..a86cd09a1096 100644
--- a/scripts/kconfig/qconf.cc
+++ b/scripts/kconfig/qconf.cc
@@ -17,6 +17,10 @@
#include <QMenuBar>
#include <QMessageBox>
#include <QToolBar>
+#include <QListWidget>
+#include <QComboBox>
+#include <QTableWidget>
+#include <QHBoxLayout>
#include <stdlib.h>
@@ -24,7 +28,14 @@
#include "qconf.h"
#include "images.h"
+#include <iostream>
+#include <QAbstractItemView>
+#include <QMimeData>
+#include <QBrush>
+#include <QColor>
+#include <future>
+#include <memory>
static QApplication *configApp;
static ConfigSettings *configSettings;
@@ -82,6 +93,14 @@ QIcon ConfigItem::choiceNoIcon;
QIcon ConfigItem::menuIcon;
QIcon ConfigItem::menubackIcon;
+/*
+ * set the new data
+ * TODO check the value
+ */
+void ConfigItem::okRename(int col)
+{
+}
+
/*
* update the displayed of a menu entry
*/
@@ -122,7 +141,6 @@ void ConfigItem::updateMenu(void)
goto set_prompt;
case P_COMMENT:
setIcon(promptColIdx, QIcon());
- prompt = "*** " + prompt + " ***";
goto set_prompt;
default:
;
@@ -140,6 +158,9 @@ void ConfigItem::updateMenu(void)
if (!sym_is_changeable(sym) && list->optMode == normalOpt) {
setIcon(promptColIdx, QIcon());
+ setText(noColIdx, QString());
+ setText(modColIdx, QString());
+ setText(yesColIdx, QString());
break;
}
expr = sym_get_tristate_value(sym);
@@ -149,10 +170,12 @@ void ConfigItem::updateMenu(void)
setIcon(promptColIdx, choiceYesIcon);
else
setIcon(promptColIdx, symbolYesIcon);
+ setText(yesColIdx, "Y");
ch = 'Y';
break;
case mod:
setIcon(promptColIdx, symbolModIcon);
+ setText(modColIdx, "M");
ch = 'M';
break;
default:
@@ -160,16 +183,31 @@ void ConfigItem::updateMenu(void)
setIcon(promptColIdx, choiceNoIcon);
else
setIcon(promptColIdx, symbolNoIcon);
+ setText(noColIdx, "N");
ch = 'N';
break;
}
+ if (expr != no)
+ setText(noColIdx, sym_tristate_within_range(sym, no) ? "_" : 0);
+ if (expr != mod)
+ setText(modColIdx, sym_tristate_within_range(sym, mod) ? "_" : 0);
+ if (expr != yes)
+ setText(yesColIdx, sym_tristate_within_range(sym, yes) ? "_" : 0);
setText(dataColIdx, QChar(ch));
break;
case S_INT:
case S_HEX:
case S_STRING:
- setText(dataColIdx, sym_get_string_value(sym));
+ const char* data;
+
+ data = sym_get_string_value(sym);
+
+ setText(dataColIdx, data);
+ if (type == S_STRING)
+ prompt = QString("%1: %2").arg(prompt).arg(data);
+ else
+ prompt = QString("(%2) %1").arg(prompt).arg(data);
break;
}
if (!sym_has_value(sym) && visible)
@@ -210,17 +248,6 @@ void ConfigItem::init(void)
if (list->mode != fullMode)
setExpanded(true);
sym_calc_value(menu->sym);
-
- if (menu->sym) {
- enum symbol_type type = menu->sym->type;
-
- // Allow to edit "int", "hex", and "string" in-place in
- // the data column. Unfortunately, you cannot specify
- // the flags per column. Set ItemIsEditable for all
- // columns here, and check the column in createEditor().
- if (type == S_INT || type == S_HEX || type == S_STRING)
- setFlags(flags() | Qt::ItemIsEditable);
- }
}
updateMenu();
}
@@ -241,65 +268,46 @@ ConfigItem::~ConfigItem(void)
}
}
-QWidget *ConfigItemDelegate::createEditor(QWidget *parent,
- const QStyleOptionViewItem &option,
- const QModelIndex &index) const
+ConfigLineEdit::ConfigLineEdit(ConfigView* parent)
+ : Parent(parent)
{
- ConfigItem *item;
-
- // Only the data column is editable
- if (index.column() != dataColIdx)
- return nullptr;
-
- // You cannot edit invisible menus
- item = static_cast<ConfigItem *>(index.internalPointer());
- if (!item || !item->menu || !menu_is_visible(item->menu))
- return nullptr;
-
- return QStyledItemDelegate::createEditor(parent, option, index);
+ connect(this, SIGNAL(editingFinished()), SLOT(hide()));
}
-void ConfigItemDelegate::setModelData(QWidget *editor,
- QAbstractItemModel *model,
- const QModelIndex &index) const
+void ConfigLineEdit::show(ConfigItem* i)
{
- QLineEdit *lineEdit;
- ConfigItem *item;
- struct symbol *sym;
- bool success;
-
- lineEdit = qobject_cast<QLineEdit *>(editor);
- // If this is not a QLineEdit, use the parent's default.
- // (does this happen?)
- if (!lineEdit)
- goto parent;
-
- item = static_cast<ConfigItem *>(index.internalPointer());
- if (!item || !item->menu)
- goto parent;
-
- sym = item->menu->sym;
- if (!sym)
- goto parent;
+ item = i;
+ if (sym_get_string_value(item->menu->sym))
+ setText(sym_get_string_value(item->menu->sym));
+ else
+ setText(QString());
+ Parent::show();
+ setFocus();
+}
- success = sym_set_string_value(sym, lineEdit->text().toUtf8().data());
- if (success) {
- ConfigList::updateListForAll();
- } else {
- QMessageBox::information(editor, "qconf",
- "Cannot set the data (maybe due to out of range).\n"
- "Setting the old value.");
- lineEdit->setText(sym_get_string_value(sym));
+void ConfigLineEdit::keyPressEvent(QKeyEvent* e)
+{
+ switch (e->key()) {
+ case Qt::Key_Escape:
+ break;
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ sym_set_string_value(item->menu->sym, text().toLatin1());
+ parent()->updateList();
+ break;
+ default:
+ Parent::keyPressEvent(e);
+ return;
}
-
-parent:
- QStyledItemDelegate::setModelData(editor, model, index);
+ e->accept();
+ parent()->list->setFocus();
+ hide();
}
-ConfigList::ConfigList(QWidget *parent, const char *name)
- : QTreeWidget(parent),
+ConfigList::ConfigList(ConfigView* p, const char *name)
+ : Parent(p),
updateAll(false),
- showName(false), mode(singleMode), optMode(normalOpt),
+ showName(false), showRange(false), showData(false), mode(singleMode), optMode(normalOpt),
rootEntry(0), headerPopup(0)
{
setObjectName(name);
@@ -309,34 +317,26 @@ ConfigList::ConfigList(QWidget *parent, const char *name)
setVerticalScrollMode(ScrollPerPixel);
setHorizontalScrollMode(ScrollPerPixel);
- setHeaderLabels(QStringList() << "Option" << "Name" << "Value");
+ setHeaderLabels(QStringList() << "Option" << "Name" << "N" << "M" << "Y" << "Value");
- connect(this, &ConfigList::itemSelectionChanged,
- this, &ConfigList::updateSelection);
+ connect(this, SIGNAL(itemSelectionChanged(void)),
+ SLOT(updateSelection(void)));
if (name) {
configSettings->beginGroup(name);
showName = configSettings->value("/showName", false).toBool();
+ showRange = configSettings->value("/showRange", false).toBool();
+ showData = configSettings->value("/showData", false).toBool();
optMode = (enum optionMode)configSettings->value("/optionMode", 0).toInt();
configSettings->endGroup();
- connect(configApp, &QApplication::aboutToQuit,
- this, &ConfigList::saveSettings);
+ connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
}
showColumn(promptColIdx);
- setItemDelegate(new ConfigItemDelegate(this));
-
- allLists.append(this);
-
reinit();
}
-ConfigList::~ConfigList()
-{
- allLists.removeOne(this);
-}
-
bool ConfigList::menuSkip(struct menu *menu)
{
if (optMode == normalOpt && menu_is_visible(menu))
@@ -350,10 +350,21 @@ bool ConfigList::menuSkip(struct menu *menu)
void ConfigList::reinit(void)
{
+ hideColumn(dataColIdx);
+ hideColumn(yesColIdx);
+ hideColumn(modColIdx);
+ hideColumn(noColIdx);
hideColumn(nameColIdx);
if (showName)
showColumn(nameColIdx);
+ if (showRange) {
+ showColumn(noColIdx);
+ showColumn(modColIdx);
+ showColumn(yesColIdx);
+ }
+ if (showData)
+ showColumn(dataColIdx);
updateListAll();
}
@@ -375,6 +386,8 @@ void ConfigList::saveSettings(void)
if (!objectName().isEmpty()) {
configSettings->beginGroup(objectName());
configSettings->setValue("/showName", showName);
+ configSettings->setValue("/showRange", showRange);
+ configSettings->setValue("/showData", showData);
configSettings->setValue("/optionMode", (int)optMode);
configSettings->endGroup();
}
@@ -400,6 +413,7 @@ void ConfigList::updateSelection(void)
if (selectedItems().count() == 0)
return;
+ emit selectedChanged(selectedItems());
ConfigItem* item = (ConfigItem*)selectedItems().first();
if (!item)
return;
@@ -460,28 +474,6 @@ update:
resizeColumnToContents(0);
}
-void ConfigList::updateListForAll()
-{
- QListIterator<ConfigList *> it(allLists);
-
- while (it.hasNext()) {
- ConfigList *list = it.next();
-
- list->updateList();
- }
-}
-
-void ConfigList::updateListAllForAll()
-{
- QListIterator<ConfigList *> it(allLists);
-
- while (it.hasNext()) {
- ConfigList *list = it.next();
-
- list->updateList();
- }
-}
-
void ConfigList::setValue(ConfigItem* item, tristate val)
{
struct symbol* sym;
@@ -502,7 +494,7 @@ void ConfigList::setValue(ConfigItem* item, tristate val)
return;
if (oldval == no && item->menu->list)
item->setExpanded(true);
- ConfigList::updateListForAll();
+ parent()->updateList();
break;
}
}
@@ -536,9 +528,13 @@ void ConfigList::changeValue(ConfigItem* item)
item->setExpanded(true);
}
if (oldexpr != newexpr)
- ConfigList::updateListForAll();
+ parent()->updateList();
+ emit UpdateConflictsViewColorization();
break;
- default:
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ parent()->lineEdit->show(item);
break;
}
}
@@ -821,6 +817,15 @@ void ConfigList::mouseReleaseEvent(QMouseEvent* e)
}
}
break;
+ case noColIdx:
+ setValue(item, no);
+ break;
+ case modColIdx:
+ setValue(item, mod);
+ break;
+ case yesColIdx:
+ setValue(item, yes);
+ break;
case dataColIdx:
changeValue(item);
break;
@@ -890,32 +895,107 @@ void ConfigList::contextMenuEvent(QContextMenuEvent *e)
headerPopup = new QMenu(this);
action = new QAction("Show Name", this);
action->setCheckable(true);
- connect(action, &QAction::toggled,
- this, &ConfigList::setShowName);
- connect(this, &ConfigList::showNameChanged,
- action, &QAction::setChecked);
+ connect(action, SIGNAL(toggled(bool)),
+ parent(), SLOT(setShowName(bool)));
+ connect(parent(), SIGNAL(showNameChanged(bool)),
+ action, SLOT(setChecked(bool)));
action->setChecked(showName);
headerPopup->addAction(action);
+
+ action = new QAction("Show Range", this);
+ action->setCheckable(true);
+ connect(action, SIGNAL(toggled(bool)),
+ parent(), SLOT(setShowRange(bool)));
+ connect(parent(), SIGNAL(showRangeChanged(bool)),
+ action, SLOT(setChecked(bool)));
+ action->setChecked(showRange);
+ headerPopup->addAction(action);
+
+ action = new QAction("Show Data", this);
+ action->setCheckable(true);
+ connect(action, SIGNAL(toggled(bool)),
+ parent(), SLOT(setShowData(bool)));
+ connect(parent(), SIGNAL(showDataChanged(bool)),
+ action, SLOT(setChecked(bool)));
+ action->setChecked(showData);
+ headerPopup->addAction(action);
}
headerPopup->exec(e->globalPos());
e->accept();
}
-void ConfigList::setShowName(bool on)
+ConfigView*ConfigView::viewList;
+QAction *ConfigList::showNormalAction;
+QAction *ConfigList::showAllAction;
+QAction *ConfigList::showPromptAction;
+QAction *ConfigList::addSymbolsFromContextMenu;
+
+ConfigView::ConfigView(QWidget* parent, const char *name)
+ : Parent(parent)
{
- if (showName == on)
- return;
+ setObjectName(name);
+ QVBoxLayout *verticalLayout = new QVBoxLayout(this);
+ verticalLayout->setContentsMargins(0, 0, 0, 0);
- showName = on;
- reinit();
- emit showNameChanged(on);
+ list = new ConfigList(this);
+ list->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ list->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(list, SIGNAL(customContextMenuRequested(const QPoint &)),
+ this, SLOT(ShowContextMenu(const QPoint &)));
+ verticalLayout->addWidget(list);
+ lineEdit = new ConfigLineEdit(this);
+ lineEdit->hide();
+ verticalLayout->addWidget(lineEdit);
+
+ this->nextView = viewList;
+ viewList = this;
}
+void ConfigView::ShowContextMenu(const QPoint& pos){
+ QMenu contextMenu(tr("Context menu"), this);
-QList<ConfigList *> ConfigList::allLists;
-QAction *ConfigList::showNormalAction;
-QAction *ConfigList::showAllAction;
-QAction *ConfigList::showPromptAction;
+ contextMenu.addAction(ConfigList::addSymbolsFromContextMenu);
+ contextMenu.exec(mapToGlobal(pos));
+}
+
+ConfigView::~ConfigView(void)
+{
+ ConfigView** vp;
+
+ for (vp = &viewList; *vp; vp = &(*vp)->nextView) {
+ if (*vp == this) {
+ *vp = nextView;
+ break;
+ }
+ }
+}
+
+void ConfigView::setShowName(bool b)
+{
+ if (list->showName != b) {
+ list->showName = b;
+ list->reinit();
+ emit showNameChanged(b);
+ }
+}
+
+void ConfigView::setShowRange(bool b)
+{
+ if (list->showRange != b) {
+ list->showRange = b;
+ list->reinit();
+ emit showRangeChanged(b);
+ }
+}
+
+void ConfigView::setShowData(bool b)
+{
+ if (list->showData != b) {
+ list->showData = b;
+ list->reinit();
+ emit showDataChanged(b);
+ }
+}
void ConfigList::setAllOpen(bool open)
{
@@ -928,6 +1008,415 @@ void ConfigList::setAllOpen(bool open)
}
}
+void ConfigView::updateList()
+{
+ ConfigView* v;
+
+ for (v = viewList; v; v = v->nextView)
+ v->list->updateList();
+}
+
+void ConfigView::updateListAll(void)
+{
+ ConfigView* v;
+
+ for (v = viewList; v; v = v->nextView)
+ v->list->updateListAll();
+}
+
+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");
+
+ // 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);
+
+ verticalLayout->addWidget(conflictsToolBar);
+
+ connect(addSymbol, SIGNAL(triggered(bool)), SLOT(addSymbol()));
+ connect(setConfigSymbolAsNo, SIGNAL(triggered(bool)), SLOT(changeToNo()));
+ connect(setConfigSymbolAsModule, SIGNAL(triggered(bool)), SLOT(changeToModule()));
+ connect(setConfigSymbolAsYes, SIGNAL(triggered(bool)), SLOT(changeToYes()));
+ connect(removeSymbol, SIGNAL(triggered(bool)), SLOT(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_, SIGNAL(triggered(bool)), SLOT(calculateFixes()));
+
+ conflictsTable = (QTableWidget*) new droppableView(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 QTableWidget::dropEvent(QDropEvent *event)
+{
+}
+
+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);
+ ConfigView::updateListAll();
+}
+
+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::menuChanged1(struct menu *m)
+{
+ currentSelectedMenu = m;
+}
+
+void ConflictsView::addSymbol()
+{
+ addSymbol(currentSelectedMenu);
+}
+
+void ConflictsView::selectedChanged(QList<QTreeWidgetItem *> selection)
+{
+ currentSelection = selection;
+
+}
+
+void ConflictsView::addSymbol(struct menu *m)
+{
+ 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, 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;
+ addSymbol(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 symbol* sym = sym_find(itemText);
+ if (sym == NULL) {
+ return;
+ }
+ struct property* prop = sym->prop;
+ struct menu* men = prop->menu;
+ // uncommenting following like somehow disables click signal of 'apply selected solution'
+ if (sym->type == symbol_type::S_BOOLEAN) {
+ conflictsToolBar->actions()[2]->setDisabled(true);
+ } else {
+ 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);
+ struct sfix_list * selected_solution =
+ select_solution(solution_output, current_solution_number);
+ struct symbol_fix* cur_symbol = select_symbol(selected_solution,i);
+
+ // Red: symbol is editable but value is not from solution
+ // Green: symbol is editable and value is from solution
+ // Grey: symbol is not editable and value is not target value
+ // Green: symbol is not editable and value is target value
+ 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)
+ 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");
+ 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");
+
+ 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));
+ }
+
+ numSolutionLabel->setText(QString("Solutions: (%1) found").arg(solution_output->size));
+ changeSolutionTable(0);
+ }
+
+ if (runSatConfAsyncThread->joinable()) {
+ runSatConfAsyncThread->join();
+ delete runSatConfAsyncThread;
+ runSatConfAsyncThread = nullptr;
+ }
+}
+
+void ConflictsView::calculateFixes(void)
+{
+ if (conflictsTable->rowCount() == 0)
+ return;
+
+ if (runSatConfAsyncThread == nullptr) {
+ 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 {
+ interrupt_rangefix();
+ std::unique_lock<std::mutex> lk{satconf_mutex};
+ satconf_cancellation_cv.wait(lk,[this] {
+ return satconf_cancelled == true;
+ });
+ }
+
+}
+
ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name)
: Parent(parent), sym(0), _menu(0)
{
@@ -938,18 +1427,15 @@ ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name)
configSettings->beginGroup(objectName());
setShowDebug(configSettings->value("/showDebug", false).toBool());
configSettings->endGroup();
- connect(configApp, &QApplication::aboutToQuit,
- this, &ConfigInfoView::saveSettings);
+ connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
}
contextMenu = createStandardContextMenu();
QAction *action = new QAction("Show Debug Info", contextMenu);
action->setCheckable(true);
- connect(action, &QAction::toggled,
- this, &ConfigInfoView::setShowDebug);
- connect(this, &ConfigInfoView::showDebugChanged,
- action, &QAction::setChecked);
+ connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
+ connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setChecked(bool)));
action->setChecked(showDebug());
contextMenu->addSeparator();
contextMenu->addAction(action);
@@ -1236,26 +1722,27 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
layout2->setSpacing(6);
layout2->addWidget(new QLabel("Find:", this));
editField = new QLineEdit(this);
- connect(editField, &QLineEdit::returnPressed,
- this, &ConfigSearchWindow::search);
+ connect(editField, SIGNAL(returnPressed()), SLOT(search()));
layout2->addWidget(editField);
searchButton = new QPushButton("Search", this);
searchButton->setAutoDefault(false);
- connect(searchButton, &QPushButton::clicked,
- this, &ConfigSearchWindow::search);
+ connect(searchButton, SIGNAL(clicked()), SLOT(search()));
layout2->addWidget(searchButton);
layout1->addLayout(layout2);
split = new QSplitter(this);
split->setOrientation(Qt::Vertical);
- list = new ConfigList(split, "search");
- list->mode = listMode;
+ list = new ConfigView(split, "search");
+ list->list->mode = listMode;
info = new ConfigInfoView(split, "search");
- connect(list, &ConfigList::menuChanged,
- info, &ConfigInfoView::setInfo);
- connect(list, &ConfigList::menuChanged,
- parent, &ConfigMainWindow::setMenuLink);
-
+ connect(list->list, SIGNAL(menuChanged(struct menu *)),
+ info, SLOT(setInfo(struct menu *)));
+ connect(list->list, SIGNAL(menuChanged(struct menu *)),
+ parent, SLOT(setMenuLink(struct menu *)));
+ connect(list->list, SIGNAL(menuChanged(struct menu *)),
+ parent, SLOT(conflictSelected(struct menu *)));
+
+ connect(list->list,SIGNAL(UpdateConflictsViewColorization()),SLOT(UpdateConflictsViewColorizationFowarder()));
layout1->addWidget(split);
QVariant x, y;
@@ -1274,10 +1761,12 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
if (ok)
split->setSizes(sizes);
configSettings->endGroup();
- connect(configApp, &QApplication::aboutToQuit,
- this, &ConfigSearchWindow::saveSettings);
+ connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
}
+void ConfigSearchWindow::UpdateConflictsViewColorizationFowarder(void){
+ emit UpdateConflictsViewColorization();
+}
void ConfigSearchWindow::saveSettings(void)
{
if (!objectName().isEmpty()) {
@@ -1298,7 +1787,7 @@ void ConfigSearchWindow::search(void)
ConfigItem *lastItem = NULL;
free(result);
- list->clear();
+ list->list->clear();
info->clear();
result = sym_re_search(editField->text().toLatin1());
@@ -1306,7 +1795,7 @@ void ConfigSearchWindow::search(void)
return;
for (p = result; *p; p++) {
for_all_prompts((*p), prop)
- lastItem = new ConfigItem(list, lastItem, prop->menu,
+ lastItem = new ConfigItem(list->list, lastItem, prop->menu,
menu_is_visible(prop->menu));
}
}
@@ -1354,44 +1843,59 @@ ConfigMainWindow::ConfigMainWindow(void)
split1->setOrientation(Qt::Horizontal);
split1->setChildrenCollapsible(false);
- menuList = new ConfigList(widget, "menu");
+ menuView = new ConfigView(widget, "menu");
+ menuList = menuView->list;
split2 = new QSplitter(widget);
split2->setChildrenCollapsible(false);
split2->setOrientation(Qt::Vertical);
// create config tree
- configList = new ConfigList(widget, "config");
+ configView = new ConfigView(widget, "config");
+ configList = configView->list;
helpText = new ConfigInfoView(widget, "help");
layout->addWidget(split2);
split2->addWidget(split1);
- split1->addWidget(configList);
- split1->addWidget(menuList);
+ split1->addWidget(configView);
+ split1->addWidget(menuView);
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();
+ // menu = menuBar();
+ toolBar = new QToolBar("Tools", this);
+ addToolBar(toolBar);
+
+
backAction = new QAction(QPixmap(xpm_back), "Back", this);
- connect(backAction, &QAction::triggered,
- this, &ConfigMainWindow::goBack);
+ connect(backAction, SIGNAL(triggered(bool)), SLOT(goBack()));
QAction *quitAction = new QAction("&Quit", this);
quitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
- connect(quitAction, &QAction::triggered,
- this, &ConfigMainWindow::close);
+ connect(quitAction, SIGNAL(triggered(bool)), SLOT(close()));
QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
loadAction->setShortcut(Qt::CTRL + Qt::Key_L);
- connect(loadAction, &QAction::triggered,
- this, &ConfigMainWindow::loadConfig);
+ connect(loadAction, SIGNAL(triggered(bool)), SLOT(loadConfig()));
saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
saveAction->setShortcut(Qt::CTRL + Qt::Key_S);
- connect(saveAction, &QAction::triggered,
- this, &ConfigMainWindow::saveConfig);
+ connect(saveAction, SIGNAL(triggered(bool)), SLOT(saveConfig()));
conf_set_changed_callback(conf_changed);
@@ -1400,37 +1904,39 @@ ConfigMainWindow::ConfigMainWindow(void)
configname = xstrdup(conf_get_configname());
QAction *saveAsAction = new QAction("Save &As...", this);
- connect(saveAsAction, &QAction::triggered,
- this, &ConfigMainWindow::saveConfigAs);
+ connect(saveAsAction, SIGNAL(triggered(bool)), SLOT(saveConfigAs()));
QAction *searchAction = new QAction("&Find", this);
searchAction->setShortcut(Qt::CTRL + Qt::Key_F);
- connect(searchAction, &QAction::triggered,
- this, &ConfigMainWindow::searchConfig);
+ connect(searchAction, SIGNAL(triggered(bool)), SLOT(searchConfig()));
singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
singleViewAction->setCheckable(true);
- connect(singleViewAction, &QAction::triggered,
- this, &ConfigMainWindow::showSingleView);
+ connect(singleViewAction, SIGNAL(triggered(bool)), SLOT(showSingleView()));
splitViewAction = new QAction(QPixmap(xpm_split_view), "Split View", this);
splitViewAction->setCheckable(true);
- connect(splitViewAction, &QAction::triggered,
- this, &ConfigMainWindow::showSplitView);
+ connect(splitViewAction, SIGNAL(triggered(bool)), SLOT(showSplitView()));
fullViewAction = new QAction(QPixmap(xpm_tree_view), "Full View", this);
fullViewAction->setCheckable(true);
- connect(fullViewAction, &QAction::triggered,
- this, &ConfigMainWindow::showFullView);
+ connect(fullViewAction, SIGNAL(triggered(bool)), SLOT(showFullView()));
+
+
QAction *showNameAction = new QAction("Show Name", this);
showNameAction->setCheckable(true);
- connect(showNameAction, &QAction::toggled,
- configList, &ConfigList::setShowName);
- showNameAction->setChecked(configList->showName);
+ connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool)));
+ showNameAction->setChecked(configView->showName());
+ QAction *showRangeAction = new QAction("Show Range", this);
+ showRangeAction->setCheckable(true);
+ connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool)));
+ QAction *showDataAction = new QAction("Show Data", this);
+ showDataAction->setCheckable(true);
+ connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool)));
QActionGroup *optGroup = new QActionGroup(this);
optGroup->setExclusive(true);
- connect(optGroup, &QActionGroup::triggered,
- configList, &ConfigList::setOptionMode);
- connect(optGroup, &QActionGroup::triggered,
- menuList, &ConfigList::setOptionMode);
+ connect(optGroup, SIGNAL(triggered(QAction*)), configList,
+ SLOT(setOptionMode(QAction *)));
+ connect(optGroup, SIGNAL(triggered(QAction *)), menuList,
+ SLOT(setOptionMode(QAction *)));
ConfigList::showNormalAction = new QAction("Show Normal Options", optGroup);
ConfigList::showNormalAction->setCheckable(true);
@@ -1438,19 +1944,18 @@ ConfigMainWindow::ConfigMainWindow(void)
ConfigList::showAllAction->setCheckable(true);
ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup);
ConfigList::showPromptAction->setCheckable(true);
+ ConfigList::addSymbolsFromContextMenu = new QAction("Add symbol from context menu");
+ connect(ConfigList::addSymbolsFromContextMenu, SIGNAL(triggered()),conflictsView, SLOT(addSymbolFromContextMenu()));
QAction *showDebugAction = new QAction("Show Debug Info", this);
showDebugAction->setCheckable(true);
- connect(showDebugAction, &QAction::toggled,
- helpText, &ConfigInfoView::setShowDebug);
+ connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool)));
showDebugAction->setChecked(helpText->showDebug());
QAction *showIntroAction = new QAction("Introduction", this);
- connect(showIntroAction, &QAction::triggered,
- this, &ConfigMainWindow::showIntro);
+ connect(showIntroAction, SIGNAL(triggered(bool)), SLOT(showIntro()));
QAction *showAboutAction = new QAction("About", this);
- connect(showAboutAction, &QAction::triggered,
- this, &ConfigMainWindow::showAbout);
+ connect(showAboutAction, SIGNAL(triggered(bool)), SLOT(showAbout()));
// init tool bar
QToolBar *toolBar = addToolBar("Tools");
@@ -1462,6 +1967,7 @@ ConfigMainWindow::ConfigMainWindow(void)
toolBar->addAction(singleViewAction);
toolBar->addAction(splitViewAction);
toolBar->addAction(fullViewAction);
+ toolBar->addSeparator();
// create file menu
QMenu *menu = menuBar()->addMenu("&File");
@@ -1478,6 +1984,8 @@ ConfigMainWindow::ConfigMainWindow(void)
// create options menu
menu = menuBar()->addMenu("&Option");
menu->addAction(showNameAction);
+ menu->addAction(showRangeAction);
+ menu->addAction(showDataAction);
menu->addSeparator();
menu->addActions(optGroup->actions());
menu->addSeparator();
@@ -1488,30 +1996,37 @@ ConfigMainWindow::ConfigMainWindow(void)
menu->addAction(showIntroAction);
menu->addAction(showAboutAction);
- connect(helpText, &ConfigInfoView::anchorClicked,
- helpText, &ConfigInfoView::clicked);
-
- connect(configList, &ConfigList::menuChanged,
- helpText, &ConfigInfoView::setInfo);
- connect(configList, &ConfigList::menuSelected,
- this, &ConfigMainWindow::changeMenu);
- connect(configList, &ConfigList::itemSelected,
- this, &ConfigMainWindow::changeItens);
- connect(configList, &ConfigList::parentSelected,
- this, &ConfigMainWindow::goBack);
- connect(menuList, &ConfigList::menuChanged,
- helpText, &ConfigInfoView::setInfo);
- connect(menuList, &ConfigList::menuSelected,
- this, &ConfigMainWindow::changeMenu);
-
- connect(configList, &ConfigList::gotFocus,
- helpText, &ConfigInfoView::setInfo);
- connect(menuList, &ConfigList::gotFocus,
- helpText, &ConfigInfoView::setInfo);
- connect(menuList, &ConfigList::gotFocus,
- this, &ConfigMainWindow::listFocusChanged);
- connect(helpText, &ConfigInfoView::menuSelected,
- this, &ConfigMainWindow::setMenuLink);
+ connect (helpText, SIGNAL (anchorClicked (const QUrl &)),
+ helpText, SLOT (clicked (const QUrl &)) );
+
+ connect(configList, SIGNAL(menuChanged(struct menu *)),
+ helpText, SLOT(setInfo(struct menu *)));
+ connect(configList, SIGNAL(menuSelected(struct menu *)),
+ SLOT(changeMenu(struct menu *)));
+ connect(configList, SIGNAL(itemSelected(struct menu *)),
+ SLOT(changeItens(struct menu *)));
+ connect(configList, SIGNAL(parentSelected()),
+ SLOT(goBack()));
+ connect(menuList, SIGNAL(menuChanged(struct menu *)),
+ helpText, SLOT(setInfo(struct menu *)));
+ connect(menuList, SIGNAL(menuSelected(struct menu *)),
+ SLOT(changeMenu(struct menu *)));
+
+ //pass the list of selected items in configList to conflictsView so that
+ //when right click 'add symbols to conflict' is clicked it will be added
+ //to the list
+ connect(configList, SIGNAL(selectedChanged(QList<QTreeWidgetItem*>)),
+ conflictsView, SLOT(selectedChanged(QList<QTreeWidgetItem*>)));
+ connect(configList, SIGNAL(menuChanged(struct menu *)),
+ conflictsView, SLOT(menuChanged1(struct menu *)));
+ connect(configList, SIGNAL(gotFocus(struct menu *)),
+ helpText, SLOT(setInfo(struct menu *)));
+ connect(menuList, SIGNAL(gotFocus(struct menu *)),
+ helpText, SLOT(setInfo(struct menu *)));
+ connect(menuList, SIGNAL(gotFocus(struct menu *)),
+ SLOT(listFocusChanged(void)));
+ connect(helpText, SIGNAL(menuSelected(struct menu *)),
+ SLOT(setMenuLink(struct menu *)));
QString listMode = configSettings->value("/listMode", "symbol").toString();
if (listMode == "single")
@@ -1550,7 +2065,7 @@ void ConfigMainWindow::loadConfig(void)
free(configname);
configname = xstrdup(name);
- ConfigList::updateListAllForAll();
+ ConfigView::updateListAll();
}
bool ConfigMainWindow::saveConfig(void)
@@ -1588,8 +2103,10 @@ void ConfigMainWindow::saveConfigAs(void)
void ConfigMainWindow::searchConfig(void)
{
- if (!searchWindow)
+ if (!searchWindow){
searchWindow = new ConfigSearchWindow(this);
+ connect(searchWindow,SIGNAL(UpdateConflictsViewColorization()),conflictsView,SLOT(UpdateConflictsViewColorization()));
+ }
searchWindow->show();
}
@@ -1685,7 +2202,7 @@ void ConfigMainWindow::showSingleView(void)
backAction->setEnabled(true);
- menuList->hide();
+ menuView->hide();
menuList->setRootMenu(0);
configList->mode = singleMode;
if (configList->rootEntry == &rootmenu)
@@ -1716,10 +2233,17 @@ void ConfigMainWindow::showSplitView(void)
menuList->mode = symbolMode;
menuList->setRootMenu(&rootmenu);
menuList->setAllOpen(true);
- menuList->show();
+ menuView->show();
menuList->setFocus();
}
+void ConfigMainWindow::conflictSelected(struct menu * men)
+{
+ configList->clearSelection();
+ menuList->clearSelection();
+ emit(setMenuLink(men));
+}
+
void ConfigMainWindow::showFullView(void)
{
singleViewAction->setEnabled(true);
@@ -1731,7 +2255,7 @@ void ConfigMainWindow::showFullView(void)
backAction->setEnabled(false);
- menuList->hide();
+ menuView->hide();
menuList->setRootMenu(0);
configList->mode = fullMode;
if (configList->rootEntry == &rootmenu)
@@ -1773,26 +2297,17 @@ void ConfigMainWindow::closeEvent(QCloseEvent* e)
void ConfigMainWindow::showIntro(void)
{
- static const QString str =
- "Welcome to the qconf graphical configuration tool.\n"
- "\n"
- "For bool and tristate options, a blank box indicates the "
- "feature is disabled, a check indicates it is enabled, and a "
- "dot indicates that it is to be compiled as a module. Clicking "
- "on the box will cycle through the three states. For int, hex, "
- "and string options, double-clicking or pressing F2 on the "
- "Value cell will allow you to edit the value.\n"
- "\n"
- "If you do not see an option (e.g., a device driver) that you "
- "believe should be present, try turning on Show All Options "
- "under the Options menu. Enabling Show Debug Info will help you"
- "figure out what other options must be enabled to support the "
- "option you are interested in, and hyperlinks will navigate to "
- "them.\n"
- "\n"
- "Toggling Show Debug Info under the Options menu will show the "
- "dependencies, which you can then match by examining other "
- "options.\n";
+ static const QString str = "Welcome to the qconf graphical configuration tool.\n\n"
+ "For each option, a blank box indicates the feature is disabled, a check\n"
+ "indicates it is enabled, and a dot indicates that it is to be compiled\n"
+ "as a module. Clicking on the box will cycle through the three states.\n\n"
+ "If you do not see an option (e.g., a device driver) that you believe\n"
+ "should be present, try turning on Show All Options under the Options menu.\n"
+ "Although there is no cross reference yet to help you figure out what other\n"
+ "options must be enabled to support the option you are interested in, you can\n"
+ "still view the help of a grayed-out option.\n\n"
+ "Toggling Show Debug Info under the Options menu will show the dependencies,\n"
+ "which you can then match by examining other options.\n\n";
QMessageBox::information(this, "qconf", str);
}
@@ -1800,13 +2315,10 @@ void ConfigMainWindow::showIntro(void)
void ConfigMainWindow::showAbout(void)
{
static const QString str = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n"
- "Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n"
- "\n"
- "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n"
- "\n"
- "Qt Version: ";
+ "Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n\n"
+ "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n";
- QMessageBox::information(this, "qconf", str + qVersion());
+ QMessageBox::information(this, "qconf", str);
}
void ConfigMainWindow::saveSettings(void)
@@ -1844,6 +2356,10 @@ void ConfigMainWindow::conf_changed(void)
if (saveAction)
saveAction->setEnabled(conf_get_changed());
}
+void ConfigMainWindow::refreshMenu(void)
+{
+ configList->updateListAll();
+}
void fixup_rootmenu(struct menu *menu)
{
@@ -1875,6 +2391,7 @@ int main(int ac, char** av)
const char *name;
progname = av[0];
+ configApp = new QApplication(ac, av);
if (ac > 1 && av[1][0] == '-') {
switch (av[1][1]) {
case 's':
@@ -1895,8 +2412,6 @@ int main(int ac, char** av)
conf_read(NULL);
//zconfdump(stdout);
- configApp = new QApplication(ac, av);
-
configSettings = new ConfigSettings();
configSettings->beginGroup("/kconfig/qconf");
v = new ConfigMainWindow();
@@ -1914,3 +2429,33 @@ int main(int ac, char** av)
return 0;
}
+
+void droppableView::dropEvent(QDropEvent *event)
+{
+ event->acceptProposedAction();
+}
+
+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("");
+ }
+}
+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 78b0a1dfcd53..3b0b398ebfe6 100644
--- a/scripts/kconfig/qconf.h
+++ b/scripts/kconfig/qconf.h
@@ -11,14 +11,25 @@
#include <QPushButton>
#include <QSettings>
#include <QSplitter>
-#include <QStyledItemDelegate>
#include <QTextBrowser>
#include <QTreeWidget>
+#include <QListWidget>
+#include <QTableWidget>
+#include <QList>
+#include <QComboBox>
+#include <QLabel>
+#include <qstring.h>
+#include <thread>
+#include <condition_variable>
#include "expr.h"
+#include "configfix.h"
+
+class ConfigView;
class ConfigList;
class ConfigItem;
+class ConfigLineEdit;
class ConfigMainWindow;
class ConfigSettings : public QSettings {
@@ -29,7 +40,7 @@ class ConfigSettings : public QSettings {
};
enum colIdx {
- promptColIdx, nameColIdx, dataColIdx
+ promptColIdx, nameColIdx, noColIdx, modColIdx, yesColIdx, dataColIdx
};
enum listMode {
singleMode, menuMode, symbolMode, fullMode, listMode
@@ -38,14 +49,31 @@ enum optionMode {
normalOpt = 0, allOpt, promptOpt
};
+enum symbolStatus {
+ UNSATISFIED, SATISFIED
+};
+
+typedef struct {
+ QString symbol;
+ QString change_needed;
+ enum symbolStatus status;
+ tristate change_requested;
+} Constraint;
+
+QString tristate_value_to_string(tristate val);
+tristate string_value_to_tristate(QString s);
+
class ConfigList : public QTreeWidget {
Q_OBJECT
typedef class QTreeWidget Parent;
public:
- ConfigList(QWidget *parent, const char *name = 0);
- ~ConfigList();
+ ConfigList(ConfigView* p, const char *name = 0);
void reinit(void);
ConfigItem* findConfigItem(struct menu *);
+ ConfigView* parent(void) const
+ {
+ return (ConfigView*)Parent::parent();
+ }
void setSelected(QTreeWidgetItem *item, bool enable) {
for (int i = 0; i < selectedItems().size(); i++)
selectedItems().at(i)->setSelected(false);
@@ -71,7 +99,6 @@ public slots:
void updateSelection(void);
void saveSettings(void);
void setOptionMode(QAction *action);
- void setShowName(bool on);
signals:
void menuChanged(struct menu *menu);
@@ -79,7 +106,8 @@ public slots:
void itemSelected(struct menu *menu);
void parentSelected(void);
void gotFocus(struct menu *);
- void showNameChanged(bool on);
+ void selectedChanged(QList<QTreeWidgetItem*> selection);
+ void UpdateConflictsViewColorization();
public:
void updateListAll(void)
@@ -98,7 +126,7 @@ public slots:
bool updateAll;
- bool showName;
+ bool showName, showRange, showData;
enum listMode mode;
enum optionMode optMode;
struct menu *rootEntry;
@@ -106,11 +134,7 @@ public slots:
QPalette inactivedColorGroup;
QMenu* headerPopup;
- static QList<ConfigList *> allLists;
- static void updateListForAll();
- static void updateListAllForAll();
-
- static QAction *showNormalAction, *showAllAction, *showPromptAction;
+ static QAction *showNormalAction, *showAllAction, *showPromptAction, *addSymbolsFromContextMenu;
};
class ConfigItem : public QTreeWidgetItem {
@@ -133,6 +157,7 @@ class ConfigItem : public QTreeWidgetItem {
}
~ConfigItem(void);
void init(void);
+ void okRename(int col);
void updateMenu(void);
void testUpdateMenu(bool v);
ConfigList* listView() const
@@ -169,18 +194,104 @@ class ConfigItem : public QTreeWidgetItem {
static QIcon menuIcon, menubackIcon;
};
-class ConfigItemDelegate : public QStyledItemDelegate
-{
-private:
- struct menu *menu;
+class ConfigLineEdit : public QLineEdit {
+ Q_OBJECT
+ typedef class QLineEdit Parent;
+public:
+ ConfigLineEdit(ConfigView* parent);
+ ConfigView* parent(void) const
+ {
+ return (ConfigView*)Parent::parent();
+ }
+ void show(ConfigItem *i);
+ void keyPressEvent(QKeyEvent *e);
+
+public:
+ ConfigItem *item;
+};
+
+class ConfigView : public QWidget {
+ Q_OBJECT
+ typedef class QWidget Parent;
public:
- ConfigItemDelegate(QObject *parent = nullptr)
- : QStyledItemDelegate(parent) {}
- QWidget *createEditor(QWidget *parent,
- const QStyleOptionViewItem &option,
- const QModelIndex &index) const override;
- void setModelData(QWidget *editor, QAbstractItemModel *model,
- const QModelIndex &index) const override;
+ ConfigView(QWidget* parent, const char *name = 0);
+ ~ConfigView(void);
+ static void updateList();
+ static void updateListAll(void);
+
+ bool showName(void) const { return list->showName; }
+ bool showRange(void) const { return list->showRange; }
+ bool showData(void) const { return list->showData; }
+public slots:
+ void setShowName(bool);
+ void setShowRange(bool);
+ void setShowData(bool);
+
+ void ShowContextMenu(const QPoint& p);
+signals:
+ void showNameChanged(bool);
+ void showRangeChanged(bool);
+ void showDataChanged(bool);
+public:
+ ConfigList* list;
+ ConfigLineEdit* lineEdit;
+
+ static ConfigView* viewList;
+ ConfigView* nextView;
+
+ static QAction *showNormalAction;
+ static QAction *showAllAction;
+ static QAction *showPromptAction;
+ static QAction *addSymbolsFromContextMenu;
+};
+class ConflictsView : public QWidget {
+ ConfigLineEdit* lineEdit;
+ Q_OBJECT
+ typedef class QWidget Parent;
+public:
+ ConflictsView(QWidget* parent, const char *name = 0);
+ void addSymbol(struct menu * m);
+ int current_solution_number = -1;
+
+public slots:
+ void cellClicked(int, int);
+ void addSymbol();
+ void addSymbolFromContextMenu();
+ void removeSymbol();
+ void menuChanged1(struct menu *);
+ void changeToNo();
+ void changeToYes();
+ void changeToModule();
+ void selectedChanged(QList<QTreeWidgetItem*> selection);
+ void applyFixButtonClick();
+ void UpdateConflictsViewColorization();
+ void updateResults();
+ void changeSolutionTable(int solution_number);
+ void calculateFixes();
+signals:
+ void showNameChanged(bool);
+ void showRangeChanged(bool);
+ void showDataChanged(bool);
+ void conflictSelected(struct menu *);
+ void refreshMenu();
+ void resultsReady();
+public:
+ QTableWidget* conflictsTable;
+ QList<Constraint> constraints;
+ QComboBox* solutionSelector{nullptr};
+ QTableWidget* solutionTable{nullptr};
+ QPushButton* applyFixButton{nullptr};
+ struct sfl_list * solution_output{nullptr};
+ QToolBar *conflictsToolBar;
+ struct menu * currentSelectedMenu ;
+ QLabel* numSolutionLabel{nullptr};
+ 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 ConfigInfoView : public QTextBrowser {
@@ -223,17 +334,21 @@ class ConfigSearchWindow : public QDialog {
public slots:
void saveSettings(void);
void search(void);
+ void UpdateConflictsViewColorizationFowarder();
+signals:
+ void UpdateConflictsViewColorization();
protected:
QLineEdit* editField;
QPushButton* searchButton;
QSplitter* split;
- ConfigList *list;
+ ConfigView* list;
ConfigInfoView* info;
struct symbol **result;
};
+
class ConfigMainWindow : public QMainWindow {
Q_OBJECT
@@ -258,18 +373,34 @@ public slots:
void showIntro(void);
void showAbout(void);
void saveSettings(void);
+ void conflictSelected(struct menu *);
+ void refreshMenu();
protected:
void closeEvent(QCloseEvent *e);
ConfigSearchWindow *searchWindow;
+ ConfigView *menuView;
ConfigList *menuList;
+ ConfigView *configView;
ConfigList *configList;
ConfigInfoView *helpText;
+ ConflictsView *conflictsView;
+ QToolBar *toolBar;
+ QToolBar *conflictsToolBar;
QAction *backAction;
QAction *singleViewAction;
QAction *splitViewAction;
QAction *fullViewAction;
QSplitter *split1;
QSplitter *split2;
+ QSplitter *split3;
+};
+
+class droppableView : public QTableWidget
+{
+public:
+ droppableView(QWidget *parent = nullptr) {}
+protected:
+ void dropEvent(QDropEvent *event);
};
--
2.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC 12/12] Simplify dependencies for MODULE_SIG_KEY_TYPE_RSA & MODULE_SIG_KEY_TYPE_ECDSA
2021-10-20 9:32 [RFC 0/12] kconfig: add support for conflict resolution Thorsten Berger
` (10 preceding siblings ...)
2021-10-20 9:48 ` [RFC 11/12] Add xconfig-modifications Thorsten Berger
@ 2021-10-20 9:49 ` Thorsten Berger
2021-10-28 2:48 ` Masahiro Yamada
11 siblings, 1 reply; 15+ messages in thread
From: Thorsten Berger @ 2021-10-20 9:49 UTC (permalink / raw)
To: linux-kbuild
Cc: Luis R. Rodriguez, deltaone, phayax, Eugene Groshev, Sarah Nadi,
Mel Gorman, Luis R. Rodriguez
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@suse.com>
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>
---
certs/Kconfig | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/certs/Kconfig b/certs/Kconfig
index ae7f2e876a31..f69c92e5bc30 100644
--- a/certs/Kconfig
+++ b/certs/Kconfig
@@ -17,6 +17,7 @@ config MODULE_SIG_KEY
choice
prompt "Type of module signing key to be generated"
+ depends on MODULE_SIG || (IMA_APPRAISE_MODSIG && MODULES)
default MODULE_SIG_KEY_TYPE_RSA
help
The type of module signing key type to generate. This option
@@ -24,14 +25,12 @@ choice
config MODULE_SIG_KEY_TYPE_RSA
bool "RSA"
- depends on MODULE_SIG || (IMA_APPRAISE_MODSIG && MODULES)
help
Use an RSA key for module signing.
config MODULE_SIG_KEY_TYPE_ECDSA
bool "ECDSA"
select CRYPTO_ECDSA
- depends on MODULE_SIG || (IMA_APPRAISE_MODSIG && MODULES)
help
Use an elliptic curve key (NIST P384) for module signing. Consider
using a strong hash like sha256 or sha384 for hashing modules.
--
2.33.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [RFC 12/12] Simplify dependencies for MODULE_SIG_KEY_TYPE_RSA & MODULE_SIG_KEY_TYPE_ECDSA
2021-10-20 9:49 ` [RFC 12/12] Simplify dependencies for MODULE_SIG_KEY_TYPE_RSA & MODULE_SIG_KEY_TYPE_ECDSA Thorsten Berger
@ 2021-10-28 2:48 ` Masahiro Yamada
0 siblings, 0 replies; 15+ messages in thread
From: Masahiro Yamada @ 2021-10-28 2:48 UTC (permalink / raw)
To: Thorsten Berger
Cc: Linux Kbuild mailing list, Luis R. Rodriguez, deltaone, phayax,
Eugene Groshev, Sarah Nadi, Mel Gorman, Luis R. Rodriguez
On Wed, Oct 20, 2021 at 6:49 PM Thorsten Berger <thorsten.berger@rub.de> wrote:
>
> 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@suse.com>
> 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>
>
I had sent an equivalent patch before, (with commit description).
https://lore.kernel.org/all/20211001040126.1200230-1-masahiroy@kernel.org/
> ---
> certs/Kconfig | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/certs/Kconfig b/certs/Kconfig
> index ae7f2e876a31..f69c92e5bc30 100644
> --- a/certs/Kconfig
> +++ b/certs/Kconfig
> @@ -17,6 +17,7 @@ config MODULE_SIG_KEY
>
> choice
> prompt "Type of module signing key to be generated"
> + depends on MODULE_SIG || (IMA_APPRAISE_MODSIG && MODULES)
> default MODULE_SIG_KEY_TYPE_RSA
> help
> The type of module signing key type to generate. This option
> @@ -24,14 +25,12 @@ choice
>
> config MODULE_SIG_KEY_TYPE_RSA
> bool "RSA"
> - depends on MODULE_SIG || (IMA_APPRAISE_MODSIG && MODULES)
> help
> Use an RSA key for module signing.
>
> config MODULE_SIG_KEY_TYPE_ECDSA
> bool "ECDSA"
> select CRYPTO_ECDSA
> - depends on MODULE_SIG || (IMA_APPRAISE_MODSIG && MODULES)
> help
> Use an elliptic curve key (NIST P384) for module signing. Consider
> using a strong hash like sha256 or sha384 for hashing modules.
> --
> 2.33.0
>
>
--
Best Regards
Masahiro Yamada
^ permalink raw reply [flat|nested] 15+ messages in thread