packages feed

sbv-14.8: Data/SBV/Compilers/C/PseudoBoolean.hs

-----------------------------------------------------------------------------
-- |
-- Module    : Data.SBV.Compilers.C.PseudoBoolean
-- Copyright : (c) Levent Erkok
-- License   : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Overflow-safe pseudo-Boolean comparisons shared by the C backends.
-----------------------------------------------------------------------------

{-# OPTIONS_GHC -Wall -Werror #-}

module Data.SBV.Compilers.C.PseudoBoolean (assignPseudoBoolean) where

import Text.PrettyPrint.HughesPJ
import qualified Text.PrettyPrint.HughesPJ as P ((<>))

import Data.SBV.Core.Data (PBOp(..))

-- | Assign a pseudo-Boolean comparison to an already declared Boolean result.
-- Use an exact unsigned sum when the maximum possible total fits in 64 bits.
-- Otherwise stop adding after exceeding the bound: non-negative coefficients
-- cannot bring the sum back down. Both the bound and each coefficient fit in
-- a signed 64-bit integer, so every addition to a sum at most the bound fits
-- in uint64_t, even when the mathematical total would require more bits.
-- Retain zero-weight operands so their generated bindings remain referenced.
assignPseudoBoolean :: PBOp -> [Doc] -> Doc -> Doc
assignPseudoBoolean operation arguments result
  | length coefficients /= length arguments
  = error "SBV->C: Pseudo-Boolean coefficient/argument count mismatch."
  | any (\n -> n < 0 || n > 2 ^ (63 :: Int) - 1) (bound : coefficients)
  = error "SBV->C: Pseudo-Boolean coefficients and bounds must be non-negative signed 64-bit values."
  | sum coefficients <= 2 ^ (64 :: Int) - 1
  = assign (case terms of [] -> unsigned 0; _ -> parens (hsep (punctuate (text " +") terms)))
  | True
  = text "{"
 $$ nest 2 (text "uint64_t sbv_pb_sum = UINT64_C(0);"
         $$ vcat [ text "if" P.<> parens (accumulator <+> text "<=" <+> unsigned bound <+> text "&&" <+> parens argument)
                $$ nest 2 (accumulator <+> text "+=" <+> unsigned coefficient P.<> semi)
                 | (coefficient, argument) <- weighted
                 ]
         $$ assign accumulator)
 $$ text "}"
 where (coefficients, comparison, bound) = case operation of
         PB_AtMost  k -> (replicate (length arguments) 1, "<=", toInteger k)
         PB_AtLeast k -> (replicate (length arguments) 1, ">=", toInteger k)
         PB_Exactly k -> (replicate (length arguments) 1, "==", toInteger k)
         PB_Le cs   k -> (map toInteger cs,               "<=", toInteger k)
         PB_Ge cs   k -> (map toInteger cs,               ">=", toInteger k)
         PB_Eq cs   k -> (map toInteger cs,               "==", toInteger k)

       weighted      = zip coefficients arguments
       accumulator   = text "sbv_pb_sum"
       unsigned n    = text "UINT64_C" P.<> parens (integer n)
       assign sumDoc = result <+> text "=" <+> sumDoc <+> text comparison <+> unsigned bound P.<> semi
       terms = [parens (argument <+> text "?" <+> unsigned coefficient <+> text ":" <+> unsigned 0)
               | (coefficient, argument) <- weighted]