-- | Math functions.
module Music.Theory.Math where

import Data.List {- base -}
import Data.Maybe {- base -}
import Data.Ratio {- base -}

import qualified Music.Theory.Math.Convert as T {- hmt-base -}

-- | 'mod' 5.
mod5 :: Integral i => i -> i
mod5 :: forall i. Integral i => i -> i
mod5 i
n = i
n forall a. Integral a => a -> a -> a
`mod` i
5

-- | 'mod' 7.
mod7 :: Integral i => i -> i
mod7 :: forall i. Integral i => i -> i
mod7 i
n = i
n forall a. Integral a => a -> a -> a
`mod` i
7

-- | 'mod' 12.
mod12 :: Integral i => i -> i
mod12 :: forall i. Integral i => i -> i
mod12 i
n = i
n forall a. Integral a => a -> a -> a
`mod` i
12

-- | 'mod' 16.
mod16 :: Integral i => i -> i
mod16 :: forall i. Integral i => i -> i
mod16 i
n = i
n forall a. Integral a => a -> a -> a
`mod` i
16

-- | <http://reference.wolfram.com/mathematica/ref/FractionalPart.html>
--   i.e. 'properFraction'
--
-- > integral_and_fractional_parts 1.5 == (1,0.5)
integral_and_fractional_parts :: (Integral i, RealFrac t) => t -> (i,t)
integral_and_fractional_parts :: forall i t. (Integral i, RealFrac t) => t -> (i, t)
integral_and_fractional_parts = forall a b. (RealFrac a, Integral b) => a -> (b, a)
properFraction

-- | Type specialised.
integer_and_fractional_parts :: RealFrac t => t -> (Integer,t)
integer_and_fractional_parts :: forall t. RealFrac t => t -> (Integer, t)
integer_and_fractional_parts = forall i t. (Integral i, RealFrac t) => t -> (i, t)
integral_and_fractional_parts

-- | <http://reference.wolfram.com/mathematica/ref/FractionalPart.html>
--
-- > import Sound.SC3.Plot {- hsc3-plot -}
-- > plot_p1_ln [map fractional_part [-2.0,-1.99 .. 2.0]]
fractional_part :: RealFrac a => a -> a
fractional_part :: forall a. RealFrac a => a -> a
fractional_part = forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t. RealFrac t => t -> (Integer, t)
integer_and_fractional_parts

-- | 'floor' of 'T.real_to_double'.
real_floor :: (Real r,Integral i)  => r -> i
real_floor :: forall r i. (Real r, Integral i) => r -> i
real_floor = forall a b. (RealFrac a, Integral b) => a -> b
floor forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t. Real t => t -> Double
T.real_to_double

-- | Type specialised 'real_floor'.
real_floor_int :: Real r => r -> Int
real_floor_int :: forall r. Real r => r -> Int
real_floor_int = forall r i. (Real r, Integral i) => r -> i
real_floor

-- | 'round' of 'T.real_to_double'.
real_round :: (Real r,Integral i)  => r -> i
real_round :: forall r i. (Real r, Integral i) => r -> i
real_round = forall a b. (RealFrac a, Integral b) => a -> b
round forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t. Real t => t -> Double
T.real_to_double

-- | Type specialised 'real_round'.
real_round_int :: Real r => r -> Int
real_round_int :: forall r. Real r => r -> Int
real_round_int = forall r i. (Real r, Integral i) => r -> i
real_round

-- | Type specialised 'round'
round_int :: RealFrac t => t -> Int
round_int :: forall t. RealFrac t => t -> Int
round_int = forall a b. (RealFrac a, Integral b) => a -> b
round

-- | Type-specialised 'fromIntegral'
from_integral_to_int :: Integral i => i -> Int
from_integral_to_int :: forall i. Integral i => i -> Int
from_integral_to_int = forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | Type-specialised 'id'
int_id :: Int -> Int
int_id :: Int -> Int
int_id = forall a. a -> a
id

