diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3146c6f506889fc7b262f0ff3b9826cdf86c9707..3cb7a5a3fa50b618318010a142954b2bc8c7b861 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,6 +39,7 @@ SET(${PROJECT_NAME}_HEADERS
   spatial/fwd.hpp
   spatial/skew.hpp
   multibody/constraint.hpp
+  multibody/force-set.hpp
   multibody/joint.hpp
   multibody/joint/joint-base.hpp
   multibody/joint/joint-revolute.hpp
@@ -84,6 +85,9 @@ ENDFOREACH(header)
 ADD_EXECUTABLE(tspatial EXCLUDE_FROM_ALL unittest/tspatial.cpp)
 PKG_CONFIG_USE_DEPENDENCY(tspatial eigenpy)
 
+ADD_EXECUTABLE(constraint EXCLUDE_FROM_ALL unittest/constraint.cpp)
+PKG_CONFIG_USE_DEPENDENCY(constraint eigenpy)
+
 ADD_EXECUTABLE(rnea EXCLUDE_FROM_ALL unittest/rnea.cpp)
 PKG_CONFIG_USE_DEPENDENCY(rnea eigenpy)
 
diff --git a/src/algorithm/crba.hpp b/src/algorithm/crba.hpp
index cc49ad2cda42dcf55b1f1c954fee144fdadd1322..c21c101d20d68328f0d4a6bad5ad55f812c8e1ba 100644
--- a/src/algorithm/crba.hpp
+++ b/src/algorithm/crba.hpp
@@ -68,57 +68,36 @@ namespace se3
 		     int i)
     {
       /*
-       * F = Yi*Si
-       * for j<i 
-       *    F = ljXj.act(F)
-       *    M.block(j,i,nvj,nvi) = Sj'*F
+       * F[:,i] = Yi*Si
+       * M[i,i:subtree] = Si'*F[:,i:substree]
+       * if li>0:
+       *    Yli += liMi.act(Yi)
+       *    F[:,i:subtree] = liMi.act(F[:,i:subtree])
        */
-      std::cout << "*** joint " << i << std::endl;
-
-      Model::Index parent = model.parents[i];
-      if( parent>0 )
-	data.Ycrb[parent] += data.liMi[i].act(data.Ycrb[i]);
 
-      //std::cout << "liMi = " << (SE3::Matrix4)data.liMi[i] << std::endl;
+      const Model::Index & parent   = model.parents[i];
+      const int &          nsubtree = data.nvSubtree[i];
 
-      Eigen::Matrix<double,6,JointModel::nv> F;
       ConstraintXd S = jdata.S();
-      F = data.Ycrb[i].toMatrix() * S.matrix() ;
+      data.Fcrb.block<6,JointModel::nv>(0,jmodel.idx_v()) = data.Ycrb[i].toMatrix() * S.matrix() ;
+
+      data.M.block(jmodel.idx_v(),jmodel.idx_v(),JointModel::nv,nsubtree)
+	= S.matrix().transpose() * data.Fcrb.block(0,jmodel.idx_v(), 6,nsubtree);
 
       std::cout << "*** joint " << i << std::endl;
       std::cout << "iYi = " << (Inertia::Matrix6)data.Ycrb[i] << std::endl;
       std::cout << "iSi = " << S.matrix() << std::endl;
-      std::cout << "iFi = " << F << std::endl;
+      std::cout << "iFi = " << data.Fcrb.block<6,JointModel::nv>(0,jmodel.idx_v()) << std::endl;
+      std::cout << "F = " <<  data.Fcrb << std::endl;
+      std::cout << "M = " <<  data.M << std::endl;
 
-      data.M.block<JointModel::nv,JointModel::nv>(jmodel.idx_v(),jmodel.idx_v())
-	= S.matrix().transpose() * F;
-
-      SE3::Matrix6 ljXj = data.liMi[i];
-      while(parent>0)
+      if( parent>0 )
 	{
-	  JointDataGeneric jdataparent( data.joints[parent] );
-	  JointModelGeneric jmodelparent( model.joints[parent] );
-
-	  F = ljXj.inverse().transpose()*F; // equivalent to ljF = ljMj.act(jF)
-	  const ConstraintXd::DenseBase & S = jdataparent.S.matrix();
-	  std::cout << "jFi = " << F <<std::endl;
-	  std::cout << "jS = " << S <<std::endl;
-
-	  data.M.block(jmodelparent.idx_v(),jmodel.idx_v(),S.cols(),JointModel::nv)
-	    = S.transpose() * F;
-
-	  std::cout << "\t\t on parent #i,j = " << i<<","<<parent << "   ... compute block "
-		    << jmodelparent.idx_v() << ":" 
-		    << jmodelparent.idx_v() + S.cols() << " x "
-		    << jmodel.idx_v() << ":"
-		    << jmodel.idx_v() + JointModel::nv << std::endl;
-	  std::cout << "jFi = " << F << std::endl;
-	  std::cout << "jSj = " << S << std::endl;
-
-	  ljXj = data.liMi[parent];
-	  parent = model.parents[parent];
+	  data.Ycrb[parent] += data.liMi[i].act(data.Ycrb[i]);
+	  SE3::Matrix6 ljXj = data.liMi[i];
+	  data.Fcrb.block(0,jmodel.idx_v(), 6,nsubtree)
+	    = ljXj.transpose().inverse() * data.Fcrb.block(0,jmodel.idx_v(), 6,nsubtree);
 	}
-
     }
   };
 
