I'm learning Haskell and I needed to work with discrete cosine transform matrices, so I made a program that generates text (usable in WolframAlpha) containing the generated matrix.
The elements of the n-square matrix C are defined as:
\$[C]_{jk} = \gamma_jcos\dfrac{\pi(2k+1)j}{2n}\$ where \$\gamma_{j} = \begin{cases}\frac{1}{\sqrt{n}} & \text{j = 0} \\ \frac{2}{\sqrt{n}} & \text{otherwise} \end{cases}\$
I also wanted the program to simplify fractions and sqrt
, which is where most of the "dirtiness" came from. Is there any way to clean this up? It's simple enough, but I want to learn how to be concise.
import Ratio
-- Pretty-print a fraction
showFraction n d
| n == 0 = "0"
| d == 0 = error "Division by zero."
| n == 1 && d == 1 = "1"
| n == 1 = "1/" ++ show d
| d == 1 = show n
| otherwise = show n ++ "/" ++ show d
-- Pretty-print a fraction, reducing the fraction
showFractionReduced n d = let x = n % d in showFraction (numerator x) (denominator x)
-- Pretty-print a fraction, with an additional numerator factor
showFraction' n d n'
| n == 0 = "0"
| d == 0 = error "Division by zero."
| n == 1 && d == 1 = n'
| n == 1 = n' ++ "/" ++ show d
| d == 1 = show n ++ "*" ++ n'
| otherwise = show n ++ "*" ++ n' ++ "/" ++ show d
-- Pretty-print a fraction, reducing the fraction, with an additional numerator factor
showFractionReduced' n d = let x = n % d in showFraction' (numerator x) (denominator x)
-- Discrete cosine transform matrix element
dctElement j k n
| n <= 1 = "1"
| n == 2 && j > 0 = "cos(" ++ showFractionReduced' (j * (2 * k + 1)) (2 * n) "pi" ++ ")"
| j == 0 = "sqrt(" ++ showFractionReduced 1 n ++ ")"
| otherwise = "sqrt(" ++ showFractionReduced 2 n ++ ")*cos(" ++ showFractionReduced' (j * (2 * k + 1)) (2 * n) "pi" ++ ")"
-- Discrete cosine transform matrix row
dctRow j n = "{" ++ foldr1 (\acc x -> acc ++ "," ++ x) [dctElement j x n | x <- [0..n - 1]] ++ "}"
-- Discrete cosine transform matrix
dctMatrix n = "{" ++ foldr1 (\acc x -> acc ++ ",\n" ++ x) [dctRow x n | x <- [0..n - 1]] ++ "}"
-- Inverse discrete cosine transform matrix row
dctRowInverse j n = "{" ++ foldr1 (\acc x -> acc ++ "," ++ x) [dctElement x j n | x <- [0..n - 1]] ++ "}"
-- Inverse discrete cosine transform matrix
dctMatrixInverse n = "{" ++ foldr1 (\acc x -> acc ++ ",\n" ++ x) [dctRowInverse x n | x <- [0..n - 1]] ++ "}"
The string generated by dctMatrix 3
, for example, is:
{{sqrt(1/3),sqrt(1/3),sqrt(1/3)},
{sqrt(2/3)*cos(pi/6),sqrt(2/3)*cos(pi/2),sqrt(2/3)*cos(5*pi/6)},
{sqrt(2/3)*cos(pi/3),sqrt(2/3)*cos(pi),sqrt(2/3)*cos(5*pi/3)}}
while the string generated by dctMatrix 2
is:
{{sqrt(1/3),sqrt(1/3),sqrt(1/3)},
{sqrt(2/3)*cos(pi/6),sqrt(2/3)*cos(pi/2),sqrt(2/3)*cos(5*pi/6)},
{sqrt(2/3)*cos(pi/3),sqrt(2/3)*cos(pi),sqrt(2/3)*cos(5*pi/3)}}
(Notice the simplification of the fractions in the former, and the square root in the latter.)