-- | Is /r/ zero to /k/ decimal places.
--
-- > map (flip zero_to_precision 0.00009) [4,5] == [True,False]
-- > map (zero_to_precision 4) [0.00009,1.00009] == [True,False]
zero_to_precision :: Real r => Int -> r -> Bool
zero_to_precision :: forall r. Real r => Int -> r -> Bool
zero_to_precision Int
k r
r = forall r. Real r => r -> Int
real_floor_int (r
r forall a. Num a => a -> a -> a
* forall a b. (Integral a, Num b) => a -> b
fromIntegral ((Int
10::Int) forall a b. (Num a, Integral b) => a -> b -> a
^ Int
k)) forall a. Eq a => a -> a -> Bool
== Int
0

-- | Is /r/ whole to /k/ decimal places.
--
-- > map (flip whole_to_precision 1.00009) [4,5] == [True,False]
whole_to_precision :: Real r => Int -> r -> Bool
whole_to_precision :: forall r. Real r => Int -> r -> Bool
whole_to_precision Int
k = forall r. Real r => Int -> r -> Bool
zero_to_precision Int
k forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. RealFrac a => a -> a
fractional_part forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t. Real t => t -> Double
T.real_to_double

-- | <http://reference.wolfram.com/mathematica/ref/SawtoothWave.html>
--
-- > plot_p1_ln [map sawtooth_wave [-2.0,-1.99 .. 2.0]]
sawtooth_wave :: RealFrac a => a -> a
sawtooth_wave :: forall a. RealFrac a => a -> a
sawtooth_wave a
n = a
n forall a. Num a => a -> a -> a
- forall a b. (RealFrac a, Num b) => a -> b
floor_f a
n

-- | Predicate that is true if @n/d@ can be simplified, ie. where
-- 'gcd' of @n@ and @d@ is not @1@.
--
-- > map rational_simplifies [(2,3),(4,6),(5,7)] == [False,True,False]
rational_simplifies :: Integral a => (a,a) -> Bool
rational_simplifies :: forall a. Integral a => (a, a) -> Bool
rational_simplifies (a
n,a
d) = forall a. Integral a => a -> a -> a
gcd a
n a
d forall a. Eq a => a -> a -> Bool
/= a
1

-- | 'numerator' and 'denominator' of rational.
rational_nd :: Integral t => Ratio t -> (t,t)
rational_nd :: forall t. Integral t => Ratio t -> (t, t)
rational_nd Ratio t
r = (forall a. Ratio a -> a
numerator Ratio t
r,forall a. Ratio a -> a
denominator Ratio t
r)

-- | Rational as a whole number, or 'Nothing'.
rational_whole :: Integral a => Ratio a -> Maybe a
rational_whole :: forall a. Integral a => Ratio a -> Maybe a
rational_whole Ratio a
r = if forall a. Ratio a -> a
denominator Ratio a
r forall a. Eq a => a -> a -> Bool
== a
1 then forall a. a -> Maybe a
Just (forall a. Ratio a -> a
numerator Ratio a
r) else forall a. Maybe a
Nothing

-- | Erroring variant.
rational_whole_err :: Integral a => Ratio a -> a
rational_whole_err :: forall a. Integral a => Ratio a -> a
rational_whole_err = forall a. a -> Maybe a -> a
fromMaybe (forall a. HasCallStack => [Char] -> a
error [Char]
"rational_whole") forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => Ratio a -> Maybe a
rational_whole

-- | Sum of numerator & denominator.
ratio_nd_sum :: Integral t => Ratio t -> t
ratio_nd_sum :: forall a. Integral a => Ratio a -> a
ratio_nd_sum Ratio t
r = forall a. Ratio a -> a
numerator Ratio t
r forall a. Num a => a -> a -> a
+ forall a. Ratio a -> a
denominator Ratio t
r

-- | Is /n/ a whole (integral) value.
--
-- > map real_is_whole [-1.0,-0.5,0.0,0.5,1.0] == [True,False,True,False,True]
real_is_whole :: Real n => n -> Bool
real_is_whole :: forall n. Real n => n -> Bool
real_is_whole = (forall a. Eq a => a -> a -> Bool
== Integer
1) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Ratio a -> a
denominator forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Real a => a -> Rational
toRational

