diff --git a/Ipopt.hs b/Ipopt.hs
--- a/Ipopt.hs
+++ b/Ipopt.hs
@@ -5,7 +5,7 @@
 module Ipopt (
   -- * high-level
   -- ** variables
-  var', var, varFresh,
+  var', var, varFresh', varFresh,
   AnyRF(..), Identity(..),
   -- ** functions
   addG, addF,
@@ -15,10 +15,7 @@
   module Control.Monad.State,
   solveNLP',
   -- *** solver options
-  -- $solverOptRef
-  addIpoptNumOption,
-  addIpoptStrOption, 
-  addIpoptIntOption,
+  ipopts,
   setIpoptProblemScaling,
   openIpoptOutputFile,
  
@@ -30,11 +27,10 @@
 ) where
 
 import Ipopt.PP
+import Ipopt.Options
 import Ipopt.NLP
 import Ipopt.Raw
 import Ipopt.AnyRF
 import Control.Monad.State
 import Control.Monad.Identity (Identity(..))
 
--- $solverOptRef
--- see <http://www.coin-or.org/Ipopt/documentation/node39.html ipopt's options reference>
diff --git a/Ipopt/AnyRF.hs b/Ipopt/AnyRF.hs
--- a/Ipopt/AnyRF.hs
+++ b/Ipopt/AnyRF.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE ConstraintKinds, RankNTypes, TypeFamilies, FlexibleInstances #-}
 {- | Description: representing functions that can be differentiated
 
 The 'AnyRF' wrapper holds functions that can be used
@@ -32,7 +33,8 @@
 data AnyRF cb = AnyRF (forall a. AnyRFCxt a => Vector a -> cb a)
 
 -- | RealFloat gives most numerical operations,
--- 'VectorSpace' is involved to allow using definitions from the splines package
+-- 'VectorSpace' is involved to allow using definitions from the
+-- <http://hackage.haskell.org/package/splines splines> package
 type AnyRFCxt a = (RealFloat a, VectorSpace a, Scalar a ~ a)
 
 -- *** helpers for defining instances
@@ -118,6 +120,10 @@
         negateV = negate
 
 -- * orphan instances 
+
+-- $orphans
+-- these belong somewhere between the @ad@ package and @vector-space@
+
 instance (Num a, AD.Mode f) => VectorSpace.AdditiveGroup (AD.AD f a) where
         zeroV = AD.zero
         (^+^) = (AD.<+>)
diff --git a/Ipopt/NLP.hs b/Ipopt/NLP.hs
--- a/Ipopt/NLP.hs
+++ b/Ipopt/NLP.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE TemplateHaskell #-}
 -- | Description : a EDSL for describing nonlinear programs
 --
--- see usage in @examples/Test3.hs@
+-- see usage in @examples/Test3.hs@ (and other examples)
 --
 -- IPOPT does support naming variables if you use c++
 -- (by overriding a @virtual void finalize_metadata@), but
@@ -47,18 +47,25 @@
 data NLPState = NLPState
     { -- | current maximum index
       _nMax :: Ix,
-      -- | what namespace (see 'inEnv')
+      -- | what namespace are we currently in (see 'inEnv')
       _currentEnv :: [String],
+      -- | fully qualified (see 'inEnv') name
       _variables :: M.Map String Ix,
-      _variablesInv :: IxMap String, -- invert variables map
+      -- | invert _variables
+      _variablesInv :: IxMap String,
       -- | human-readable descriptions for the constraint, objective and
       -- variables
       _constraintLabels, _objLabels :: IntMap String,
       _varLabels :: IxMap String,
+      -- | in what environments is a given var used?
       _varEnv :: IxMap (S.Set [String]),
       _constraintEnv, _objEnv :: IntMap [String],
       _nlpfun :: NLPFun,
+      -- | the default @(xL,xU)@ for @xL < x < xU@
       _defaultBounds :: (Double,Double),
+      -- | for nlopt (lower/upper)
+      _defaultConstraintTol :: (Double,Double),
+      _constraintTol :: Seq (Double,Double),
       -- | inital state variable for the solver
       _initX :: Vector Double }
     deriving (Show)
@@ -78,7 +85,9 @@
     mempty
     mempty mempty mempty -- env
     (mempty :: NLPFun)
-    (-1/0, 1/0)
+    (-1/0, 1/0) -- bounds at infinity
+    (1e-6,1e-6) -- arbitrary constraint tol
+    mempty
     V.empty
 
 type NLPT = StateT NLPState
@@ -125,9 +134,8 @@
 
     p <- liftIO (createIpoptProblemAD xl xu gl gu (F.sum . fs) (V.fromList . toList . gs))
     liftIO (setOpts p)
-    x0 <- uses initX (V.convert . V.map CDouble)
+    x0 <- uses initX V.convert
     r <- liftIO (ipoptSolve p =<< VS.thaw x0)
-    liftIO $ freeIpoptProblem p
     return r
 
 -- | add a constraint
@@ -141,6 +149,7 @@
     nlpfun . funG %= \(AnyRF fs) -> AnyRF $ \x -> fs x Seq.|> runIdentity (f x)
     n <- use (nlpfun . boundG . to Seq.length)
     copyEnv constraintEnv n
+    join $ uses defaultConstraintTol $ \t -> constraintTol %= (<> Seq.singleton t)
     addDesc constraintLabels d n
 
 {- | add a piece of the objective function, which is added in the form
@@ -195,7 +204,7 @@
     F.traverse_ (narrowBounds n) bs
     return n
 
--- | a combination of 'var'' and from'Ix'
+-- | a combination of 'var'' and 'ixToVar'
 var bs s = ixToVar <$> var' bs Nothing s
 
 {- | 'var', except this causes the solver to get a new variable,
@@ -205,20 +214,21 @@
 
 and the different letters can take different values (between 0 and 10)
 in the optimal solution (depending on what you do with @a@ and similar
-in the objective function).
+in the objective function and other constraints).
 -}
 varFresh' :: (Monad m, Functor m) => Maybe (Double,Double) -> String -> NLPT m Ix
 varFresh' bs s = do
     existing <- gets (^? variables . ix s)
     case existing of
-        Just a -> return a
-        Nothing -> do
+        Just _ -> do
             m <- use variables
             let n = M.size m + 1
             -- get the first of "x", "x1", "x1_", "x1__", "x1___"
             Just sUniq <- return $ find (`M.notMember` m) $ s : iterate (++"_") (s ++ show n)
             var' bs Nothing sUniq
+        Nothing -> var' bs Nothing s
 
+-- | see 'varFresh''
 varFresh bs s = fmap ixToVar $ varFresh' bs s
 
 -- *** namespace
@@ -302,6 +312,6 @@
 -- * internal
 seqToVecs :: MonadIO m => Seq (Double,Double) -> m (Vec,Vec)
 seqToVecs x = let (a,b) = unzip (toList x) in liftIO $ do
-    a' <- VS.thaw (VS.map CDouble (VS.fromList a))
-    b' <- VS.thaw (VS.map CDouble (VS.fromList b))
+    a' <- VS.thaw (VS.fromList a)
+    b' <- VS.thaw (VS.fromList b)
     return (a',b')
diff --git a/Ipopt/Options.hs b/Ipopt/Options.hs
new file mode 100644
--- /dev/null
+++ b/Ipopt/Options.hs
@@ -0,0 +1,347 @@
+{-# LANGUAGE TemplateHaskell #-}
+-- | Description: a quasiquote for the solver's options
+module Ipopt.Options where
+
+import Text.ParserCombinators.UU
+import Text.ParserCombinators.UU.Utils
+import Text.ParserCombinators.UU.BasicInstances hiding (Parser)
+
+import Ipopt.Raw
+import Language.Haskell.TH.Quote
+import Language.Haskell.TH
+import Language.Haskell.TH.Syntax
+import Control.Monad
+import Data.Char
+import qualified Data.Map as M
+
+{- | an expression-only quasiquote intended to be the second argument for
+'Ipopt.NLP.solveNLP'' (so @solveNLP [ipopts| tol = 1e-3 |]@). This is a shortcut
+for calling 'addIpoptNumOption' 'addIpoptStrOption' or 'addIpoptIntOption'. 
+
+Refer to <http://www.coin-or.org/Ipopt/documentation/node39.html ipopt's options reference>,
+the syntax is like @option_name = value;option2 = value2@. The semicolons are optional and
+whitespace alone can separate the option name from the value. A few examples of the parser:
+
+>>> :set -XQuasiQuotes
+>>> let f = [ipopts| tol = 1e-3; print_level = 0 |]
+>>> :t f
+f :: IpProblem -> IO ()
+
+>>> let p x = uuParseTest parseOpts x
+>>> p "tol = 3"
+([("tol",ANum 3.0)],[])
+
+>>> p "tol = 3 tol = 4" -- the last one wins. No warnings done (yet).
+([("tol",ANum 3.0),("tol",ANum 4.0)],[])
+
+>>> p "tol = 3\n\ntol = 4"
+([("tol",ANum 3.0),("tol",ANum 4.0)],[])
+
+>>> p "acceptable_iter = 25; output_file = foobar" -- quotation marks optional
+([("acceptable_iter",AInt 25),("output_file",AStr "foobar")],[])
+
+>>> p "output_file = \"foo bar\"" -- but needed here
+([("output_file",AStr "foo bar")],[])
+
+>>> putStrLn $ (++"...") $ take 100 $ show $ p "atol = 1.8" -- typo gets corrected
+([("tol",ANum 1.8)],[--    Deleted   'a' at position LineColPos 0 0 0 expecting one of [Whitespace, ...
+
+>>> p "tol = $xY" -- interpolating haskell variables
+([("tol",AVar OptionNum "xY")],[])
+
+-}
+ipopts :: QuasiQuoter
+ipopts = QuasiQuoter { quoteExp = \s -> do
+    Loc _filename _pkg _mod (lStart,cStart) (lEnd,cEnd) <- location
+    let (settings, errs) = parse ((,) <$> parseOpts <*> pEnd)
+                        (createStr (LineColPos 0 lStart cStart) s)
+    unless (null errs) $ reportWarning (unlines (map show errs))
+    [| \c -> $( foldr
+                (\(n,v) next -> [| do $(addIpoptOpt 'c n v); $next |])
+                [| return () |]
+                settings)
+        |],
+ quotePat = error "ipopts",
+ quoteDec = error "ipopts",
+ quoteType = error "ipopts"
+ }
+
+uuParseTest p x = parse ((,) <$> p <*> pEnd)
+                        (createStr (LineColPos 0 0 0) x)
+
+addIpoptOpt :: Name -> String -> OptionVal -> ExpQ
+addIpoptOpt c n (AStr s) = [| addIpoptStrOption $(varE c) n s |]
+addIpoptOpt c n (AInt i) = [| addIpoptIntOption $(varE c) n (i :: Int) |]
+addIpoptOpt c n (ANum i) = [| addIpoptNumOption $(varE c) n $(liftDouble i) |]
+addIpoptOpt c n (AVar OptionNum  x) = [| addIpoptNumOption $(varE c) n ($(dyn x)::Double) |]
+addIpoptOpt c n (AVar OptionInt  x) = [| addIpoptIntOption $(varE c) n ($(dyn x)::Int) |]
+addIpoptOpt c n (AVar OptionBool x) = [| addIpoptStrOption $(varE c) n
+                                            (if $(dyn x) then "yes" else "no") |]
+addIpoptOpt c n (AVar OptionStr x) = [| addIpoptStrOption $(varE c) n $(dyn x) |]
+
+-- | what 'lift' should do for Double
+liftDouble :: Double -> ExpQ
+liftDouble n | isNaN n = [| 0/0 :: Double |]
+    | isInfinite n, n > 0 = [| 1/0 :: Double |]
+    | isInfinite n, n < 0 = [| - 1 / 0 :: Double |]
+    | otherwise = [| $(litE (rationalL (toRational n))) :: Double |]
+
+parseOpts = pSpaces *> pListSep (optional (pSymbol ";") *> pSpaces) (parseOpt <* pSpaces)
+
+parseOpt =
+    pAny (\(a,b) -> (,) <$>
+                pSymbol a <*
+                pSymbol "=" <*>
+                (parseVar b <<|> parseLit b ))
+        (M.toList ipoptOptions)
+ where
+  parseVar b = AVar b <$> (pSym '$' *> hsIdent)
+
+  -- [_a-z][A-Za-z_']*
+  hsIdent = (:) <$> (pRange ('a','z') <|> pSym '_')
+                <*> pMany (pRange ('A','Z') <|>
+                            pRange ('a','z') <|>
+                            pSym '_' <|>
+                            pSym '\'')
+  parseLit b = case b of
+    OptionNum  -> ANum <$> pDouble
+    OptionBool -> AStr <$> pAny pSymbol ["yes","no"]
+    OptionStr  -> AStr <$> (strLit <<|> pMany notSpaceOrSemicolonAscii)
+    OptionInt  -> AInt <$> pInteger
+
+  notSpaceOrSemicolonAscii = pRange ('!',':') <|> pRange ('<','~')
+  
+  strLit = pSym '"' *>
+      pMany
+          (pSatisfy (/= '"')
+                      (Insertion "\\\"" '"' 0))
+      <* pSym '"'
+
+data OptionVal = ANum Double | AStr String | AInt Int
+               | AVar OptionType String -- ^ @$x@
+    deriving (Show)
+data OptionType = OptionNum
+                | OptionStr
+                | OptionInt
+                | OptionBool -- ^ actually string yes or string no
+    deriving (Show,Eq)
+
+-- | a list of all the options in
+-- <http://www.coin-or.org/Ipopt/documentation/node39.html>
+ipoptOptions :: M.Map String OptionType
+ipoptOptions = M.fromList [
+  ("print_level",                            OptionInt),
+  ("print_user_options",                     OptionBool),
+  ("print_options_documentation",            OptionBool),
+  ("print_frequency_iter",                   OptionInt),
+  ("print_frequency_time",                   OptionNum),
+  ("output_file",                            OptionStr),
+  ("file_print_level",                       OptionInt),
+  ("option_file_name",                       OptionStr),
+  ("print_info_string",                      OptionBool),
+  ("inf_pr_output",                          OptionStr),
+  ("print_timing_statistics" ,               OptionBool),
+  -- tolerances
+  ("tol",                                    OptionNum),
+  ("max_iter",                               OptionInt),
+  ("max_cpu_time",                           OptionNum),
+  ("dual_inf_tol",                           OptionNum),
+  ("constr_viol_tol",                        OptionNum),
+  ("compl_inf_tol",                          OptionNum),
+  ("acceptable_tol",                         OptionNum),
+  ("acceptable_iter",                        OptionInt),
+  ("acceptable_constr_viol_tol",             OptionNum),
+  ("acceptable_dual_inf_tol",                OptionNum),
+  ("acceptable_compl_inf_tol",               OptionNum),
+  ("acceptable_obj_change_tol",              OptionNum),
+  ("diverging_iterates_tol",                 OptionNum),
+
+  -- scaling
+  ("obj_scaling_factor",                     OptionNum),
+  ("nlp_scaling_method",                     OptionStr),
+  ("nlp_scaling_max_gradient",               OptionNum),
+  ("nlp_scaling_min_value",                  OptionNum),
+
+  -- NLP
+  ("bound_relax_factor",                     OptionNum),
+  ("honor_original_bounds",                  OptionBool),
+  ("check_derivatives_for_naninf",           OptionBool),
+  ("nlp_lower_bound_inf",                    OptionNum),
+  ("nlp_upper_bound_inf",                    OptionNum),
+  ("fixed_variable_treatment",               OptionStr),
+  ("jac_c_constant",                         OptionBool),
+  ("jac_d_constant",                         OptionBool),
+  ("hessian_constant",                       OptionBool),
+
+
+  -- initialization
+  ("bound_frac",                             OptionNum),
+  ("bound_push",                             OptionNum),
+  ("slack_bound_frac",                       OptionNum),
+  ("slack_bound_push",                       OptionNum),
+  ("bound_mult_init_val",                    OptionNum),
+  ("constr_mult_init_max",                   OptionNum),
+  ("bound_mult_init_method",                 OptionStr),
+
+  -- barrier parameter
+  ("mehrotra_algorithm",                     OptionBool),
+  ("mu_strategy",                            OptionStr),
+  ("mu_oracle",                              OptionStr),
+  ("quality_function_max_section_steps",     OptionInt),
+  ("fixed_mu_oracle",                        OptionStr),
+  ("adaptive_mu_globalization",              OptionStr),
+  ("mu_init",                                OptionNum),
+  ("mu_max_fact",                            OptionNum),
+  ("mu_max",                                 OptionNum),
+  ("mu_min",                                 OptionNum),
+  ("mu_target",                              OptionNum),
+  ("barrier_tol_factor",                     OptionNum),
+  ("mu_linear_decrease_factor",              OptionNum),
+  ("mu_superlinear_decrease_power",          OptionNum),
+
+
+  -- multiplier updates
+  ("alpha_for_y",                            OptionStr),
+  ("alpha_for_y_tol",                        OptionNum),
+  ("recalc_y",                               OptionBool),
+  ("recalc_y_feas_tol",                      OptionNum),
+
+  -- line search
+  ("max_soc",                                OptionInt),
+  ("watchdog_shortened_iter_trigger",        OptionInt),
+  ("watchdog_trial_iter_max",                OptionInt),
+  ("accept_every_trial_step",                OptionBool),
+  ("corrector_type",                         OptionStr),
+
+  -- warm start
+  ("warm_start_init_point",                  OptionBool),
+  ("warm_start_bound_push",                  OptionNum),
+  ("warm_start_bound_frac",                  OptionNum),
+  ("warm_start_slack_bound_frac",            OptionNum),
+  ("warm_start_slack_bound_push",            OptionNum),
+  ("warm_start_mult_bound_push",             OptionNum),
+  ("warm_start_mult_init_max",               OptionNum),
+
+  -- restoration phase
+  ("expect_infeasible_problem",              OptionBool),
+  ("expect_infeasible_problem_ctol",         OptionNum),
+  ("expect_infeasible_problem_ytol",         OptionNum),
+  ("start_with_resto",                       OptionBool),
+  ("soft_resto_pderror_reduction_factor",    OptionNum),
+  ("required_infeasibility_reduction",       OptionNum),
+  ("bound_mult_reset_threshold",             OptionNum),
+  ("constr_mult_reset_threshold",            OptionNum),
+  ("evaluate_orig_obj_at_resto_trial",       OptionBool),
+
+  -- linear solver
+  ("linear_solver",                          OptionStr),
+  ("linear_system_scaling",                  OptionStr),
+  ("linear_scaling_on_demand",               OptionBool),
+  ("max_refinement_steps",                   OptionInt),
+  ("min_refinement_steps",                   OptionInt),
+
+  -- hessian perturbation
+  ("max_hessian_perturbation",               OptionNum),
+  ("min_hessian_perturbation",               OptionNum),
+  ("first_hessian_perturbation",             OptionNum),
+  ("perturb_inc_fact_first",                 OptionNum),
+  ("perturb_inc_fact",                       OptionNum),
+  ("perturb_dec_fact",                       OptionNum),
+  ("jacobian_regularization_value",          OptionNum),
+
+  -- quasi newton
+  ("hessian_approximation",                  OptionStr),
+  ("limited_memory_update_type",             OptionStr),
+  ("limited_memory_max_history",             OptionInt),
+  ("limited_memory_max_skipping",            OptionInt),
+  ("limited_memory_initialization",          OptionStr),
+  ("limited_memory_init_val",                OptionNum),
+  ("limited_memory_init_val_max",            OptionNum),
+  ("limited_memory_init_val_min",            OptionNum),
+  ("limited_memory_special_for_resto",       OptionBool),
+
+  -- derivative test
+  ("derivative_test",                        OptionStr),
+  ("derivative_test_perturbation",           OptionNum),
+  ("derivative_test_tol",                    OptionNum),
+  ("derivative_test_print_all",              OptionBool),
+  ("derivative_test_first_index",            OptionInt),
+  ("point_perturbation_radius",              OptionNum),
+
+  -- ma27
+  ("ma27_pivtol",                            OptionNum),
+  ("ma27_pivtolmax",                         OptionNum),
+  ("ma27_liw_init_factor",                   OptionNum),
+  ("ma27_la_init_factor",                    OptionNum),
+  ("ma27_meminc_factor",                     OptionNum),
+
+  -- ma57
+  ("ma57_pivtol",                            OptionNum),
+  ("ma57_pivtolmax",                         OptionNum),
+  ("ma57_pre_alloc",                         OptionNum),
+  ("ma57_pivot_order",                       OptionInt),
+  ("ma57_automatic_scaling",                 OptionBool),
+  ("ma57_block_size",                        OptionInt),
+  ("ma57_node_amalgamation",                 OptionInt),
+  ("ma57_small_pivot_flag",                  OptionInt),
+
+  -- ma77
+  ("ma77_print_level",                       OptionInt),
+  ("ma77_buffer_lpage",                      OptionInt),
+  ("ma77_buffer_npage",                      OptionInt),
+  ("ma77_file_size",                         OptionInt),
+  ("ma77_maxstore",                          OptionInt),
+  ("ma77_nemin",                             OptionInt),
+  ("ma77_order",                             OptionStr),
+  ("ma77_small",                             OptionNum),
+  ("ma77_static",                            OptionNum),
+  ("ma77_u",                                 OptionNum),
+  ("ma77_umax",                              OptionNum),
+
+  -- ma86
+  ("ma86_print_level",                       OptionInt),
+  ("ma86_nemin",                             OptionInt),
+  ("ma86_order",                             OptionStr),
+  ("ma86_scaling",                           OptionStr),
+  ("ma86_small",                             OptionNum),
+  ("ma86_static",                            OptionNum),
+  ("ma86_u",                                 OptionNum),
+  ("ma86_umax",                              OptionNum),
+
+  -- ma97
+  ("ma97_print_level",                       OptionInt),
+  ("ma97_nemin",                             OptionInt),
+  ("ma97_order",                             OptionStr),
+  ("ma97_scaling",                           OptionStr),
+  ("ma97_scaling1",                          OptionStr),
+  ("ma97_scaling2",                          OptionStr),
+  ("ma97_scaling3",                          OptionStr),
+  ("ma97_small",                             OptionNum),
+  ("ma97_solve_blas3",                       OptionBool),
+  ("ma97_switch1",                           OptionStr),
+  ("ma97_switch2",                           OptionStr),
+  ("ma97_switch3",                           OptionStr),
+  ("ma97_u",                                 OptionNum),
+  ("ma97_umax",                              OptionNum),
+
+  -- mumps
+  ("mumps_pivtol",                           OptionNum),
+  ("mumps_pivtolmax",                        OptionNum),
+  ("mumps_mem_percent",                      OptionInt),
+  ("mumps_permuting_scaling",                OptionInt),
+  ("mumps_pivot_order",                      OptionInt),
+  ("mumps_scaling",                          OptionInt),
+
+  -- paradiso
+  ("pardiso_matching_strategy",              OptionStr),
+  ("pardiso_max_iterative_refinement_steps", OptionInt),
+  ("pardiso_msglvl",                         OptionInt),
+  ("pardiso_order",                          OptionStr),
+
+  -- wsmp
+  ("wsmp_num_threads",                       OptionInt),
+  ("wsmp_ordering_option",                   OptionInt),
+  ("wsmp_pivtol",                            OptionNum),
+  ("wsmp_pivtolmax",                         OptionNum),
+  ("wsmp_scaling",                           OptionInt),
+  ("wsmp_singularity_threshold:",            OptionNum)]
diff --git a/Ipopt/PP.hs b/Ipopt/PP.hs
--- a/Ipopt/PP.hs
+++ b/Ipopt/PP.hs
@@ -11,6 +11,7 @@
 import Text.Printf
 import qualified Data.IntMap as IM
 import qualified Data.Vector.Storable as VS
+import qualified Data.Vector.Generic as VG
 import Data.Vector (Vector)
 import qualified Data.Vector as V
 import Control.Monad.State
@@ -24,8 +25,6 @@
 makeLensesWith (lensRules & lensField  .~ stripPrefix "_ipOptSolved_")  ''IpOptSolved
 
 -- * pretty printing
-ppSoln :: (MonadIO m, Functor m) =>
-    NLPState -> NLPT m (IpOptSolved Vector) -> m (IpOptSolved Vector, Doc)
 ppSoln state0 problem = flip evalStateT state0 $ runWriterT $ do
     s <- lift problem
     st <- get
@@ -34,16 +33,17 @@
     br
     tell $ "obj_tot" <> double (s^.objective)
     join $ uses (nlpfun . funF) $ \(AnyRF f) -> case sortBy (comparing fst) $
-                                                    toList (f (s^.x)) `zip` [1 .. ] of
+                                toList (f (s^.x&VG.convert)) `zip` [1 .. ] of
         [_] -> return ()
         [] -> return ()
         xs -> for_ xs $ \(x,i) -> tell $ "obj" <> int i <> colon <$> double x
 
     for_ (st ^. variablesInv . from ixMap . to IM.toList) $ \(k,desc) -> do
         br
-        tell $ string desc <> "(" <> int k <> ")" <> "=" <> string (printf "%.3g" (s ^?! x . ix k))
+        tell $ string desc <> "(" <> int k <> ")" <> "=" <>
+            string (printf "%.3g" (s ^?! x . ix k))
     br
-    tell $ "g" <$> foldMap (\e -> mempty <$$> double e) (s ^. g)
+    tell $ "g" <$> foldMap (\e -> mempty <$$> double e) (s ^. g & VG.convert :: V.Vector Double)
 
     return s
 
diff --git a/Ipopt/Raw.chs b/Ipopt/Raw.chs
--- a/Ipopt/Raw.chs
+++ b/Ipopt/Raw.chs
@@ -104,7 +104,8 @@
 {#enum AlgorithmMode as ^ {underscoreToCase} deriving (Show) #}
 
 
-newtype IpProblem = IpProblem { unIpProblem :: Ptr ()}
+{#pointer IpoptProblem as IpProblem foreign newtype #}
+ipp x = withIpProblem x
 
 type family UnFunPtr a
 type instance UnFunPtr (FunPtr a) = a
@@ -163,15 +164,21 @@
 wrapIpH fSparsity fEval = wrapIpH1 (wrapIpH2 fSparsity fEval)
 
 
-vmUnsafeWith = VM.unsafeWith
+vmUnsafeWith :: Vec -> (Ptr CDouble -> IO r) -> IO r
+vmUnsafeWith v f = VM.unsafeWith v (f . castPtr)
 
 -- | Vector of numbers
-type Vec = VM.IOVector IpNumber
+type Vec = VM.IOVector Double
 
+-- depend on CDouble being just a newtype on Double which
+-- is the same as the IpNumber defined in the ipopt header, so this
+-- should cause a compile failure if that's not the case...
+_ = CDouble (5 :: Double) :: IpNumber
+
 fromVec :: VG.Vector v Double => Vec -> IO (v Double)
 fromVec mv = do
     v <- VS.freeze mv 
-    return (VG.convert (VS.map (\(CDouble a) -> a) v))
+    return (VG.convert v)
 
 
 createIpoptProblem :: Vec -> Vec -> Vec -> Vec
@@ -180,31 +187,33 @@
     | lx <- VM.length xL,
       lx == VM.length xU,
       lg <- VM.length gL,
-      lg == VM.length gU = createIpoptProblem3 lx xL xU lg gL gU nJac nHess 0 f g gradF jacG hess
+      lg == VM.length gU = do
+        p <- createIpoptProblem3 lx xL xU lg gL gU nJac nHess 0 f g gradF jacG hess
+        p' <- newForeignPtr freeIpoptProblem (castPtr p)
+        return (IpProblem (castForeignPtr p'))
    | otherwise = error "dimensions wrong!"
 
+
 {#fun CreateIpoptProblem as createIpoptProblem3
         { `Int', vmUnsafeWith* `Vec', vmUnsafeWith* `Vec',
           `Int', vmUnsafeWith* `Vec', vmUnsafeWith* `Vec',
           `Int', `Int', `Int', id `IpF', id `IpG', id `IpGradF',
-          id `IpJacG', id `IpH' } -> `IpProblem' IpProblem #}
-_ = {#fun AddIpoptNumOption as ^
-    { unIpProblem `IpProblem', `String', `Double' } -> `Bool' #}
+          id `IpJacG', id `IpH' } -> `Ptr IpProblem' id #}
 
-_ = {#fun AddIpoptStrOption as ^
-    { unIpProblem `IpProblem', `String', `String' } -> `Bool' #}
+{#fun AddIpoptNumOption as ^ { ipp* `IpProblem', `String', `Double' } -> `Bool' #}
 
-_ = {#fun AddIpoptIntOption as ^
-    { unIpProblem `IpProblem', `String', `Int' } -> `Bool' #}
+{#fun AddIpoptStrOption as ^ { ipp* `IpProblem', `String', `String' } -> `Bool' #}
 
-_ = {#fun FreeIpoptProblem as ^
-    { unIpProblem `IpProblem' } -> `()' #}
+{#fun AddIpoptIntOption as ^ { ipp* `IpProblem', `String', `Int' } -> `Bool' #}
 
-_ = {#fun OpenIpoptOutputFile as ^
-    { unIpProblem `IpProblem', `String', `Int' } -> `Bool' #}
+foreign import ccall unsafe "&FreeIpoptProblem"
+    freeIpoptProblem :: FunPtr (Ptr () -> IO ())
+-- {#fun FreeIpoptProblem as ^ { ipp* `IpProblem' } -> `()' #}
 
-_ = {#fun SetIpoptProblemScaling as ^
-    { unIpProblem `IpProblem',
+{#fun OpenIpoptOutputFile as ^ { ipp* `IpProblem', `String', `Int' } -> `Bool' #}
+
+{#fun SetIpoptProblemScaling as ^
+    { ipp* `IpProblem',
     `Double',
      vmUnsafeWith* `Vec',
      vmUnsafeWith* `Vec'
@@ -253,8 +262,8 @@
       mult_x_L'
       mult_x_U'
 
-_ = {#fun IpoptSolve as ipoptSolve2
-        { unIpProblem `IpProblem',
+{#fun IpoptSolve as ipoptSolve2
+        { ipp* `IpProblem',
         vmUnsafeWith* `Vec',
         vmUnsafeWith* `Vec',
         alloca- `Double' peekFloatConv*,
diff --git a/Nlopt/NLP.hs b/Nlopt/NLP.hs
new file mode 100644
--- /dev/null
+++ b/Nlopt/NLP.hs
@@ -0,0 +1,52 @@
+module Nlopt.NLP where
+
+import Nlopt.Raw
+import Ipopt.NLP
+import Ipopt.AnyRF
+import qualified Data.Sequence as Seq
+import qualified Data.Vector.Generic as VG
+import qualified Data.Vector.Storable as VS
+import qualified Data.Vector.Storable.Mutable as VM
+import qualified Data.Vector as V
+import Control.Monad
+import Control.Monad.State
+import Control.Lens
+import Foreign.C
+import Control.Monad.Trans
+import qualified Data.Foldable as F
+import Control.Lens
+
+solveNlopt :: (VG.Vector v Double, MonadIO m)
+    => NloptAlgorithm
+    -> (NLOpt -> IO t) -- ^ set additional options
+    -> NLPT m (v Double, Double, NloptResult) -- ^ @x,objective,exitCode@
+solveNlopt alg moreOpts = do
+    (xl,xu) <- join $ uses (nlpfun . boundX) seqToVecs
+    gSeq <- use (nlpfun . boundG)
+    let ng = Seq.length gSeq
+
+    AnyRF fs <- use (nlpfun . funF)
+    AnyRF gs <- use (nlpfun . funG)
+
+    m <- liftIO . nloptCreate alg . (+1) =<< use (nMax . varIx)
+
+    x0 <- join $ uses initX (liftIO . VS.thaw . VG.convert)
+
+    gTol <- liftIO . VS.thaw . VS.fromList
+         =<< uses constraintTol (F.concatMap (\(a,b) -> [a,b]))
+    
+    (status,obj) <- liftIO $ do
+        nloptSetMinObjective m (toFuncAD (F.sum . fs))
+        nloptSetLowerBounds m xl
+        nloptSetUpperBounds m xu
+
+        let toLE :: (AnyRFCxt a) => Seq.Seq a -> V.Vector a
+            toLE xs = V.fromList $ concatMap (\(x,(l,u)) -> [realToFrac l-x,x- realToFrac u])
+                        (F.toList xs `zip` F.toList gSeq)
+        -- XXX drop constraints with bounds more than 1e20 or so? as ipopt does
+        nloptAddInequalityMconstraint m (2*ng) (toFuncMAD (toLE . gs)) gTol
+        moreOpts m
+        nloptOptimize m x0
+
+    x <- liftIO (VS.freeze x0)
+    return (VG.convert x, obj, status)
diff --git a/Nlopt/Raw.chs b/Nlopt/Raw.chs
new file mode 100644
--- /dev/null
+++ b/Nlopt/Raw.chs
@@ -0,0 +1,366 @@
+{-# LANGUAGE TypeFamilies,DeriveDataTypeable, RankNTypes, ConstraintKinds, FlexibleContexts #-}
+module Nlopt.Raw where
+
+import Ipopt.AnyRF
+
+import Foreign.C.Types
+import Foreign.Ptr
+import Foreign.Marshal
+import Foreign.Storable
+import Data.Int
+import C2HS
+
+import qualified Data.Vector.Storable.Mutable as VM
+import qualified Data.Vector as V
+import qualified Data.Vector.Storable as VS
+import qualified Data.Vector.Generic as VG
+
+import Control.Exception
+import Data.Typeable
+import Numeric.AD (grad, jacobian, hessian)
+import Control.Monad
+
+#include "nlopt.h"
+
+-- * converting haskell functions
+
+{- $conventions
+
+NLOpt has three different types for functions 'FunPtrFunc' 'FunPtrMFunc' and 'FunPtrPrecond'.
+
+[@AD@] means derivatives are calculated, and the haskell function does no IO.
+
+[@G@] mean you are providing the derivatives
+
+[@M@] means m functions are calculated at a time
+
+-}
+
+-- | an exact hessian calculated with AD. See 'toPrecondG'
+-- XXX BFGS approx could also be done...
+toPrecondAD :: (forall a. AnyRFCxt a => V.Vector a -> a) -> Precond
+toPrecondAD f n xs vs r _usrData =
+    toPrecondG (\x v -> return (hessian f x `mXv` v)) n xs vs r _usrData
+
+-- | see <http://ab-initio.mit.edu/wiki/index.php/NLopt_Reference#Preconditioning_with_approximate_Hessians>
+-- only applies to 'NLOPT_LD_CCSAQ'
+toPrecondG :: (VG.Vector vec Double, v ~ vec Double)
+    => (v -> v -> IO v) -- ^ given @x,v@ calculate  @H(x)v@ where H the hessian
+    -> Precond
+toPrecondG f n xs vs r _usrData = do
+    xs' <- ptrToV n xs
+    vs' <- ptrToV n vs
+    copyInto n r =<< f xs' vs'
+
+toFuncM :: VG.Vector v Double => (v Double -> IO (v Double) ) -> MFunc
+toFuncM f m r n xs _g _usrData = do
+    copyInto m r =<< f =<< ptrToV n xs
+
+-- | @n@ and @m@ type variables indicate the vector size as
+-- number of inputs and number of outputs respectively
+toFuncMG :: (VG.Vector n Double, VG.Vector n (m Double), n ~ m)
+    => (n Double -> IO (m Double) ) -- @f@
+    -> (n Double -> IO (n (m Double))) -- @grad f@
+    -> MFunc
+toFuncMG f g m r n xs g' _usrData = do
+    xs' <- ptrToV n xs
+    copyInto m r =<< f xs'
+    -- probably should do this better...
+    copyInto (n*m) g' . VG.concat . VG.toList =<< g xs'
+
+toFuncMAD :: (forall a. AnyRFCxt a => V.Vector a -> V.Vector a) -> MFunc
+toFuncMAD f m r n xs g _usrData = do
+    xs' <- ptrToV n xs
+    copyInto m r (f xs')
+    -- grad[i*n + j] should be satisfied...
+    copyInto (n*m) g (V.concat (V.toList (jacobian f xs')))
+
+toFunc :: (VG.Vector v Double) => (v Double -> IO Double) -> Func
+toFunc f n xs _g _usrData = fmap CDouble $ f =<< ptrToV n xs
+
+-- | where the gradient happens via AD
+toFuncAD :: (forall a. AnyRFCxt a => V.Vector a -> a) -> Func
+toFuncAD f n xs g _usrData = do
+    xs' <- ptrToV n xs
+    copyInto n g (grad f xs')
+    return (CDouble (f xs'))
+
+toFuncG :: (VG.Vector v Double) => (v Double -> IO Double) -- ^ @f@
+    -> (v Double -> IO (v Double)) -- ^ @grad(f)@
+    -> Func
+toFuncG f g n xs g' _usrData = do
+    xs' <- ptrToV n xs
+    copyInto n g' =<< g xs'
+    CDouble `fmap` f xs'
+
+type family UnFunPtr a
+type instance UnFunPtr (FunPtr a) = a
+
+type Func = UnFunPtr FunPtrFunc
+type MFunc = UnFunPtr FunPtrMFunc
+type Precond = UnFunPtr FunPtrPrecond
+
+type FunPtrFunc    = {# type nlopt_func #}
+type FunPtrMFunc   = {# type nlopt_mfunc #}
+type FunPtrPrecond = {# type nlopt_precond #}
+
+{#pointer *nlopt_opt as NLOpt foreign newtype #}
+{#enum nlopt_algorithm as ^ {} deriving (Show,Bounded,Ord,Eq) #}
+
+-- | negative (above NLOPT_SUCCESS) values of these are thrown as exceptions. The positive ones are
+-- return values.
+{#enum nlopt_result as ^ {} deriving (Show,Typeable) #}
+
+instance Exception NloptResult
+
+checkEC :: CInt -> IO NloptResult
+checkEC n | n < 0 = throwIO e
+          | otherwise = return e
+    where e = fromCInt n :: NloptResult
+
+{#fun nlopt_srand as ^ { `Int' } -> `()' #}
+{#fun nlopt_srand_time as ^ { } -> `()' #}
+{#fun nlopt_version as ^ {
+        alloca- `Int' peekInt*,
+        alloca- `Int' peekInt*,
+        alloca- `Int' peekInt*} -> `()' #}
+{#fun nlopt_create as ^ { toCInt `NloptAlgorithm', `Int' } -> `NLOpt' ptrToNLOpt * #}
+{#fun nlopt_copy as ^ { withNLOpt_* `NLOpt' } -> `NLOpt' ptrToNLOpt * #}
+
+-- | should not need to be called manually
+{#fun nlopt_destroy as ^ { id `Ptr ()' } -> `()' #}
+
+{#fun nlopt_optimize as ^ {
+    withNLOpt_* `NLOpt', vmUnsafeWith* `Vec', alloca- `Double' peekDouble* }
+        -> `NloptResult' checkEC* #}
+
+{#fun nlopt_set_min_objective as ^ {
+    withNLOpt_* `NLOpt', withFunc* `Func', withNull- `()' }
+        -> `NloptResult' checkEC* #}
+{#fun nlopt_set_max_objective as ^ {
+    withNLOpt_* `NLOpt', withFunc* `Func', withNull- `()' }
+        -> `NloptResult' checkEC* #}
+
+{#fun nlopt_set_precond_min_objective as ^ {
+    withNLOpt_* `NLOpt',
+    withFunc* `Func',
+    withPrecond* `Precond',
+    withNull- `()' }
+    -> `NloptResult' checkEC*  #}
+
+{#fun nlopt_set_precond_max_objective as ^ {
+    withNLOpt_* `NLOpt',
+    withFunc* `Func',
+    withPrecond* `Precond',
+    withNull- `()' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_algorithm as ^ { withNLOpt_* `NLOpt' } -> `NloptAlgorithm' fromCInt #}
+{#fun nlopt_get_dimension as ^ { withNLOpt_* `NLOpt' } -> `Int' #}
+
+-- * constraints
+{#fun nlopt_set_lower_bounds as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
+    -> `NloptResult' checkEC* #}
+{#fun nlopt_set_upper_bounds as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_lower_bounds as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
+    -> `NloptResult' checkEC* #}
+{#fun nlopt_get_upper_bounds as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_set_lower_bounds1 as ^ { withNLOpt_* `NLOpt', `Double' }
+    -> `NloptResult' checkEC* #}
+{#fun nlopt_set_upper_bounds1 as ^ { withNLOpt_* `NLOpt', `Double' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_remove_inequality_constraints as ^ { withNLOpt_* `NLOpt' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_add_inequality_constraint as ^
+    { withNLOpt_* `NLOpt',
+      withFunc* `Func',
+      withNull- `()',
+      `Double' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_add_precond_inequality_constraint as ^
+    { withNLOpt_* `NLOpt',
+      withFunc* `Func',
+      withPrecond* `Precond',
+      withNull- `()',
+      `Double' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_add_inequality_mconstraint as ^
+    { withNLOpt_* `NLOpt',
+      `Int',
+      withMFunc* `MFunc',
+      withNull- `()',
+      vmUnsafeWith* `Vec' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_remove_equality_constraints as ^ { withNLOpt_* `NLOpt' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_add_equality_constraint as ^
+    { withNLOpt_* `NLOpt',
+      withFunc* `Func',
+      withNull- `()',
+      `Double' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_add_precond_equality_constraint as ^
+    { withNLOpt_* `NLOpt',
+      withFunc* `Func',
+      withPrecond* `Precond',
+      withNull- `()',
+      `Double' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_add_equality_mconstraint as ^
+    { withNLOpt_* `NLOpt',
+      `Int',
+      withMFunc* `MFunc',
+      withNull- `()',
+      vmUnsafeWith* `Vec' } -> `NloptResult' checkEC* #}
+
+-- * stopping criteria
+{#fun nlopt_set_stopval as ^ { withNLOpt_* `NLOpt', `Double' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_stopval as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
+
+{#fun nlopt_set_ftol_rel as ^ { withNLOpt_* `NLOpt', `Double' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_ftol_rel as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
+
+{#fun nlopt_set_ftol_abs as ^ { withNLOpt_* `NLOpt', `Double' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_ftol_abs as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
+
+{#fun nlopt_set_xtol_rel as ^ { withNLOpt_* `NLOpt', `Double' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_xtol_rel as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
+
+{#fun nlopt_set_xtol_abs1 as ^ { withNLOpt_* `NLOpt', `Double' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_set_xtol_abs as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_xtol_abs as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_set_maxeval as ^ { withNLOpt_* `NLOpt', `Int' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_maxeval as ^ { withNLOpt_* `NLOpt' } -> `Int'  #}
+
+{#fun nlopt_set_maxtime as ^ { withNLOpt_* `NLOpt', `Double' }
+    -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_maxtime as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
+
+{#fun nlopt_force_stop as ^ { withNLOpt_* `NLOpt' } -> `NloptResult' checkEC* #}
+{#fun nlopt_set_force_stop as ^ { withNLOpt_* `NLOpt', `Int' } -> `NloptResult' checkEC* #}
+{#fun nlopt_get_force_stop as ^ { withNLOpt_* `NLOpt' } -> `Int' #}
+
+
+-- * more algorithm-specific parameters
+{#fun nlopt_set_local_optimizer as ^
+    { withNLOpt_* `NLOpt', withNLOpt_* `NLOpt' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_set_population as ^
+    { withNLOpt_* `NLOpt', `Int' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_population as ^ { withNLOpt_* `NLOpt' } -> `Int' #}
+
+{#fun nlopt_set_vector_storage as ^
+    { withNLOpt_* `NLOpt', `Int' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_vector_storage as ^ { withNLOpt_* `NLOpt' } -> `Int' #}
+
+{#fun nlopt_set_initial_step as ^ { withNLOpt_* `NLOpt',
+    vmUnsafeWith* `Vec' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_get_initial_step as ^ { withNLOpt_* `NLOpt',
+    vmUnsafeWith* `Vec',
+    vmUnsafeWith* `Vec' } -> `NloptResult' checkEC* #}
+
+{#fun nlopt_set_initial_step1 as ^ { withNLOpt_* `NLOpt',
+    `Double' } -> `NloptResult' checkEC* #}
+
+-- * utils for ffi
+
+-- $note
+-- much is copied from Ipopt.Raw
+
+withNLOpt_ x f = withNLOpt x (f . castPtr)
+
+vmUnsafeWith :: VM.IOVector Double -> (Ptr CDouble -> IO b) -> IO b
+vmUnsafeWith v f = VM.unsafeWith v (f . castPtr)
+
+ptrToVS :: Integral n => n -> Ptr CDouble -> IO Vec
+ptrToVS n p = do
+    fp <- newForeignPtr_ (castPtr p)
+    return (VM.unsafeFromForeignPtr0 fp (fromIntegral n))
+
+ptrToV :: (VG.Vector v Double, Integral n) => n -> Ptr CDouble -> IO (v Double)
+ptrToV n p = fmap V.convert $ VS.unsafeFreeze =<< ptrToVS n p
+
+copyInto _ p _ | p == nullPtr = return ()
+copyInto n dest src = do
+    to <- ptrToVS n dest
+    VM.copy to =<< VS.unsafeThaw (V.convert src)
+
+type Vec = VM.IOVector Double
+
+toCInt x = fromIntegral (fromEnum x)
+fromCInt x = toEnum (fromIntegral x)
+peekInt ptr = fmap fromIntegral (peek ptr)
+
+
+ptrToNLOpt :: Ptr () -> IO NLOpt
+ptrToNLOpt p = do
+    fp <- newForeignPtr nloptDestroyFP p
+    return (NLOpt (castForeignPtr fp))
+
+foreign import ccall "wrapper" mkNloptFinalizer :: (Ptr () -> IO ()) -> IO (FunPtr (Ptr () -> IO ()))
+
+foreign import ccall unsafe "& nlopt_destroy" nloptDestroyFP :: FunPtr (Ptr () -> IO ())
+
+foreign import ccall "wrapper" mkFunc :: Func -> IO (FunPtr Func)
+foreign import ccall "wrapper" mkPrecond :: Precond -> IO (FunPtr Precond)
+foreign import ccall "wrapper" mkMFunc :: MFunc -> IO (FunPtr MFunc)
+
+withFunc :: Func -> (FunPtr Func -> IO b) -> IO b
+withFunc f g = do
+    f' <- mkFunc f
+    r <- g f'
+    -- freeHaskellFunPtr f'
+    return r
+
+withPrecond :: Precond -> (FunPtr Precond -> IO b) -> IO b
+withPrecond f g = do
+    f' <- mkPrecond f
+    r <- g f'
+    -- freeHaskellFunPtr f'
+    return r
+
+withMFunc :: MFunc -> (FunPtr MFunc -> IO b) -> IO b
+withMFunc f g = do
+    f' <- mkMFunc f
+    r <- g f'
+    -- freeHaskellFunPtr f'
+    return r
+
+withNull f = f nullPtr
+
+-- | c2hs generates CDouble peek a Double instead
+peekDouble :: Ptr CDouble -> IO Double
+peekDouble p = peek (castPtr p)
+
+
+-- | naive matrix × vector
+mXv :: Num a => V.Vector (V.Vector a) -> V.Vector a -> V.Vector a
+mXv m v = V.map (V.sum . V.zipWith (*) v) m
+
+-- * c2hs-generated
diff --git a/examples/AllTests.hs b/examples/AllTests.hs
--- a/examples/AllTests.hs
+++ b/examples/AllTests.hs
@@ -1,23 +1,41 @@
-import Test1
-import Test2
-import Test3
-import Test4
-import Test5
+import Ipopt.PP
+import HS71manual
+import HS71ad
+import HS71nlpMonad
+import HS71adN
+import HS71nlpMonadN
+-- import HS71c
+import Spline1
 import System.Environment
 
--- probably should get autogenerated if lots of examples get generated...
+import Text.Printf
+import qualified Data.Vector.Storable as V
+import Text.PrettyPrint.ANSI.Leijen (putDoc)
+import Text.Read
+import Control.Lens
+import Nlopt.Raw
+
+ppError v x = do
+    let x_official = V.fromList [1, 4.74299964, 3.82114998, 1.37940829]
+
+    let sse :: Double
+        sse = V.sum $ V.zipWith (\a b -> (a-b)^2) (x^.v) x_official
+    printf "\n||x - x_official|| = %f\n" sse
+
 main = do
     as <- getArgs
     case as of
         [] -> error "run like: ipopt-hs_Tests 1\nipopt-hs_Tests all"
-        ["1"] -> main1
-        ["2"] -> main2
-        ["3"] -> main3
-        ["4"] -> main4
-        ["5"] -> main5
-        ["all"] -> do
-            main1
-            main2
-            main3
-            main4
-            main5
+        [a] | Just n <- readMaybe a,
+              n < length examples -> examples !! n
+        ["all"] -> sequence_ examples
+
+-- probably should get autogenerated if lots of examples get generated...
+examples = 
+ [ppError x =<< hs71manual
+ ,ppError x =<< hs71ad
+ ,ppError (_1.x) =<< hs71nlpMonad
+ ,ppError _1 =<< hs71adN NLOPT_LD_SLSQP
+ ,ppError (_1.to V.convert) =<< hs71nlpMonadN NLOPT_LD_SLSQP
+ ,spline1
+ ]
diff --git a/ipopt-hs.cabal b/ipopt-hs.cabal
--- a/ipopt-hs.cabal
+++ b/ipopt-hs.cabal
@@ -1,5 +1,5 @@
 name:                ipopt-hs
-version:             0.3.0.0
+version:             0.4.0.0
 synopsis:            haskell binding to ipopt including automatic differentiation
 description:  a haskell binding to the nonlinear programming solver ipopt
               <http://projects.coin-or.org/Ipopt>
@@ -14,11 +14,10 @@
               A embedded language, similar to the one provided by glpk-hs, is
               defined in "Ipopt.NLP". The goal is to define problems at a level
               similar to other "algebraic modeling languages", but retain some
-              of the safety and flexibility available in haskell.
-             .
-              Refer to @examples/Test1.hs@ for an example where the derivatives
-              are computed by hand, @Test2.hs@ for the use of
-              'createIpoptProblemAD' and @Test3.hs@ for the highest level.
+              of the safety and flexibility available in haskell. There is some
+              overhead <http://code.haskell.org/~aavogt/ipopt-hs/examples/bench.html>
+              but at least on the small 4-variable constrained optimization
+              problem.
              .
               Current limitations include:
              .
@@ -27,13 +26,9 @@
                 somehow.  Currently it is done because AD needs a Traversable
                 structure, but Storable vectors are not traversable.
              .
-              * sparseness of derivatives isn't used to decide which way to calculate (forward vs. backward mode)
-             .
-              * probably doesn't work if @IpStdCInterface.h@ has Number =/= 'CDouble'
+              * sparseness of derivatives isn't used
              .
               * no binding to SetIntermediateCallback
-             .
-              * garbage collection of 'IpProblem' won't free C-side resources
 license:             BSD3
 license-file:        LICENSE
 author:              Adam Vogt <vogt.adam@gmail.com>
@@ -46,6 +41,10 @@
   description: build executable from examples/
   default: False
 
+flag nlopt
+  description: also include nlopt bindings
+  default: True
+
 source-repository head
     type:   darcs
     location: http://code.haskell.org/~aavogt/ipopt-hs
@@ -56,15 +55,24 @@
                        Ipopt.Raw,
                        Ipopt.Sparsity,
                        Ipopt.NLP,
+                       Ipopt.Options,
                        Ipopt.PP
+
+  if flag(nlopt)
+    exposed-modules:   Nlopt.Raw,
+                       Nlopt.NLP
+    pkgconfig-depends: nlopt
+
   other-modules:       C2HS
   build-depends:       base < 5,
-                       vector ==0.10.*,
-                       ad ==3.4.*,
-                       containers == 0.5.*,
-                       mtl == 2.*,
-                       lens >= 3.10,
+                       ad >=3.4,
                        ansi-wl-pprint >= 0.6.7,
+                       containers < 0.6,
+                       lens >= 3.10 && < 5,
+                       mtl == 2.*,
+                       template-haskell,
+                       uu-parsinglib >= 2.8,
+                       vector ==0.10.*,
                        vector-space >= 0.8.6
   default-language:    Haskell2010
   default-extensions:  ConstraintKinds,
@@ -80,15 +88,17 @@
   build-tools: c2hs
 
 
-
 executable ipopt-hs_Tests
   main-is:             AllTests.hs
   build-depends:       base <= 5, vector ==0.10.*,
                        ipopt-hs, lens, mtl, ansi-wl-pprint,
-                       Rlang-QQ, vector-space, splines, ad
+                       Rlang-QQ, vector-space, splines, ad,
+                       criterion, random-shuffle,
+                       optimization >= 0.1.3, linear
 
   hs-source-dirs:      examples
   default-language:    Haskell2010
+  other-extensions:    QuasiQuotes
   other-modules: Paths_ipopt_hs
   if !flag(build_examples)
     buildable:         False
