-----------------------------------------------------------------------------
--
-- Module      :  $Headers
-- Copyright   :  (c) 2021 Brian W Bush
-- License     :  MIT
--
-- Maintainer  :  Brian W Bush <code@functionally.io>
-- Stability   :  Experimental
-- Portability :  Portable
--
-- | Bit operations.
--
-----------------------------------------------------------------------------


{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE Trustworthy       #-}


module Plutus.OnChain.Bits (
-- * Functions
  xor
, odd
, even
) where


import PlutusTx.Prelude hiding (even)


{-# INLINABLE xor #-}

-- | Compute the bitwise exclusive-or of two non-negative integers.
xor :: Integer -- ^ The first integer.
    -> Integer -- ^ The second integer.
    -> Integer -- ^ The bitwise exclusive-or.
xor :: Integer -> Integer -> Integer
xor = Integer -> Integer -> Integer -> Integer -> Integer
xor' Integer
1 Integer
0
  where
    xor' :: Integer
         -> Integer
         -> Integer
         -> Integer
         -> Integer
    xor' :: Integer -> Integer -> Integer -> Integer -> Integer
xor' Integer
m Integer
z Integer
x Integer
y
      | Integer
x Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
0           = Integer
z Integer -> Integer -> Integer
forall a. AdditiveSemigroup a => a -> a -> a
+ Integer
m Integer -> Integer -> Integer
forall a. MultiplicativeSemigroup a => a -> a -> a
* Integer
y
      | Integer
y Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
0           = Integer
z Integer -> Integer -> Integer
forall a. AdditiveSemigroup a => a -> a -> a
+ Integer
m Integer -> Integer -> Integer
forall a. MultiplicativeSemigroup a => a -> a -> a
* Integer
x
      | Integer -> Bool
even Integer
x Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Integer -> Bool
even Integer
y = Integer -> Integer -> Integer -> Integer -> Integer
xor' (Integer
2 Integer -> Integer -> Integer
forall a. MultiplicativeSemigroup a => a -> a -> a
* Integer
m)  Integer
z      (Integer
x Integer -> Integer -> Integer
`divide` Integer
2) (Integer
y Integer -> Integer -> Integer
`divide` Integer
2) 
      | Bool
otherwise        = Integer -> Integer -> Integer -> Integer -> Integer
xor' (Integer
2 Integer -> Integer -> Integer
forall a. MultiplicativeSemigroup a => a -> a -> a
* Integer
m) (Integer
z Integer -> Integer -> Integer
forall a. AdditiveSemigroup a => a -> a -> a
+ Integer
m) (Integer
x Integer -> Integer -> Integer
`divide` Integer
2) (Integer
y Integer -> Integer -> Integer
`divide` Integer
2)


{-# INLINABLE even #-}

-- | Test whether a non-negative integer is even.
even :: Integer -- ^ The integer.
     -> Bool    -- ^ Whether the integer is even.
even :: Integer -> Bool
even Integer
x = Integer
x Integer -> Integer -> Integer
`modulo` Integer
2 Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
0


{-# INLINABLE odd #-}

-- | Test whether a non-negative integer is odd.
odd :: Integer -- ^ The integer.
    -> Bool    -- ^ Whether the integer is odd.
odd :: Integer -> Bool
odd Integer
x = Integer
x Integer -> Integer -> Integer
`modulo` Integer
2 Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
1