module Simple where

-- einfache Funktionen

add :: Int -> Int -> Int
add    x      y   =  x+y

square :: Int -> Int
square    x   =  x * x

simple       :: Int -> Int -> Int -> Int
simple a b c =  a * (b+c)

-- Konstanten

newline :: Char
newline =  '\n'

eps :: Double
eps =  0.000001

-- Test auf identische Argumente

allEqual       :: Int -> Int -> Int -> Bool
allEqual n m p =  (n==m) && (m==p)


-- eigene Maximumsfunktion, max ist vordefiniert
maxi                 :: Int -> Int -> Int
maxi n m | n>=m      =  n
         | otherwise =  m



--------------------------------------------------------------
-- Beispiel: Zahlen eines Intervalls multiplizieren
--------------------------------------------------------------
-- Die Zahlen in einem gegebenen Intervall [low|high] sollen 
-- multipliziert werden: low * (low+1) * .... * high
--
-- !!! Implizite Annahme:  low <= high !!!
--

-- sequentielle Definition
prod'                   :: Int -> Int -> Int
prod' low high 
        | low == high   =  low
        | otherwise     =  low * prod' (low+1) high


-- Divide et impera!
prod''                  :: Int -> Int -> Int
prod'' low high  
        | low == high   =  low 
        | low+1 ==high  =  low * high  
        | otherwise     =  prod'' low ((low+high) `div` 2)  
                           * prod'' ((low+high) `div` 2 + 1) high


-- Vermeidung von Mehrfachauswertung 
prod                    :: Int -> Int -> Int
prod low high  
        | low == high   =  low 
        | low+1 ==high  =  low * high  
        | otherwise     =  let mid = (low+high) `div` 2 
                           in  prod low mid * prod (mid+1) high






--------------------------------------------------------------
-- Beispiel: Verkaufszahlen analysieren
--------------------------------------------------------------


-- sales gibt zu einer Wochennummer die Verkaufszahl
sales :: Int -> Int
sales    n   =  (n `mod` 3) + (n `mod` 5) 


-- totalSales bestimmt zu einer Wochennummer 
-- die Summe der Verkaufszahlen bis zu dieser Woche
totalSales     :: Int -> Int
totalSales n 
      |  n==0  =  sales 0
      |  n>0   =  totalSales (n-1) + sales n
      |  n<0   =  0


-- maxSales bestimmt die Wochennummer mit der hoechsten 
-- Verkaufszahl bis zur eingegebenen Wochennummer
maxSales'           :: Int -> Int
maxSales' n   
      | n==0        =  0
      | (n>0) && sales (maxSales' (n-1)) > sales n  
                    =  maxSales' (n-1)
      | otherwise   =  n

-- maxSales' wertet maxSales' (n-1) mehrfach aus
-- Hier ist eine effizientere Version, die die
-- Mehrfachauswertung vermeidet:

maxSales            :: Int -> Int
maxSales n   
      | n==0        =  0
      | (n>0) && sales (maxAlt) > sales n  
                    =  maxAlt
      | otherwise   =  n
      where maxAlt = maxSales (n-1)



-- 
-- alternative endrekursive Berechnung von searchMax
--
maxSales2 :: Int -> Int
maxSales2    n   =  searchMax n 0 0

searchMax  n i m
   | n<0   = n
   | i>n   = m
   | i<=n && sales m > sales i   
           = searchMax n (i+1) m
   | otherwise 
           = searchMax n (i+1) i
