From patchwork Wed Oct 20 09:35:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571897 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1A20FC433F5 for ; Wed, 20 Oct 2021 09:35:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E3A9561361 for ; Wed, 20 Oct 2021 09:35:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229985AbhJTJhW (ORCPT ); Wed, 20 Oct 2021 05:37:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40064 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229555AbhJTJhV (ORCPT ); Wed, 20 Oct 2021 05:37:21 -0400 Received: from out2.mail.ruhr-uni-bochum.de (out2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2ae5]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 33680C06161C for ; Wed, 20 Oct 2021 02:35:07 -0700 (PDT) Received: from mx2.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out2.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ57F6XrMz8SQb; Wed, 20 Oct 2021 11:35:05 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634722505; bh=STUSXPc3lrcun77btY/ueSlVGyxBpr0iiAthOOVwzu8=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=FuIB6n26f7V3pWJNtDgSFjmhF+Wfxt1sEk12OEK6duc0ZhCqOZ7ZJ9ToVVo/FnWlo Hba7zQa/rBvOp+nUrO5EjmcrVh2+rWBA6mZNMl2YO1it4ZuCWAiVVOG7PH+8IfUmCx 3n2SZ9SFx6XR5KkymTBjUO5BFFv4wN6lWsQ2X9Vg= Received: from out2.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx2.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ57F5VzFz8SQ4; Wed, 20 Oct 2021 11:35:05 +0200 (CEST) X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec X-Envelope-Sender: Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out2.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ57F3YT3z8SNZ; Wed, 20 Oct 2021 11:35:05 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx2.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ57D60X7zDgyl; Wed, 20 Oct 2021 11:35:04 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: <0e7cd166-1d5e-8b05-e748-1c50f44cbfa4@rub.de> Date: Wed, 20 Oct 2021 11:35:03 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 01/12] Add picosat.h Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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 +#include +#include + +/*------------------------------------------------------------------------*/ +/* 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 n */ +int picosat_added_original_clauses (PicoSAT *);         /* p cnf m */ +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 From patchwork Wed Oct 20 09:36:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571899 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C6E74C433F5 for ; Wed, 20 Oct 2021 09:36:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 894176135E for ; Wed, 20 Oct 2021 09:36:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230029AbhJTJi2 (ORCPT ); Wed, 20 Oct 2021 05:38:28 -0400 Received: from out3.mail.ruhr-uni-bochum.de ([134.147.53.155]:17093 "EHLO out3.mail.ruhr-uni-bochum.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230058AbhJTJi1 (ORCPT ); Wed, 20 Oct 2021 05:38:27 -0400 X-Greylist: delayed 57959 seconds by postgrey-1.27 at vger.kernel.org; Wed, 20 Oct 2021 05:38:26 EDT Received: from mx3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out3.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ58V1LQnz8S8L; Wed, 20 Oct 2021 11:36:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634722570; bh=moYHj/lP3XWYHAhfQrPg0o9C4tiT55gT1fivRG/AUQ4=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=MzKlCryCUEnFNfluMj7BrUC+4GdS1+S5B4paq5Tns+W8no1qP3L87n4oixR2c1emk xR8ImBlCJDIttyDd7Zt693JO/RgVH33yL86itbitt4yZ86nsMejLBFWmiiCwqpWeqg grGe7OXd9LHmhNYcpG42I+sLuwyxSyXgN/8HcN2U= Received: from out3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx3.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ58V0J49z8SDR; Wed, 20 Oct 2021 11:36:10 +0200 (CEST) X-RUB-Notes: Internal origin=134.147.42.236 X-Envelope-Sender: Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [134.147.42.236]) by out3.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ58T49XSz8S9q; Wed, 20 Oct 2021 11:36:09 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx3.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ58T0gDfzDgyg; Wed, 20 Oct 2021 11:36:09 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: Date: Wed, 20 Oct 2021 11:36:07 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 02/12] Add picosat.c (1/3) Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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 +#include +#include +#include +#include +#include +#include +#include + +#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         /* for 'usleep' */ +#endif +#endif + +#ifdef RCODE +#include +#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<= 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; From patchwork Wed Oct 20 09:37:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571901 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4319DC433F5 for ; Wed, 20 Oct 2021 09:37:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1D84F61359 for ; Wed, 20 Oct 2021 09:37:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229704AbhJTJjw (ORCPT ); Wed, 20 Oct 2021 05:39:52 -0400 Received: from out3.mail.ruhr-uni-bochum.de ([134.147.53.155]:18854 "EHLO out3.mail.ruhr-uni-bochum.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229555AbhJTJjv (ORCPT ); Wed, 20 Oct 2021 05:39:51 -0400 Received: from mx3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out3.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5B83m6sz8S8g; Wed, 20 Oct 2021 11:37:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634722656; bh=nUzMwANwU2IQ09ecrjCfeVgC6nrYljivUwNxsrBe9eM=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=vRg/e8zNYXf0OX91G+zQKHID+5tsk42YS0nynKtZeSsNqw1JQk8SkxeLgUkeL2zuo vKo3JqLMNsKHvv6+zqpmxTdOT0jNs+VErAMv+7C0gV/EIkxYPSWYA3+Q4ctcyVRzFN GIR9M7wphsOom56gDMz5lnfxjbJqaDiX3scoP8W8= Received: from out3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx3.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5B82b1pz8S71; Wed, 20 Oct 2021 11:37:36 +0200 (CEST) X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec X-Envelope-Sender: Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out3.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5B80sKjz8S7h; Wed, 20 Oct 2021 11:37:36 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx3.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5B74VHdzDgyl; Wed, 20 Oct 2021 11:37:35 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: <7a82ff8a-cf0b-d234-58ce-a99c262c76ee@rub.de> Date: Wed, 20 Oct 2021 11:37:34 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 03/12] Add picosat.c (2/3) Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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); From patchwork Wed Oct 20 09:38:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571907 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 82439C433F5 for ; Wed, 20 Oct 2021 09:38:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5F80C6135E for ; Wed, 20 Oct 2021 09:38:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229881AbhJTJlJ (ORCPT ); Wed, 20 Oct 2021 05:41:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40920 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229555AbhJTJlI (ORCPT ); Wed, 20 Oct 2021 05:41:08 -0400 Received: from out1.mail.ruhr-uni-bochum.de (out1.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:8:1001::8693:3595]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E4B01C06161C for ; Wed, 20 Oct 2021 02:38:53 -0700 (PDT) Received: from mx1.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out1.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5Cc10SWz8S5T; Wed, 20 Oct 2021 11:38:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634722732; bh=0Hy5/5JvT7i3Zud40JHckKtpwttjzl0l18d3t6ddIHk=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=EFr3tPf4X0sXncocrqAr2RNy4K1mOS0zsKiNJ/xciPe5yvK74XplAHfzfF/tRkwpI bjioHz+lN55xZqP/oM0f+AIU04+i7TdePXZ7hR3fDKkVA/vaYsAoQtWAnLFzh4nv7R eOYsjMhYDxn68BJHInfCD7Tqal22R2JPkwkHJ6Hk= Received: from out1.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx1.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5Cb6w17z8S5g; Wed, 20 Oct 2021 11:38:51 +0200 (CEST) X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec X-Envelope-Sender: Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out1.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5Cb4PzBz8S5Q; Wed, 20 Oct 2021 11:38:51 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx1.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5Cb0hmhzDgyl; Wed, 20 Oct 2021 11:38:51 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: Date: Wed, 20 Oct 2021 11:38:49 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 04/12] Add picosat.c (3/3) Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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 +#include +#include +#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); +} From patchwork Wed Oct 20 09:40:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571909 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0A38CC433EF for ; Wed, 20 Oct 2021 09:40:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DED0861359 for ; Wed, 20 Oct 2021 09:40:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229702AbhJTJmj (ORCPT ); Wed, 20 Oct 2021 05:42:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41252 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229555AbhJTJmj (ORCPT ); Wed, 20 Oct 2021 05:42:39 -0400 Received: from out3.mail.ruhr-uni-bochum.de (out3.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:8:1001::8693:359b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C16F9C06161C for ; Wed, 20 Oct 2021 02:40:24 -0700 (PDT) Received: from mx3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out3.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5FM0mQMz8SDc; Wed, 20 Oct 2021 11:40:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634722823; bh=RsfdadZ9oZ4mZTwyeKMED1l2KzHbZnN45a2z2PbA5Mg=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=Ec/x6qhlqrnuSdBS5WD/IknwkOMNIF0F9DbkuEA4vKPfnzby6nbKTjw/XzZorC5k9 NlNz8PaOYPmgbdtGMXyoy6Nw/RV89rRi0Wv/oVH4X+Rv899RfrGK5g5VOh5P9RWm6k jXIN1IMkD1xW/zBuszSfla86aTM2XoEdY1rLCGfs= Received: from out3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx3.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5FL6rFfz8SDH; Wed, 20 Oct 2021 11:40:22 +0200 (CEST) X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec X-Envelope-Sender: Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out3.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5FL5Lstz8SCP; Wed, 20 Oct 2021 11:40:22 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx3.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5FL2qmfzDgyg; Wed, 20 Oct 2021 11:40:22 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: <7b210b5f-48eb-ae82-23fe-2e17686c187f@rub.de> Date: Wed, 20 Oct 2021 11:40:20 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 05/12] Add definitions Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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 + */ + +#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) From patchwork Wed Oct 20 09:41:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571911 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AC9D0C433EF for ; Wed, 20 Oct 2021 09:41:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8126A6112D for ; Wed, 20 Oct 2021 09:41:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229702AbhJTJny (ORCPT ); Wed, 20 Oct 2021 05:43:54 -0400 Received: from out2.mail.ruhr-uni-bochum.de ([134.147.42.229]:28557 "EHLO out2.mail.ruhr-uni-bochum.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229555AbhJTJny (ORCPT ); Wed, 20 Oct 2021 05:43:54 -0400 Received: from mx2.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out2.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5Gp4ph1z8SPt; Wed, 20 Oct 2021 11:41:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634722898; bh=sSEylN+YFlhX4JHCyOv12PsVuq/xQt4PtD+50FqmKH4=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=wgJMzzMkT3P1O8Qe00teBVTc2oWFldUiNEIM1oYpke5twNw7ml3ArPJi7mPYfBqFz 7BkZ4EenbShhBNMWMJ0yZ0FGgCygHDYo5bhrnrqL2FD90kfwZ1R74RPMM5gCYh7TXE FCamXkeEvK0kMxsLrxQeDhXxtBfFpYKUiKXzzOkg= Received: from out2.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx2.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5Gp3pHvz8SNx; Wed, 20 Oct 2021 11:41:38 +0200 (CEST) X-Envelope-Sender: X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out2.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5Gp24SGz8SPt; Wed, 20 Oct 2021 11:41:38 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx2.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5Gn4zLrzDgyf; Wed, 20 Oct 2021 11:41:37 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: Date: Wed, 20 Oct 2021 11:41:36 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 06/12] Add files for building constraints Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#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 From patchwork Wed Oct 20 09:43:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571917 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1EE15C433F5 for ; Wed, 20 Oct 2021 09:43:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DC136611C6 for ; Wed, 20 Oct 2021 09:43:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229632AbhJTJpW (ORCPT ); Wed, 20 Oct 2021 05:45:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41830 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229570AbhJTJpV (ORCPT ); Wed, 20 Oct 2021 05:45:21 -0400 Received: from out3.mail.ruhr-uni-bochum.de (out3.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:8:1001::8693:359b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A77D5C06161C for ; Wed, 20 Oct 2021 02:43:06 -0700 (PDT) Received: from mx3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out3.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5JT2sfhz8SFg; Wed, 20 Oct 2021 11:43:05 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634722985; bh=5aOcVbLbB16UnueR64SouTRjcbgfk/VdJycpNeD3RFQ=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=oSNdUl6m0/OmSrCZgl0pkQqNF0E7g45JbV4Cm5Ofw8THnLX/+2TcAs6ZbnZT0JCG1 TjIZAgj2wegZZ2LTd8jv2A/Zois95zQE7ldGpP+COv6rnxcncAvPHne5Nlyizn/hca NBBQl9kQMAnzYp/BSnS5QImubnEmKYkAyGhn9q30= Received: from out3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx3.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5JT1fx0z8SDH; Wed, 20 Oct 2021 11:43:05 +0200 (CEST) X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec X-Envelope-Sender: Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out3.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5JS6w9fz8SFZ; Wed, 20 Oct 2021 11:43:04 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx3.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5JS3gVJzDgyr; Wed, 20 Oct 2021 11:43:04 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: Date: Wed, 20 Oct 2021 11:43:03 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 07/12] Add files for handling expressions Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#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 From patchwork Wed Oct 20 09:44:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571919 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0562FC433EF for ; Wed, 20 Oct 2021 09:44:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DE7B5611C6 for ; Wed, 20 Oct 2021 09:44:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229704AbhJTJq4 (ORCPT ); Wed, 20 Oct 2021 05:46:56 -0400 Received: from out1.mail.ruhr-uni-bochum.de ([134.147.53.149]:52549 "EHLO out1.mail.ruhr-uni-bochum.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229555AbhJTJqz (ORCPT ); Wed, 20 Oct 2021 05:46:55 -0400 Received: from mx1.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out1.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5LJ0kxWz8SrJ; Wed, 20 Oct 2021 11:44:40 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634723080; bh=2axuZtEaLUwz/3XPgdwN8id6FG/THtxJxeBiMhcxjfo=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=Nc52ODQZenvJeCreriNoUh2aX3QZBRizR+beEa1enhOgn5AySDqN+dNJLfL7dCP8Z oFqCv1OB05jFiKmLBurtl/+TH7ElZxk+POSS7JA5DN3Ppa2ckIGQKqmPqXk1xDEdt4 u6CSkLc/Y3WEkmYrqdzHYsa2dPe+wEklZEHfgOm0= Received: from out1.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx1.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5LH6pXhz8SrR; Wed, 20 Oct 2021 11:44:39 +0200 (CEST) X-Envelope-Sender: X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out1.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5LH3sWtz8SrX; Wed, 20 Oct 2021 11:44:39 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx1.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5LH17q0zDgyr; Wed, 20 Oct 2021 11:44:39 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: Date: Wed, 20 Oct 2021 11:44:37 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 08/12] Add files for RangeFix Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#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 From patchwork Wed Oct 20 09:45:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571921 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E8D96C433EF for ; Wed, 20 Oct 2021 09:45:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BCE5E61361 for ; Wed, 20 Oct 2021 09:45:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229702AbhJTJsG (ORCPT ); Wed, 20 Oct 2021 05:48:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42478 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229632AbhJTJsF (ORCPT ); Wed, 20 Oct 2021 05:48:05 -0400 Received: from out3.mail.ruhr-uni-bochum.de (out3.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:8:1001::8693:359b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 53D64C06161C for ; Wed, 20 Oct 2021 02:45:51 -0700 (PDT) Received: from mx3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out3.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5Mf0dykz8SB1; Wed, 20 Oct 2021 11:45:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634723150; bh=5SQfu9IzddQVcJkR9bXghCWcsgSu/q0P9JH0yd2ui/Y=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=pDUf/gS3LuGzTD/tEs1Gq+MjLCFPQQbaOCh+ZZuvzmGyFZfE+m5Zy/WueKLKXLAIe F+U72GTCxMf5XHIZxzxTnarfRDBilvka6BlhH3P5ZVAwEKFVtSMXLeoX8u80quUQv4 decA+20GtkVfGLXcGlC7lXAxxQ3GEx6ggULck+BY= Received: from out3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx3.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5Md6vXSz8S9w; Wed, 20 Oct 2021 11:45:49 +0200 (CEST) X-Envelope-Sender: X-RUB-Notes: Internal origin=134.147.42.236 Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [134.147.42.236]) by out3.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5Md5J2sz8S7h; Wed, 20 Oct 2021 11:45:49 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx3.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5Md2b5qzDgys; Wed, 20 Oct 2021 11:45:49 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: <492928ed-2c67-de0c-7f22-996795bf9cb3@rub.de> Date: Wed, 20 Oct 2021 11:45:47 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 09/12] Add files with utility functions Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#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 From patchwork Wed Oct 20 09:46:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571923 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 67EF0C433F5 for ; Wed, 20 Oct 2021 09:46:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4CB3C6135E for ; Wed, 20 Oct 2021 09:46:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230029AbhJTJtG (ORCPT ); Wed, 20 Oct 2021 05:49:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42698 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229632AbhJTJtG (ORCPT ); Wed, 20 Oct 2021 05:49:06 -0400 Received: from out1.mail.ruhr-uni-bochum.de (out1.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:8:1001::8693:3595]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E907EC06161C for ; Wed, 20 Oct 2021 02:46:51 -0700 (PDT) Received: from mx1.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out1.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5Np4tmMz8S6W; Wed, 20 Oct 2021 11:46:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634723210; bh=roT9zTzwyw8G/G65efyW2SFc7r0pJ9I7dEDFal3bM9M=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=sNIB2n6yiZZTNmOvzoWENoaTGUcWN4JRXd0WpbDAh6SZRvqhTct48SpVVt6Kvho/J x8hvAlFtqv0rIsqT3neYj+4ZNAOqg97k+N0pI+YzfriN1kXwYfPLR0rlJvTO0p9iTr jQkyEJYXsm1dpYU0ZBhbQLGHX+0VBP0n/fR3nN+8= Received: from out1.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx1.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5Np3rnnz8S4S; Wed, 20 Oct 2021 11:46:50 +0200 (CEST) X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec X-Envelope-Sender: Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out1.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5Np2F1Xz8S6R; Wed, 20 Oct 2021 11:46:50 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx1.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5Nn6Y5GzDgyn; Wed, 20 Oct 2021 11:46:49 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: Date: Wed, 20 Oct 2021 11:46:48 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 10/12] Add tools Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#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 From patchwork Wed Oct 20 09:48:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571945 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 233F5C433EF for ; Wed, 20 Oct 2021 09:48:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E91D761361 for ; Wed, 20 Oct 2021 09:48:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230164AbhJTJup (ORCPT ); Wed, 20 Oct 2021 05:50:45 -0400 Received: from out3.mail.ruhr-uni-bochum.de ([134.147.53.155]:32364 "EHLO out3.mail.ruhr-uni-bochum.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230179AbhJTJul (ORCPT ); Wed, 20 Oct 2021 05:50:41 -0400 Received: from mx3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out3.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5Qc4Yw4z8S82; Wed, 20 Oct 2021 11:48:24 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634723304; bh=Gro0RKm90LH3e1nWZGyaCXpcRjD/RMdCM2989xjjVCg=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=YBV7qfJ7VKmuiflLrGZHqIF9ud39J+lAFHjcROy9Qi+u4F0z83YvX6t/oKk+nPNc9 anbs4x5RPLjeACkRVnbkBFMjJVuoL5loQoRWLoC34l5PMu2NMSSwWv6WvnGzcefVVY Gxt81S5oR1gzm0mn/ZSJ6ibIpSCa4SEQMbxBxAoY= Received: from out3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx3.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5Qc3lzRz8S7R; Wed, 20 Oct 2021 11:48:24 +0200 (CEST) X-Envelope-Sender: X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out3.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5Qc1dBRz8S7h; Wed, 20 Oct 2021 11:48:24 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx3.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5Qb4hq2zDgyr; Wed, 20 Oct 2021 11:48:23 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: Date: Wed, 20 Oct 2021 11:48:22 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 11/12] Add xconfig-modifications Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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  #include  #include +#include +#include +#include +#include    #include   @@ -24,7 +28,14 @@  #include "qconf.h"    #include "images.h" +#include   +#include +#include +#include +#include +#include +#include    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(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(editor); -    // If this is not a QLineEdit, use the parent's default. -    // (does this happen?) -    if (!lineEdit) -        goto parent; - -    item = static_cast(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 it(allLists); - -    while (it.hasNext()) { -        ConfigList *list = it.next(); - -        list->updateList(); -    } -} - -void ConfigList::updateListAllForAll() -{ -    QListIterator 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::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::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 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 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(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(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 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 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 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)), +        conflictsView, SLOT(selectedChanged(QList))); +    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 .\n" -        "Copyright (C) 2015 Boris Barbulovski .\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 .\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  #include  #include -#include  #include  #include +#include +#include +#include +#include +#include +#include +#include +#include    #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 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 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 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 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 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);  }; From patchwork Wed Oct 20 09:49:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thorsten Berger X-Patchwork-Id: 12571947 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 19F49C433EF for ; Wed, 20 Oct 2021 09:49:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E70BA61361 for ; Wed, 20 Oct 2021 09:49:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229764AbhJTJwG (ORCPT ); Wed, 20 Oct 2021 05:52:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43484 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230052AbhJTJwE (ORCPT ); Wed, 20 Oct 2021 05:52:04 -0400 Received: from out3.mail.ruhr-uni-bochum.de (out3.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:8:1001::8693:359b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B18E3C06161C for ; Wed, 20 Oct 2021 02:49:50 -0700 (PDT) Received: from mx3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by out3.mail.ruhr-uni-bochum.de (Postfix mo-ext) with ESMTP id 4HZ5SF1lBHz8S3H; Wed, 20 Oct 2021 11:49:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rub.de; s=mail-2017; t=1634723389; bh=KkxkAGWOoWRlhmMW/7vza9LiR80gL0bs9baNZTeSPSg=; h=Date:Subject:From:To:Cc:References:In-Reply-To:From; b=V5CTrcsgyzC8MQHmTWYyRQ5+YSxoc/PhIQVigIlcY/39+jjVSsW06zC6ofHeoAQ4H ELqvVQh9mHrt9lUbZFsuxBqLptRpkWSjx/6Tcs6FNh/ePnu++/zLo12lJ3rpwOdo+D a+i8aQvozWFTIQDUm7mfsp89Zx80r1I9ZPXBhawM= Received: from out3.mail.ruhr-uni-bochum.de (localhost [127.0.0.1]) by mx3.mail.ruhr-uni-bochum.de (Postfix idis) with ESMTP id 4HZ5SD3skMz8SFg; Wed, 20 Oct 2021 11:49:48 +0200 (CEST) X-RUB-Notes: Internal origin=IPv6:2a05:3e00:c:1001::8693:2aec X-Envelope-Sender: Received: from mail2.mail.ruhr-uni-bochum.de (mail2.mail.ruhr-uni-bochum.de [IPv6:2a05:3e00:c:1001::8693:2aec]) by out3.mail.ruhr-uni-bochum.de (Postfix mi-int) with ESMTP id 4HZ5SD0hgxz8SFP; Wed, 20 Oct 2021 11:49:48 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.1 at mx3.mail.ruhr-uni-bochum.de Received: from [192.168.188.22] (unknown [5.63.49.65]) by mail2.mail.ruhr-uni-bochum.de (Postfix) with ESMTPSA id 4HZ5SC58rWzDgyf; Wed, 20 Oct 2021 11:49:47 +0200 (CEST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.0 at mail2.mail.ruhr-uni-bochum.de Message-ID: Date: Wed, 20 Oct 2021 11:49:46 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.2.0 Subject: [RFC 12/12] Simplify dependencies for MODULE_SIG_KEY_TYPE_RSA & MODULE_SIG_KEY_TYPE_ECDSA Content-Language: en-US From: Thorsten Berger To: linux-kbuild@vger.kernel.org Cc: "Luis R. Rodriguez" , deltaone@debian.org, phayax@gmail.com, Eugene Groshev , Sarah Nadi , Mel Gorman , "Luis R. Rodriguez" References: In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger ---  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.