Index: src/CodeTools/DeclStats.cc
===================================================================
--- src/CodeTools/DeclStats.cc	(revision 567903e2d5b2b6c7d7cf34ad8b999f73fdfa5cbc)
+++ src/CodeTools/DeclStats.cc	(revision 6a5f0e7dfb84bde161ef99c4d0e14d90d0266007)
@@ -33,12 +33,13 @@
 		struct Stats {
 			unsigned n_decls;     ///< Total number of declarations
-			unsigned mono_decls;  ///< Monomorphic declarations
-			unsigned poly_decls;  ///< Polymorphic declarations
+			/// Count of declarations with each number of assertion parameters
+			VectorMap<unsigned> n_type_params;
 			/// Count of declarations with each name
 			std::unordered_map<std::string, unsigned> by_name;
+
 			/// Count of declarations with each number of parameters
-			VectorMap<unsigned> by_params;
+			VectorMap<unsigned> n_params;
 			/// Count of declarations with each number of return types
-			VectorMap<unsigned> by_returns;
+			VectorMap<unsigned> n_returns;
 			/// Count of declarations with each number of polymorphic parameters
 			VectorMap<unsigned> n_poly_params;
@@ -50,42 +51,69 @@
 			std::map<unsigned, unsigned> p_poly_returns;
 
-			Stats() : n_decls(0), mono_decls(0), poly_decls(0), by_name(), by_params(), by_returns(), n_poly_params(), n_poly_returns(), p_poly_params(), p_poly_returns() {}
-
+			/// Count of declarations with each number of assertions
+			std::map<unsigned, unsigned> n_assertions;
+			/// Count of assertions with each number of parameters
+			VectorMap<unsigned> n_assn_params;
+			/// Count of assertions with each number of return types
+			VectorMap<unsigned> n_assn_returns;
+			/// Count of assertions with each number of polymorphic parameters
+			VectorMap<unsigned> n_assn_poly_params;
+			/// Count of assertions with each number of polymorphic return types
+			VectorMap<unsigned> n_assn_poly_returns;
+			/// Count of assertions with each percentage of polymorphic parameters
+			std::map<unsigned, unsigned> p_assn_poly_params;
+			/// Count of assertions with each percentage of polymorphic returns
+			std::map<unsigned, unsigned> p_assn_poly_returns;
+
+			Stats()
+				: n_decls(0), n_type_params(), by_name(),
+				  n_params(), n_returns(), n_poly_params(), n_poly_returns(), p_poly_params(), p_poly_returns(),
+				  n_assertions(), n_assn_params(), n_assn_returns(), n_assn_poly_params(), n_assn_poly_returns(), p_assn_poly_params(), p_assn_poly_returns() {}
+
+		private:
+			template<typename T>
+			static void sum(T& a, const T& b) { a += b; }
+
+			static void sum(VectorMap<unsigned>& a, const VectorMap<unsigned>& b) {
+				a.reserve( b.size() );
+				for ( unsigned i = 0; i < b.size(); ++i ) {
+					a[i] += b[i];
+				}
+			}
+
+			template<typename K>
+			static void sum(std::map<K, unsigned>& a, const std::map<K, unsigned>& b) {
+				for ( const auto& entry : b ) {
+					a[ entry.first ] += entry.second;
+				}
+			}
+
+			template<typename K>
+			static void sum(std::unordered_map<K, unsigned>& a, const std::unordered_map<K, unsigned>& b) {
+				for ( const auto& entry : b ) {
+					a[ entry.first ] += entry.second;
+				}
+			}
+
+		public:
 			Stats& operator+= (const Stats& o) {
-				n_decls += o.n_decls;
-				mono_decls += o.mono_decls;
-				poly_decls += o.poly_decls;
+				sum( n_decls, o.n_decls );
+				sum( n_type_params, o.n_type_params );
+				sum( by_name, o.by_name );
 				
-				for ( auto& entry : o.by_name ) {
-					by_name[ entry.first ] += entry.second;
-				}
-				
-				by_params.reserve( o.by_params.size() );
-				for ( unsigned i = 0; i < o.by_params.size(); ++i ) {
-					by_params[i] += o.by_params[i];
-				}
-
-				by_returns.reserve( o.by_returns.size() );
-				for ( unsigned i = 0; i < o.by_returns.size(); ++i ) {
-					by_returns[i] += o.by_returns[i];
-				}
-
-				n_poly_params.reserve( o.n_poly_params.size() );
-				for ( unsigned i = 0; i < o.n_poly_params.size(); ++i ) {
-					n_poly_params[i] += o.n_poly_params[i];
-				}
-
-				n_poly_returns.reserve( o.n_poly_returns.size() );
-				for ( unsigned i = 0; i < o.n_poly_returns.size(); ++i ) {
-					n_poly_returns[i] += o.n_poly_returns[i];
-				}
-
-				for ( const auto& entry : o.p_poly_params ) {
-					p_poly_params[ entry.first ] += entry.second;
-				}
-
-				for ( const auto& entry : o.p_poly_returns ) {
-					p_poly_returns[ entry.first ] += entry.second;
-				}
+				sum( n_params, o.n_params );
+				sum( n_returns, o.n_returns );
+				sum( n_poly_params, o.n_poly_params );
+				sum( n_poly_returns, o.n_poly_returns );
+				sum( p_poly_params, o.p_poly_params );
+				sum( p_poly_returns, o.p_poly_returns );
+
+				sum( n_assertions, o.n_assertions );
+				sum( n_assn_params, o.n_assn_params );
+				sum( n_assn_returns, o.n_assn_returns );
+				sum( n_assn_poly_params, o.n_assn_poly_params );
+				sum( n_assn_poly_returns, o.n_assn_poly_returns );
+				sum( p_assn_poly_params, o.p_assn_poly_params );
+				sum( p_assn_poly_returns, o.p_assn_poly_returns );
 
 				return *this;
@@ -96,4 +124,28 @@
 		std::unordered_set<std::string> seen_names;  ///< Stores manglenames already seen to avoid double-counting
 		Stats total;
+
+		void analyzeFunc( FunctionType* fnTy, VectorMap<unsigned>& by_params, VectorMap<unsigned>& by_poly_params,
+		                  VectorMap<unsigned>& by_returns, VectorMap<unsigned>& by_poly_returns,
+		                  std::map<unsigned, unsigned>& p_poly_params, std::map<unsigned, unsigned>& p_poly_returns ) {
+			unsigned n_params = 0;
+			unsigned n_poly_params = 0;
+			for ( auto pdecl : fnTy->get_parameters() ) {
+				n_params += pdecl->get_type()->size();
+				if ( GenPoly::hasPolyBase( pdecl->get_type() ) ) ++n_poly_params;
+			}
+			++by_params.at( n_params );
+			++by_poly_params.at( n_poly_params );
+			if ( n_params > 0 ) ++p_poly_params[ n_poly_params*100/n_params ];
+
+			unsigned n_returns = 0;
+			unsigned n_poly_returns = 0;
+			for ( auto rdecl : fnTy->get_returnVals() ) {
+				n_returns += rdecl->get_type()->size();
+				if ( GenPoly::hasPolyBase( rdecl->get_type() ) ) ++n_poly_returns;
+			}
+			++by_returns.at( n_returns );
+			++by_poly_returns.at( n_poly_returns );
+			if ( n_returns > 0 ) ++p_poly_returns[ n_poly_returns*100/n_returns ];
+		}
 
 	public:
@@ -109,27 +161,28 @@
 			++stats.n_decls;
 			FunctionType* fnTy = decl->get_functionType();
-			if ( fnTy->get_forall().empty() ) ++stats.mono_decls; else ++stats.poly_decls;
+			const Type::ForallList& forall = fnTy->get_forall();
+			++stats.n_type_params.at( forall.size() );
+			unsigned n_assertions = 0;
+			for ( TypeDecl* fdecl : forall ) {
+				n_assertions += fdecl->get_assertions().size();
+				for ( DeclarationWithType* assn : fdecl->get_assertions() ) {
+					FunctionType *assnTy = 0;
+					if ( ObjectDecl *assnObj = dynamic_cast<ObjectDecl*>(assn) ) {
+						if ( PointerType *ptrTy = dynamic_cast<PointerType*>(assnObj->get_type()) ) {
+							assnTy = dynamic_cast<FunctionType*>(ptrTy->get_base());
+						} else assnTy = dynamic_cast<FunctionType*>(assnObj->get_type());
+					} else if ( FunctionDecl *assnDecl = dynamic_cast<FunctionDecl*>(assn) ) {
+						assnTy = assnDecl->get_functionType();
+					}
+					if ( assnTy ) analyzeFunc( assnTy, stats.n_assn_params, stats.n_assn_poly_params, stats.n_assn_returns,
+					                           stats.n_assn_poly_returns, stats.p_assn_poly_params, stats.p_assn_poly_returns );
+				}
+			}
+			++stats.n_assertions[ n_assertions ];
 
 			++stats.by_name[ decl->get_name() ];
 
-			unsigned n_params = 0;
-			unsigned n_poly_params = 0;
-			for ( auto pdecl : fnTy->get_parameters() ) {
-				n_params += pdecl->get_type()->size();
-				if ( GenPoly::hasPolyBase( pdecl->get_type() ) ) ++n_poly_params;
-			}
-			++stats.by_params.at( n_params );
-			++stats.n_poly_params.at( n_poly_params );
-			if ( n_params > 0 ) ++stats.p_poly_params[ n_poly_params*100/n_params ];
-
-			unsigned n_returns = 0;
-			unsigned n_poly_returns = 0;
-			for ( auto rdecl : fnTy->get_returnVals() ) {
-				n_returns += rdecl->get_type()->size();
-				if ( GenPoly::hasPolyBase( rdecl->get_type() ) ) ++n_poly_returns;
-			}
-			++stats.by_returns.at( n_returns );
-			++stats.n_poly_returns.at( n_poly_returns );
-			if ( n_returns > 0 ) ++stats.p_poly_returns[ n_poly_returns*100/n_returns ];
+			analyzeFunc( fnTy, stats.n_params, stats.n_poly_params, stats.n_returns,
+			             stats.n_poly_returns, stats.p_poly_params, stats.p_poly_returns );
 		}
 
@@ -181,4 +234,27 @@
 			}
 		}
