-----------------------------------------------------------------------------
-- |
-- Copyright   :  (C) 2015 Anselm Jonas Scholl, (C) 2023 Julia Longtin
-- License     :  BSD3
-- Maintainer  :  Julia Longtin
-- Stability   :  experimental
-- Portability :  GHC-specific
--
-- Primitive operations to coerce floating point numbers to integral numbers
-- preserving their bitwise representations as well as functions operating on
-- boxed values.
----------------------------------------------------------------------------

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE MagicHash                #-}
{-# LANGUAGE GHCForeignImportPrim     #-}
{-# LANGUAGE UnliftedFFITypes         #-}
{-# LANGUAGE CPP                      #-}

-- Note: HLint fails to find this, and will always warn.
#include "MachDeps.h"

module Data.Bits.Floating.Prim (
  double2WordBitwise,
  float2WordBitwise,
  word2DoubleBitwise,
  word2FloatBitwise) where

-- We use nothing from the Base library here.
import Prelude ()

import GHC.Exts (Double#, Double(D#), Float#, Float(F#))
import GHC.Word (Word32(W32#), Word64(W64#))

#if defined(WORD_SIZE_IN_BITS)
#if WORD_SIZE_IN_BITS == 64 && MIN_VERSION_base(4,17,0)
-- The name of Word# changed to Word64# in base 4.17.0 (GHC 9.4.1)
import GHC.Exts (Word64#, Word32#)
#define WORD64 Word64
#define WORD32 Word32
-- FIXME: this is wrong. what's a better test here?
#elif WORD_SIZE_IN_BITS == 64
-- Earlier than GHC 9.4? then Word is our 64 bit type.
import GHC.Exts (Word#)
#define WORD64 Word
#if MIN_VERSION_base(4,16,0)
-- Word32 and Word diverged in base 4.16.0 (GHC 9.2.1?))
import GHC.Exts (Word32#)
#define WORD32 Word32
#else
-- Earlier than GHC9.2? then use Word for both 32 and 64 bit FFI calls.
#define WORD32 Word
#endif
#else
#error non-X86_64 architectures not supported
#endif
#endif

foreign import prim "double2WordBwzh"
    double2WordBitwise# :: Double# -> WORD64#
foreign import prim "word2DoubleBwzh"
    word2DoubleBitwise# :: WORD64# -> Double#
foreign import prim "float2WordBwzh"
    float2WordBitwise# :: Float# -> WORD32#
foreign import prim "word2FloatBwzh"
    word2FloatBitwise# :: WORD32# -> Float#

#undef WORD64
#undef WORD32

-- | Convert a 'Double' to a 'Word64' while preserving the bit-pattern.
{-# INLINE double2WordBitwise #-}
double2WordBitwise :: Double -> Word64
double2WordBitwise :: Double -> Word64
double2WordBitwise (D# Double#
d) = Word# -> Word64
W64# (Double# -> Word#
double2WordBitwise# Double#
d)

-- | Convert a 'Word64' to a 'Double' while preserving the bit-pattern.
{-# INLINE word2DoubleBitwise #-}
word2DoubleBitwise :: Word64 -> Double
word2DoubleBitwise :: Word64 -> Double
word2DoubleBitwise (W64# Word#
w) = Double# -> Double
D# (Word# -> Double#
word2DoubleBitwise# Word#
w)

-- | Convert a 'Float' to a 'Word32' while preserving the bit-pattern.
{-# INLINE float2WordBitwise #-}
float2WordBitwise :: Float -> Word32
float2WordBitwise :: Float -> Word32
float2WordBitwise (F# Float#
f) = Word32# -> Word32
W32# (Float# -> Word32#
float2WordBitwise# Float#
f)

-- | Convert a 'Word32' to a 'Float' while preserving the bit-pattern.
{-# INLINE word2FloatBitwise #-}
word2FloatBitwise :: Word32 -> Float
word2FloatBitwise :: Word32 -> Float
word2FloatBitwise (W32# Word32#
w) = Float# -> Float
F# (Word32# -> Float#
word2FloatBitwise# Word32#
w)