{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module What4.Utils.BVDomain.Arith
( Domain(..)
, proper
, bvdMask
, member
, pmember
, interval
, size
, asSingleton
, ubounds
, sbounds
, eq
, slt
, ult
, domainsOverlap
, arithDomainData
, bitbounds
, unknowns
, fillright
, any
, singleton
, range
, fromAscEltList
, union
, concat
, select
, zext
, sext
, shl
, lshr
, ashr
, add
, negate
, scale
, mul
, udiv
, urem
, sdiv
, srem
, What4.Utils.BVDomain.Arith.not
, genDomain
, genElement
, genPair
, correct_any
, correct_ubounds
, correct_sbounds
, correct_singleton
, correct_overlap
, correct_union
, correct_zero_ext
, correct_sign_ext
, correct_concat
, correct_shrink
, correct_trunc
, correct_select
, correct_add
, correct_neg
, correct_mul
, correct_scale
, correct_scale_eq
, correct_udiv
, correct_urem
, correct_sdivRange
, correct_sdiv
, correct_srem
, correct_not
, correct_shl
, correct_lshr
, correct_ashr
, correct_eq
, correct_ult
, correct_slt
, correct_unknowns
, correct_bitbounds
) where
import qualified Data.Bits as Bits
import Data.Bits hiding (testBit, xor)
import Data.Parameterized.NatRepr
import GHC.TypeNats
import GHC.Stack
import qualified Prelude
import Prelude hiding (any, concat, negate, and, or, not)
import Test.Verification ( Property, property, (==>), Gen, chooseInteger )
data Domain (w :: Nat)
= BVDAny !Integer
| BVDInterval !Integer !Integer !Integer
deriving Show
sameDomain :: Domain w -> Domain w -> Bool
sameDomain (BVDAny _) (BVDAny _) = True
sameDomain (BVDInterval _ x w) (BVDInterval _ x' w') = x == x' && w == w'
sameDomain _ _ = False
size :: Domain w -> Integer
size (BVDAny mask) = mask + 1
size (BVDInterval _ _ sz) = sz + 1
member :: Domain w -> Integer -> Bool
member (BVDAny _) _ = True
member (BVDInterval mask lo sz) x = ((x' - lo) .&. mask) <= sz
where x' = x .&. mask
proper :: NatRepr w -> Domain w -> Bool
proper w (BVDAny mask) = mask == maxUnsigned w
proper w (BVDInterval mask lo sz) =
mask == maxUnsigned w &&
lo .|. mask == mask &&
sz .|. mask == mask &&
sz < mask
bvdMask :: Domain w -> Integer
bvdMask x =
case x of
BVDAny mask -> mask
BVDInterval mask _ _ -> mask
genDomain :: NatRepr w -> Gen (Domain w)
genDomain w =
do let mask = maxUnsigned w
lo <- chooseInteger (0, mask)
sz <- chooseInteger (0, mask)
pure $! interval mask lo sz
genElement :: Domain w -> Gen Integer
genElement (BVDAny mask) = chooseInteger (0, mask)
genElement (BVDInterval mask lo sz) =
do x <- chooseInteger (0, sz)
pure ((x+lo) .&. mask)
genPair :: NatRepr w -> Gen (Domain w, Integer)
genPair w =
do a <- genDomain w
x <- genElement a
return (a,x)
halfRange :: (1 <= w) => NatRepr w -> Integer
halfRange w = bit (widthVal w - 1)
asSingleton :: Domain w -> Maybe Integer
asSingleton x =
case x of
BVDAny _ -> Nothing
BVDInterval _ xl xd
| xd == 0 -> Just xl
| otherwise -> Nothing
isSingletonZero :: Domain w -> Bool
isSingletonZero x =
case x of
BVDInterval _ 0 0 -> True
_ -> False
isBVDAny :: Domain w -> Bool
isBVDAny x =
case x of
BVDAny {} -> True
BVDInterval {} -> False
ubounds :: Domain w -> (Integer, Integer)
ubounds a =
case a of
BVDAny mask -> (0, mask)
BVDInterval mask al aw
| ah > mask -> (0, mask)
| otherwise -> (al, ah)
where ah = al + aw
sbounds :: (1 <= w) => NatRepr w -> Domain w -> (Integer, Integer)
sbounds w a = (lo - delta, hi - delta)
where
delta = halfRange w
(lo, hi) = ubounds (add a (BVDInterval (bvdMask a) delta 0))
arithDomainData :: Domain w -> Maybe (Integer, Integer)
arithDomainData (BVDAny _) = Nothing
arithDomainData (BVDInterval _ al aw) = Just (al, aw)
domainsOverlap :: Domain w -> Domain w -> Bool
domainsOverlap a b =
case a of
BVDAny _ -> True
BVDInterval _ al aw ->
case b of
BVDAny _ -> True
BVDInterval mask bl bw ->
diff <= bw || diff + aw > mask
where diff = (al - bl) .&. mask
eq :: Domain w -> Domain w -> Maybe Bool
eq a b
| Just x <- asSingleton a
, Just y <- asSingleton b = Just (x == y)
| domainsOverlap a b == False = Just False
| otherwise = Nothing
slt :: (1 <= w) => NatRepr w -> Domain w -> Domain w -> Maybe Bool
slt w a b
| a_max < b_min = Just True
| a_min >= b_max = Just False
| otherwise = Nothing
where
(a_min, a_max) = sbounds w a
(b_min, b_max) = sbounds w b
ult :: (1 <= w) => Domain w -> Domain w -> Maybe Bool
ult a b
| a_max < b_min = Just True
| a_min >= b_max = Just False
| otherwise = Nothing
where
(a_min, a_max) = ubounds a
(b_min, b_max) = ubounds b
any :: (1 <= w) => NatRepr w -> Domain w
any w = BVDAny (maxUnsigned w)
singleton :: (HasCallStack, 1 <= w) => NatRepr w -> Integer -> Domain w
singleton w x = BVDInterval mask (x .&. mask) 0
where mask = maxUnsigned w
range :: NatRepr w -> Integer -> Integer -> Domain w
range w al ah = interval mask al ((ah - al) .&. mask)
where mask = maxUnsigned w
interval :: Integer -> Integer -> Integer -> Domain w
interval mask al aw =
if aw >= mask then BVDAny mask else BVDInterval mask (al .&. mask) aw
fromAscEltList :: (1 <= w) => NatRepr w -> [Integer] -> Domain w
fromAscEltList w [] = singleton w 0
fromAscEltList w [x] = singleton w x
fromAscEltList w (x0 : x1 : xs) = go (x0, x0) (x1, x1) xs
where
go (a, b) (c, d) [] = union (range w a b) (range w c d)
go (a, b) (c, d) (e : rest)
| e - d > c - b = go (a, d) (e, e) rest
| otherwise = go (a, b) (c, e) rest
union :: (1 <= w) => Domain w -> Domain w -> Domain w
union a b =
case a of
BVDAny _ -> a
BVDInterval _ al aw ->
case b of
BVDAny _ -> b
BVDInterval mask bl bw ->
interval mask cl (ch - cl)
where
sz = mask + 1
ac = 2 * al + aw
bc = 2 * bl + bw
al' = if ac + mask < bc then al + sz else al
bl' = if bc + mask < ac then bl + sz else bl
ah' = al' + aw
bh' = bl' + bw
cl = min al' bl'
ch = max ah' bh'
concat :: NatRepr u -> Domain u -> NatRepr v -> Domain v -> Domain (u + v)
concat u a v b =
case a of
BVDAny _ -> BVDAny mask
BVDInterval _ al aw -> interval mask (cat al bl) (cat aw bw)
where
cat i j = (i `shiftL` widthVal v) + j
mask = maxUnsigned (addNat u v)
(bl, bh) = ubounds b
bw = bh - bl
shrink ::
NatRepr i ->
Domain (i + n) -> Domain n
shrink i a =
case a of
BVDAny mask -> BVDAny (shr mask)
BVDInterval mask al aw ->
interval (shr mask) bl (bh - bl)
where
bl = shr al
bh = shr (al + aw)
where
shr x = x `shiftR` widthVal i
trunc ::
(n <= w) =>
NatRepr n ->
Domain w -> Domain n
trunc n a =
case a of
BVDAny _ -> BVDAny mask
BVDInterval _ al aw -> interval mask al aw
where
mask = maxUnsigned n
select ::
(1 <= n, i + n <= w) =>
NatRepr i ->
NatRepr n ->
Domain w -> Domain n
select i n a = shrink i (trunc (addNat i n) a)
zext :: (1 <= w, w+1 <= u) => Domain w -> NatRepr u -> Domain u
zext a u = range u al ah
where (al, ah) = ubounds a
sext ::
forall w u. (1 <= w, w + 1 <= u) =>
NatRepr w ->
Domain w ->
NatRepr u ->
Domain u
sext w a u =
case fProof of
LeqProof ->
range u al ah
where (al, ah) = sbounds w a
where
wProof :: LeqProof 1 w
wProof = LeqProof
uProof :: LeqProof (w+1) u
uProof = LeqProof
fProof :: LeqProof 1 u
fProof = leqTrans (leqAdd wProof (knownNat :: NatRepr 1)) uProof
shl :: (1 <= w) => NatRepr w -> Domain w -> Domain w -> Domain w
shl w a b
| isBVDAny a = a
| isSingletonZero a = a
| isSingletonZero b = a
| otherwise = interval mask lo (hi - lo)
where
mask = bvdMask a
sz = mask + 1
(bl, bh) = ubounds b
bl' = clamp w bl
bh' = clamp w bh
cl = if (mask `shiftR` bl' == 0) then sz else bit bl'
ch = if (mask `shiftR` bh' == 0) then sz else bit bh'
(lo, hi) = mulRange (zbounds a) (cl, ch)
lshr :: (1 <= w) => NatRepr w -> Domain w -> Domain w -> Domain w
lshr w a b = interval mask cl (ch - cl)
where
mask = bvdMask a
(al, ah) = ubounds a
(bl, bh) = ubounds b
cl = al `shiftR` clamp w bh
ch = ah `shiftR` clamp w bl
ashr :: (1 <= w) => NatRepr w -> Domain w -> Domain w -> Domain w
ashr w a b = interval mask cl (ch - cl)
where
mask = bvdMask a
(al, ah) = sbounds w a
(bl, bh) = ubounds b
cl = al `shiftR` (if al < 0 then clamp w bl else clamp w bh)
ch = ah `shiftR` (if ah < 0 then clamp w bh else clamp w bl)
clamp :: NatRepr w -> Integer -> Int
clamp w x = fromInteger (min (intValue w) x)
add :: (1 <= w) => Domain w -> Domain w -> Domain w
add a b =
case a of
BVDAny _ -> a
BVDInterval _ al aw ->
case b of
BVDAny _ -> b
BVDInterval mask bl bw ->
interval mask (al + bl) (aw + bw)
negate :: (1 <= w) => Domain w -> Domain w
negate a =
case a of
BVDAny _ -> a
BVDInterval mask al aw -> BVDInterval mask ((-ah) .&. mask) aw
where ah = al + aw
scale :: (1 <= w) => Integer -> Domain w -> Domain w
scale k a
| k == 0 = BVDInterval (bvdMask a) 0 0
| k == 1 = a
| otherwise =
case a of
BVDAny _ -> a
BVDInterval mask al aw
| k >= 0 -> interval mask (k * al) (k * aw)
| otherwise -> interval mask (k * ah) (abs k * aw)
where ah = al + aw
mul :: (1 <= w) => Domain w -> Domain w -> Domain w
mul a b
| isSingletonZero a = a
| isSingletonZero b = b
| isBVDAny a = a
| isBVDAny b = b
| otherwise = interval mask cl (ch - cl)
where
mask = bvdMask a
(cl, ch) = mulRange (zbounds a) (zbounds b)
zbounds :: Domain w -> (Integer, Integer)
zbounds a =
case a of
BVDAny mask -> (0, mask)
BVDInterval mask lo sz -> (lo', lo' + sz)
where lo' = if 2*lo + sz > mask then lo - (mask + 1) else lo
mulRange :: (Integer, Integer) -> (Integer, Integer) -> (Integer, Integer)
mulRange (al, ah) (bl, bh) = (cl, ch)
where
(albl, albh) = scaleRange al (bl, bh)
(ahbl, ahbh) = scaleRange ah (bl, bh)
cl = min albl ahbl
ch = max albh ahbh
scaleRange :: Integer -> (Integer, Integer) -> (Integer, Integer)
scaleRange k (lo, hi)
| k < 0 = (k * hi, k * lo)
| otherwise = (k * lo, k * hi)
udiv :: (1 <= w) => Domain w -> Domain w -> Domain w
udiv a b = interval mask ql (qh - ql)
where
mask = bvdMask a
(al, ah) = ubounds a
(bl, bh) = ubounds b
ql = al `div` max 1 bh
qh = ah `div` max 1 bl
urem :: (1 <= w) => Domain w -> Domain w -> Domain w
urem a b
| qh == ql = interval mask rl (rh - rl)
| otherwise = interval mask 0 (bh - 1)
where
mask = bvdMask a
(al, ah) = ubounds a
(bl, bh) = ubounds b
(ql, rl) = al `divMod` max 1 bh
(qh, rh) = ah `divMod` max 1 bl
data ReciprocalRange = ReciprocalRange Integer Integer
rbounds :: (1 <= w) => NatRepr w -> Domain w -> ReciprocalRange
rbounds w a =
case a of
BVDAny _ -> ReciprocalRange (-1) 1
BVDInterval mask al aw
| ah > mask + 1 -> ReciprocalRange (-1) 1
| otherwise -> ReciprocalRange (signed (min mask ah)) (signed (max 1 al))
where
ah = al + aw
signed x = if x < halfRange w then x else x - (mask + 1)
sdivRange :: (Integer, Integer) -> ReciprocalRange -> (Integer, Integer)
sdivRange (al, ah) (ReciprocalRange bl bh) = (ql, qh)
where
(ql1, qh1) = scaleDownRange (al, ah) bh
(ql2, qh2) = scaleDownRange (al, ah) bl
ql = min ql1 ql2
qh = max qh1 qh2
scaleDownRange :: (Integer, Integer) -> Integer -> (Integer, Integer)
scaleDownRange (lo, hi) k
| k > 0 = (lo `quot` k, hi `quot` k)
| k < 0 = (hi `quot` k, lo `quot` k)
| otherwise = (lo, hi)
sdiv :: (1 <= w) => NatRepr w -> Domain w -> Domain w -> Domain w
sdiv w a b = interval mask ql (qh - ql)
where
mask = bvdMask a
(ql, qh) = sdivRange (sbounds w a) (rbounds w b)
srem :: (1 <= w) => NatRepr w -> Domain w -> Domain w -> Domain w
srem w a b =
if ql == qh then
(if ql < 0
then interval mask (al - ql * bl) (aw - ql * bw)
else interval mask (al - ql * bh) (aw + ql * bw))
else interval mask rl (rh - rl)
where
mask = bvdMask a
(al, ah) = sbounds w a
(bl, bh) = sbounds w b
(ql, qh) = sdivRange (al, ah) (rbounds w b)
rl = if al < 0 then min (bl+1) (-bh+1) else 0
rh = if ah > 0 then max (-bl-1) (bh-1) else 0
aw = ah - al
bw = bh - bl
not :: Domain w -> Domain w
not a =
case a of
BVDAny _ -> a
BVDInterval mask al aw ->
BVDInterval mask (complement ah .&. mask) aw
where ah = al + aw
bitbounds :: Domain w -> (Integer, Integer)
bitbounds a =
case a of
BVDAny mask -> (0, mask)
BVDInterval mask al aw
| al + aw > mask -> (0, mask)
| otherwise -> (lo, hi)
where
au = unknowns a
hi = al .|. au
lo = hi `Bits.xor` au
unknowns :: Domain w -> Integer
unknowns (BVDAny mask) = mask
unknowns (BVDInterval mask al aw) = mask .&. (fillright (al `Bits.xor` (al+aw)))
bitle :: Integer -> Integer -> Bool
bitle x y = (x .|. y) == y
fillright :: Integer -> Integer
fillright = go 1
where
go :: Int -> Integer -> Integer
go i x
| x' == x = x
| otherwise = go (2 * i) x'
where x' = x .|. (x `shiftR` i)
pmember :: NatRepr n -> Domain n -> Integer -> Bool
pmember n a x = proper n a && member a x
correct_any :: (1 <= n) => NatRepr n -> Integer -> Property
correct_any w x = property (pmember w (any w) x)
correct_ubounds :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> Property
correct_ubounds n (a,x) = pmember n a x' ==> lo <= x' && x' <= hi
where
x' = toUnsigned n x
(lo,hi) = ubounds a
correct_sbounds :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> Property
correct_sbounds n (a,x) = pmember n a x' ==> lo <= x' && x' <= hi
where
x' = toSigned n x
(lo,hi) = sbounds n a
correct_singleton :: (1 <= n) => NatRepr n -> Integer -> Integer -> Property
correct_singleton n x y = property (pmember n (singleton n x') y' == (x' == y'))
where
x' = toUnsigned n x
y' = toUnsigned n y
correct_overlap :: Domain n -> Domain n -> Integer -> Property
correct_overlap a b x =
member a x && member b x ==> domainsOverlap a b
correct_union :: (1 <= n) => NatRepr n -> Domain n -> Domain n -> Integer -> Property
correct_union n a b x =
(member a x || member b x) ==> pmember n (union a b) x
correct_zero_ext :: (1 <= w, w+1 <= u) => NatRepr w -> Domain w -> NatRepr u -> Integer -> Property
correct_zero_ext w a u x = member a x' ==> pmember u (zext a u) x'
where
x' = toUnsigned w x
correct_sign_ext :: (1 <= w, w+1 <= u) => NatRepr w -> Domain w -> NatRepr u -> Integer -> Property
correct_sign_ext w a u x = member a x' ==> pmember u (sext w a u) x'
where
x' = toSigned w x
correct_concat :: NatRepr m -> (Domain m,Integer) -> NatRepr n -> (Domain n,Integer) -> Property
correct_concat m (a,x) n (b,y) = member a x' ==> member b y' ==> pmember (addNat m n) (concat m a n b) z
where
x' = toUnsigned m x
y' = toUnsigned n y
z = x' `shiftL` (widthVal n) .|. y'
correct_shrink :: NatRepr i -> NatRepr n -> (Domain (i + n), Integer) -> Property
correct_shrink i n (a,x) = member a x' ==> pmember n (shrink i a) (x' `shiftR` widthVal i)
where
x' = x .&. bvdMask a
correct_trunc :: (n <= w) => NatRepr n -> (Domain w, Integer) -> Property
correct_trunc n (a,x) = member a x' ==> pmember n (trunc n a) (toUnsigned n x')
where
x' = x .&. bvdMask a
correct_select :: (1 <= n, i + n <= w) =>
NatRepr i -> NatRepr n -> (Domain w, Integer) -> Property
correct_select i n (a, x) = member a x ==> pmember n (select i n a) y
where
y = toUnsigned n ((x .&. bvdMask a) `shiftR` (widthVal i))
correct_add :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_add n (a,x) (b,y) = member a x ==> member b y ==> pmember n (add a b) (x + y)
correct_neg :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> Property
correct_neg n (a,x) = member a x ==> pmember n (negate a) (Prelude.negate x)
correct_not :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> Property
correct_not n (a,x) = member a x ==> pmember n (not a) (complement x)
correct_mul :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_mul n (a,x) (b,y) = member a x ==> member b y ==> pmember n (mul a b) (x * y)
correct_scale :: (1 <= n) => NatRepr n -> Integer -> (Domain n, Integer) -> Property
correct_scale n k (a,x) = member a x ==> pmember n (scale k' a) (k' * x)
where
k' = toSigned n k
correct_scale_eq :: (1 <= n) => NatRepr n -> Integer -> Domain n -> Property
correct_scale_eq n k a = property $ sameDomain (scale k' a) (mul (singleton n k) a)
where
k' = toSigned n k
correct_udiv :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_udiv n (a,x) (b,y) = member a x' ==> member b y' ==> y' /= 0 ==> pmember n (udiv a b) (x' `quot` y')
where
x' = toUnsigned n x
y' = toUnsigned n y
correct_urem :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_urem n (a,x) (b,y) = member a x' ==> member b y' ==> y' /= 0 ==> pmember n (urem a b) (x' `rem` y')
where
x' = toUnsigned n x
y' = toUnsigned n y
correct_sdivRange :: (Integer, Integer) -> (Integer, Integer) -> Integer -> Integer -> Property
correct_sdivRange a b x y =
mem a x ==> mem b y ==> y /= 0 ==> mem (sdivRange a b') (x `quot` y)
where
b' = ReciprocalRange (snd b) (fst b)
mem (lo,hi) v = lo <= v && v <= hi
correct_sdiv :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_sdiv n (a,x) (b,y) =
member a x ==> member b y ==> y /= 0 ==> pmember n (sdiv n a b) (x' `quot` y')
where
x' = toSigned n x
y' = toSigned n y
correct_srem :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_srem n (a,x) (b,y) =
member a x ==> member b y ==> y /= 0 ==> pmember n (srem n a b) (x' `rem` y')
where
x' = toSigned n x
y' = toSigned n y
correct_shl :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_shl n (a,x) (b,y) = member a x ==> member b y ==> pmember n (shl n a b) z
where
z = (toUnsigned n x) `shiftL` fromInteger (min (intValue n) y)
correct_lshr :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_lshr n (a,x) (b,y) = member a x ==> member b y ==> pmember n (lshr n a b) z
where
z = (toUnsigned n x) `shiftR` fromInteger (min (intValue n) y)
correct_ashr :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_ashr n (a,x) (b,y) = member a x ==> member b y ==> pmember n (ashr n a b) z
where
z = (toSigned n x) `shiftR` fromInteger (min (intValue n) y)
correct_eq :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_eq n (a,x) (b,y) =
member a x ==> member b y ==>
case eq a b of
Just True -> toUnsigned n x == toUnsigned n y
Just False -> toUnsigned n x /= toUnsigned n y
Nothing -> True
correct_ult :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_ult n (a,x) (b,y) =
member a x ==> member b y ==>
case ult a b of
Just True -> toUnsigned n x < toUnsigned n y
Just False -> toUnsigned n x >= toUnsigned n y
Nothing -> True
correct_slt :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> (Domain n, Integer) -> Property
correct_slt n (a,x) (b,y) =
member a x ==> member b y ==>
case slt n a b of
Just True -> toSigned n x < toSigned n y
Just False -> toSigned n x >= toSigned n y
Nothing -> True
correct_unknowns :: (1 <= n) => Domain n -> Integer -> Integer -> Property
correct_unknowns a x y = member a x ==> member a y ==> ((x .|. u) == (y .|. u)) && (u .|. mask == mask)
where
u = unknowns a
mask = bvdMask a
correct_bitbounds :: (1 <= n) => NatRepr n -> (Domain n, Integer) -> Property
correct_bitbounds n (a,x) =
member a x ==> (bitle lo x' && bitle x' hi && bitle hi (maxUnsigned n))
where
x' = toUnsigned n x
(lo, hi) = bitbounds a