+
+		template<typename F>
+		void printAllSparseHisto( const char* name, F extract ) {
+			std::map<unsigned, unsigned> histos[LinkageSpec::NoOfSpecs];
+			std::map<unsigned, unsigned> thisto;
+
+			for ( const auto& entry : extract(total) ) { ++thisto[ entry.second ]; }
+
+			for ( unsigned i = 0; i < LinkageSpec::NoOfSpecs; ++i ) {
+				for ( const auto& entry : extract(for_linkage[i]) ) { ++histos[i][entry.second]; }
+			}
+
+			for ( const auto& entry : thisto ) {
+				const auto& key = entry.first;
+				std::cout << "\"" << name << "\"," << key;
+				for ( unsigned i = 0; i < LinkageSpec::NoOfSpecs; ++i ) {
+					auto it = histos[i].find( key );
+					if ( it == histos[i].end() ) std::cout << ",0";
+					else std::cout << "," << it->second;
+				}
+				std::cout << "," << entry.second << std::endl;
+			}
+		}
 		
 	public:
@@ -190,16 +266,23 @@
 			std::cout << ",,\"intrinsic\",\"Cforall\",\"C\",\"autogen\",\"builtin\",\"TOTAL\"" << std::endl;
 
-			printAll("mono_decls", [](const Stats& stats) { return stats.mono_decls; });
-			printAll("poly_decls", [](const Stats& stats) { return stats.poly_decls; });
+			printAllMap("n_type_params", [](const Stats& stats) { return stats.n_type_params; });
 			printAll("n_decls", [](const Stats& stats) { return stats.n_decls; });
 			printAll("unique_names", [](const Stats& stats) { return stats.by_name.size(); });
