diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py
index 49d2a177a..b49811fb9 100644
--- a/doc/mk_tactic_doc.py
+++ b/doc/mk_tactic_doc.py
@@ -5,7 +5,10 @@ Tactic documentation generator script
 
 import os
 import re
+import sys
+import subprocess
 
+BUILD_DIR='../build'
 SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
 OUTPUT_DIRECTORY = os.path.join(os.getcwd(), 'api')
 
@@ -14,10 +17,30 @@ def doc_path(path):
 
 is_doc = re.compile("Tactic Documentation")
 is_doc_end = re.compile("\-\-\*\/")
+is_tac_name = re.compile("## Tactic (.*)")
 
+def extract_params(ous, tac):
+    z3_exe = BUILD_DIR + "/z3"
+    out = subprocess.Popen([z3_exe, f"-tactics:{tac}"], stdout=subprocess.PIPE).communicate()[0]
+    if not out:
+        return
+    out = out.decode(sys.stdout.encoding)
+    ous.write("#### Parameters\n")
+    ous.write("```\n")
+    for line in out:
+        ous.write(line.replace("\r",""))
+    ous.write("\n")
+    ous.write("```\n")
+    
 def generate_tactic_doc(ous, f, ins):
+    tac_name = None
     for line in ins:
+        m = is_tac_name.search(line)
+        if m:
+            tac_name = m.group(1)
         if is_doc_end.search(line):
+            if tac_name:
+                extract_params(ous, tac_name)
             break
         ous.write(line)
 
diff --git a/src/tactic/core/nnf_tactic.h b/src/tactic/core/nnf_tactic.h
index a821f56d0..fa724f2b8 100644
--- a/src/tactic/core/nnf_tactic.h
+++ b/src/tactic/core/nnf_tactic.h
@@ -13,7 +13,47 @@ Author:
 
     Leonardo de Moura (leonardo) 2011-12-28.
 
-Revision History:
+Note:
+
+   tactic documentation below co-created using gptchat (with some corrections) :-)
+
+Tactic Documentation:
+
+## Tactic nnf
+
+### Short Description:
+
+The tactic converts formulas to negation normal form (NNF)
+
+### Long Description
+
+In NNF, negations only appear in front of atomic formulas. 
+
+Standard rules for conversion into negation normal form are:
+- `(not (and p q))` is converted to `(or (not p) (not q))`
+- `(not (or p q))` is converted to `(and (not p) (not q))`
+- `(not (not p))` is converted to `p`
+- `(not (exists x. p))` is converted to `(forall x. (not p))`
+- `(not (forall x. p))` is converted to `(exists x. (not p))`
+
+
+Once all negations are pushed inside, the resulting formula is in NNF.
+
+### Example
+
+```z3
+  (declare-const x Int)
+  (assert (not (or (> x 0) (< x 0))))
+  (apply nnf)
+```
+
+This would convert the formula (not (or (> x 0) (< x 0))) to (and (<= x 0) (>= x 0)), which is in NNF.
+
+### Notes
+
+* supports unsat cores, proof terms
+
+
 
 --*/
 #pragma once
diff --git a/src/tactic/core/reduce_args_tactic.h b/src/tactic/core/reduce_args_tactic.h
index ed4dc3fb3..5fa1f74cd 100644
--- a/src/tactic/core/reduce_args_tactic.h
+++ b/src/tactic/core/reduce_args_tactic.h
@@ -13,7 +13,72 @@ Author:
 
     Leonardo (leonardo) 2012-02-19
 
-Notes:
+Tactic Documentation:
+
+## Tactic reduce-args
+
+### Short Description:
+
+Reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.
+
+### Long Description
+
+Example, suppose we have a function `f` with `2` arguments. 
+There are 1000 applications of this function, but the first argument is always "a", "b" or "c".
+Thus, we replace the `f(t1, t2)` with 
+
+*      `f_a(t2)`   if   `t1 = a`
+*      `f_b(t2)`   if   `t2 = b`
+*      `f_c(t2)`   if   `t2 = c`
+
+Since `f_a`, `f_b`, `f_c` are new symbols, satisfiability is preserved.
+   
+This transformation is very similar in spirit to the Ackermman's reduction. 
+
+This transformation should work in the following way:
+
+```
+   1- Create a mapping decl2arg_map from declarations to tuples of booleans, an entry [f -> (true, false, true)]
+       means that f is a declaration with 3 arguments where the first and third arguments are always values.
+   2- Traverse the formula and populate the mapping. 
+        For each function application f(t1, ..., tn) do
+          a) Create a boolean tuple (is_value(t1), ..., is_value(tn)) and do
+             the logical-and with the tuple that is already in the mapping. If there is no such tuple
+             in the mapping, we just add a new entry.
+
+   If all entries are false-tuples, then there is nothing to be done. The transformation is not applicable.
+
+   Now, we create a mapping decl2new_decl from (decl, val_1, ..., val_n) to decls. Note that, n may be different for each entry,
+   but it is the same for the same declaration.
+   For example, suppose we have [f -> (true, false, true)] in decl2arg_map, 
+  and applications f(1, a, 2), f(1, b, 2), f(1, b, 3), f(2, b, 3), f(2, c, 3) in the formula.
+   Then, decl2arg_map would contain
+        (f, 1, 2) -> f_1_2
+        (f, 1, 3) -> f_1_3
+        (f, 2, 3) -> f_2_3
+   where f_1_2, f_1_3 and f_2_3 are new function symbols.
+   Using the new map, we can replace the occurrences of f.
+```
+
+### Example
+ 
+```z3
+(declare-fun f (Int Int) Bool)
+(declare-const x Int)
+(assert (f 1 2))
+(assert (f 1 3))
+(assert (f 2 4))
+(assert (f 2 5))
+(assert (f 1 6))
+(assert (f 1 7))
+(assert (f 1 x))
+(apply reduce-args)
+```
+
+### Notes
+
+* supports unsat cores
+* does not support proof terms
 
 --*/
 #pragma once