{-# LANGUAGE GeneralizedNewtypeDeriving, ViewPatterns, GADTs, OverloadedStrings #-}
{-
Copyright (C) 2016-2023 John MacFarlane <jgm@berkeley.edu>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

-}

module Text.TeXMath.Writers.Eqn (writeEqn) where

import Data.List (transpose)
import Data.Char (isAscii, ord)
import qualified Data.Text as T
import Text.Printf (printf)
import Text.TeXMath.Types
import qualified Text.TeXMath.Shared as S
import Data.Generics (everywhere, mkT)
import Data.Ratio ((%))
import Data.Text (Text)

-- import Debug.Trace
-- tr' x = trace (show x) x

-- | Transforms an expression tree to equivalent Eqn
writeEqn :: DisplayType -> [Exp] -> T.Text
writeEqn :: DisplayType -> [Exp] -> Text
writeEqn DisplayType
dt [Exp]
exprs =
  [Text] -> Text
T.unwords ([Text] -> Text) -> [Text] -> Text
forall a b. (a -> b) -> a -> b
$ (Exp -> Text) -> [Exp] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Exp -> Text
writeExp ([Exp] -> [Text]) -> [Exp] -> [Text]
forall a b. (a -> b) -> a -> b
$ (forall a. Data a => a -> a) -> forall a. Data a => a -> a
everywhere ((Exp -> Exp) -> a -> a
forall a b. (Typeable a, Typeable b) => (b -> b) -> a -> a
mkT ((Exp -> Exp) -> a -> a) -> (Exp -> Exp) -> a -> a
forall a b. (a -> b) -> a -> b
$ DisplayType -> Exp -> Exp
S.handleDownup DisplayType
dt) [Exp]
exprs

-- like writeExp but inserts {} if contents contain a space
writeExp' :: Exp -> T.Text
writeExp' :: Exp -> Text
writeExp' e :: Exp
e@(EGrouped [Exp]
_) = Exp -> Text
writeExp Exp
e
writeExp' Exp
e = if (Char -> Bool) -> Text -> Bool
T.any (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ') Text
s
                 then Text -> Text
asgroup Text
s
                 else Text
s
               where s :: Text
s = Exp -> Text
writeExp Exp
e

writeExps :: [Exp] -> T.Text
writeExps :: [Exp] -> Text
writeExps = Text -> [Text] -> Text
T.intercalate Text
" " ([Text] -> Text) -> ([Exp] -> [Text]) -> [Exp] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Exp -> Text) -> [Exp] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Exp -> Text
writeExp

asgroup :: Text -> Text
asgroup :: Text -> Text
asgroup Text
"" = Text
"{\"\"}"  -- see #198
asgroup Text
t = Text
"{" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
t Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"}"

writeExp :: Exp -> T.Text
writeExp :: Exp -> Text
writeExp (ENumber Text
s) = Text
s
writeExp (EGrouped [Exp]
es) = Text -> Text
asgroup (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ [Exp] -> Text
writeExps [Exp]
es
writeExp (EDelimited Text
open Text
close [InEDelimited]
es) =
  Text
"left " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
forall {a}. (Eq a, IsString a) => a -> a
mbQuote Text
open Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> [Text] -> Text
T.intercalate Text
" " ((InEDelimited -> Text) -> [InEDelimited] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map InEDelimited -> Text
fromDelimited [InEDelimited]
es) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
  Text
" right " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
forall {a}. (Eq a, IsString a) => a -> a
mbQuote Text
close
  where fromDelimited :: InEDelimited -> Text
fromDelimited (Left Text
e)  = Text
"\"" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
e Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\""
        fromDelimited (Right Exp
e) = Exp -> Text
writeExp Exp
e
        mbQuote :: a -> a
mbQuote a
"" = a
"\"\""
        mbQuote a
s  = a
s
writeExp (EMathOperator Text
s) =
  if Text
s Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"sin", Text
"cos", Text
"tan", Text
"sinh", Text
"cosh",
               Text
"tanh", Text
"arc", Text
"max", Text
"min", Text
"lim",
               Text
"log", Text
"ln", Text
"exp"]
     then Text
s
     else Text
"\"" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
s Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\""
writeExp (ESymbol TeXSymbolType
Ord (Text -> String
T.unpack -> [Char
c]))  -- do not render "invisible operators"
  | Char
c Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'\x2061'..Char
'\x2064'] = Text
"" -- see 3.2.5.5 of mathml spec
writeExp (EIdentifier Text
s) = Exp -> Text
writeExp (TeXSymbolType -> Text -> Exp
ESymbol TeXSymbolType
Ord Text
s)
writeExp (ESymbol TeXSymbolType
t Text
s) =
  case Text
