--
-- orbit-int credits (for checking termination of orbit computation)
--
module Credit( credit
             , credit_atomic
             , debit_atomic
             , debit_atomic_nz
             , zero
             , one
             , is_zero
             , is_one) where

-- An *atomic credit* is represented as a non-negative integer k;
-- it stands for the credit 1/{2^k}.
--
-- A *credit* is represented as list of non-negative integers, sorted in
-- strict descending order; it represents the sum of atomic credits
-- represented by the integers on the list, where zero credit is
-- represented by the empty list. The maximally possible credit, 1,
-- is represented by [0].


-- credit_atomic(K, C) adds the atomic credit 1/{2^K} to the credit C.
credit_atomic :: Int -> [Int] -> [Int]
credit_atomic k [] = [k]
credit_atomic k (c : cs) | k > c  = k : c : cs
                         | k == c = credit_atomic (k - 1) cs
                         | otherwise = c : credit_atomic k cs

-- credit(C1, C2) returns a list representing the sum of the credit
-- represented by the lists C1 and C2.
credit :: [Int] -> [Int] -> [Int]
credit c1 c2 = foldl (flip credit_atomic) c2 c1

-- debit_atomic(C) returns a pair {K',C'} where K' is an integer
-- representing some atomic credit and C' is a list of integers representing
-- some credit (which may be zero) such that the sum of the credits
-- represented by K' and C' equals the credit represented by C.
-- Precondition: C must represent non-zero credit.
debit_atomic :: [Int] -> (Int, [Int])
debit_atomic (c : cs) = (c, cs) -- debit smallest unit of credit

-- debit_atomic_nz(C) returns a pair {K',C'} where K' is an integer
-- representing some atomic credit and C' is a list of integers representing
-- some non-zero credit such that the sum of the credits
-- represented by K' and C' equals the credit represented by C.
-- Precondition: C must represent non-zero credit.
debit_atomic_nz :: [Int] -> (Int, [Int])
debit_atomic_nz [c] = (c + 1, [c + 1]) -- debit half the credit
debit_atomic_nz (c : cs) = (c, cs) -- debit smallest unit of credit;
-- case only applies if Cs non-empty

-- zero/0 produces zero credit.
zero :: [Int]
zero = []

-- one/0 produces credit one.
one :: [Int]
one = [0]

-- is_zero/1 tests whether its argument represents zero credit.
is_zero :: [Int] -> Bool
is_zero [] = True
is_zero _  = False

-- is_one/1 tests whether its argument represents maximal credit 1.
is_one :: [Int] -> Bool
is_one [0] = True
is_one _   = False