{-# OPTIONS_GHC -fno-warn-unused-imports #-}
{-# OPTIONS_HADDOCK show-extensions #-}

-- | Module    :  Debug.SimpleExpr.Tutorial
-- Copyright   :  (C) 2023 Alexey Tochin
-- License     :  BSD3 (see the file LICENSE)
-- Maintainer  :  Alexey Tochin <Alexey.Tochin@gmail.com>
--
-- Tutorial, Quick start or Demo for 'simple-expr' package.
module Debug.SimpleExpr.Tutorial
  ( -- * Quick start
    -- $quick_start2

    -- * Expression simplification
    -- $expression_simplification

    -- * Visualisation
    -- $visualisation
  )
where

import Control.Concurrent (ThreadId)
import Data.Graph.DGraph (DGraph)
import Debug.SimpleExpr
import Debug.SimpleExpr.GraphUtils
import NumHask (sin, (**))
import Prelude (FilePath, IO, String)

-- $quick_start2 #simple_expr_tutorial_head#
--
-- >>> import Prelude (String)
-- >>> import Debug.SimpleExpr (variable, unaryFunc, binaryFunc)
-- >>> import NumHask (sin, (**))
--
-- Let us build an example symbolic expression for
--
-- \[
--   f(x) := \sin x^2
-- \]
--
-- It can be done as follows
--
-- >>> x = variable "x"
-- >>> sin (x ** 2)
-- sin(x^2)
--
-- where terms @x@ and @sin (x ** 2)@ have type 'SimpleExpr'.
-- It is just a syntactic tree where the role of leaves is played by
-- variables and numbers.
-- We used
-- 'variable'@ :: @'String'@ -> @'SimpleExpr'
-- to build the expression for variable @x@.
-- For the sine function we attracted a predefined term
-- 'sin'@ :: @'SimpleExpr'@ -> @'SimpleExpr'.
--
-- As well we can define a custom function using 'unaryFunc' and binary functoins using 'binaryFunc' as follows
--
-- >>> f = unaryFunc "f"
-- >>> (-*-) = binaryFunc "-*-"
-- >>> f x -*- f x
-- f(x)-*-f(x)
--
-- There is also a typeclass 'Expr' that includes `SimpleExpr` as well as it's tuples and lists.

-- $expression_simplification
-- >>> import Prelude (($))
-- >>> import Debug.SimpleExpr (variable, simplify)
-- >>> import NumHask ((+), (-), (*))
--
-- We can try to simplify an expressions with the aid of quite a primitive 'simplify' method
--
-- >>> x = variable "x"
-- >>> simplify $ (x + 0) * 1 - x * (3 - 2)
-- 0

-- $visualisation
-- >>> import Debug.SimpleExpr (variable, unaryFunc)
-- >>> import Debug.SimpleExpr.GraphUtils (plotExpr, plotDGraphPng, exprToGraph)
-- >>> import NumHask (exp, (*), (+), (-))
--
-- There is a built-in tool to visualize expression that attracts
-- [graphite](https://hackage.haskell.org/package/graphite)
-- package to transform expressions to
-- [graphs](https://hackage.haskell.org/package/graphs)
-- and
-- [graphviz](https://hackage.haskell.org/package/graphviz)
-- to render the images.
--
-- Consider first a simple composition for two functions @f@ and @g@
--
-- >>> x = variable "x"
-- >>> f = unaryFunc "f"
-- >>> g = unaryFunc "g"
-- >>> expr = g (f x)
-- >>> expr
-- g(f(x))
--
-- This symbolic expression can be plotted by
-- 'plotExpr'@ :: @'Expr'@ d => d -> @'IO' 'ThreadId'
-- like
--
-- @ plotExpr expr @
--
-- ![image description](doc/images/composition.png)
--
-- To save the image as a file use, for example,
--
-- 'plotDGraphPng'@ (@'exprToGraph'@ expr) pathToFile @,
--
-- where
--
-- 'exprToGraph'@ :: @'Expr'@ d => d -> @'DGraph' 'String'@ () @
--
-- transforms an expression to a graph
-- and
--
-- 'plotDGraphPng'@ :: @'DGraph'@ v e -> @'FilePath'@ -> @'IO' 'FilePath'.
--
-- plats the graph.
--
-- Consider now a more representative example
--
-- \[
--   e^{i k x} + e^{ - i k x}
-- \]
--
-- >>> :{
--   x, k, i, expr :: SimpleExpr
--   x = variable "x"
--   k = variable "k"
--   i = variable "i"
--   expr = exp (i * k * x) + exp (-(i * k * x))
-- :}
--
-- ![image description](doc/images/imaginary_expr_sum.png)