diff --git a/src/multibody/joint/joint-revolute.hpp b/src/multibody/joint/joint-revolute.hpp
index a9f9d25ebaf96bde099dbb52014f83e2771ff62f..f67d2a6f7796029075a0beb9a0f9530ae35ed1c6 100644
--- a/src/multibody/joint/joint-revolute.hpp
+++ b/src/multibody/joint/joint-revolute.hpp
@@ -3,6 +3,7 @@
 
 #include "pinocchio/multibody/joint/joint-base.hpp"
 #include "pinocchio/multibody/constraint.hpp"
+#include "pinocchio/multibody/force-set.hpp"
 
 namespace se3
 {
@@ -62,12 +63,30 @@ namespace se3
       template<typename D>
       MotionRevolute operator*( const Eigen::MatrixBase<D> & v ) const { return MotionRevolute(v[0]); }
 
-      const ConstraintRevolute & transpose() const { return *this; }
-     //template<typename D> D operator*( const Force& f ) const
-     Force::Vector3::ConstFixedSegmentReturnType<1>::Type
-     operator*( const Force& f ) const
-     { return f.angular().segment<1>(axis); }
-
+      struct TransposeConst
+      {
+	const ConstraintRevolute & ref; 
+	TransposeConst(const ConstraintRevolute & ref) : ref(ref) {} 
+
+	Force::Vector3::ConstFixedSegmentReturnType<1>::Type
+	operator*( const Force& f ) const
+	{ return f.angular().segment<1>(axis); }
+
+	Eigen::Block<const typename ForceSet::Matrix3x>
+	operator*( const typename ForceSet::Block & F )
+	{
+	  return F.ref.angular().block(axis,F.idx,1,F.len);
+	}
+
+      };
+      TransposeConst transpose() const { return TransposeConst(*this); }
+
+    /* CRBA joint operators
+     *   - ForceSet::Block = ForceSet
+     *   - ForceSet operator* (Inertia Y,Constraint S)
+     *   - MatrixBase operator* (Constraint::Transpose S, ForceSet::Block)
+     *   - SE3::act(ForceSet::Block)
+     */
       operator ConstraintXd () const
       {
 	Eigen::Matrix<double,6,1> S;
@@ -155,6 +174,25 @@ namespace se3
       return R3;
     }
 
+  /* [CRBA] ForceSet operator* (Inertia Y,Constraint S) */
+  ForceSet operator*( const Inertia& Y,const JointRevolute<0>::ConstraintRevolute & )
+  { 
+    /* Y(:,3) = ( 0,-z, y,  I00+yy+zz,  I01-xy   ,  I02-xz   ) */
+    /* Y(:,4) = ( z, 0,-x,  I10-xy   ,  I11+xx+zz,  I12-yz   ) */
+    /* Y(:,5) = (-y, x, 0,  I20-xz   ,  I21-yz   ,  I22+xx+yy) */
+    const double 
+      &m = Y.mass(),
+      &x = Y.lever()[0],
+      &y = Y.lever()[1],
+      &z = Y.lever()[2];
+    const Inertia::Symmetric3 & I = Y.inertia();
+    return ForceSet( Eigen::Vector3d(0,-m*z,m*y),
+		     Eigen::Vector3d(I(0,0)+m*(y*y+z*z),
+				     I(0,1)-m*x*y,
+				     I(0,2)-m*x*z) );
+  }
+  /* [CRBA]  MatrixBase operator* (Constraint::Transpose S, ForceSet::Block) */
+  //template<int axis>
 
 
   template<int axis>
@@ -224,12 +262,15 @@ namespace se3
 
   };
 