s of
    Text
"{"     -> Text
"\\[lC]"
    Text
"}"     -> Text
"\\[rC]"
    Text
"\8722" -> Text
"-"  -- minus sign, see #200
    Text
"\8943" -> Text
"cdots" -- centered ellipses, see #200
    Text
"\8805" -> Text
">="
    Text
"\8804" -> Text
"<="
    Text
"\8801" -> Text
"=="
    Text
"\8800" -> Text
"!="
    Text
"\177"  -> Text
"+-"
    Text
"\8594" -> Text
"->"
    Text
"\8592" -> Text
"<-"
    Text
"\8810" -> Text
"<<"
    Text
"\8811" -> Text
">>"
    Text
"\8734" -> Text
"inf"
    Text
"\8706" -> Text
"partial"
    Text
"\189"  -> Text
"half"
    Text
"\8242" -> Text
"prime"
    Text
"\8776" -> Text
"approx"
    Text
"\183"  -> Text
"cdot"
    Text
"\215"  -> Text
"times"
    Text
"\8711" -> Text
"grad"
    Text
"\8230" -> Text
"..."
    Text
"\8721" -> Text
"sum"
    Text
"\8747" -> Text
"int"
    Text
"\8719" -> Text
"prod"
    Text
"\8898" -> Text
"union"
    Text
"\8899" -> Text
"inter"
    Text
"\945" -> Text
"alpha"
    Text
"\946" -> Text
"beta"
    Text
"\967" -> Text
"chi"
    Text
"\948" -> Text
"delta"
    Text
"\916" -> Text
"DELTA"
    Text
"\1013" -> Text
"epsilon"
    Text
"\951" -> Text
"eta"
    Text
"\947" -> Text
"gamma"
    Text
"\915" -> Text
"GAMMA"
    Text
"\953" -> Text
"iota"
    Text
"\954" -> Text
"kappa"
    Text
"\955" -> Text
"lambda"
    Text
"\923" -> Text
"LAMBDA"
    Text
"\956" -> Text
"mu"
    Text
"\957" -> Text
"nu"
    Text
"\969" -> Text
"omega"
    Text
"\937" -> Text
"OMEGA"
    Text
"\981" -> Text
"phi"
    Text
"\966" -> Text
"varphi"
    Text
"\934" -> Text
"PHI"
    Text
"\960" -> Text
"pi"
    Text
"\928" -> Text
"PI"
    Text
"\968" -> Text
"psi"
    Text
"\936" -> Text
"PSI"
    Text
"\961" -> Text
"rho"
    Text
"\963" -> Text
"sigma"
    Text
"\931" -> Text
"SIGMA"
    Text
"\964" -> Text
"tau"
    Text
"\952" -> Text
"theta"
    Text
"\920" -> Text
"THETA"
    Text
"\965" -> Text
"upsilon"
    Text
"\933" -> Text
"UPSILON"
    Text
"\958" -> Text
"xi"
    Text
"\926" -> Text
"XI"
    Text
"\950" -> Text
"zeta"
    Text
_      -> let s' :: Text
s' = if (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
isAscii Text
s
                          then Text
s
                          else Text
"\\[" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Text] -> Text
T.unwords ((Char -> Text) -> String -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Char -> Text
toUchar (String -> [Text]) -> String -> [Text]
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
s) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"]"
                  toUchar :: Char -> Text
toUchar Char
c = String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String -> Int -> String
forall r. PrintfType r => String -> r
printf String
"u%04X" (Char -> Int
ord Char
c)
              in  if Text -> Int
