diff --git a/src/lib/CoAlgFormula.ml b/src/lib/CoAlgFormula.ml
index f72e7f67534930f6c562728b068268f5ff52397d..1af782123b1bec761bc1a634704e4ecd31611e53 100644
--- a/src/lib/CoAlgFormula.ml
+++ b/src/lib/CoAlgFormula.ml
@@ -2390,6 +2390,42 @@ let rec hc_freeIn variable (f: hcFormula) =
      (* Do we need to exclude bound variables here? *)
      hc_freeIn variable f1
 
+let rec hc_freeIn2 variable (f: hcFormula) =
+  match f.HC.node with
+  | HCTRUE
+  | HCFALSE
+  | HCAP _
+  | HCNOT _
+  | HCCONST _
+  | HCCONSTN _ -> false
+  | HCVAR s ->
+     if compare variable s == 0
+     then true
+     else false
+  | HCAT (s, f1) ->
+     hc_freeIn variable f1
+  | HCOR (f1, f2)
+  | HCAND (f1, f2) ->
+     hc_freeIn variable f1 || hc_freeIn variable f2
+  | HCEX (_, f1)
+  | HCAX (_, f1)
+  | HCENFORCES (_, f1)
+  | HCALLOWS (_, f1)
+  | HCMORETHAN  (_, _, f1)
+  | HCMAXEXCEPT  (_, _, f1)
+  | HCATLEASTPROB (_, f1)
+  | HCLESSPROBFAIL (_, f1)
+  | HCID f1 ->
+     hc_freeIn variable f1
+  | HCNORM (f1, f2)
+  | HCNORMN (f1, f2)
+  | HCCHC (f1, f2) ->
+     hc_freeIn variable f1 || hc_freeIn variable f2
+  | HCFUS (first, f1) ->
+     hc_freeIn variable f1
+  | HCMU (var, f1)
+  | HCNU (var, f1) ->
+     if variable = var then false else hc_freeIn variable f1
 
 (** An instantiation of a hash table (of the standard library)
     for hash-consed formulae. The test for equality
diff --git a/src/lib/CoAlgFormula.mli b/src/lib/CoAlgFormula.mli
index cee483d6e346dc2286d16be9d6a87ab75afd4d8a..6231f7d77c6e7aff802d20c8a985fa10f1379cd9 100644
--- a/src/lib/CoAlgFormula.mli
+++ b/src/lib/CoAlgFormula.mli
@@ -177,6 +177,11 @@ val hc_formula : HcFormula.t -> formula -> hcFormula
 val hc_replace : HcFormula.t -> string -> hcFormula -> hcFormula -> hcFormula
 val hc_freeIn : string -> hcFormula -> bool
 
+(* Variant of freeIn that respects bound variables
+   FIXME: Merge into regular freeIn
+ *)
+val hc_freeIn2 : string -> hcFormula -> bool
+
 module HcFHt : (Hashtbl.S with type key = hcFormula)
 
 (* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/lib/CoAlgMisc.ml b/src/lib/CoAlgMisc.ml
index 8daf890dd49e3f9b498beab59c8b413198fdc48a..3916a80d3b766a9a6d0bcb46990d9072b1294eb8 100644
--- a/src/lib/CoAlgMisc.ml
+++ b/src/lib/CoAlgMisc.ml
@@ -940,13 +940,95 @@ let fragment = ref AlternationFree
 let getFragment () = !fragment
 
 
+type fixpoint_type = MU | NU
+type bound_vars = (fixpoint_type * string) list
+
+let alternationLevel bound_vars =
+  let rec intern current count = function
+    | [] -> count
+    | (MU, _) :: xs ->
+       let count = if current = MU then count else count + 1 in
+       intern MU count xs
+    | (NU, _) :: xs ->
+       let count = if current = NU then count else count + 1 in
+       intern NU count xs
+  in
+  intern NU 0 bound_vars
+
+let rec outerMU bound_vars = match bound_vars with
+  | [] -> None
+  | (MU, x) :: xs -> Some x
+  | _ :: xs -> outerMU xs
+
+let formulaAlternationLevel nomTbl hcF altLevel s f =
+  let seen = Array.init (Array.length !sortTable) (fun _ -> C.HcFHt.create 128) in
+  let rec intern fixpoints s f =
+    if C.HcFHt.mem seen.(s) f then ()
+    else begin
+      let free_vars = List.filter (fun (_,x) -> C.hc_freeIn2 x f) fixpoints in
+      C.HcFHt.replace altLevel.(s) f (alternationLevel free_vars);
+      C.HcFHt.replace seen.(s) f ();
+      let (func, sortlst) = !sortTable.(s) in
+      match f.HC.node with
+      | C.HCVAR _
+      | C.HCTRUE
+      | C.HCFALSE
+      | C.HCAP _
+      | C.HCNOT _
+      | C.HCCONST _
+      | C.HCCONSTN _ -> ()
+      | C.HCAT (name, f1) ->
+        let s1 =
+          match nomTbl name with
+          | None -> raise (C.CoAlgException ("Unknown nominal: " ^ name))
+          | Some sort -> sort
+        in
+        let hcnom = C.HcFormula.hashcons hcF (C.HCAP name) in
+        intern fixpoints s1 hcnom;
+        intern fixpoints s1 f1
+      | C.HCOR (f1, f2)
+      | C.HCAND (f1, f2) ->
+        intern fixpoints s f1;
+        intern fixpoints s f2
+      | C.HCEX (_, f1)
+      | C.HCAX (_, f1)
+      | C.HCENFORCES (_, f1)
+      | C.HCALLOWS (_, f1)
+      | C.HCMORETHAN (_,_,f1)
+      | C.HCMAXEXCEPT (_,_,f1)
+      | C.HCATLEASTPROB (_,f1)
+      | C.HCLESSPROBFAIL (_,f1)
+      | C.HCID f1 ->
+         intern fixpoints (List.hd sortlst) f1;
+      | C.HCNORM (f1, f2)
+      | C.HCNORMN(f1, f2)
+      | C.HCCHC (f1, f2) ->
+         intern fixpoints (List.nth sortlst 0) f1;
+         intern fixpoints (List.nth sortlst 1) f2
+      | C.HCFUS (first, f1) ->
+         let s1 = List.nth sortlst (if first then 0 else 1) in
+         intern fixpoints s1 f1
+      | C.HCMU (name, f1) ->
+         let unfold = C.hc_replace hcF name f f1 in
+         let appendvars = List.append free_vars [(MU,name)] in
+         C.HcFHt.replace altLevel.(s) f (alternationLevel appendvars);
+         intern appendvars s unfold
+      | C.HCNU (name, f1) ->
+         let unfold = C.hc_replace hcF name f f1 in
+         let appendvars = List.append free_vars [(NU, name)] in
+         C.HcFHt.replace altLevel.(s) f (alternationLevel appendvars);
+         intern appendvars s unfold
+      end
+  in
+  intern [] s f
+
 (* Calculate all possible formulae. This includes all subformulae and
    in the case of μ-Calculus the Fischer-Ladner closure.
 
    TODO: variable sort needs to match epected sort
  *)
 let rec detClosure vars nomTbl hcF fset vset atset nomset s f =
-  let newvars = List.filter (fun (x) -> C.hc_freeIn x f) vars in
+  let newvars = List.filter (fun x -> C.hc_freeIn x f) vars in
   let detClosure_ = detClosure newvars in
   let deferral = if List.length newvars > 0
                  then (List.hd newvars)
@@ -1080,7 +1162,6 @@ let rec detClosure vars nomTbl hcF fset vset atset nomset s f =
        let unfold = C.hc_replace hcF name f f1 in
        detClosure_ nomTbl hcF fset vset atset nomset s unfold
 
-
 let detClosureAt hcF atset name f () =
   match f.HC.node with
   | C.HCTRUE
@@ -1279,17 +1360,26 @@ let ppFormulae fragSpec nomTbl tbox (s, f) =
 
   let fset = Array.init card (fun _ -> C.HcFHt.create 128) in
   let vset = Array.init card (fun _ -> C.HcFHt.create 128) in
+  let altLevel = Array.init card (fun _ -> C.HcFHt.create 128) in
   let atset = C.HcFHt.create 64 in
   let nomset = Hashtbl.create 16 in
   for i = 0 to card-1 do
     detClosure [] nomTbl hcF fset vset atset nomset i hcfalse;
+    formulaAlternationLevel nomTbl hcF altLevel i hcfalse;
     detClosure [] nomTbl hcF fset vset atset nomset i hctrue;
+    formulaAlternationLevel nomTbl hcF altLevel i hctrue;
   done;
   detClosure [] nomTbl hcF fset vset atset nomset s  hcf;
+  formulaAlternationLevel nomTbl hcF altLevel s hcf;
   detClosure [] nomTbl hcF fset vset atset nomset s nhcf;
-  List.iter (fun (s, f) -> detClosure [] nomTbl hcF fset vset atset nomset s f)
+  formulaAlternationLevel nomTbl hcF altLevel s nhcf;
+  List.iter (fun (s, f) ->
+      detClosure [] nomTbl hcF fset vset atset nomset s f;
+      formulaAlternationLevel nomTbl hcF altLevel s f)
             hctbox;
-  List.iter (fun (s, f) -> detClosure [] nomTbl hcF fset vset atset nomset s f)
+  List.iter (fun (s, f) ->
+      detClosure [] nomTbl hcF fset vset atset nomset s f;
+      formulaAlternationLevel nomTbl hcF altLevel s f)
             nhctbox;
   Hashtbl.iter (fun n s -> C.HcFHt.iter (detClosureAt hcF atset n) fset.(s)) nomset;
 
diff --git a/src/lib/CoAlgMisc.mli b/src/lib/CoAlgMisc.mli
index 7815d8df5d25c5c6472608e1bdc5f1c1dd8cd30f..40eed0b240c4aa00d841746ecc68a6585e0a7c33 100644
--- a/src/lib/CoAlgMisc.mli
+++ b/src/lib/CoAlgMisc.mli
@@ -399,6 +399,21 @@ type fragment_spec = Auto | Fragment of fragment
 (** Return the fragment of the mu-calculus that the original formula uses *)
 val getFragment : unit -> fragment
 
+
+type fixpoint_type = MU | NU
+type bound_vars = (fixpoint_type * string) list
+
+(** Calculate the level of alternation between fixpoint types.
+
+   Bound vars must be ordered outer-first. *)
+val alternationLevel : bound_vars -> int
+
+val formulaAlternationLevel : (string -> int option) -> CoAlgFormula.HcFormula.t
+                 -> int CoAlgFormula.HcFHt.t array (* table for alternation levels *)
+                 -> sort
+                 -> (CoAlgFormula.hcFormula_node, CoAlgFormula.formula) HashConsing.hash_consed
+                 -> unit
+
 (** [ppFormulae fragment nomTbl tbox sf] initializes the array representation and various
     tables for the formula.
   
diff --git a/src/unit-tests/CoAlgMisc_tests.ml b/src/unit-tests/CoAlgMisc_tests.ml
new file mode 100644
index 0000000000000000000000000000000000000000..4606c92535b9bbffab4ebf9c6c83825e44eecdd2
--- /dev/null
+++ b/src/unit-tests/CoAlgMisc_tests.ml
@@ -0,0 +1,43 @@
+open OUnit2
+open CoAlgMisc
+
+module C = CoAlgFormula
+
+let alternationLevel_test _ =
+  assert_equal (alternationLevel []) 0;
+  assert_equal (alternationLevel [(MU,"x")]) 1;
+  assert_equal (alternationLevel [(NU,"x")]) 0;
+  assert_equal (alternationLevel [(NU,"x"); (MU,"y")]) 1;
+  assert_equal (alternationLevel [(NU,"x"); (NU, "y")]) 0;
+  assert_equal (alternationLevel [(NU,"x"); (MU, "y"); (NU, "z")]) 2;
+  assert_equal (alternationLevel [(NU,"x"); (MU, "a"); (MU, "y"); (NU, "z")]) 2
+
+let formulaAlternationLevel_test _ =
+  let wrapper formula subformula =
+    sortTable := FunctorParsing.sortTableFromString "K";
+    let hcF = C.HcFormula.create true in
+    let hcf = C.hc_formula hcF formula in
+    let nomTbl _ = Some 0 in
+    let altLevel = Array.init (Array.length !sortTable) (fun _ -> C.HcFHt.create 128) in
+    formulaAlternationLevel nomTbl hcF altLevel 0 hcf;
+    let hcSubF = C.hc_formula hcF subformula in
+    C.HcFHt.find altLevel.(0) hcSubF
+  in
+  let equal msg = assert_equal ~printer:string_of_int ~msg:msg in
+  equal "p & q" 0 (wrapper (C.AND(C.AP "p", C.AP "q")) (C.AP "q"));
+  equal "νX.[]X" 0 (wrapper (C.NU("X",(C.AX("", C.VAR "X")))) (C.NU("X",(C.AX("", C.VAR "X")))));
+  equal "µX.[]X" 1 (wrapper (C.MU("X",(C.AX("", C.VAR "X")))) (C.MU("X",(C.AX("", C.VAR "X")))));
+  (* ɸ = µX.p & νY.(<>(Y & p) | <>X) *)
+  let psi = C.NU("Y",C.OR(C.EX("",(C.AND(C.VAR "Y", C.AP "p"))) ,C.EX("",(C.VAR "X")))) in
+  let phi = (C.MU("X",(C.AND(C.AP "p", psi)))) in
+  equal "ɸ in ɸ" 1 (wrapper phi phi);
+  equal "p in ɸ" 0 (wrapper phi (C.AP "p"));
+  let psi1 = C.NU("Y",C.OR(C.EX("",(C.AND(C.VAR "Y", C.AP "p"))) ,C.EX("",phi))) in
+  equal "ψ in ɸ" 2 (wrapper phi psi1)
+
+let tests = "CoAlgMisc" >:::
+              [ ("alternationLevel" >:: alternationLevel_test)
+              ; ("formulaAlternationLevel" >:: formulaAlternationLevel_test)
+              ]
+
+(* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/unit-tests/CoAlgMisc_tests.mli b/src/unit-tests/CoAlgMisc_tests.mli
new file mode 100644
index 0000000000000000000000000000000000000000..ac3e6e6da69f9f977785c220e30ef4bf3a11e645
--- /dev/null
+++ b/src/unit-tests/CoAlgMisc_tests.mli
@@ -0,0 +1,3 @@
+val tests : OUnit2.test
+
+(* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/unit-tests/cool_unit_tests.ml b/src/unit-tests/cool_unit_tests.ml
index 65954fb02793073bee8fd49dda3dfa3b725462c8..291ddd78ccce6f5d96b943b6400b223ce17c2165 100644
--- a/src/unit-tests/cool_unit_tests.ml
+++ b/src/unit-tests/cool_unit_tests.ml
@@ -3,6 +3,7 @@ open OUnit2
 let tests =
   [ CoolUtils_tests.tests
   ; CoAlgFormula_tests.tests
+  ; CoAlgMisc_tests.tests
   ]
 
 let () = run_test_tt_main (test_list tests)