import Data.List (intercalate)

-- Widened borrow of a particular repeated amount
-- i.e., for borrow2 qr 2, the borrow is 22 = 100
-- `qr` is an integer division function returning the quotient and remainder.
-- i.e., either `divMod` or `quotRem`
borrow2' qr b
  | b == 1    = error "Cannot borrow 1: implies positional symbol zero disallowed"
  | otherwise = borrow' []
    where borrow' zs (x:y:z:xs)
            | abs x >= b = zipUp ys zs              -- carry here
            | otherwise  = borrow' (x:zs) (y:z:xs)  -- try borrowing at a higher place value
              where ys     = r : y-x+r : z+q : xs
                    (q, r) = x `qr` b
          zipUp = foldl (flip (:))

borrow2 = borrow2' quotRem

--degenerate `borrow2 1` that remedies inadequacies in below for 1, i.e., the borrow is 11 = 100
--this system is VERY bad because we invoke the place value '0' to expand into
--borrow1' zs (x:y:z:xs)  | abs x > 1 = zipUp ys zs
--                        | otherwise = borrow1' (x:zs) (y:z:xs)
--                          where ys  = (signum x):(y-x+signum x):z+(x-1):xs

-- Truncate the adic expansion to n digits, for a system where borrows are two digits wide and both b
truncadic' n b = (!! n) . iterate (borrow2 b)
truncadic n = (take n .) . truncadic' n

-- same but, produce a list of expansions, taking modulo m in between borrows
truncadicMod b m n = map (take n) . take n . iterate (map (`rem` m) . borrow2 2)

-- Find moduli starting from `s` where truncations agree
-- Caveat: the last of truncadicMod is not guaranteed to exhaust terms outside alphabet
-- i.e., for b = 2, the alphabet {-1, 0, 1}
findAccurate n b s xs = map fst $ filter ((==good) . snd) bads
  where good = truncadic n b xs
        bads = map (\m -> (,) m $ last $ truncadicMod b m n xs) [s..]

-- Given an amount of digits `n`, a 2-wide borrow `b`, and a canonical representation of `b`
-- construct integer multiples of `b`
evens n b = ([0]:) . map (take n) . iterate addb
  -- add b to an adic expansion
  where addb = truncadic' n b . (b:) . tail

-- first 200 digits of cendree-adic expansions of even numbers
adics  = evens 200 2 $ 0:0:cycle [1,-1]
adic2  = adics !! 1
-- Expansion of 4, generated from incrementing the expansion of 2 twice
adic4  = adics !! 2
-- Expansion of 4, generated from pointwise multiplication of the expansion of 2
adic4' = truncadic 200 2 $ (++repeat 0) $ map (*2) adic2

-- Note: `truncadic 200 n $ 4:repeat 0` is the aggressive application of the carry to 4
-- but this could be done better since there are only 3 terms in the head, and no higher
-- series terms get in the way; the remainders would be emitted while the thunk continued

truncadicQR' qr n b = (!! n) . iterate (borrow2' qr b)
truncadicQR qr n b = take n . truncadicQR' qr n b

-- Given an amount of digits `n`, a 2-wide borrow `b`, and a canonical representation of `b`
-- construct integer multiples of `b`
evensQR qr n b = ([0]:) . map (take n) . iterate addb
  -- add b to an adic expansion
  where addb = truncadicQR' qr n b . (b:) . tail

data QRMethod = QuotRem | DivMod deriving Show
qrMethod QuotRem = quotRem
qrMethod DivMod = divMod

cendreeEvens' :: QRMethod -> Int -> Int -> [[Int]]
cendreeEvens' qr m n = take m $ evensQR qr' n 2 $ truncadicQR qr' (n + 2) 2 $ 2:repeat 0
  where qr' = qrMethod qr

cendreeEvens :: QRMethod -> Int -> Int -> IO ()
cendreeEvens qr m n = writeFile fn $ display $ cendreeEvens' qr m n
  where display = unlines  . map (intercalate "," . map show)
        fn = "cendree_" ++ show qr ++ "_count_" ++ show m ++ "_" ++ show n ++ "_digits.csv"
