@Override
    public Formula visitFunction(
        Formula pF, List<Formula> pArgs, FunctionDeclaration<?> pFunctionDeclaration) {

      List<Formula> newArgs = Lists.transform(pArgs, f -> fmgrView.visit(f, this));

      if ((pFunctionDeclaration.getKind().equals(UF)
              && pFunctionDeclaration.getName().equalsIgnoreCase("Integer__*_"))
          || (pFunctionDeclaration.getKind().equals(MUL)
              && !isConstant(newArgs.get(0))
              && !isConstant(newArgs.get(1)))) {
        assert newArgs.size() == 2;
        return transformNonLinearMultiplication(
            newArgs.get(0), newArgs.get(1), pFunctionDeclaration.getType());

      } else {
        return fmgr.makeApplication(pFunctionDeclaration, newArgs);
      }
    }
    /**
     * Transform a non linear multiplication operation into a new linear {@link Formula} and adds it
     * to {@link #additionalAxioms}. The returned {@link Formula} represents the multiplication
     * opertion's result if that {@link Formula} is satisfied.
     *
     * @return a {@link Formula} representing the result of the multiplication operation
     */
    private Formula transformNonLinearMultiplication(
        Formula a, Formula b, FormulaType<?> formulaType) {
      BooleanFormulaManagerView bfmgr = fmgrView.getBooleanFormulaManager();

      Formula multAux =
          fmgr.makeVariable(
              formulaType,
              TERMINATION_AUX_VARS_PREFIX + "MULT_AUX_VAR_" + ID_GENERATOR.getFreshId());

      List<BooleanFormula> cases = Lists.newArrayList();
      Formula one = fmgrView.makeNumber(formulaType, 1);
      Formula minusOne = fmgrView.makeNumber(formulaType, -1);
      Formula zero = fmgrView.makeNumber(formulaType, 0);

      BooleanFormula aIsZero = fmgrView.makeEqual(a, zero);
      BooleanFormula bIsZero = fmgrView.makeEqual(b, zero);
      BooleanFormula factorIsZero = fmgrView.makeOr(aIsZero, bIsZero);
      cases.add(fmgrView.makeAnd(factorIsZero, fmgrView.makeEqual(multAux, zero)));

      BooleanFormula aIsOne = fmgrView.makeEqual(a, one);
      BooleanFormula bIsOne = fmgrView.makeEqual(b, one);
      cases.add(fmgrView.makeAnd(bIsOne, fmgrView.makeEqual(multAux, a)));
      cases.add(fmgrView.makeAnd(aIsOne, fmgrView.makeEqual(multAux, b)));

      // 0 < a < 1,  0 < b < 1, -1 < a < 0, -1 < b < 0, a > 1, ...
      BooleanFormula zeroLessALessOne =
          bfmgr.and(fmgrView.makeLessThan(zero, a, true), fmgrView.makeLessThan(a, one, true));
      BooleanFormula zeroLessBLessOne =
          bfmgr.and(fmgrView.makeLessThan(zero, b, true), fmgrView.makeLessThan(b, one, true));
      BooleanFormula minusOneLessALessZero =
          bfmgr.and(fmgrView.makeLessThan(minusOne, a, true), fmgrView.makeLessThan(a, zero, true));
      BooleanFormula minusOneLessBLessZero =
          bfmgr.and(fmgrView.makeLessThan(minusOne, b, true), fmgrView.makeLessThan(b, zero, true));
      BooleanFormula aGreaterOne = bfmgr.and(fmgrView.makeGreaterThan(a, one, true));
      BooleanFormula bGreaterOne = bfmgr.and(fmgrView.makeGreaterThan(b, one, true));
      BooleanFormula aLessMinuseOne = bfmgr.and(fmgrView.makeLessThan(a, one, true));
      BooleanFormula bLessMinuseOne = bfmgr.and(fmgrView.makeLessThan(b, one, true));

      // 0 < multAux < 1, -1 < multAux < 0, multAux > 1, ...
      BooleanFormula zeroLessResultLessOne =
          bfmgr.and(
              fmgrView.makeLessThan(zero, multAux, true),
              fmgrView.makeLessThan(multAux, one, true));
      BooleanFormula minusOneLessResultLessZero =
          bfmgr.and(
              fmgrView.makeLessThan(minusOne, multAux, true),
              fmgrView.makeLessThan(multAux, zero, true));
      BooleanFormula resultGreaterOne = bfmgr.and(fmgrView.makeGreaterThan(multAux, one, true));
      BooleanFormula resultLessMinuseOne = bfmgr.and(fmgrView.makeLessThan(multAux, one, true));
      BooleanFormula positiveResult = fmgrView.makeGreaterThan(multAux, zero, true);
      BooleanFormula negativeResult = fmgrView.makeLessThan(multAux, zero, true);

      cases.add(bfmgr.and(zeroLessALessOne, zeroLessBLessOne, zeroLessResultLessOne));
      cases.add(bfmgr.and(zeroLessALessOne, minusOneLessBLessZero, minusOneLessResultLessZero));
      cases.add(bfmgr.and(zeroLessALessOne, bGreaterOne, positiveResult));
      cases.add(bfmgr.and(zeroLessALessOne, bLessMinuseOne, negativeResult));
      cases.add(bfmgr.and(minusOneLessALessZero, zeroLessBLessOne, minusOneLessResultLessZero));
      cases.add(bfmgr.and(minusOneLessALessZero, minusOneLessBLessZero, zeroLessResultLessOne));
      cases.add(bfmgr.and(minusOneLessALessZero, bGreaterOne, negativeResult));
      cases.add(bfmgr.and(minusOneLessALessZero, bLessMinuseOne, positiveResult));
      cases.add(bfmgr.and(aGreaterOne, zeroLessBLessOne, positiveResult));
      cases.add(bfmgr.and(aGreaterOne, minusOneLessBLessZero, negativeResult));
      cases.add(bfmgr.and(aGreaterOne, bGreaterOne, resultGreaterOne));
      cases.add(bfmgr.and(aGreaterOne, bLessMinuseOne, resultLessMinuseOne));
      cases.add(bfmgr.and(aLessMinuseOne, zeroLessBLessOne, negativeResult));
      cases.add(bfmgr.and(aLessMinuseOne, minusOneLessBLessZero, positiveResult));
      cases.add(bfmgr.and(aLessMinuseOne, bGreaterOne, resultLessMinuseOne));
      cases.add(bfmgr.and(aLessMinuseOne, bLessMinuseOne, resultGreaterOne));

      additionalAxioms.add(bfmgr.or(cases));
      return multAux;
    }