-
-			printAllHisto("overloads", [](const Stats& stats) { return stats.by_name; });
+			printAllSparseHisto("overloads", [](const Stats& stats) { return stats.by_name; });
+			
 			printAllMap("n_poly_params", [](const Stats& stats) { return stats.n_poly_params; });
-			printAllMap("n_params", [](const Stats& stats) { return stats.by_params; });
+			printAllMap("n_params", [](const Stats& stats) { return stats.n_params; });
 			printAllMap("%_poly_params", [](const Stats& stats) { return stats.p_poly_params; });
 			printAllMap("n_poly_returns", [](const Stats& stats) { return stats.n_poly_returns; });
-			printAllMap("n_returns", [](const Stats& stats) { return stats.by_returns; });
+			printAllMap("n_returns", [](const Stats& stats) { return stats.n_returns; });
 			printAllMap("%_poly_returns", [](const Stats& stats) { return stats.p_poly_returns; });
+
+			printAllMap("n_assertions", [](const Stats& stats) { return stats.n_assertions; });
+			printAllMap("n_assn_poly_params", [](const Stats& stats) { return stats.n_assn_poly_params; });
+			printAllMap("n_assn_params", [](const Stats& stats) { return stats.n_assn_params; });
+			printAllMap("%_assn_poly_params", [](const Stats& stats) { return stats.p_assn_poly_params; });
+			printAllMap("n_assn_poly_returns", [](const Stats& stats) { return stats.n_assn_poly_returns; });
+			printAllMap("n_assn_returns", [](const Stats& stats) { return stats.n_assn_returns; });
+			printAllMap("%_assn_poly_returns", [](const Stats& stats) { return stats.p_assn_poly_returns; });
 		}
 	};
