#!/usr/bin/env python

# Kmax
# Copyright (C) 2012-2019 Paul Gazzillo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

if __name__ == '__main__':
  import os
  import sys
  import traceback
  import argparse
  from collections import defaultdict
  import time
  import regex  # pip install regex
  import z3
  import z3.z3printer
  from kmax import expression_converter
  from kmax import find_selectable
  import pickle
  import kmax.about
  import re
  import itertools

  # this script takes the check_dep --dimacs output and converts it into
  # a dimacs-compatible format

  sys.setrecursionlimit(2000) # temporary fix until we improve parsing

  argparser = argparse.ArgumentParser(
      description="""\
  Convert Kmax-produced Kconfig constraints from stdin into a dimacs file.
      """,
      epilog="""\
      """
      )
  argparser.add_argument('-d',
                         '--debug',
                         action="store_true",
                         help="""turn on debugging output""")
  argparser.add_argument('-e',
                         '--debug-expressions',
                         action="store_true",
                         help="""print all expressions""")
  argparser.add_argument('-q',
                         '--quiet',
                         action="store_true",
                         help="""Quiet mode prints no messages to stderr.""")
  argparser.add_argument('--verbose',
                         action="store_true",
                         help="""Print additional information and warnings.""")
  argparser.add_argument('--normal-dependencies-only',
                         action="store_true",
                         help="""Print normal dependencies only.""")
  argparser.add_argument('--reverse-dependencies-only',
                         action="store_true",
                         help="""Print reverse dependencies only.""")
  argparser.add_argument('--selects-only',
                         action="store_true",
                         help="""Print selects only.""")
  argparser.add_argument('--remove-all-nonvisibles',
                         action="store_true",
                         help="""whether to leave only those config vars that can be selected by the user.  this is defined as config vars that have a kconfig prompt.""")
  argparser.add_argument('--remove-independent-nonvisibles',
                         action="store_true",
                         help="""remove all nonvisibles that aren't used in dependencies""")
  argparser.add_argument('--remove-bad-selects',
                         action="store_true",
                         help="""remove reverse dependencies when not used on a non-visible or not used on a visible variables without dependencies""")
  argparser.add_argument('--remove-reverse-dependencies',
                         action="store_true",
                         help="""only use direct dependencies, ignoring reverse dependencies""")
  argparser.add_argument('--remove-orphaned-nonvisibles',
                         action="store_true",
                         help="""remove nonvisibles that either don't have defaults or aren't enabled by a select statement""")
  argparser.add_argument('--include-bool-defaults',
                         action="store_true",
                         help="""add constraints that reflect the conditions under which boolean default values are set""")
  argparser.add_argument('--include-nonvisible-bool-defaults',
                         action="store_true",
                         help="""add constraints that reflect the conditions under which boolean default values for nonvisible variables are set""")
  argparser.add_argument('--include-nonbool-defaults',
                         action="store_true",
                         help="""support non-boolean defaults by creating a new boolean variable for  each nonbool default value""")
  argparser.add_argument('--comment-format-v2',
                         action="store_true",
                         help="""add extra formatting information to dimacs comments to distinguish them from normal comments""")
  argparser.add_argument('--selectable',
                         action="store_true",
                         help="""list the selectable options as the output.  performs no transformation to logic and ignores all other options.""")
  argparser.add_argument('--unselectable',
                         action="store_true",
                         help="""list the unselectable options as the output.  performs no transformation to logic and ignores all other options.""")
  argparser.add_argument('--version',
                         action="version",
                         version="%s %s" % (kmax.about.__title__, kmax.about.__version__),
                         help="""Print the version number.""")
  args = argparser.parse_args()

  debug = args.debug
  quiet = args.quiet  # to be implemented for quieter output
  show_warnings = args.verbose  # to be implemented for checker output
  debug_expressions = args.debug_expressions
  unselectable = args.unselectable
  selectable = args.selectable
  remove_true_clauses = True
  deduplicate_clauses = True
  normal_dep_only = args.normal_dependencies_only
  reverse_dep_only = args.reverse_dependencies_only
  selects_only = args.selects_only

  if unselectable or selectable:
    # don't do output, which won't make much sense for --(un)selectable
    quiet = True

  if selectable and unselectable:
    sys.stderr.write("only one of --selectable or --unselectable may be specified\n")
    exit(1)

  if normal_dep_only + reverse_dep_only + selects_only > 1:
    sys.stderr.write("only one of --normal-dependencies-only, --reverse-dependencies-only, or --selects-only may be specified\n")
    exit(1)

  z3_clauses = {}

  # collect dep lines
  dep_exprs = {}
  rev_dep_exprs = {}

  # collect select lines
  selects = {}

  # collect visibility conditions
  prompt_lines = {}

  # collect defaults (lines are stored in list to preserve order)
  def_bool_lines = defaultdict(list)

  # keep track of variables that have default values
  has_defaults = set()
  has_prompt = set()

  defined_vars = set()
  
  bools = set()
  choice_vars = set()
  bool_choices = []
  tristate_choices = []
  bool_opt_choices = []
  tristate_opt_choices = []

  # nonbool var attributes
  nonbools = set()
  nonbools_with_dep = set()
  nonbools_nonvisibles = set()
  nonbool_defaults = {}
  nonbool_types = {}

  # vars that can be set by the user via an environment variable (and thus are free variables)
  envs = set()

  ghost_bools = {}

  # whether to always enable nonbools or not regardless of whether they
  # have dependencies.  off by default.  warning: forcing nonbools that
  # have dependencies can restrict the space of configurations because
  # this just adds the nonbools as clauses; this is not recommended.
  force_all_nonbools_on = False

  ghost_bool_count = 0
  def get_ghost_bool_name(var):
    global ghost_bool_count
    ghost_bool_count += 1
    return "GHOST_BOOL_%d_%s" % (ghost_bool_count, var)

  def add_clause(varname, new_clause):
    # if new_clause is not None:
    if new_clause is not None and not new_clause.eq(z3.BoolVal(True)):
      if varname not in z3_clauses.keys():
        z3_clauses[varname] = [ new_clause ]
      else:
        z3_clauses[varname].append(new_clause)
    else:
      # todo: warning
      pass

  def convert_and_add_clause(varname, expr):
    add_clause(varname, expression_converter.convert_to_z3(expr))

  def pretty_printer(expr, stream=sys.stdout):
    depth = 0
    for i in range(0, len(expr)):
      if expr[i] == "(":
        if (i < len(expr) - 1 and expr[i + 1] == "("):
          stream.write("\n")
          stream.write("%s" % (". " * depth))
          stream.write("%s" % (expr[i]))
          depth += 1
        else:
          stream.write("\n")
          stream.write("%s" % (". " * depth))
          stream.write("%s" % (expr[i]))
          stream.write("\n")
          depth += 1
          stream.write("%s" % (". " * depth))
      elif expr[i] == ")":
        if (i < len(expr) - 1 and expr[i + 1] == ")"):
          stream.write("\n")
          depth -= 1
          stream.write("%s" % (". " * depth))
          stream.write("%s" % (expr[i]))
        else:
          stream.write("\n")
          depth -= 1
          stream.write("%s" % (". " * depth))
          stream.write("%s" % (expr[i]))
          stream.write("\n")
          stream.write("%s" % (". " * depth))
      elif expr[i] == " ":
        pass
      else:
        stream.write("%s" % (expr[i]))

  def implication(antecedent, consequent):
    return "((not (%s)) or (%s))" % (antecedent, consequent)

  def biimplication(antecedent, consequent):
    return "((%s) and (%s))" % (implication(antecedent, consequent), implication(consequent, antecedent))

  def conjunction(a, b):
    return "((%s) and (%s))" % (a, b)

  # def disjunction(a, b):
  #   return "((%s) or (%s))" % (a, b)

  def disjunction(*args):
    return "(%s)" % (" or ".join(map(lambda x: "(%s)" % (x), args)))

  def existential_disjunction(*args):
    """Create a string expression disjoining all non-null elements of args."""
    filtered_args = [ x for x in args if x is not None ]
    if len(filtered_args) == 0:
      return None
    else:
      return disjunction(*filtered_args)

  def negation(a):
    if a is None:
      return a
    else:
      return "(not (%s))" % (a)

  # print convert_to_cnf("not a or (b and (c or d)) and not (e and f)")
  # print convert_to_cnf("not a or (b and (c or d)) and not (e and f)")
  # print convert_to_cnf("not a or (b and (c or d)) and not (e and f)")
  # print convert_to_cnf("not a or (b and (c or d)) and not (e and f)")
  # print convert_to_cnf("not a or (b and (c or d)) and not (e and f) and g or h")
  # print convert_to_cnf("a or b or c or d or f")
  # exit(1)

  if not quiet: sys.stderr.write("[STEP 1/3] reading kextract file\n")
  for line in sys.stdin:
    if debug: sys.stderr.write("started %s\n" % (line))
    instr, data = line.strip().split(" ", 1)
    if instr.startswith("#"):
      # skip a comment
      pass
    elif (instr == "config"):
      varname, typename = data.split(" ", 1)
      defined_vars.add(varname)
      if typename == "bool" or typename == "tristate":
        bools.add(varname)
      else:
        nonbools.add(varname)
        nonbool_types[varname] = typename
    elif (instr == "prompt"):
      varname, condition = data.split(" ", 1)
      if varname in prompt_lines:
        if show_warnings: sys.stderr.write("found duplicate prompt for %s. currently unsupported\n" % (varname))
      has_prompt.add(varname)
      prompt_lines[varname] = condition
      
      # NOTE: 2 underscores to avoid naming clashes with other config options
      vis_var = "__VISIBILITY__" + varname
      # visibility boolean variables for nonboolean options are generated here.
      # these visibility variables may used by klocalizer to determine whether
      # a nonboolean should be set to a placeholder value when writing a config file.
      if varname in nonbools:
        bools.add(vis_var)
        def_bool_lines[vis_var].append("1|" + condition) # condition is the prompt's expression
        def_bool_lines[vis_var].append("0| ( not " + condition + ")")
    elif (instr == "env"):
      varname = data
      envs.add(varname)
      # treat variables that can be passed in as environment variables as
      # always selectable.
      has_prompt.add(varname)
      prompt_lines[varname] = "(1)"  # note: this depends on env lines being after prompt lines
    elif (instr == "def_bool"):
      var, line = data.split(" ", 1)
      def_bool_lines[var].append(line)
    elif (instr == "def_nonbool"):
      var, val_and_expr = data.split(" ", 1)
      val, expr = val_and_expr.split("|", 1)
      if args.include_nonbool_defaults:
        has_defaults.add(var)
        # model nonbool values with ghost boolean values
        ghost_bool_name = get_ghost_bool_name(var)
        ghost_bools[ghost_bool_name] = (var, val)
        # print ghost_bool_name, varnum, val
        if expr == "(1)":
          defined_vars.add(var)
          add_clause(var, z3.Bool(var))
        else:
          full_expr = "(not (" + expr + ")) or (" + ghost_bool_name + ")"
          # print line
          # print full_expr
          defined_vars.add(var)
          convert_and_add_clause(var, full_expr)
      else:
        # just add the first nonbool default
        if var not in nonbool_defaults:
          nonbool_defaults[var] = val
    elif (instr == "clause"):
      vars = data.split(" ")
      clause = []
      for var in vars:
        if (var[0] == "-"):  # negation
          realvar = var[1:]
          clause.append(z3.Not(z3.Bool(realvar)))
        else:
          clause.append(z3.Bool(realvar))
      if len(clause) > 0:
        pass
      elif len(clause) == 1:
        defined_vars.add(var)
        add_clause(var, clause[0])
      else:
        defined_vars.add(var)
        add_clause(var, z3.Or(clause))
    elif (instr == "bool_choice"):
      var_string, dep_expr = data.split("|",1)
      config_vars = var_string.split(" ")
      bool_choices.append((config_vars, dep_expr))
    elif (instr == "tristate_choice"):
      var_string, dep_expr = data.split("|",1)
      config_vars = var_string.split(" ")
      tristate_choices.append((config_vars, dep_expr))
    elif (instr == "bool_opt_choice"):
      var_string, dep_expr = data.split("|",1)
      config_vars = var_string.split(" ")
      bool_opt_choices.append((config_vars, dep_expr))
    elif (instr == "tristate_opt_choice"):
      var_string, dep_expr = data.split("|",1)
      config_vars = var_string.split(" ")
      tristate_opt_choices.append((config_vars, dep_expr))
    elif (instr == "dep"):
      var, expr = data.split(" ", 1)
      if var in dep_exprs:
        sys.stderr.write("found duplicate dep for %s. currently unsupported\n" % (var))
        exit(1)
      dep_exprs[var] = expr
    elif (instr == "rev_dep"):
      var, expr = data.split(" ", 1)
      if var in rev_dep_exprs:
        sys.stderr.write("found duplicate rev_dep for %s. currently unsupported\n" % (var))
        exit(1)
      rev_dep_exprs[var] = expr
    elif (instr == "select"):
      selected_var, selecting_var, expr = data.split(" ", 2)
      if selected_var not in selects.keys():
        selects[selected_var] = {}
      if selecting_var not in selects[selected_var].keys():
        selects[selected_var][selecting_var] = set()
      selects[selected_var][selecting_var].add(expr)
    elif (instr == "bi"):
      expr1, expr2 = data.split("|", 1)
      # print expr1, expr2
      final_expr = "(((not %s) or %s) and ((%s) or (not %s)))" % (expr1, expr2, expr1, expr2)
      # print final_expr
      convert_and_add_clause("<NONE>", final_expr_expr)
    elif (instr == "constraint"):
      expr = data
      convert_and_add_clause("<NONE>", expr)
    else:
      sys.stderr.write("unsupported instruction: %s\n" % (line))
      exit(1)
    if debug: sys.stderr.write("done %s\n" % (line))
  if not quiet: sys.stderr.write("[STEP 1/3] finished reading kextract file\n")

  if normal_dep_only or reverse_dep_only or selects_only:
    if not quiet: sys.stderr.write("[STEP 2/3] converting the constraints to smtlib2 format..\n")

    # Undeclared symbols will be "FOO" instead of CONFIG_FOO
    def is_sym_declared(sym_name):
      return not z3.is_false( expression_converter.convert_to_z3(sym_name + " and 1") )

    def convert_str_expr_to_smtlib2(str_expr):
      # Always and with 1 to eliminate "SYM" cases
      str_expr = str_expr + " and 1"

      # to z3 clause
      z3_clause = expression_converter.convert_to_z3(str_expr)

      # to smt2
      s = z3.Solver()
      s.add(z3_clause)
      return s.to_smt2()

    assert normal_dep_only + reverse_dep_only + selects_only < 2

    if normal_dep_only or reverse_dep_only:
      # both dep_exprs and rev_dep_exprs have type { sym: dep_expr }
      if normal_dep_only:
        exprs = dep_exprs
      else:
        exprs = rev_dep_exprs
      formatted_exprs = {}
      for sym, sym_dep in exprs.items():
        if is_sym_declared(sym):
          formatted_exprs[sym] = convert_str_expr_to_smtlib2(sym_dep)
      exprs_smt2 = formatted_exprs
    elif selects_only:
      # selects has type { selected_sym : { selecter_sym : [visibility_exprs] } }
      formatted_selects = {}
      for k1, i1 in selects.items():
        if is_sym_declared(k1):
          formatted_selects[ k1 ] = dict()
          for k2, i2 in i1.items():
              if is_sym_declared(k2):
                formatted_selects[k1][k2] = sorted(list(set([convert_str_expr_to_smtlib2(x) for x in i2])))
      exprs_smt2 = formatted_selects
    else:
      assert False
    
    if not quiet: sys.stderr.write("[STEP 2/3] finished converting the constraints to smtlib2 format.\n")
    
    if not quiet: sys.stderr.write("[STEP 3/3] pickling the map\n")

    with os.fdopen(sys.stdout.fileno(), 'wb') as f:
      pickle.dump(exprs_smt2, f)
    
    if not quiet: sys.stderr.write("[STEP 3/3] done \n")
    exit(0)

  if unselectable or selectable:
    # this algorithm finds the set of options that can never be
    # selected.  it is a reimplementation of the kmax FSE 2017
    # heuristic.  it is overapproximate, since it assumes no
    # dependencies means it is selectable.  unselectable options are
    # those that are never defined and depend only on options that are
    # not unselectable.  a more precise version would just use the
    # logical formulas to determine this.
    sys.stderr.write("[STEP 1/1] finding unselectable configuration options\n")

    find_selectable_object = find_selectable.FindSelectable(dep_exprs, rev_dep_exprs)
    selectable_options = find_selectable_object.get_selectable(set(bools).union(set(nonbools)))

    if unselectable:
      selectability = False
    elif selectable:
      selectability = True
    else:
      assert(False)
      
    print("\n".join([ option for option in selectable_options.keys() if selectable_options[option] == selectability ]))
    exit(0)

  # the names of configuration variables that have dependencies, either
  # direct or reverse
  has_dependencies = set()

  # the names of configuration variables used in another variable's
  # dependency expression
  in_dependencies = set()

  # keep track of variables that have reverse dependencies
  has_selects = set()

  def split_top_level_clauses(expr, separator):
    terms = []
    cur_term = ""
    depth = 0
    for c in expr:
      if c == "(":
        depth += 1
      elif c == ")":
        depth -= 1

      if depth == 0:
        cur_term += c
        if cur_term.endswith(separator):
          # save the term we just saw
          terms.append(cur_term[:-len(separator)])
          cur_term = ""
      elif depth > 0:
        cur_term += c
      else:
        sys.stderr.write("fatal: misnested parentheses in reverse dependencies: %s\n" % expr)
        exit(1)
    # save the last term
    terms.append(cur_term)
    return terms

  def split_top_level_clauses(expr, separator):
    terms = []
    cur_term = ""
    depth = 0
    for c in expr:
      if c == "(":
        depth += 1
      elif c == ")":
        depth -= 1

      if depth == 0:
        cur_term += c
        if cur_term.endswith(separator):
          # save the term we just saw
          terms.append(cur_term[:-len(separator)])
          cur_term = ""
      elif depth > 0:
        cur_term += c
      else:
        sys.stderr.write("fatal: misnested parentheses in reverse dependencies: %s\n" % expr)
        exit(1)
    # save the last term
    terms.append(cur_term)
    return terms

  token_pattern = regex.compile("(\(|\)|[^() ]+| +)+")

  def tokenize(expr):
    return token_pattern.match(expr).captures(1)

  def replace_tokens(expr, match_expr, rep_expr):
    # sys.stderr.write("replace_tokens('%s', '%s', '%s')\n" % (expr, match_expr, rep_expr))
    expr_tokens = tokenize(expr)
    match_tokens = tokenize(match_expr)
    # 1 2 3 4 5
    # a a
    #   a a
    #     a a
    #       a a
    return_expr = ""
    if len(match_tokens) > len(expr_tokens):
      return_expr = expr
    elif len(match_tokens) == 0:
      return_expr = expr
    else:
      new_expr = ""
      i = 0
      while i < len(expr_tokens):
        matches = True
        for j in range(len(match_tokens)):
          if i + j < len(expr_tokens) and expr_tokens[i + j] == match_tokens[j]:
            # still matches
            pass
          else:
            matches = False
            break
        if matches:
          new_expr += rep_expr
          i += len(match_tokens)
        else:
          new_expr += expr_tokens[i]
          i += 1
      return_expr = new_expr
    # sys.stderr.write("return_expr:  %s\n" % (return_expr))
    return return_expr

  def remove_direct_dep_from_rev_dep_term(term):
    # the first factor of the term is the SEL var the selects the
    # variable.  this is how kconfig stores the term.
    split_term = term.split(" and ", 1)
    if len(split_term) < 2:
      return term
    else:
      first_factor, remaining_factors = term.split(" and ", 1)
      if first_factor in dep_exprs.keys():
        # get the dep expression for the reverse dep
        dep_expr = dep_exprs[first_factor]
        # remove the parens
        if dep_expr.startswith("(") and dep_expr.endswith(")"): dep_expr = dep_expr[1:-1]
        # now we an expression that we can cut out from the reverse dependency's term
        remaining_factors_replaced = replace_tokens(remaining_factors, dep_expr, "1")
        # remaining_factors_replaced_str = str.replace(remaining_factors, dep_expr, "1")
        # if remaining_factors_replaced != remaining_factors_replaced_str:
        #   sys.stderr.write("DIFF\n%s\n%s\n" % (remaining_factors_replaced, remaining_factors_replaced_str))
        # reconstruct the term
        if len(remaining_factors_replaced) > 0:
          term = "%s and %s" % (first_factor, remaining_factors_replaced)
        else:
          # the entire dependency was replaced, so the term is just the
          # reverse dependency variable
          term = first_factor
        # print "new_term", term
        return term
      else:  # it has no dependencies, so there is nothing to do
        return term

  # Generate mutex choice clauses for choices
  # Mutex choice applies to all choice types, since tristate choices are currently treated as bool choices
  for (config_vars, dep_expr) in bool_choices + tristate_choices + bool_opt_choices + tristate_opt_choices:
    # print config_vars
    # mutex choice: a -> !b, a -> !c, ..., b -> !a, b -> !c, ...
    
    # TODO: Update choice_vars with tristate_choice as well when print_dimacs() method
    # is modified for tristate_choices.
    choice_vars.update(set(config_vars))
    defined_vars.update(set(config_vars))
    for c1, c2 in itertools.combinations(config_vars, 2):
      add_clause(c1, z3.Or(z3.Not(z3.Bool(c1)), z3.Not(z3.Bool(c2))))
    assert len(config_vars) > 0
    
  # TODO: generate mutex choice clauses for tristate choices
  # Different than the boolean choices, tristate choices allow multiple options to be set to "m".
  # However, only one option can be set to "y". Since we cannot differentiate between "m" and "y"
  # with kclause, we cannot express the conditions for tristate choices in full. We can treat
  # them the same way we treat bool_choices, which would prevent setting multiple choices to "m".
  # Or, we can just not consider the mutex "y" choice at all. Currently, we are doing the former.

  # generate 'at least one' clause for boolean and tristate choices
  for (config_vars, dep_expr) in bool_choices + tristate_choices + bool_opt_choices + tristate_opt_choices:
    # adding the following clauses:

    # dep_expr and individual_conditions -> possible_choices
    # possible_choices -> dep_expr

    # this says that, if the dependencies are met for any choice and
    # for the choice block itself, one of the options must be
    # selected.  otherwise no option needs to be selected.
    # conversely, if a choice is selected, that implies the choice
    # blocks conditions have been met.  the individual dependencies of
    # the choice options are met by the "dep" lines.

    possible_choices = ""
    individual_conditions = ""
    delim = ""
    for var in config_vars:
      possible_choices = possible_choices + delim + var

      # for a choice value to be selectable, both its
      # depends on and visibility constraints should be
      # satisfied.  Since depends on constraint is 
      # conjuncted to visibility constraint by Kconfig,
      # individual_conditions use visibiltiy constrains
      if var in prompt_lines and prompt_lines[var] != None:
        individual_dep_expr = "(%s)" % (prompt_lines[var])
      else:
        individual_dep_expr = "(1)"
      individual_conditions = individual_conditions + delim + individual_dep_expr
      delim = " or "

    # when conditions are met, at least one choiceval should be selected
    clause1 = implication(conjunction(dep_expr, individual_conditions), possible_choices)
    # force no choiceval to be selected when either dep_expr is not met or individual_conditions is not met
    clause2 = implication(possible_choices, conjunction(dep_expr, individual_conditions))

    # for optinal choices, clause1 is not needed
    # (even when conditions are met, possible_choices can be false)
    final_expression = conjunction(clause1, clause2) if (config_vars, dep_expr) in bool_choices + tristate_choices else clause2

    if debug_expressions:
      sys.stderr.write("bool choice")
      pretty_printer(final_expression, stream=sys.stderr)
    convert_and_add_clause("<CHOICE>", final_expression)

  # generate clauses for dependencies and defaults
  if not quiet: sys.stderr.write("generating clauses for dependencies")
  configuration_options_to_translate = set(dep_exprs.keys()).union(set(rev_dep_exprs.keys())).union(set(selects.keys())).union(set(def_bool_lines.keys())).union(set(prompt_lines.keys()))
  num_vars = len(configuration_options_to_translate)
  processing = 0
  last_status_line_size = 0
  for var in configuration_options_to_translate:
    processing = processing + 1
    if not quiet and not debug:
      translation_status = "[STEP 2/3] translated %d/%d configuration option dependencies: %s" % (processing, num_vars, var)
      sys.stderr.write("%s\r" % (" " * last_status_line_size))
      sys.stderr.write("%s\r" % (translation_status))
      last_status_line_size = len(translation_status)
    if debug: sys.stderr.write("processing %s\n" % (var))
    # get direct dependencies
    if var in dep_exprs.keys():
      dep_expr = dep_exprs[var]
      if dep_expr != "(1)":
        has_dependencies.add(var)  # track vars that have dependencies
        in_dependencies.update(expression_converter.get_identifiers(dep_expr))  # track vars that are in other dependencies
    else:
      dep_expr = None

    # get reverse dependencies
    if True:  # use rev_dep lines always
      if var in rev_dep_exprs.keys():
        rev_dep_expr = rev_dep_exprs[var]
        has_selects.add(var)
        if rev_dep_expr != "(1)":
          has_dependencies.add(var)  # track vars that have dependencies
          in_dependencies.update(expression_converter.get_identifiers(rev_dep_expr))  # track vars that are in other dependencies
      else:
        rev_dep_expr = None

      # filter out dependency expressions from configurations that are
      # reverse dependencies.  if a var SEL is a reverse dependency for
      # VAR, VAR's rev_dep_expr will contain "SEL and DIR_DEP".  we can
      # remove the DIR_DEP for SEL, since it will be covered when
      # processing SEL.  this reduces clause size.
      if False and rev_dep_expr != None and rev_dep_expr != "(1)":
        # get all the top-level terms of this clause.  the reverse
        # dependency will be a union of "SEL and DIR_DEP" terms,
        # representing each of the reverse dependencies.
        expr = rev_dep_expr
        if debug: sys.stderr.write("rev_dep_expr %s\n" % (rev_dep_expr))
        # (1) strip surrounding parens (as added by check_dep on all dependencies)
        if expr.startswith("(") and expr.endswith(")"): expr = expr[1:-1]
        # (2) split into ORed clauses
        terms = split_top_level_clauses(expr, " or ")
        # (3) remove the direct dependencies conjoined with the SEL vars
        # if debug: sys.stderr.write("after split: %s %s\n" % (var, terms))
        terms = map(remove_direct_dep_from_rev_dep_term, terms)
        # if debug: sys.stderr.write("after rem:   %s %s\n" % (var, terms))

        if False:  # don't ever use the unoptimized version
          rev_dep_expr = "(%s)" % (" or ".join(terms))
        else:
          # optimization to reduce cnf blowup: combine factors by
          # disjunction for top-level terms, i.e., A & D1 or A & D2 => A &
          # (D1 or D2).

          # assume the first factor is the reverse dependency and the rest
          # of the term is the reverse dependency's dependency (heuristic
          # based on how kconfig emits expressions)
          split_factors = [ term.split(" and ", 1) for term in terms ]
          # split_factors is now a list containing lists of either 1 or 2
          # elements, the former having now dependency factor.

          # aggregate split factors
          # if debug: sys.stderr.write("split_factors %s\n" % (split_factors))
          combined_terms = defaultdict(set)
          for split_factor in split_factors:
            assert len(split_factor) == 1 or len(split_factor) == 2
            if len(split_factor) == 1:
              combined_terms[split_factor[0]].add("(1)")
            elif len(split_factor) == 2:
              combined_terms[split_factor[0]].add(split_factor[1])
          # if debug: sys.stderr.write("combined_terms %s\n" % (combined_terms))

          stringified_terms = ["%s and (%s)" % (select_var, " or ".join(combined_terms[select_var]))
                               for select_var in combined_terms.keys()]
          # rev_dep_expr = "(%s)" % (" or ".join(stringified_terms))
          rev_dep_expr = "(%s)" % (" or ".join(stringified_terms))
          if debug: # sys.stderr.write("stringified %s\n" % (rev_dep_expr))
            sys.stderr.write("stringified\n")
            pretty_printer(rev_dep_expr, stream=sys.stderr)

    else:  # use select lines (deprecated)
      if var in selects.keys():
        has_selects.add(var)
        has_dependencies.add(var)  # track vars that have dependencies
        selecting_vars = selects[var]
        rev_dep_expr = None
        for selecting_var in selecting_vars.keys():
          deps = selecting_vars[selecting_var]

          # compute the select dependency (select_dep) for the var that
          # does the selecting.  this is complicated by the fact that one
          # var can be used many times to select the same var under
          # different conditions.
          if len(deps) == 0:
            select_dep = None
          else:
            # optimization: if any are (1), then the whole thing is (1).
            # we set it to None since it is as if there is no dependency.
            if len(filter(lambda x: x == "(1)", deps)) > 0:
              select_dep = None
            else:
              # generate the union of all dependencies for this selecting
              # var
              if len(deps) == 1:
                select_dep = list(deps)[0]
              else:
                select_dep = "(%s)" % (" or ".join(deps))
          if select_dep == None:
            selecting_term = selecting_var
          else:
            selecting_term = conjunction(selecting_var, select_dep)

          # update reverse dependency
          if rev_dep_expr == None:
            rev_dep_expr = selecting_term
          else:
            rev_dep_expr = disjunction(rev_dep_expr, selecting_term)
        in_dependencies.update(get_identifiers(rev_dep_expr))
        if debug_expressions: sys.stderr.write("rev_dep_expr: %s\n" % (rev_dep_expr))
      else:
        rev_dep_expr = None

    if args.remove_reverse_dependencies:
      rev_dep_expr = None

    # ignore reverse dependencies if symbol is a choice value
    if var in choice_vars:
      rev_dep_expr = None

    # collect prompt condition
    if var in has_prompt:
      prompt_expr = prompt_lines[var]
    else:
      prompt_expr = None

    # check for bad selects
    if rev_dep_expr != None:
      good_select = var not in has_prompt or var not in has_dependencies
      if not good_select:
        if args.remove_bad_selects:
          # restrict reverse dependencies to variables that are not
          # user-visible and have no dependencies.
          if show_warnings: sys.stderr.write("warning: removing bad select statements for %s.  this is the expression: %s\n" % (var, rev_dep_expr))
          rev_dep_expr = None
        else:
          if show_warnings: sys.stderr.write("warning: found bad select statement(s) for %s\n" % (var))

    # collect boolean default expression
    # When multiple defaults are defined, kconfig uses 
    # the first statement that satisfies its condition 
    # Tristate defaults are also considered as def_bool 
    # by kextract, where m is changed into 1
    # TODO: kclause currenlty considers boolean defaults
    # only. Nonbool default will need update to consider
    # different possible default values.
    if var in def_bool_lines.keys():
      has_defaults.add(var)
      def_y_expr = "(0)"
      delim = " or "
      ite_expr = "(0)"
      for def_bool_line in def_bool_lines[var]:
        val, expr = def_bool_line.split("|", 1)
        # sys.stderr.write("%s|%s|%s\n" % (var, val, expr))
        if val == "1" or val == "y":
          def_y_expr = def_y_expr + delim + "(" + expr + " and not (" + ite_expr + "))"
        # Semantically, below has no effect to the formula 
        # as it is merely disjuncting False to def_y_expr. 
        # However, this line is kept for understanding the 
        # equation. The output formula will be simplified 
        # by z3 regardless.
        else:
          def_y_expr = def_y_expr + delim + "((" + val + ") and (" + expr + " and not (" + ite_expr + ")))"
        
        ite_expr = ite_expr + delim + expr
    else:
      def_y_expr = None

    # create clauses for dependencies and defaults
    if var in bools:
      # compute the expression for the visible condition
      super_simplified_expression = False
      if super_simplified_expression:
        # V prompt P
        # R selects V
        # V depends on D
        # V default y if F
        # V is the variable
        # D is the direct dependency
        # R is the reverse dependency
        # P is the prompt condition
        # F is the default true condition for nonvisibles

        # this is a currently broken attempt
        clauses = [
        # (P or R or D or !V) and
          [ prompt_expr, rev_dep_expr, dep_expr, negation(var) ],
        # (P or R or !D or V or !F) and
          [ prompt_expr, rev_dep_expr, negation(dep_expr), var, negation(def_y_expr) ],
        # (P or !R or V) and
          [ prompt_expr, negation(rev_dep_expr), var ],
        # (P or !V or !F) and
          [ prompt_expr, negation(var), def_y_expr ],
        # (!P or R or D or !V) and
          [ negation(prompt_expr), rev_dep_expr, dep_expr, negation(var) ],
        # (R or D or !V) and
          [ rev_dep_expr, dep_expr, negation(var) ],
        # (R or D or !V or !F) and
          [ rev_dep_expr, dep_expr, negation(var), negation(def_y_expr) ],
        # (!P or !R or V) and
          [ negation(prompt_expr), negation(rev_dep_expr), var ],
        # (R! or V)
          [ negation(rev_dep_expr), var ]
        ]
        for clause in clauses:
          if clause is not None:
            expression = existential_disjunction(*clause)
            defined_vars.add(var)
            convert_and_add_clause(var, expression)

      else:
        if var not in has_prompt:
          # don't bother computing the visible expression if there is no
          # prompt, because it couldn't be user-selectable
          visible_expr = None
        else:
          if dep_expr != None and rev_dep_expr == None:
            # only direct dependency
            visible_expr = implication(var, dep_expr)
          elif dep_expr == None and rev_dep_expr != None:
            # only reverse dependency
            visible_expr = implication(rev_dep_expr, var)
            pass
          elif dep_expr != None and rev_dep_expr != None:
            # both kinds
            clause1 = disjunction(rev_dep_expr, disjunction(dep_expr, negation(var)))
            clause2 = disjunction(negation(rev_dep_expr), var)
            visible_expr = conjunction(clause1, clause2)

            # # unsimplified form
            # clause1 = conjunction(rev_dep_expr, var)
            # clause2 = conjunction(negation(rev_dep_expr), implication(var, dep_expr))
            # visible_expr = disjunction(clause1, clause2)
            pass
          else:
            # neither kind means it's a free variable
            visible_expr = "(1)"

          if args.include_bool_defaults and var in def_bool_lines.keys():
            if show_warnings: sys.stderr.write("warning: defaults are ignored for visibles, because they are user-selectable: %s\n" % (var))

          # # V -> (E | A) and A -> V, where E is direct dep, and A is reverse dep

          # # intuitively, this makes sense.  if V is on, then either its
          # # direct dependency E or reverse dependency A must have been
          # # satisfied.  if the direct dependency is off, then the variable
          # # must have only been set when its reverse dependency is set
          # # (biimplication).


          # # clause1 = var -> (dep_expr or rev_dep_expr)
          # # clause2 = rev_dep_expr -> var

          # if rev_dep_expr != None:
          #   if dep_expr != None:
          #     clause1 = implication(var, disjunction(dep_expr, rev_dep_expr))
          #     clause2 = implication(rev_dep_expr, var)
          #     visible_expr = conjunction(clause1, clause2)
          #   else: # no direct dependency means biimplication between rev_dep and var
          #     visible_expr = biimplication(var, rev_dep_expr)
          # else:
          #   if dep_expr != None:  # no reverse dependency means that only the direct dependency applies
          #     clause1 = implication(var, dep_expr)
          #     clause2 = None
          #     visible_expr = clause1
          #   else: # no direct or reverse dependencies
          #     clause1 = None
          #     clause2 = None
          #     visible_expr = None

        # compute the expression for the nonvisible condition
        # if prompt_expr == "(1)" or prompt_expr == dep_expr:
        #   sys.stderr.write("no prompt\n")
        if prompt_expr == "(1)":
          # there is no possibility of the variable being nonvisible, so
          # don't bother computing the expression
          nonvisible_expr = None
        else:
          # nonvisibles that have no default, default to off
          if def_y_expr == None:
            def_y_expr = "(0)"
          simplified_nonvisibles = False
          if simplified_nonvisibles:
            # visible
            # elif dep_expr != None and rev_dep_expr != None:
              # clause1 = disjunction(rev_dep_expr, disjunction(dep_expr, negation(var)))
              # clause2 = disjunction(negation(rev_dep_expr), var)
              # visible_expr = conjunction(clause1, clause2)


            # A rev_dep_expr
            # B dep_expr
            # Idef def_y_expr
            # I var

            # unsimplified:
            # (rev_dep_expr and var) or (not rev_dep_expr and (var biimp (dep_expr and def_y_expr)))

            # simplified
            # (    A or     B or not I            ) and
            # (    A or not B or     I or not Idef) and
            # (not A          or     I            ) and
            # (         not B or     I or not Idef)


            if dep_expr != None and rev_dep_expr == None:
              clause1 = disjunction(dep_expr, negation(var))
              clause2 = disjunction(negation(dep_expr), disjunction(var, negation(def_y_expr)))
              clause3 = var
              clause4 = disjunction(negation(dep_expr), disjunction(var, negation(def_y_expr)))
              nonvisible_expr = conjunction(clause1, conjunction(clause2, conjunction(clause3, clause4)))
            elif dep_expr == None and rev_dep_expr != None:
              clause1 = "(1)"
              clause2 = disjunction(rev_dep_expr, disjunction(var, negation(def_y_expr)))
              clause3 = disjunction(negation(rev_dep_expr), var)
              clause4 = disjunction(var, negation(def_y_expr))
              nonvisible_expr = conjunction(clause1, conjunction(clause2, conjunction(clause3, clause4)))
            elif dep_expr != None and rev_dep_expr != None:
              clause1 = disjunction(rev_dep_expr, disjunction(dep_expr, negation(var)))
              clause2 = disjunction(rev_dep_expr, disjunction(negation(dep_expr), disjunction(var, negation(def_y_expr))))
              clause3 = disjunction(negation(rev_dep_expr), var)
              clause4 = disjunction(negation(dep_expr), disjunction(var, negation(def_y_expr)))
              nonvisible_expr = conjunction(clause1, conjunction(clause2, conjunction(clause3, clause4)))
            else:
              clause1 = "(1)"
              clause2 = disjunction(rev_dep_expr, disjunction(var, negation(def_y_expr)))
              clause3 = disjunction(negation(rev_dep_expr), var)
              clause4 = disjunction(var, negation(def_y_expr))
              nonvisible_expr = conjunction(clause1, conjunction(clause2, conjunction(clause3, clause4)))

          else:
            # the conditions under which a nonvisible variable can be enabled

            # I <-> ((E & D) | A)
            # I is the variable, A is the selects (reverse deps), E is the direct dep, D is the default y condition

            # because nonvisible variables cannot be modified by the user
            # interactively, we use a bi-implication between it's dependencies
            # and default values and selects.  this says that the default
            # value will be taken as long as the conditions are met.
            # nonvisibles default to off if these conditions are not met.

            # var biimp (dep_expr and def_y_expr or rev_dep_expr)
            consequent = dep_expr
            if consequent == None:
              consequent = def_y_expr
            elif def_y_expr != None:
              consequent = conjunction(consequent, def_y_expr)

            if consequent == None:
              consequent = rev_dep_expr
            elif rev_dep_expr != None:
              consequent = disjunction(consequent, rev_dep_expr)

            if consequent != None:
              nonvisible_expr = biimplication(var, consequent)
              # print nonvisible_expr
            else:
              nonvisible_expr = None

        # compute the complete expression for the variable, combining both the visible and nonvisible semantics
        # prompt_expr and visible_expr or not prompt_expr and nonvisible_expr
        if visible_expr != None:
          if prompt_expr != None:
            cond_visible_expr = conjunction(prompt_expr, visible_expr)
          else:
            # if there is no prompt expression, it means there is no possibility of visibility
            cond_visible_expr = None
        else:
          cond_visible_expr = None

        if nonvisible_expr != None:
          if prompt_expr != None:
            cond_nonvisible_expr = conjunction(negation(prompt_expr), nonvisible_expr)
          else:
            # if there is no prompt_expr, it means the variable is unconditionally nonvisible
            cond_nonvisible_expr = nonvisible_expr
        else:
          cond_nonvisible_expr = None

        if debug_expressions: sys.stderr.write("var %s\n" % (var))
        if debug_expressions: sys.stderr.write("dep_expr %s\n" % (dep_expr))
        if debug_expressions: sys.stderr.write("rev_dep_expr %s\n" % (rev_dep_expr))
        if debug_expressions: sys.stderr.write("def_y_expr %s\n" % (def_y_expr))
        if debug_expressions: sys.stderr.write("prompt_expr %s\n" % (prompt_expr))
        if debug_expressions: sys.stderr.write("conditional visible %s\n" % (cond_visible_expr))
        if debug_expressions: sys.stderr.write("unconditional visible %s\n" % (visible_expr))
        if debug_expressions: sys.stderr.write("conditional nonvisible %s\n" % (cond_nonvisible_expr))
        if debug_expressions: sys.stderr.write("unconditional nonvisible %s\n" % (nonvisible_expr))
        if debug_expressions: sys.stderr.write("unconditional nonvisible z3 %s\n" % (expression_converter.convert_to_z3(nonvisible_expr) if nonvisible_expr is not None else None))

        if cond_visible_expr != None and cond_nonvisible_expr != None:
          final_expr = disjunction(cond_visible_expr, cond_nonvisible_expr)
        elif cond_visible_expr != None and cond_nonvisible_expr == None:
          final_expr = cond_visible_expr
        elif cond_visible_expr == None and cond_nonvisible_expr != None:
          final_expr = cond_nonvisible_expr
        else: # cond_visible_expr == None and cond_nonvisible_expr == None:
          final_expr = None

        if final_expr != None:
          if debug_expressions: sys.stderr.write("%s final expression is %s\n" % (var, final_expr))
          if debug_expressions: sys.stderr.write("%s final expression z3 is %s\n" % (var, expression_converter.convert_to_z3(final_expr) if final_expr is not None else None))
          defined_vars.add(var)
          convert_and_add_clause(var, final_expr)
        else:
          sys.stderr.write("%s has no final expression\n" % (var))

    elif var in nonbools:
      nonbools_with_dep.add(var)
      if var not in has_prompt:
        # no support for nonbool nonvisibles, because we don't support
        # nonboolean constraints and there is no way for the user to
        # directly modify these.
        nonbools_nonvisibles.add(var)
        if show_warnings: sys.stderr.write("warning: no support for nonvisible nonbools: %s\n" % (var))
      else:  # var is visible
        if rev_dep_expr != None:
          if show_warnings: sys.stderr.write("warning: no support for reverse dependencies on nonbooleans: %s\n" % (var))
        # include the prompt condition as part of the dependency
        if prompt_expr != None and dep_expr != None:
          dep_expr = conjunction(prompt_expr, dep_expr)
        elif prompt_expr != None and dep_expr == None:
          dep_expr = prompt_expr
        # bi-implication var <-> dep_expr, because a nonboolean always
        # has a value as long as its dependencies are met.
        if dep_expr == None:
          # the nonbool is always on when no dependencies
          final_expr = var
        else:
          final_expr = biimplication(var, dep_expr)
        # print final_expr
        defined_vars.add(var)
        convert_and_add_clause(var, final_expr)
    else:
      assert True

  # set orphaned nonvisible bools to false
  def is_orphaned_nonvisible(var):
    return var not in has_prompt and var in bools and var not in has_defaults and var not in has_selects
  
  for var in defined_vars:
    if is_orphaned_nonvisible(var):
      add_clause(var, z3.Not(z3.Bool(var)))

  if not quiet and not debug:
    sys.stderr.write("%s\r" % (" " * last_status_line_size))
    sys.stderr.write("[STEP 2/3] translated %d/%d configuration option dependencies\n" % (processing, num_vars))
  
  used_vars = set()
  num_vars = len(z3_clauses.keys())
  processing = 1
  for var in z3_clauses.keys():
    if not quiet: sys.stderr.write("[STEP 3/3] converted %d/%d constraints to smtlib2 format\r" % (processing, num_vars))
    processing += 1
    # for clause in z3_clauses[var]:
    #   used = z3.z3util.get_vars(clause)
    #   used = [ str(varname) for varname in used ]
    #   used_vars.update(used)
    def convert_to_smtlib2(z3_clause):
      s = z3.Solver()
      s.add(z3_clause)
      return s.to_smt2()
    z3_clauses[var] = [ convert_to_smtlib2(clause) for clause in z3_clauses[var] ]
  if not quiet: sys.stderr.write("\n")
  if not quiet: sys.stderr.write("[STEP 3/3] pickling the map\n")
  # used_vars = [ var for var in used_vars if var.startswith("CONFIG_") ]
  # pickle.dump((z3_clauses, defined_vars, used_vars), fp, 0)
  fp = os.fdopen(sys.stdout.fileno(), 'wb')
  pickle.dump(z3_clauses, fp, 0)
  if not quiet: sys.stderr.write("[STEP 3/3] done \n")

  # quit and don't do dimacs clause processing
  exit(0)

  if force_all_nonbools_on:
    for nonbool in (nonbools):
      varnum = lookup_varnum(nonbool)
      clauses.append([varnum])

  # filter clauses and variables
  def remove_dups(l):
    seen = set()
    seen_add = seen.add
    return [x for x in l if not (x in seen or seen_add(x))]
  clauses = map(lambda clause: remove_dups(clause), clauses)

  def remove_condition(var):
    return \
      var in variables_to_remove or \
      var in nonbools_nonvisibles or \
      args.remove_all_nonvisibles and var not in has_prompt or \
      args.remove_independent_nonvisibles and var not in has_prompt and var not in in_dependencies or \
      args.remove_orphaned_nonvisibles and var not in has_prompt and var in bools and var not in in_dependencies and var not in has_defaults and var not in has_selects

  if debug: sys.stderr.write("filtering vars\n")
  keep_vars = filter(lambda var: not remove_condition(var), varnums.keys())
  keep_varnums = filter(lambda name, num: name in keep_vars, sorted(varnums.items(), key=lambda tup: tup[1]))

  for var in varnums.keys():
    if var not in has_prompt and var in bools and var not in has_defaults and var not in has_selects:
      if args.remove_orphaned_nonvisibles:
        if show_warnings: sys.stderr.write("warning: remove orphaned nonvisible variables: %s\n" % (var))
      else:
        if show_warnings: sys.stderr.write("warning: %s is an orphaned nonvisible variable\n" % (var))

  if debug: sys.stderr.write("renumbering\n")
  # renumber variables
  num_mapping = {}
  for (name, old_num) in keep_varnums:
    num_mapping[old_num] = len(num_mapping) + 1

  # update varnums
  varnums = {name: num_mapping[old_num] for (name, old_num) in keep_varnums}

  if debug: sys.stderr.write("trimming clauses\n")
  # remove vars from clauses
  filtered_clauses = []
  original_num_clauses = len(clauses)
  num_processed = 0
  start_time = time.time()
  for clause in clauses:
    # trim undefined vars from clauses
    filtered_clause = filter(lambda x: abs(x) in num_mapping.keys(), clause)
    remapped_clause = [num_mapping[x] if x >= 0 else -num_mapping[abs(x)] for x in filtered_clause]
    if len(remapped_clause) == 1 and len(clause) > 1:
      # if all vars but one was removed, then there is no constraint
      pass
    elif len(remapped_clause) == 0:
      # nothing to print now
      pass
    else:
      filtered_clauses.append(remapped_clause)
    num_processed += 1
    if debug:
      if time.time() - start_time > 2:
        sys.stderr.write("trimmed %s/%s clauses\n" % (num_processed, original_num_clauses))
        start_time = time.time()
  if debug: sys.stderr.write("finished trimming %s/%s clauses\n" % (num_processed, original_num_clauses))

  def is_true(clause):
    neg = set()
    pos = set()
    for elem in clause:
      if elem > 0:
        if abs(elem) in neg:
          return True
        pos.add(abs(elem))
        pass
      elif elem < 0:
        if abs(elem) in pos:
          return True
        neg.add(abs(elem))
        pass
      else:
        # should never have a varnum of 0
        assert True
    return False

  if debug: sys.stderr.write("trimming true clauses\n")
  # remove true clauses
  if remove_true_clauses:
    filtered_clauses = filter(lambda x: not is_true(x), filtered_clauses)

  if debug: sys.stderr.write("stringifying clauses\n")
  # convert clauses to strings
  string_clauses = ["%s 0" % (" ".join([str(num) for num in sorted(clause, key=abs)])) for clause in filtered_clauses]

  if debug: sys.stderr.write("deduplicating clauses\n")
  # deduplicate clauses
  if deduplicate_clauses:
    string_clauses = list(set(string_clauses))

  # emit dimacs format
  def print_dimacs(varnum_map, clause_list):
    if args.comment_format_v2:
      print("c format kmax_kconfig_v2")
      comment_prefix = "c kconfig_variable"
    else:
      comment_prefix = "c"
    for varname in sorted(varnum_map, key=varnum_map.get):
      if varname in nonbools:
        # TODO: Account for tristate choices here. Then, include tristate_choices in choice_vars as well.
        if varname in choice_vars:
          sys.stderr.write("choice variable is not boolean: %s\n" % (varname))
        if varname in nonbool_defaults:
          defaultval = nonbool_defaults[varname]
          if nonbool_types[varname] != "string":
            defaultval = defaultval[1:-1]  # strip off quotes for nonstrings
          if nonbool_types[varname] == "string":
            defaultval = defaultval.replace("\\", "\\\\") # escape
        else:
          defaultval = '""' if nonbool_types[varname] == "string" else "0"
        defaultval = " " + defaultval
        if args.comment_format_v2:
          typename = "string" if nonbool_types[varname] == "string" else "int"
        else:
          typename = "nonbool"
        print("%s %d %s %s%s" % (comment_prefix, varnum_map[varname], varname, typename, defaultval))
      else:
        # TODO: Account for tristate choices here. Then, include tristate_choices in choice_vars as well.
        if varname in choice_vars:
          print("%s %d %s choice_bool" % (comment_prefix, varnum_map[varname], varname))
        elif varname in has_prompt:
          print("%s %d %s bool" % (comment_prefix, varnum_map[varname], varname))
        elif varname in ghost_bools.keys():
          nonbool_var, defval = ghost_bools[varname]
          print("%s %d %s ghost_bool %s %s" % (comment_prefix, varnum_map[varname], varname, nonbool_var, defval))
        else:
          print("%s %d %s hidden_bool" % (comment_prefix, varnum_map[varname], varname))
    print("p cnf %d %d" % (len(varnum_map), len(clause_list)))
    for clause in clause_list:
      print(clause)

  if debug: sys.stderr.write("printing dimacs file\n")
  print_dimacs(varnums, string_clauses)
  sys.stderr.write("done\n")