+  typedef JointRevolute<0> JointRX;
   typedef JointDataRevolute<0> JointDataRX;
   typedef JointModelRevolute<0> JointModelRX;
 
+  typedef JointRevolute<1> JointRY;
   typedef JointDataRevolute<1> JointDataRY;
   typedef JointModelRevolute<1> JointModelRY;
 
+  typedef JointRevolute<2> JointRZ;
   typedef JointDataRevolute<2> JointDataRZ;
   typedef JointModelRevolute<2> JointModelRZ;
 
diff --git a/src/multibody/joint/joint-variant.hpp b/src/multibody/joint/joint-variant.hpp
index de332d43d39a4b7b4068e93a1264c5112812a220..3e7ae192f8588ec213c1f141e12c07458be0743d 100644
--- a/src/multibody/joint/joint-variant.hpp
+++ b/src/multibody/joint/joint-variant.hpp
@@ -23,6 +23,50 @@ namespace se3
     { return boost::apply_visitor( CreateJointData(), jmodel ); }
   };
 
+  class Joint_nv: public boost::static_visitor<int>
+  {
+  public:
+    template<typename D>
+    int operator()(const JointModelBase<D> & ) const
+    { return D::nv; }
+    
+    static int run( const JointModelVariant & jmodel)
+    { return boost::apply_visitor( Joint_nv(), jmodel ); }
+  };
+
+  class Joint_nq: public boost::static_visitor<int>
+  {
+  public:
+    template<typename D>
+    int operator()(const JointModelBase<D> & ) const
+    { return D::nq; }
+    
+    static int run( const JointModelVariant & jmodel)
+    { return boost::apply_visitor( Joint_nq(), jmodel ); }
+  };
+
+  class Joint_idx_q: public boost::static_visitor<int>
+  {
+  public:
+    template<typename D>
+    int operator()(const JointModelBase<D> & jmodel) const
+    { return jmodel.idx_q(); }
+    
+    static int run( const JointModelVariant & jmodel)
+    { return boost::apply_visitor( Joint_idx_q(), jmodel ); }
+  };
+
+  class Joint_idx_v: public boost::static_visitor<int>
+  {
+  public:
+    template<typename D>
+    int operator()(const JointModelBase<D> & jmodel) const
+    { return jmodel.idx_v(); }
+    
+    static int run( const JointModelVariant & jmodel)
+    { return boost::apply_visitor( Joint_idx_v(), jmodel ); }
+  };
+
 } // namespace se3
 
 EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::JointModelVariant);
diff --git a/src/multibody/model.hpp b/src/multibody/model.hpp
index eda2c96f1c82cbb413f1b1e688fa46d1a78898a6..34380b4b2edfb5c67af581989c10f401be6eae23 100644
--- a/src/multibody/model.hpp
+++ b/src/multibody/model.hpp
@@ -76,6 +76,7 @@ namespace se3
     Eigen::MatrixXd Fcrb;                 // Spatial forces set, used in CRBA
 
     std::vector<Model::Index> lastChild;  // Index of the last child (for CRBA)