-- | 'fromInteger' . 'floor'.
floor_f :: (RealFrac a, Num b) => a -> b
floor_f :: forall a b. (RealFrac a, Num b) => a -> b
floor_f = forall a. Num a => Integer -> a
fromInteger forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (RealFrac a, Integral b) => a -> b
floor

-- | Round /b/ to nearest multiple of /a/.
--
-- > map (round_to 0.25) [0,0.1 .. 1] == [0.0,0.0,0.25,0.25,0.5,0.5,0.5,0.75,0.75,1.0,1.0]
-- > map (round_to 25) [0,10 .. 100] == [0,0,25,25,50,50,50,75,75,100,100]
round_to :: RealFrac n => n -> n -> n
round_to :: forall n. RealFrac n => n -> n -> n
round_to n
a n
b = if n
a forall a. Eq a => a -> a -> Bool
== n
0 then n
b else forall a b. (RealFrac a, Num b) => a -> b
floor_f ((n
b forall a. Fractional a => a -> a -> a
/ n
a) forall a. Num a => a -> a -> a
+ n
0.5) forall a. Num a => a -> a -> a
* n
a

-- | Variant of 'recip' that checks input for zero.
--
-- > map recip [1,1/2,0,-1]
-- > map recip_m [1,1/2,0,-1] == [Just 1,Just 2,Nothing,Just (-1)]
recip_m :: (Eq a, Fractional a) => a -> Maybe a
recip_m :: forall a. (Eq a, Fractional a) => a -> Maybe a
recip_m a
x = if a
x forall a. Eq a => a -> a -> Bool
== a
0 then forall a. Maybe a
Nothing else forall a. a -> Maybe a
Just (forall a. Fractional a => a -> a
recip a
x)

-- * One-indexed

-- | One-indexed 'mod' function.
--
-- > map (`oi_mod` 5) [1..10] == [1,2,3,4,5,1,2,3,4,5]
oi_mod :: Integral a => a -> a -> a
oi_mod :: forall a. Integral a => a -> a -> a
oi_mod a
n a
m = ((a
n forall a. Num a => a -> a -> a
- a
1) forall a. Integral a => a -> a -> a
`mod` a
m) forall a. Num a => a -> a -> a
+ a
1

-- | One-indexed 'divMod' function.
--
-- > map (`oi_divMod` 5) [1,3 .. 9] == [(0,1),(0,3),(0,5),(1,2),(1,4)]
oi_divMod :: Integral t => t -> t -> (t, t)
oi_divMod :: forall t. Integral t => t -> t -> (t, t)
oi_divMod t
n t
m = let (t
i,t
j) = (t
n forall a. Num a => a -> a -> a
- t
1) forall t. Integral t => t -> t -> (t, t)
`divMod` t
m in (t
i,t
j forall a. Num a => a -> a -> a
+ t
1)

-- * I = integral

-- | Integral square root (sqrt) function.
--
-- > map i_square_root [0,1,4,9,16,25,36,49,64,81,100] == [0 .. 10]
-- > map i_square_root [4 .. 16] == [2,2,2,2,2,3,3,3,3,3,3,3,4]
i_square_root :: Integral t => t -> t
i_square_root :: forall i. Integral i => i -> i
i_square_root t
n =
    let babylon :: t -> t
babylon t
a =
            let b :: t
b  = forall a. Integral a => a -> a -> a
quot (t
a forall a. Num a => a -> a -> a
+ forall a. Integral a => a -> a -> a
quot t
n t
a) t
2
            in if t
a forall a. Ord a => a -> a -> Bool
> t
b then t -> t
babylon t
b else t
a
    in case forall a. Ord a => a -> a -> Ordering
compare t
n t
0 of
         Ordering
GT -> t -> t
babylon t
n
         Ordering
EQ -> t
0
         Ordering
_ -> forall a. HasCallStack => [Char] -> a
error [Char]
"i_square_root: negative?"

-- * Interval

