docs: def solve_boolexpr():
      """
      sudo pip install git+https://github.com/tpircher/quine-mccluskey.git
      sudo pip uninstall quine_mccluskey
      pip uninstall quine_mccluskey
  
      pip install git+https://github.com/tpircher/quine-mccluskey.git
  
  
      Args:
          varnames (?):
  
      Returns:
          ?:
  
      CommandLine:
          python -m utool.util_alg solve_boolexpr --show
  
      Example:
          >>> # DISABLE_DOCTEST
          >>> from utool.util_alg import *  # NOQA
          >>> import utool as ut
          >>> varnames = ['sa', 'said', 'aid']
          >>> result = solve_boolexpr()
          >>> print(result)
  
      """
      #false_cases = [
      #    int('111', 2),
      #    int('011', 2),
      #    int('001', 2),
      #]
      #true_cases = list(set(range(2 ** 3)) - set(false_cases))
      varnames = ['sa', 'said', 'aid']
  
      #import utool as ut
      truth_table = [
          dict(sa=True,  said=True,  aid=True,  output=False),
          dict(sa=True,  said=True,  aid=False, output=True),
          dict(sa=True,  said=False, aid=True,  output=True),
          dict(sa=True,  said=False, aid=False, output=True),
          dict(sa=False, said=True,  aid=True,  output=False),
          dict(sa=False, said=True,  aid=False, output=True),
          dict(sa=False, said=False, aid=True,  output=False),
          dict(sa=False, said=False, aid=False, output=True),
      ]
      truth_tuples = [ut.dict_take(d, varnames) for d in truth_table]
      outputs = [d['output'] for d in truth_table]
      true_tuples = ut.compress(truth_tuples, outputs)
      #false_tuples = ut.compress(truth_tuples, ut.not_list(outputs))
      true_cases = [''.join([str(int(t)) for t in tup]) for tup in true_tuples]
      true_cases = [''.join([str(int(t)) for t in tup]) for tup in true_tuples]
      #truth_nums = [int(s, 2) for s in true_cases]
  
      from quine_mccluskey.qm import QuineMcCluskey
      qm = QuineMcCluskey(use_xor=False)
      result = qm.simplify_los(true_cases, num_bits=len(varnames))
      print(result)
      #ut.chr_range(3)
  
      #symbol_map = {
      #    '-': '',
      #    '1': '{v}',
      #    '0': 'not {v}',
      #    '^': '^',
      #}
  
      #'-' don't care: this bit can be either zero or one.
      #'1' the bit must be one.
      #'0' the bit must be zero.
      #'^' all bits with the caret are XOR-ed together.
      #'~' all bits with the tilde are XNOR-ed together.
  
      #formulas = [[symbol_map[r].format(v=v) for v, r in zip(varnames, rs)] for rs in result]
      grouped_terms = [dict(ut.group_items(varnames, rs)) for rs in result]
      def parenjoin(char, list_):
          if len(list_) == 0:
              return ''
          else:
              return '(' + char.join(list_) + ')'
  
      expanded_terms = [
          (
              term.get('1', []) +
              ['(not ' + b + ')' for b in term.get('0', [])] +
              [
                  parenjoin(' ^ ', term.get('^', [])),
                  parenjoin(' ~ ', term.get('~', [])),
              ]
          ) for term in grouped_terms
      ]
  
      final_terms = [[t for t in term if t] for term in expanded_terms]
  
      products = [parenjoin(' and ', [f for f in form if f]) for form in final_terms]
      final_expr = ' or '.join(products)
      print(final_expr)