diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.markdown
@@ -0,0 +1,5 @@
+0.1.1.0
+-------
+* parse* functions fails if the parser does not consume all of the inputs
+* generate '#product=', 'sizeproduct=' and '#soft=' in header line of OPB/WBO files
+* sort literals in a non-linear term when generating OPB/WBO files
diff --git a/pseudo-boolean.cabal b/pseudo-boolean.cabal
--- a/pseudo-boolean.cabal
+++ b/pseudo-boolean.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                pseudo-boolean
-version:             0.1.0.1
+version:             0.1.1.0
 synopsis:            Reading/Writing OPB/WBO files used in pseudo boolean competition
 description:         Reading\/Writing OPB\/WBO files used in pseudo boolean competition
 homepage:            https://github.com/msakai/pseudo-boolean
@@ -16,6 +16,7 @@
 build-type:          Simple
 extra-source-files:
     README.md
+    CHANGELOG.markdown
     test/samples/*.opb
     test/samples/normalized-1096.cudf.paranoid.opb
     test/samples/*.wbo
diff --git a/src/Data/PseudoBoolean/Attoparsec.hs b/src/Data/PseudoBoolean/Attoparsec.hs
--- a/src/Data/PseudoBoolean/Attoparsec.hs
+++ b/src/Data/PseudoBoolean/Attoparsec.hs
@@ -33,7 +33,7 @@
   ) where
 
 import Prelude hiding (sum)
-import Control.Applicative ((<|>))
+import Control.Applicative ((<|>), (<*))
 import Control.Monad
 import Data.Attoparsec.ByteString.Char8 hiding (isDigit)
 import qualified Data.Attoparsec.ByteString.Lazy as L
@@ -201,7 +201,7 @@
 
 -- | Parse a OPB format string containing pseudo boolean problem.
 parseOPBByteString :: BSLazy.ByteString -> Either String Formula
-parseOPBByteString s = L.eitherResult $ L.parse formula s
+parseOPBByteString s = L.eitherResult $ L.parse (formula <* endOfInput) s
 
 -- | Parse a OPB file containing pseudo boolean problem.
 parseOPBFile :: FilePath -> IO (Either String Formula)
@@ -260,7 +260,7 @@
 
 -- | Parse a WBO format string containing weighted boolean optimization problem.
 parseWBOByteString :: BSLazy.ByteString -> Either String SoftFormula
-parseWBOByteString s = L.eitherResult $ L.parse softformula s
+parseWBOByteString s = L.eitherResult $ L.parse (softformula <* endOfInput) s
 
 -- | Parse a WBO file containing weighted boolean optimization problem.
 parseWBOFile :: FilePath -> IO (Either String SoftFormula)
diff --git a/src/Data/PseudoBoolean/Builder.hs b/src/Data/PseudoBoolean/Builder.hs
--- a/src/Data/PseudoBoolean/Builder.hs
+++ b/src/Data/PseudoBoolean/Builder.hs
@@ -21,9 +21,14 @@
   , toWBOString
   ) where
 
+import qualified Prelude
 import Prelude hiding (sum)
 import qualified Data.DList as DList
+import qualified Data.IntSet as IntSet
+import qualified Data.Set as Set
+import Data.List (sortBy)
 import Data.Monoid hiding (Sum (..))
+import Data.Ord
 import Data.String
 import Text.Printf
 import Data.PseudoBoolean.Types
@@ -34,7 +39,12 @@
   where
     nv = pbNumVars opb
     nc = pbNumConstraints opb
-    size = fromString (printf "* #variable= %d #constraint= %d\n" nv nc)
+    p = pbProducts opb
+    np = Set.size p
+    sp = Prelude.sum [IntSet.size tm | tm <- Set.toList p]
+    size = fromString (printf "* #variable= %d #constraint= %d" nv nc)
+         <> (if np >= 1 then fromString (printf " #product= %d sizeproduct= %d" np sp) else mempty)
+         <> fromString "\n"
     part1 = 
       case pbObjectiveFunction opb of
         Nothing -> mempty
@@ -47,7 +57,13 @@
   where
     nv = wboNumVars wbo
     nc = wboNumConstraints wbo
-    size = fromString (printf "* #variable= %d #constraint= %d\n" nv nc)
+    p = wboProducts wbo
+    np = Set.size p
+    sp = Prelude.sum [IntSet.size tm | tm <- Set.toList p]
+    size = fromString (printf "* #variable= %d #constraint= %d" nv nc)
+         <> (if np >= 1 then fromString (printf " #product= %d sizeproduct= %d" np sp) else mempty)
+         <> fromString (printf " #soft= %d" (wboNumSoft wbo))
+         <> fromString "\n"
     part1 = 
       case wboTopCost wbo of
         Nothing -> fromString "soft: ;\n"
@@ -61,7 +77,7 @@
 showWeightedTerm (c, lits) = foldr (\f g -> f <> fromString " " <> g) mempty (x:xs)
   where
     x = if c >= 0 then fromString "+" <> fromString (show c) else fromString (show c)
-    xs = map showLit lits
+    xs = map showLit $ sortBy (comparing abs) lits
 
 showLit :: (Monoid a, IsString a) => Lit -> a
 showLit lit = if lit > 0 then v else fromString "~" <> v
diff --git a/src/Data/PseudoBoolean/ByteStringBuilder.hs b/src/Data/PseudoBoolean/ByteStringBuilder.hs
--- a/src/Data/PseudoBoolean/ByteStringBuilder.hs
+++ b/src/Data/PseudoBoolean/ByteStringBuilder.hs
@@ -27,10 +27,15 @@
   , hPutWBO
   ) where
 
+import qualified Prelude
 import Prelude hiding (sum)
+import qualified Data.IntSet as IntSet
+import qualified Data.Set as Set
+import Data.List (sortBy)
 import Data.Monoid hiding (Sum (..))
 import qualified Data.ByteString.Lazy as BS
 import Data.ByteString.Builder (Builder, intDec, integerDec, char7, string7, hPutBuilder, toLazyByteString)
+import Data.Ord
 import System.IO
 import Data.PseudoBoolean.Types
 
@@ -40,7 +45,12 @@
   where
     nv = pbNumVars opb
     nc = pbNumConstraints opb
-    size = string7 "* #variable= " <> intDec nv <> string7 " #constraint= " <> intDec nc <> char7 '\n'
+    p = pbProducts opb
+    np = Set.size p
+    sp = Prelude.sum [IntSet.size tm | tm <- Set.toList p]
+    size = string7 "* #variable= " <> intDec nv <> string7 " #constraint= " <> intDec nc
+         <> (if np >= 1 then string7 " #product= " <> intDec np <> string7 " sizeproduct= " <> intDec sp else mempty)
+         <> char7 '\n'
     part1 = 
       case pbObjectiveFunction opb of
         Nothing -> mempty
@@ -53,7 +63,13 @@
   where
     nv = wboNumVars wbo
     nc = wboNumConstraints wbo
-    size = string7 "* #variable= " <> intDec nv <> string7 " #constraint= " <> intDec nc <> char7 '\n'
+    p = wboProducts wbo
+    np = Set.size p
+    sp = Prelude.sum [IntSet.size tm | tm <- Set.toList p]
+    size = string7 "* #variable= " <> intDec nv <> string7 " #constraint= " <> intDec nc
+         <> (if np >= 1 then string7 " #product= " <> intDec np <> string7 " sizeproduct= " <> intDec sp else mempty)
+         <> string7 " #soft= " <> intDec (wboNumSoft wbo)
+         <> char7 '\n'
     part1 = 
       case wboTopCost wbo of
         Nothing -> string7 "soft: ;\n"
@@ -67,7 +83,7 @@
 showWeightedTerm (c, lits) = foldr (\f g -> f <> char7 ' ' <> g) mempty (x:xs)
   where
     x = if c >= 0 then char7 '+' <> integerDec c else integerDec c
-    xs = map showLit lits
+    xs = map showLit $ sortBy (comparing abs) lits
 
 showLit :: Lit -> Builder
 showLit lit = if lit > 0 then v else char7 '~' <> v
diff --git a/src/Data/PseudoBoolean/Parsec.hs b/src/Data/PseudoBoolean/Parsec.hs
--- a/src/Data/PseudoBoolean/Parsec.hs
+++ b/src/Data/PseudoBoolean/Parsec.hs
@@ -35,6 +35,7 @@
   ) where
 
 import Prelude hiding (sum)
+import Control.Applicative ((<*))
 import Control.Monad
 import Data.ByteString.Lazy (ByteString)
 import Data.Maybe
@@ -200,15 +201,15 @@
 
 -- | Parse a OPB format string containing pseudo boolean problem.
 parseOPBString :: SourceName -> String -> Either ParseError Formula
-parseOPBString = parse formula
+parseOPBString = parse (formula <* eof)
 
 -- | Parse a OPB format lazy bytestring containing pseudo boolean problem.
 parseOPBByteString :: SourceName -> ByteString -> Either ParseError Formula
-parseOPBByteString = parse formula
+parseOPBByteString = parse (formula <* eof)
 
 -- | Parse a OPB file containing pseudo boolean problem.
 parseOPBFile :: FilePath -> IO (Either ParseError Formula)
-parseOPBFile = ParsecBS.parseFromFile formula
+parseOPBFile = ParsecBS.parseFromFile (formula <* eof)
 
 
 -- <softformula>::= <sequence_of_comments> <softheader> <sequence_of_comments_or_constraints>
@@ -262,12 +263,12 @@
 
 -- | Parse a WBO format string containing weighted boolean optimization problem.
 parseWBOString :: SourceName -> String -> Either ParseError SoftFormula
-parseWBOString = parse softformula
+parseWBOString = parse (softformula <* eof)
 
 -- | Parse a WBO format lazy bytestring containing pseudo boolean problem.
 parseWBOByteString :: SourceName -> ByteString -> Either ParseError SoftFormula
-parseWBOByteString = parse softformula
+parseWBOByteString = parse (softformula <* eof)
 
 -- | Parse a WBO file containing weighted boolean optimization problem.
 parseWBOFile :: FilePath -> IO (Either ParseError SoftFormula)
-parseWBOFile = ParsecBS.parseFromFile softformula
+parseWBOFile = ParsecBS.parseFromFile (softformula <* eof)
diff --git a/src/Data/PseudoBoolean/Types.hs b/src/Data/PseudoBoolean/Types.hs
--- a/src/Data/PseudoBoolean/Types.hs
+++ b/src/Data/PseudoBoolean/Types.hs
@@ -33,12 +33,20 @@
 
   -- * Internal utilities
   , pbComputeNumVars
+  , pbProducts
   , wboComputeNumVars
+  , wboProducts
+  , wboNumSoft
   ) where
 
 import GHC.Generics (Generic)
+import Control.Monad
 import Control.DeepSeq
 import Data.Data
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Data.IntSet (IntSet)
+import qualified Data.IntSet as IntSet
 import Data.Hashable
 import Data.Maybe
 
@@ -117,3 +125,22 @@
       (_, tm) <- s
       lit <- tm
       return $ abs lit
+
+pbProducts :: Formula -> Set IntSet
+pbProducts formula = Set.fromList $ do  
+  s <- maybeToList (pbObjectiveFunction formula) ++ [s | (s,_,_) <- pbConstraints formula]
+  (_, tm)  <- s
+  let tm2 = IntSet.fromList tm
+  guard $ IntSet.size tm2 > 1
+  return tm2
+
+wboProducts :: SoftFormula -> Set IntSet
+wboProducts softformula = Set.fromList $ do
+  (_,(s,_,_)) <- wboConstraints softformula
+  (_, tm) <- s
+  let tm2 = IntSet.fromList tm
+  guard $ IntSet.size tm2 > 1
+  return tm2
+
+wboNumSoft :: SoftFormula -> Int
+wboNumSoft softformula = length [() | (Just _, _) <- wboConstraints softformula]
diff --git a/test/TestPBFile.hs b/test/TestPBFile.hs
--- a/test/TestPBFile.hs
+++ b/test/TestPBFile.hs
@@ -2,6 +2,7 @@
 {-# OPTIONS_GHC -Wall #-}
 module Main (main) where
 
+import Data.Either
 import qualified Data.ByteString.Lazy.Char8 as BSChar8
 import System.IO
 import System.IO.Temp
@@ -36,6 +37,30 @@
 case_normalized_mds_50_10_4 = checkOPBFile "test/samples/normalized-mds_50_10_4.opb"
 case_normalized_opt_market_split_4_30_2 = checkOPBFile "test/samples/normalized-opt-market-split_4_30_2.opb"
 case_pigeonhole_5_4 = checkOPBFile "test/samples/pigeonhole_5_4.opb"
+
+case_trailing_junk = do
+  isError (parseOPBString "" trailingJunk) @?= True
+  isError (parseOPBByteString "" (BSChar8.pack trailingJunk)) @?= True
+  isError (A.parseOPBByteString (BSChar8.pack trailingJunk)) @?= True
+  where
+    -- isLeft is available only on base >=4.7.0.0.
+    isError :: Either a b -> Bool
+    isError (Left _) = True
+    isError (Right _) = False
+    
+    trailingJunk = unlines
+      [ "* #variable= 5 #constraint= 4"
+      , "*"
+      , "* this is a dummy instance"
+      , "*"
+      , "min: 1 x2 -1 x3 ;"
+      , "1 x1 +4 x2 -2 x5 >= 2;"
+      , "-1 x1 +4 x2 -2 x5 >= +3;"
+      , "12345678901234567890 x4 +4 x3 >= 10;"
+      , "* an equality constraint"
+      , "2 x2 +3 x4 +2 x1 +3 x5 = 5;"
+      , "foo"
+      ]
 
 case_readUnsignedInteger_maxBound_bug :: IO ()
 case_readUnsignedInteger_maxBound_bug =