-- | (0,1) = {x | 0 < x < 1}
in_open_interval :: Ord a => (a, a) -> a -> Bool
in_open_interval :: forall a. Ord a => (a, a) -> a -> Bool
in_open_interval (a
p,a
q) a
n = a
p forall a. Ord a => a -> a -> Bool
< a
n Bool -> Bool -> Bool
&& a
n forall a. Ord a => a -> a -> Bool
< a
q

-- | [0,1] = {x | 0 ≤ x ≤ 1}
in_closed_interval :: Ord a => (a, a) -> a -> Bool
in_closed_interval :: forall a. Ord a => (a, a) -> a -> Bool
in_closed_interval (a
p,a
q) a
n = a
p forall a. Ord a => a -> a -> Bool
<= a
n Bool -> Bool -> Bool
&& a
n forall a. Ord a => a -> a -> Bool
<= a
q

-- | (p,q] (0,1] = {x | 0 < x ≤ 1}
in_left_half_open_interval :: Ord a => (a, a) -> a -> Bool
in_left_half_open_interval :: forall a. Ord a => (a, a) -> a -> Bool
in_left_half_open_interval (a
p,a
q) a
n = a
p forall a. Ord a => a -> a -> Bool
< a
n Bool -> Bool -> Bool
&& a
n forall a. Ord a => a -> a -> Bool
<= a
q

-- | [p,q) [0,1) = {x | 0 ≤ x < 1}
in_right_half_open_interval :: Ord a => (a, a) -> a -> Bool
in_right_half_open_interval :: forall a. Ord a => (a, a) -> a -> Bool
in_right_half_open_interval (a
p,a
q) a
n = a
p forall a. Ord a => a -> a -> Bool
<= a
n Bool -> Bool -> Bool
&& a
n forall a. Ord a => a -> a -> Bool
< a
q

-- | Calculate /n/th root of /x/.
--
-- > 12 `nth_root` 2 == 1.0594630943592953
nth_root :: (Floating a,Eq a) => a -> a -> a
nth_root :: forall a. (Floating a, Eq a) => a -> a -> a
nth_root a
n a
x =
    let f :: (a, a) -> (a, a)
f (a
_,a
x0) = (a
x0, ((a
n forall a. Num a => a -> a -> a
- a
1) forall a. Num a => a -> a -> a
* a
x0 forall a. Num a => a -> a -> a
+ a
x forall a. Fractional a => a -> a -> a
/ a
x0 forall a. Floating a => a -> a -> a
** (a
n forall a. Num a => a -> a -> a
- a
1)) forall a. Fractional a => a -> a -> a
/ a
n)
        eq :: (a, a) -> Bool
eq = forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry forall a. Eq a => a -> a -> Bool
(==)
    in forall a b. (a, b) -> a
fst (forall a. (a -> Bool) -> (a -> a) -> a -> a
until (a, a) -> Bool
eq forall {a}. (a, a) -> (a, a)
f (a
x, a
xforall a. Fractional a => a -> a -> a
/a
n))

-- | Arithmetic mean (average) of a list.
--
-- > map arithmetic_mean [[-3..3],[0..5],[1..5],[3,5,7],[7,7],[3,9,10,11,12]] == [0,2.5,3,5,7,9]
arithmetic_mean :: Fractional a => [a] -> a
arithmetic_mean :: forall a. Fractional a => [a] -> a
arithmetic_mean [a]
x = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [a]
x forall a. Fractional a => a -> a -> a
/ forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
x)

-- | Numerically stable mean
--
-- > map ns_mean [[-3..3],[0..5],[1..5],[3,5,7],[7,7],[3,9,10,11,12]] == [0,2.5,3,5,7,9]
ns_mean :: Floating a => [a] -> a
ns_mean :: forall a. Floating a => [a] -> a
ns_mean =
    let f :: (b, b) -> b -> (b, b)
f (b
m,b
n) b
x = (b
m forall a. Num a => a -> a -> a
+ (b
x forall a. Num a => a -> a -> a
- b
m) forall a. Fractional a => a -> a -> a
/ (b
n forall a. Num a => a -> a -> a
+ b
1),b
n forall a. Num a => a -> a -> a
+ b
1)
    in forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' forall {b}. Fractional b => (b, b) -> b -> (b, b)
