let intersect (m1 : nfa) 
              (m2 : nfa)
              (p1 : state hashset) : ((state, state list) Hashtbl.t * nfa) =
  let m1 = (if !Options.maxsize > 0 && size m1.q > !Options.maxsize then 
              minimize m1 else  m1) in
  let m2 = (if !Options.maxsize > 0 && size m2.q > !Options.maxsize then 
              minimize m2 else m2) in
    
  let lhs = Hashtbl.create (size m2.q * size p1) in
  let put tbl x y = 
    let list = try Hashtbl.find tbl x with Not_found -> [] in
      Hashtbl.replace tbl x (y::list) in

  let cur_id = ref 0 in
  let queue  = ref [] in
  let newstates = Hashtbl.create (size m1.q) in

  let state (x,y) = (try Hashtbl.find newstates (x,y) 
                     with Not_found -> 
                       queue := (x,y)::(!queue);
                       Hashtbl.replace newstates (x,y) !cur_id;
                       if Hashset.mem p1 x then put lhs x !cur_id;
                       incr cur_id;
                       !cur_id - 1
                    ) in

  let result = new_nfa_states (state(m1.s,m2.s)) (state(m1.f,m2.f)) in

  let step (q1, q2) = 
    let delta_step q1' cset1 q2' cset2 =
      let charset = Charset.cap cset1 cset2 in
        if not (Charset.empty charset) then
          add_set_trans result (state (q1,q2)) charset (state (q1',q2')) 
    in
    let left_eps_step m1rhs =
      iter (fun m1q2 -> 
              Hashtbl.iter (fun (a,b) y -> 
                              if a = q1 then
                                let newstate = state (m1q2, b) in
                                  add_trans result y Epsilon newstate
                           ) newstates
           ) m1rhs
    in
    let right_eps_step m2rhs =
      iter (fun m2q2 -> 
              Hashtbl.iter (fun (a,b) y -> 
                              if b = q2 then
                                let newstate = state (a, m2q2) in
                                  add_trans result y Epsilon newstate
                           ) newstates
           ) m2rhs
    in

    let map1 = all_delta ~create:false m1.delta q1 in
    let map2 = all_delta ~create:false m2.delta q2 in
    let eps1 = which_states ~create:false m1.epsilon q1 in
    let eps2 = which_states ~create:false m2.epsilon q2 in
      nested_ht_iter map1 map2 delta_step;
      left_eps_step  eps1;
      right_eps_step eps2 in

  let rec walk () = match !queue with
    | (q1,q2)::qs -> queue := qs; step (q1,q2); walk ()
    | _ -> () in

    queue := [ m1.s, m2.s ];
    walk ();
    (lhs, result)