+    std::vector<int> nvSubtree;           // Dimension of the subtree motion space (for CRBA)
 
     Data( const Model& ref );
 
@@ -154,6 +155,7 @@ namespace se3
     ,M(ref.nv,ref.nv)
     ,Fcrb(6,ref.nv)
     ,lastChild(ref.nbody)
+    ,nvSubtree(ref.nbody)
   {
     for(int i=0;i<model.nbody;++i) 
       joints.push_back(CreateJointData::run(model.joints[i]));
@@ -173,6 +175,10 @@ namespace se3
 	if(lastChild[i] == -1) lastChild[i] = i;
 	const Index & parent = model.parents[i];
 	lastChild[parent] = std::max(lastChild[i],lastChild[parent]);
+
+	nvSubtree[i] 
+	  = Joint_idx_v::run(model.joints[lastChild[i]]) + Joint_nv::run(model.joints[lastChild[i]])
+	  - Joint_idx_v::run(model.joints[i]);
       }
   }
 
diff --git a/src/spatial/inertia.hpp b/src/spatial/inertia.hpp
index 263a67c629a6bf6171329325a591f543c0b9a5b1..5cadda30f45b7ae9ee1ad72f69ff3517802f2025 100644
--- a/src/spatial/inertia.hpp
+++ b/src/spatial/inertia.hpp
@@ -100,13 +100,13 @@ namespace se3
     {
       const double & mm = m+other.m;
       const Matrix3 & X = skew((c-other.c).eval());
-      Matrix3 mmmXX = m*other.m/mm*X*X; // TODO clean this mess
+      Matrix3 mmmXX = (m*other.m/mm*X*X).eval(); // TODO clean this mess
       return InertiaTpl(mm, (m*c+other.m*other.c)/mm, dense_I+other.dense_I-mmmXX); // TODO: += in Eigen::Symmetric?
     }
     Inertia& operator+=(const InertiaTpl &other)
     {
       const Matrix3 & X = skew((c-other.c).eval());
-      Matrix3 mmXX = m*other.m*X*X; // TODO: clean this mess
+      Matrix3 mmXX = (m*other.m*X*X).eval(); // TODO: clean this mess
       c *=m; c+=other.m*other.c;
       m+=other.m; c/=m;  
       dense_I+=other.dense_I-mmXX/m; // TODO: += in Eigen::Symmetric?
diff --git a/src/spatial/se3.hpp b/src/spatial/se3.hpp
index a2515c9ff3dd2d10593eeda85ae25d721e2be5d5..63e109c47f40bee3c60cf24be12956cfef2239fd 100644
--- a/src/spatial/se3.hpp
+++ b/src/spatial/se3.hpp
@@ -8,6 +8,12 @@
 namespace se3
 {
 
+  namespace internal 
+  {
+    template<typename D>
+    struct ActionReturn    { typedef D Type; };
+  }
+
   /** The rigid transform aMb can be seen in two ways: 
    *
    * - given a point p expressed in frame B by its coordinate vector Bp, aMb
@@ -100,9 +106,11 @@ namespace se3
     /* --- GROUP ACTIONS ON M6, F6 and I6 --- */
 
      /// ay = aXb.act(by)
-    template<typename D> D act   (const D & d) const { return d.se3Action(*this); }
+    template<typename D> typename internal::ActionReturn<D>::Type act   (const D & d) const 
+    { return d.se3Action(*this); }
     /// by = aXb.actInv(ay)
-    template<typename D> D actInv(const D & d) const { return d.se3ActionInverse(*this); }
+    template<typename D> typename internal::ActionReturn<D>::Type actInv(const D & d) const
+    { return d.se3ActionInverse(*this); }
 
     Vector3 act   (const Vector3& p) const { return (rot*p+trans).eval(); }
     Vector3 actInv(const Vector3& p) const { return (rot.transpose()*(p-trans)).eval(); }