f (a
0,a
0)

-- | Square of /n/.
--
-- > square 5 == 25
square :: Num a => a -> a
square :: forall a. Num a => a -> a
square a
n = a
n forall a. Num a => a -> a -> a
* a
n

-- | The totient function phi(n), also called Euler's totient function.
--
-- > import Sound.SC3.Plot {- hsc3-plot -}
-- > plot_p1_stp [map totient [1::Int .. 100]]
totient :: Integral i => i -> i
totient :: forall i. Integral i => i -> i
totient i
n = forall i a. Num i => [a] -> i
genericLength (forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Eq a => a -> a -> Bool
==i
1) (forall a b. (a -> b) -> [a] -> [b]
map (forall a. Integral a => a -> a -> a
gcd i
n) [i
1..i
n]))

{- | The /n/-th order Farey sequence.

> farey 1 == [0,                                                                                    1]
> farey 2 == [0,                                        1/2,                                        1]
> farey 3 == [0,                        1/3,            1/2,            2/3,                        1]
> farey 4 == [0,                1/4,    1/3,            1/2,            2/3,    3/4,                1]
> farey 5 == [0,            1/5,1/4,    1/3,    2/5,    1/2,    3/5,    2/3,    3/4,4/5,            1]
> farey 6 == [0,        1/6,1/5,1/4,    1/3,    2/5,    1/2,    3/5,    2/3,    3/4,4/5,5/6,        1]
> farey 7 == [0,    1/7,1/6,1/5,1/4,2/7,1/3,    2/5,3/7,1/2,4/7,3/5,    2/3,5/7,3/4,4/5,5/6,6/7,    1]
> farey 8 == [0,1/8,1/7,1/6,1/5,1/4,2/7,1/3,3/8,2/5,3/7,1/2,4/7,3/5,5/8,2/3,5/7,3/4,4/5,5/6,6/7,7/8,1]
-}
farey :: Integral i => i -> [Ratio i]
farey :: forall i. Integral i => i -> [Ratio i]
farey i
n =
  let step :: (i, i, i, i) -> Maybe (Ratio i, (i, i, i, i))
step (i
a,i
b,i
c,i
d) =
        if i
c forall a. Ord a => a -> a -> Bool
> i
n
        then forall a. Maybe a
Nothing
        else let k :: i
k = (i
n forall a. Num a => a -> a -> a
+ i
b) forall a. Integral a => a -> a -> a
`quot` i
d in forall a. a -> Maybe a
Just (i
c forall a. Integral a => a -> a -> Ratio a
% i
d, (i
c,i
d,i
k forall a. Num a => a -> a -> a
* i
c forall a. Num a => a -> a -> a
- i
a,i
k forall a. Num a => a -> a -> a
* i
d forall a. Num a => a -> a -> a
- i
b))
  in Ratio i
0 forall a. a -> [a] -> [a]
: forall b a. (b -> Maybe (a, b)) -> b -> [a]
unfoldr (i, i, i, i) -> Maybe (Ratio i, (i, i, i, i))
step (i
0,i
1,i
1,i
n)

-- | The length of the /n/-th order Farey sequence.
--
-- > map farey_length [1 .. 12] == [2,3,5,7,11,13,19,23,29,33,43,47]
-- > map (length . farey) [1 .. 12] == map farey_length [1 .. 12]
farey_length :: Integral i => i -> i
farey_length :: forall i. Integral i => i -> i
farey_length i
n = if i
n forall a. Eq a => a -> a -> Bool
== i
0 then i
1 else forall i. Integral i => i -> i
farey_length (i
n forall a. Num a => a -> a -> a
- i
1) forall a. Num a => a -> a -> a
+ forall i. Integral i => i -> i
totient i
n

-- | Function to generate the Stern-Brocot tree from an initial row.
--   '%' normalises so 1/0 cannot be written as a 'Rational', hence (n,d).
stern_brocot_tree_f :: Num n => [(n,n)] -> [[(n,n)]]
stern_brocot_tree_f :: forall n. Num n => [(n, n)] -> [[(n, n)]]
stern_brocot_tree_f =
   let med_f :: (a, b) -> (a, b) -> (a, b)
