{-# LANGUAGE FlexibleContexts #-}

module Jikka.Core.Language.Runtime where

import Jikka.Common.Error

floorDiv :: MonadError Error m => Integer -> Integer -> m Integer
floorDiv :: Integer -> Integer -> m Integer
floorDiv Integer
_ Integer
0 = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError String
"zero div"
floorDiv Integer
a Integer
b = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
a Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
b)

floorMod :: MonadError Error m => Integer -> Integer -> m Integer
floorMod :: Integer -> Integer -> m Integer
floorMod Integer
_ Integer
0 = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError String
"zero div"
floorMod Integer
a Integer
b = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
a Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` Integer
b)

ceilDiv :: MonadError Error m => Integer -> Integer -> m Integer
ceilDiv :: Integer -> Integer -> m Integer
ceilDiv Integer
_ Integer
0 = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError String
"zero div"
ceilDiv Integer
a Integer
b = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return ((Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
b Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
1) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
b)

ceilMod :: MonadError Error m => Integer -> Integer -> m Integer
ceilMod :: Integer -> Integer -> m Integer
ceilMod Integer
_ Integer
0 = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError String
"zero div"
ceilMod Integer
a Integer
b = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- ((Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
b Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
1) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
b) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
b)

justDiv :: MonadError Error m => Integer -> Integer -> m Integer
justDiv :: Integer -> Integer -> m Integer
justDiv Integer
_ Integer
0 = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError String
"zero div"
justDiv Integer
a Integer
b | Integer
a Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` Integer
b Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
0 = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError String
"not a just div"
justDiv Integer
a Integer
b = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
a Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
b)

modinv :: MonadError Error m => Integer -> Integer -> m Integer
modinv :: Integer -> Integer -> m Integer
modinv Integer
a Integer
m | Integer
m Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
0 Bool -> Bool -> Bool
|| Integer
a Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` Integer
m Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
0 = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError (String -> m Integer) -> String -> m Integer
forall a b. (a -> b) -> a -> b
$ String
"invalid argument for inv: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Integer, Integer) -> String
forall a. Show a => a -> String
show (Integer
a, Integer
m)
modinv Integer
a Integer
m = Integer
-> Integer -> Integer -> Integer -> Integer -> Integer -> m Integer
forall (m :: * -> *).
MonadError Error m =>
Integer
-> Integer -> Integer -> Integer -> Integer -> Integer -> m Integer
go Integer
a Integer
m Integer
0 Integer
1 Integer
1 Integer
0
  where
    go :: Integer
-> Integer -> Integer -> Integer -> Integer -> Integer -> m Integer
go Integer
0 Integer
b Integer
x Integer
y Integer
_ Integer
_ = if Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
m Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
y Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
b Bool -> Bool -> Bool
&& Integer
b Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
1 then Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return Integer
x else String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError String
"Jikka.Core.Language.Runtime.modinv: something wrong"
    go Integer
a Integer
b Integer
x Integer
y Integer
u Integer
v = let q :: Integer
q = Integer
b Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
a in Integer
-> Integer -> Integer -> Integer -> Integer -> Integer -> m Integer
go (Integer
b Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
q Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
a) Integer
a Integer
u Integer
v (Integer
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
q Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
u) (Integer
y Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
q Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
v)

modpow :: MonadError Error m => Integer -> Integer -> Integer -> m Integer
modpow :: Integer -> Integer -> Integer -> m Integer
modpow Integer
_ Integer
_ Integer
m | Integer
m Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
0 = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError (String -> m Integer) -> String -> m Integer
forall a b. (a -> b) -> a -> b
$ String
"invalid argument for modpow: MOD = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Integer -> String
forall a. Show a => a -> String
show Integer
m
modpow Integer
a Integer
b Integer
m = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> m Integer) -> Integer -> m Integer
forall a b. (a -> b) -> a -> b
$ Integer -> Integer -> Integer
go (Integer
a Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` Integer
m) Integer
b
  where
    go :: Integer -> Integer -> Integer
go Integer
a Integer
0 = Integer
a
    go Integer
a Integer
b = Integer -> Integer -> Integer
go (if Integer
b Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` Integer
2 Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
1 then Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
b Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` Integer
m else Integer
a) (Integer
b Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
2)

fact :: MonadError Error m => Integer -> m Integer
fact :: Integer -> m Integer
fact Integer
n | Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
0 = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError (String -> m Integer) -> String -> m Integer
forall a b. (a -> b) -> a -> b
$ String
"invalid argument for fact: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Integer -> String
forall a. Show a => a -> String
show Integer
n
fact Integer
n = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> m Integer) -> Integer -> m Integer
forall a b. (a -> b) -> a -> b
$ [Integer] -> Integer
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
product [Integer
1 .. Integer
n]

choose :: MonadError Error m => Integer -> Integer -> m Integer
choose :: Integer -> Integer -> m Integer
choose Integer
n Integer
r | Bool -> Bool
not (Integer
0 Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
r Bool -> Bool -> Bool
&& Integer
r Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
n) = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError (String -> m Integer) -> String -> m Integer
forall a b. (a -> b) -> a -> b
$ String
"invalid argument for choose: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Integer, Integer) -> String
forall a. Show a => a -> String
show (Integer
n, Integer
r)
choose Integer
n Integer
r = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> m Integer) -> Integer -> m Integer
forall a b. (a -> b) -> a -> b
$ [Integer] -> Integer
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
product [Integer
n Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
r Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
1 .. Integer
n] Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` [Integer] -> Integer
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
product [Integer
1 .. Integer
r]

permute :: MonadError Error m => Integer -> Integer -> m Integer
permute :: Integer -> Integer -> m Integer
permute Integer
n Integer
r | Bool -> Bool
not (Integer
0 Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
r Bool -> Bool -> Bool
&& Integer
r Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
n) = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError (String -> m Integer) -> String -> m Integer
forall a b. (a -> b) -> a -> b
$ String
"invalid argument for choose: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Integer, Integer) -> String
forall a. Show a => a -> String
show (Integer
n, Integer
r)
permute Integer
n Integer
r = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> m Integer) -> Integer -> m Integer
forall a b. (a -> b) -> a -> b
$ [Integer] -> Integer
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
product [Integer
n Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
r Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
1 .. Integer
n]

multichoose :: MonadError Error m => Integer -> Integer -> m Integer
multichoose :: Integer -> Integer -> m Integer
multichoose Integer
n Integer
r | Bool -> Bool
not (Integer
0 Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
r Bool -> Bool -> Bool
&& Integer
r Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
n) = String -> m Integer
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwRuntimeError (String -> m Integer) -> String -> m Integer
forall a b. (a -> b) -> a -> b
$ String
"invalid argument for multichoose: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Integer, Integer) -> String
forall a. Show a => a -> String
show (Integer
n, Integer
r)
multichoose Integer
0 Integer
0 = Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return Integer
1
multichoose Integer
n Integer
r = Integer -> Integer -> m Integer
forall (m :: * -> *).
MonadError Error m =>
Integer -> Integer -> m Integer
choose (Integer
n Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
r Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
1) Integer
r