packages feed

lorentz-0.14.0: src/Lorentz/CustomArith/Conversions.hs

-- SPDX-FileCopyrightText: 2021 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ

{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE NoApplicativeDo #-}
{-# OPTIONS_GHC -Wno-unused-do-bind #-}
{-# OPTIONS_GHC -Wno-orphans #-}

module Lorentz.CustomArith.Conversions
 ( -- * Rational to Fixed/NFixed
   convertFixedToRational
 , convertNFixedToRational
 , convertRationalToFixed
 , convertRationalToNFixed
 , unsafeConvertRationalToNFixed

   -- * NRational to Fixed/NFixed
 , convertFixedToNRational
 , unsafeConvertFixedToNRational
 , convertNFixedToNRational
 , convertNRationalToFixed
 , convertNRationalToNFixed
 ) where

import Data.Fixed (HasResolution)
import Prelude (Maybe)

import Lorentz.CustomArith.FixedArith
import Lorentz.CustomArith.RationalArith

import Lorentz.Base
import Lorentz.Coercions
import Lorentz.Errors
import Lorentz.Instr
import Lorentz.Macro
import Lorentz.Rebinded

convertRationalToNRational :: Rational : s :-> Maybe (NRational) : s
convertRationalToNRational = do
  dup; numerator
  ifGe0 (unsafeConvertRationalToNRational # some) (drop # none)

unsafeConvertRationalToNRational :: Rational : s :-> NRational : s
unsafeConvertRationalToNRational = deconstructRational # abs # constructRational

convertNRationalToRational :: NRational : s :-> Rational : s
convertNRationalToRational = deconstructRational # int # constructRational

----------------------------------------------------------------------------------------------------
-- Rationals
----------------------------------------------------------------------------------------------------

-- | Converts 'Rational' to 'Fixed' using value inside as numerator and base as denominator
convertFixedToRational :: forall a s. HasResolution a =>  Fixed a : s :-> Rational : s
convertFixedToRational = do
  coerceUnwrap
  push (resolution_ @a)
  swap
  constructRational

-- | Converts 'Rational' to 'NFixed' using value inside as numerator and base as denominator
convertNFixedToRational ::forall a s. HasResolution a => NFixed a : s :-> Rational : s
convertNFixedToRational = do
  coerceUnwrap; int
  push (resolution_ @a)
  swap
  constructRational

-- | Converts 'Fixed' to 'Rational' according to formulae: numerator * base / denominator
-- Note: Since result's base is determined by user, it is recommended to use higher value in order to achieve
-- better accuracy
convertRationalToFixed :: forall a s. HasResolution a => Rational : s :-> Fixed a : s
convertRationalToFixed = do
  deconstructRational
  push (resolution_ @a)
  mul
  ediv
  assertSome (Impossible @"Division by zero impossible")
  unpair
  add
  unsafeCoerceWrap

unsafeConvertRationalToNFixed :: forall a s. HasResolution a => Rational : s :-> NFixed a : s
unsafeConvertRationalToNFixed = convertRationalToFixed # unsafeCastFixedToNFixed

convertRationalToNFixed :: forall a s. HasResolution a => Rational : s :-> Maybe (NFixed a) : s
convertRationalToNFixed = convertRationalToFixed # castFixedToNFixed

----------------------------------------------------------------------------------------------------
-- NRationals
----------------------------------------------------------------------------------------------------
-- | Converts 'NRational' to 'Fixed'.
convertNRationalToFixed :: forall a s. HasResolution a => NRational : s :-> Fixed a : s
convertNRationalToFixed = convertNRationalToRational # convertRationalToFixed

-- | Converts 'NRational' to 'NFixed'.
convertNRationalToNFixed :: forall a s. HasResolution a => NRational : s :-> NFixed a : s
convertNRationalToNFixed = do
  deconstructRational
  push (resolution_ @a)
  mul
  ediv
  assertSome (Impossible @"Division by zero impossible")
  unpair
  add
  unsafeCoerceWrap

-- | Converts 'NFixed' to 'NRational'.
convertNFixedToNRational :: forall a s. HasResolution a => NFixed a : s :-> NRational : s
convertNFixedToNRational = do
  coerceUnwrap
  push (resolution_ @a)
  swap
  constructRational

-- | Converts 'Fixed' to 'NRational' if it is greater than zero. If not, returns 'Data.Maybe.Nothing'
convertFixedToNRational :: forall a s. HasResolution a => Fixed a : s :-> Maybe (NRational) : s
convertFixedToNRational = convertFixedToRational # convertRationalToNRational

-- | Converts 'Fixed' to 'NRational', using 'abs' on numerator.
unsafeConvertFixedToNRational :: forall a s. HasResolution a => Fixed a : s :-> NRational : s
unsafeConvertFixedToNRational = convertFixedToRational # unsafeConvertRationalToNRational