med_f (a
n1,b
d1) (a
n2,b
d2) = (a
n1 forall a. Num a => a -> a -> a
+ a
n2,b
d1 forall a. Num a => a -> a -> a
+ b
d2)
       f :: [(a, b)] -> [(a, b)]
f [(a, b)]
x = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (forall a. [[a]] -> [[a]]
transpose [[(a, b)]
x, forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith forall {a} {b}. (Num a, Num b) => (a, b) -> (a, b) -> (a, b)
med_f [(a, b)]
x (forall a. [a] -> [a]
tail [(a, b)]
x)])
   in forall a. (a -> a) -> a -> [a]
iterate forall {a} {b}. (Num a, Num b) => [(a, b)] -> [(a, b)]
f

{- | The Stern-Brocot tree from (0/1,1/0).

> let t = stern_brocot_tree
> t !! 0 == [(0,1),(1,0)]
> t !! 1 == [(0,1),(1,1),(1,0)]
> t !! 2 == [(0,1),(1,2),(1,1),(2,1),(1,0)]
> t !! 3 == [(0,1),(1,3),(1,2),(2,3),(1,1),(3,2),(2,1),(3,1),(1,0)]

> map length (take 12 stern_brocot_tree) == [2,3,5,9,17,33,65,129,257,513,1025,2049] -- A000051
-}
stern_brocot_tree :: Num n => [[(n,n)]]
stern_brocot_tree :: forall n. Num n => [[(n, n)]]
stern_brocot_tree = forall n. Num n => [(n, n)] -> [[(n, n)]]
stern_brocot_tree_f [(n
0,n
1),(n
1,n
0)]

-- | Left-hand (rational) side of the the Stern-Brocot tree, ie, from (0/1,1/1).
stern_brocot_tree_lhs :: Num n => [[(n,n)]]
stern_brocot_tree_lhs :: forall n. Num n => [[(n, n)]]
stern_brocot_tree_lhs = forall n. Num n => [(n, n)] -> [[(n, n)]]
stern_brocot_tree_f [(n
0,n
1),(n
1,n
1)]

{- | 'stern_brocot_tree_f' as 'Ratio's, for finite subsets.

> let t = stern_brocot_tree_f_r [0,1]
> t !! 1 == [0,1/2,1]
> t !! 2 == [0,1/3,1/2,2/3,1]
> t !! 3 == [0,1/4,1/3,2/5,1/2,3/5,2/3,3/4,1]
> t !! 4 == [0,1/5,1/4,2/7,1/3,3/8,2/5,3/7,1/2,4/7,3/5,5/8,2/3,5/7,3/4,4/5,1]
-}
stern_brocot_tree_f_r :: Integral n => [Ratio n] -> [[Ratio n]]
stern_brocot_tree_f_r :: forall n. Integral n => [Ratio n] -> [[Ratio n]]
stern_brocot_tree_f_r = forall a b. (a -> b) -> [a] -> [b]
map (forall a b. (a -> b) -> [a] -> [b]
map (forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry forall a. Integral a => a -> a -> Ratio a
(%))) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall n. Num n => [(n, n)] -> [[(n, n)]]
stern_brocot_tree_f forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall t. Integral t => Ratio t -> (t, t)
rational_nd

{- | Outer product of vectors represented as lists, c.f. liftM2

> outer_product (*) [2..5] [2..5] == [[4,6,8,10],[6,9,12,15],[8,12,16,20],[10,15,20,25]]
> liftM2 (*) [2..5] [2..5] == [4,6,8,10,6,9,12,15,8,12,16,20,10,15,20,25]
-}
outer_product :: (a -> b -> c) -> [a] -> [b] -> [[c]]
outer_product :: forall a b c. (a -> b -> c) -> [a] -> [b] -> [[c]]
outer_product a -> b -> c
f [a]
xs [b]
ys = forall a b. (a -> b) -> [a] -> [b]
map (forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a b. (a -> b) -> [a] -> [b]
map [b]
ys forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> b -> c
f) [a]
xs