T.length Text
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
1 Bool -> Bool -> Bool
&& (TeXSymbolType
t TeXSymbolType -> TeXSymbolType -> Bool
forall a. Eq a => a -> a -> Bool
== TeXSymbolType
Rel Bool -> Bool -> Bool
|| TeXSymbolType
t TeXSymbolType -> TeXSymbolType -> Bool
forall a. Eq a => a -> a -> Bool
== TeXSymbolType
Bin Bool -> Bool -> Bool
|| TeXSymbolType
t TeXSymbolType -> TeXSymbolType -> Bool
forall a. Eq a => a -> a -> Bool
== TeXSymbolType
Op)
                     then Text
"roman{\"" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                          (if TeXSymbolType
t TeXSymbolType -> TeXSymbolType -> Bool
forall a. Eq a => a -> a -> Bool
== TeXSymbolType
Rel Bool -> Bool -> Bool
|| TeXSymbolType
t TeXSymbolType -> TeXSymbolType -> Bool
forall a. Eq a => a -> a -> Bool
== TeXSymbolType
Bin
                              then Text
" "
                              else Text
"") Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                          Text
s' Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                          (if TeXSymbolType
t TeXSymbolType -> TeXSymbolType -> Bool
forall a. Eq a => a -> a -> Bool
== TeXSymbolType
Rel Bool -> Bool -> Bool
|| TeXSymbolType
t TeXSymbolType -> TeXSymbolType -> Bool
forall a. Eq a => a -> a -> Bool
== TeXSymbolType
Bin Bool -> Bool -> Bool
|| TeXSymbolType
t TeXSymbolType -> TeXSymbolType -> Bool
forall a. Eq a => a -> a -> Bool
== TeXSymbolType
Op
                              then Text
" "
                              else Text
"") Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                          Text
"\"}"
                     else Text
s'

writeExp (ESpace Rational
d) =
  case Rational
d of
      Rational
_ | Rational
d Rational -> Rational -> Bool
forall a. Ord a => a -> a -> Bool
> Rational
0 Bool -> Bool -> Bool
&& Rational
d Rational -> Rational -> Bool
forall a. Ord a => a -> a -> Bool
< (Integer
2 Integer -> Integer -> Rational
forall a. Integral a => a -> a -> Ratio a
% Integer
9) -> Text
"^"
        | Rational
d Rational -> Rational -> Bool
forall a. Ord a => a -> a -> Bool
>= (Integer
2 Integer -> Integer -> Rational
forall a. Integral a => a -> a -> Ratio a
% Integer
9) Bool -> Bool -> Bool
&& Rational
d Rational -> Rational -> Bool
forall a. Ord a => a -> a -> Bool
< (Integer
3 Integer -> Integer -> Rational
forall a. Integral a => a -> a -> Ratio a
% Integer
9) -> Text
"~"
        | Rational
d Rational -> Rational -> Bool
forall a. Ord a => a -> a -> Bool
< Rational
0     -> Text
"back " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow (Rational -> Int
forall b. Integral b => Rational -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor (-Rational
1 Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* Rational
d Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* Rational
100) :: Int)
        | Bool
otherwise -> Text
"fwd " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow (Rational -> Int
forall b. Integral b => Rational -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor (Rational
d Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* Rational
100) :: Int)
writeExp (EFraction FractionType
fractype Exp
e1 Exp
e2) = Exp -> Text
writeExp' Exp
e1 Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
op Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e2
  where op :: Text
op = if FractionType
fractype FractionType -> FractionType -> Bool
forall a. Eq a => a -> a -> Bool
== FractionType
NoLineFrac
                then Text
" / "
                else Text
" over "
writeExp (ESub Exp
b Exp
e1) = Exp -> Text
writeExp' Exp
b Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" sub " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e1
writeExp (ESuper Exp
b Exp
e1) = Exp -> Text
writeExp' Exp
b Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" sup " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e1
writeExp (ESubsup Exp
b Exp
e1 Exp
e2) =
  Exp -> Text
writeExp' Exp
b Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" sub " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e1 Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" sup " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e2
writeExp (EOver Bool
_convertible Exp
b Exp
e1) =
  Exp -> Text
