module RSAGL.Auxiliary.Auxiliary
    (doubles,
     loopedRSdoubles,
     consecutives,
     loopedConsecutives,
     dropRandomElements,
     loopList,
     shiftR,
     zeroToOne,
     constrain,
     debugTime,
     waitParList)
    where

import Data.Array
import System.Random
import System.CPUTime
import Control.Parallel
import Control.Parallel.Strategies
import Debug.Trace
import RSAGL.Math.Types

-- doubles transforms a list to a list of adjacent elements.

-- doubles [1,2,3,4,5] = [(1,2),(2,3),(3,4),(4,5)]

doubles :: [a] -> [(a,a)]
doubles [] = []
doubles [_] = []
doubles (x:y:zs) = (x,y) : doubles (y:zs)

-- loopedRSdoubles transforms a list to a list of adjacent elements, looping back to the beginning of the list.

-- loopedRSdoubles [1,2,3,4,5] = [(1,2),(2,3),(3,4),(4,5),(5,1)]

loopedRSdoubles :: [a] -> [(a,a)]
loopedRSdoubles as = loopedRSdoubles_ (head as) as
    where loopedRSdoubles_ _ [] = []
          loopedRSdoubles_ a [x] = [(x,a)]
          loopedRSdoubles_ a (x:y:zs) = (x,y) : loopedRSdoubles_ a (y:zs)

-- consecutives answers a list containing every sequence of n consecutive
-- elements in the parameter.

--consecutives 3 [1,2,3,4] = [[1,2,3],[2,3,4]]

consecutives :: Int -> [a] -> [[a]]
consecutives n xs = let taken = take n xs
			in if (length taken == n)
			   then (taken : (consecutives n $ tail xs))
			   else []

-- loopedConsecutives answers a list containing every sequence of n consecutive
-- elements in the parameter, looping back to the beginning of the list.

-- consecutives 3 [1,2,3,4] = [[1,2,3],[2,3,4],[3,4,1],[4,1,2]]

loopedConsecutives :: Int -> [a] -> [[a]]
loopedConsecutives n xs = consecutives n $ take (n + length xs - 1) $ cycle xs

-- dropRandomElements removes some elements of a list at random.  The first parameter is the number of elements out of 100 that should be included (not dropped).
-- The second is the random number generator, and the third is the list to be operated on.

dropRandomElements :: Int -> StdGen -> [a] -> [a]
dropRandomElements percent _ _ | percent > 100 = error "dropRandomElements: percent > 100"
dropRandomElements percent _ _ | percent < 0 = error "dropRandomElements: percent < 100"
dropRandomElements _ _ [] = []
dropRandomElements percent rand_ints things = 
    let (next_int,next_gen) = next rand_ints
	rest = dropRandomElements percent next_gen (tail things)
	in if (next_int `mod` 100 < percent)
	   then ((head things) : rest)
	   else rest

-- loopList appends the first element of a list to the end of the list.  The result is a list of one greater length.

-- shiftR shifts every element of a list to the right, recyling the last element as the first.  The result is a list of the same length.

loopList :: [a] -> [a]
loopList ps = ps ++ [head ps]

shiftR :: [a] -> [a]
shiftR ps = last ps : init ps

-- zeroToOne creates a list of numbers from 0.0 to 1.0, using n steps.

zeroToOne :: Integer -> [RSdouble]
zeroToOne n | n > 100000 = trace ("Warning: zeroToOne was asked for " ++ show n ++ " subdivisions, which seems high.  Using 100,000 instead.") zeroToOne 100000
zeroToOne n | n <= 1000 = ztos ! n
zeroToOne n = zeroToOnePrim n

zeroToOnePrim :: Integer -> [RSdouble]
zeroToOnePrim n = map (*x) [0..(fromInteger $ n-1)]
    where x = recip (fromInteger $ n - 1)

ztos :: Array Integer [RSdouble]
ztos = listArray (0,1000) $ map (zeroToOnePrim) [0 .. 1000]

-- constrain restricts the effective domain of a function.  Outside of the restricted domain, the function is id

constrain :: (a -> Bool) -> (a -> a) -> a -> a
constrain f g x = if f x then g x else x

-- debugTime prints a statement indicating how long an IO action takes to complete

debugTime :: String -> IO a -> IO a
debugTime msg io_action =
    do print $ "debugTime: starting " ++ msg
       start_time <- getCPUTime
       result <- io_action
       end_time <- getCPUTime
       print $ "debugTime: done with " ++ msg ++ " " ++ show (end_time - start_time)
       return result

-- as parList, but waits until the list is fully evaluated.

waitParList :: Strategy a -> Strategy [a]
waitParList s a = parList s a `pseq` seqList s a