writeExp' Exp
b Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" to " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e1
writeExp (EUnder Bool
_convertible Exp
b Exp
e1) =
  Exp -> Text
writeExp' Exp
b Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" from " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e1
writeExp (EUnderover Bool
convertible Exp
b e1 :: Exp
e1@(ESymbol TeXSymbolType
Accent Text
_) Exp
e2) =
  Exp -> Text
writeExp (Bool -> Exp -> Exp -> Exp
EUnder Bool
convertible (Bool -> Exp -> Exp -> Exp
EOver Bool
False Exp
b Exp
e2) Exp
e1)
writeExp (EUnderover Bool
convertible Exp
b Exp
e1 e2 :: Exp
e2@(ESymbol TeXSymbolType
Accent Text
_)) =
  Exp -> Text
writeExp (Bool -> Exp -> Exp -> Exp
EOver Bool
convertible (Bool -> Exp -> Exp -> Exp
EUnder Bool
False Exp
b Exp
e1) Exp
e2)
writeExp (EUnderover Bool
_convertible Exp
b Exp
e1 Exp
e2) =
  Exp -> Text
writeExp' Exp
b Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" from " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e1 Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" to " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e2
writeExp (ESqrt Exp
e) = Text
"sqrt " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e
writeExp (ERoot Exp
i Exp
e) = Text
"\"\" sup " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
i Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" sqrt " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e
writeExp (EPhantom Exp
e) = Text
"hphantom " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Exp -> Text
writeExp' Exp
e
writeExp (EBoxed Exp
e) = Exp -> Text
writeExp Exp
e -- TODO: any way to do this?
writeExp (EScaled Rational
_size Exp
e) = Exp -> Text
writeExp Exp
e -- TODO: any way?
writeExp (EText TextType
ttype Text
s) =
  let quoted :: Text
quoted = Text
"\"" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
s Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\""
  in case TextType
ttype of
       TextType
TextNormal -> Text
"roman " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
quoted
       TextType
TextItalic -> Text
quoted
       TextType
TextBold   -> Text
"bold " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
quoted
       TextType
TextBoldItalic -> Text
"bold italic " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
quoted
       TextType
_   -> Text
quoted
writeExp (EStyled TextType
ttype [Exp]
es) =
  let contents :: Text
contents = Text -> Text
asgroup (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ [Exp] -> Text
writeExps [Exp]
es
  in case TextType
ttype of
       TextType
TextNormal -> Text
"roman " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
contents
       TextType
TextItalic -> Text
"italic " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
contents
       TextType
TextBold   -> Text
"bold " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
contents
       TextType
TextBoldItalic -> Text
"bold italic " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
contents
       TextType
_   -> Text
contents
writeExp (EArray [Alignment]
aligns [ArrayLine]
rows) =
  Text
"matrix{\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Text] -> Text
T.concat [Text]
cols Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"}"
  where cols :: [Text]
cols = (Alignment -> ArrayLine -> Text)
-> [Alignment] -> [ArrayLine] -> [Text]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Alignment -> ArrayLine -> Text
tocol [Alignment]
aligns ([ArrayLine] -> [ArrayLine]
forall a. [[a]] -> [[a]]
transpose [ArrayLine]
rows)
        tocol :: Alignment -> ArrayLine -> Text
tocol Alignment
al ArrayLine
cs =
          (case Alignment
al of
               Alignment
AlignLeft -> Text
"lcol"
               Alignment
AlignCenter -> Text
"ccol"
               Alignment
AlignRight -> Text
"rcol") Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
            Text
"{ " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> [Text] -> Text
T.intercalate Text
" above " (([Exp] -> Text) -> ArrayLine -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map [Exp] -> Text
tocell ArrayLine
cs) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" }\n"
        tocell :: [Exp] -> Text
tocell [Exp
e] = Exp -> Text
writeExp' Exp
e
        tocell [Exp]
es  = Exp -> Text
writeExp ([Exp] -> Exp
EGrouped [Exp]
es)

tshow :: Show a => a -> T.Text
tshow :: forall a. Show a => a -> Text
tshow = String -> Text
T.pack (String -> Text) -> (a -> String) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> String
forall a. Show a => a -> String
show