packages feed

egison-tutorial-4.1.3: Main.hs

module Main where

import           Control.Exception                (AsyncException (..))
import           Control.Monad.Catch              (catch)
import           Control.Monad.Except
import           Control.Monad.Reader

import           Data.Version
import           Data.List
import           Text.Regex.Posix

import           System.Environment
import           System.Directory           (getHomeDirectory)
import           System.FilePath            ((</>))
import           System.Console.Haskeline
import           System.Console.GetOpt
import           System.Exit                (ExitCode (..), exitWith)

import           Language.Egison
import qualified Language.Egison.CmdOptions as ET
import           Language.Egison.Completion  (completeEgison)
import qualified Language.Egison.Parser.NonS as Parser
import qualified Paths_egison_tutorial       as P

main :: IO ()
main = do args <- getArgs
          let (actions, _, _) = getOpt Permute tOptions args
          let tOpts = foldl (flip id) defaultEgisonTutorialOpts actions
          runWithEgisonTutorialOpts tOpts

runWithEgisonTutorialOpts :: EgisonTutorialOpts -> IO ()
runWithEgisonTutorialOpts EgisonTutorialOpts{ tOptShowSections = True } = putStrLn $ show tutorial
runWithEgisonTutorialOpts EgisonTutorialOpts{ tOptSection = Just sn, tOptSubSection = Just ssn } = do
  let sn' = (read sn) :: Int
  let ssn' = (read ssn) :: Int
  let ret = case tutorial of
              Tutorial ss ->
                if 0 < sn' && sn' <= length ss
                  then case nth sn' ss of
                         Section _ cs ->
                           if 0 < ssn' && ssn' <= length cs
                             then showContent $ nth ssn' cs
                             else "error: content out of range"
                  else "error: section out of range"
  putStrLn ret
runWithEgisonTutorialOpts EgisonTutorialOpts{ tOptShowHelp = True } = printHelp
runWithEgisonTutorialOpts EgisonTutorialOpts{ tOptShowVersion = True } = printVersionNumber
runWithEgisonTutorialOpts EgisonTutorialOpts{ tOptPrompt = prompt } = evalRuntimeT ET.defaultOption { optPrompt = prompt } run

run :: RuntimeM ()
run = do
  opts <- ask
  coreEnv <- initialEnv
  mEnv <- fromEvalT $ evalTopExprs coreEnv $ map Load (optLoadLibs opts) ++ map LoadFile (optLoadFiles opts)
  case mEnv of
    Left err  -> liftIO $ print err
    Right env -> repl env

data EgisonTutorialOpts = EgisonTutorialOpts {
    tOptShowVersion :: Bool,
    tOptShowHelp :: Bool,
    tOptPrompt :: String,
    tOptShowSections :: Bool,
    tOptSection :: Maybe String,
    tOptSubSection :: Maybe String
    }

defaultEgisonTutorialOpts :: EgisonTutorialOpts
defaultEgisonTutorialOpts = EgisonTutorialOpts {
    tOptShowVersion = False,
    tOptShowHelp = False,
    tOptPrompt = "> ",
    tOptShowSections = False,
    tOptSection = Nothing,
    tOptSubSection = Nothing
    }

tOptions :: [OptDescr (EgisonTutorialOpts -> EgisonTutorialOpts)]
tOptions = [
  Option ['v', 'V'] ["version"]
    (NoArg (\tOpts -> tOpts {tOptShowVersion = True}))
    "show version number",
  Option ['h', '?'] ["help"]
    (NoArg (\tOpts -> tOpts {tOptShowHelp = True}))
    "show usage information",
  Option ['p'] ["prompt"]
    (ReqArg (\prompt tOpts -> tOpts {tOptPrompt = prompt})
            "String")
    "set prompt string",
  Option ['l'] ["list"]
    (NoArg (\tOpts -> tOpts {tOptShowSections = True}))
    "show section list",
  Option ['s'] ["section"]
    (ReqArg (\sn tOpts -> tOpts {tOptSection = Just sn})
            "String")
    "set section number",
  Option ['c'] ["subsection"]
    (ReqArg (\ssn tOpts -> tOpts {tOptSubSection = Just ssn})
            "String")
    "set subsection number"
  ]

printHelp :: IO ()
printHelp = do
  putStrLn "Usage: egison-tutorial [options]"
  putStrLn ""
  putStrLn "EgisonTutorialOpts:"
  putStrLn "  --help                Display this information"
  putStrLn "  --version             Display egison version information"
  putStrLn "  --prompt string       Set prompt of the interpreter"
  putStrLn ""
  exitWith ExitSuccess

printVersionNumber :: IO ()
printVersionNumber = do
  putStrLn $ showVersion P.version
  exitWith ExitSuccess

showBanner :: IO ()
showBanner = do
  putStrLn $ "Egison Tutorial Version " ++ showVersion P.version
  putStrLn $ "Welcome to Egison Tutorial!"
  putStrLn $ "** Information **"
  putStrLn $ "We can use a \"Tab\" key to complete keywords on the interpreter."
  putStrLn $ "If we type a \"Tab\" key after a closed parenthesis, the next closed parenthesis will be completed."
  putStrLn $ "*****************"

showFinishMessage :: IO ()
showFinishMessage = do
  putStrLn $ "You have finished this section."
  putStrLn $ "Thank you!"

showByebyeMessage :: IO ()
showByebyeMessage = do
  putStrLn $ "Leaving Egison Tutorial.\nByebye."

yesOrNo :: String -> IO Bool
yesOrNo question = do
  input <- liftIO $ runInputT nonReplSettings $ getInputLine $ question ++ " (Y/n): "
  case input of
   Nothing -> return True
   (Just "") -> return True
   (Just "y") -> return True
   (Just "Y") -> return True
   (Just "n") -> return False
   (Just "N") -> return False
   _ -> yesOrNo question

nth :: Int -> [a] -> a
nth n = head . drop (n - 1)

selectSection :: Tutorial -> IO Section
selectSection tutorial@(Tutorial sections) = do
  putStrLn $ take 30 $ repeat '='
  putStrLn $ "List of sections in the tutorial."
  putStrLn $ show tutorial
  putStrLn $ take 30 $ repeat '='
  putStrLn $ "Choose a section to learn."
  n <- getNumber (length sections)
  return $ nth n sections

getNumber :: Int -> IO Int
getNumber n = do
  input <- liftIO $ runInputT nonReplSettings $ getInputLine $ "(1-" ++ show n  ++ "): "
  case input of
    (Just "1") -> return 1
    (Just "2") -> return 2
    (Just "3") -> return 3
    (Just "4") -> return 4
    (Just "5") -> return 5
    (Just "6") -> return 6
    (Just "7") -> return 7
    _ -> do
      putStrLn "Invalid input!"
      getNumber n

-- |Get Egison expression from the prompt. We can handle multiline input.
getEgisonExprOrNewLine :: InputT RuntimeM (Either Bool (String, TopExpr))
getEgisonExprOrNewLine = getEgisonExprOrNewLine' ""

getEgisonExprOrNewLine' :: String -> InputT RuntimeM (Either Bool (String, TopExpr))
getEgisonExprOrNewLine' prev = do
  opts <- lift ask
  mLine <- case prev of
             "" -> getInputLine $ optPrompt opts
             _  -> getInputLine $ replicate (length (optPrompt opts)) ' '
  case mLine of
    Nothing -> return $ Left False -- The user's input is 'Control-D'.
    Just [] -> return $ Left True  -- The user's input is 'Enter'.
    Just line -> do
      let input = prev ++ line
      parsedExpr <- lift $ Parser.parseTopExpr input
      case parsedExpr of
        Left err | show err =~ "unexpected end of input" ->
          getEgisonExprOrNewLine' (input ++ "\n")
        Left err -> do
          liftIO $ print err
          getEgisonExprOrNewLine
        Right topExpr -> return $ Right (input, topExpr)

replSettings :: MonadIO m => FilePath -> Env -> Settings m
replSettings home env = Settings
  { complete       = completeEgison env
  , historyFile    = Just (home </> ".egison_history")
  , autoAddHistory = True
  }

nonReplSettings :: MonadIO m => Settings m
nonReplSettings = Settings
  { complete       = noCompletion
  , historyFile    = Nothing
  , autoAddHistory = False
  }

repl :: Env -> RuntimeM ()
repl env = do
  section <- liftIO $ selectSection tutorial
  case section of
    Section _ cs -> repl' env cs True
 where
  repl' :: Env -> [Content] -> Bool -> RuntimeM ()
  repl' env [] _ = do
    repl env
  repl' env (content:contents) b = (do
    if b
      then liftIO $ putStrLn $ show content
      else return ()
    home <- liftIO $ getHomeDirectory
    input <- runInputT (replSettings home env) $ getEgisonExprOrNewLine
    case input of
      -- The user input 'Control-D'.
      Left False -> do
        b <- liftIO $ yesOrNo "Do you want to quit?"
        if b
          then return ()
          else do
            b <- liftIO $ yesOrNo "Do you want to proceed next?"
            if b
              then repl' env contents True
              else repl' env (content:contents) False
      -- The user input just 'Enter'.
      Left True -> do
        b <- liftIO $ yesOrNo "Do you want to proceed next?"
        if b
          then repl' env contents True
          else repl' env (content:contents) False
      Right (topExpr, _) -> do
        result <- fromEvalT (runTopExprStr env topExpr)
        case result of
          Left err -> do
            liftIO $ putStrLn $ show err
            repl' env (content:contents) False
          Right (Just output, env') -> liftIO (putStrLn output) >> repl' env' (content:contents) False
          Right (Nothing, env') -> repl' env' (content:contents) False)
    `catch`
    (\e -> case e of
             UserInterrupt -> liftIO (putStrLn "") >> repl' env (content:contents) False
             StackOverflow -> liftIO (putStrLn "Stack over flow!") >> repl' env (content:contents) False
             HeapOverflow -> liftIO (putStrLn "Heap over flow!") >> repl' env (content:contents) False
             _ -> liftIO (putStrLn "error!") >> repl' env (content:contents) False
     )

data Tutorial = Tutorial [Section]

-- |title and contents
data Section = Section String [Content]

-- |explanation, examples, and exercises
data Content = Content String [String] [String]

instance Show Tutorial where
  show = showTutorial

instance Show Section where
  show = showSection

instance Show Content where
  show = showContent

showTutorial :: Tutorial -> String
showTutorial (Tutorial sections) =
  let n = length sections in
  intercalate "\n" $ map (\(n, section) -> show n ++ ": " ++ show section) $ zip [1..n] sections

showSection :: Section -> String
showSection (Section title _) = title

showContent :: Content -> String
showContent (Content msg examples exercises) =
  "====================\n" ++
  msg ++ "\n" ++
  (case examples of
     [] -> ""
     _ -> "\nExamples:\n" ++ (intercalate "\n" (map (\example -> "  " ++ example) examples)) ++ "\n") ++
  (case exercises of
     [] -> ""
     _ -> "\nExercises:\n" ++ (intercalate "\n" (map (\exercise -> "  " ++ exercise) exercises)) ++ "\n") ++
  "===================="

tutorial :: Tutorial
tutorial = Tutorial
 [Section "Arithmetic"
   [
    Content "We can do arithmetic operations with \"+\", \"-\", \"*\", \"/\", and \"^\"."
     ["1 + 2", "30 - 15", "10 * 20", "20 / 5", "2 ^ 10"]
     [],
    Content "We support rational numbers."
     ["2 / 3 + 1 / 5", "4 / 8"]
     [],
    Content "We support floating-point numbers, too."
     ["10.2 + 1.3", "10.2 + 1"]
     [],
    Content "We can convert a rational number to a floating-point number using \"rtof\"."
     ["rtof (1 / 5)", "rtof (1 / 100)"]
     [],
    Content "We can handle lists of numbers.\nWe construct a list by enclosing its elements with \"[]\"."
     ["[]", "[10]", "[1, 2, 3, 4, 5]"]
     [],
    Content "Using the \"sum\" function, we can get the summation of the argument list."
     ["sum []", "sum [10]", "sum [1, 2, 3, 4, 5]"]
     [],
    Content "Using the \"take\" function, we can extract a head part of a list."
     ["take 3 [1, 2, 3, 4, 5]", "take 0 [1, 2, 3, 4, 5]"]
     [],
    Content "We can handle infinite lists.\nFor example, \"nats\" and \"primes\" are an infinite list that contains all natural numbers and prime numbers respectively.\nTry to extract a head part from them."
     ["take 10 nats", "take 30 nats", "take 10 primes", "take 30 primes"]
     ["What is the 100th prime number?"],
    Content "We can create functions using the \"lambda\" notation.\nFunctions are written like \"\\x -> ... \"."
     ["(\\x -> x + 2) 10", "(\\x -> x ^ 2) 10"]
     [],
    Content "The \"map\" function applies the first argument function to each element of the second argument list.\nThe \"map\" function is one of the most important function in functional programming."
     ["map (\\x -> x * 2) [1, 2, 3, 4, 5]", "map (\\x -> 1 / x) [1, 2, 3, 4, 5]"]
     ["Try to create a sequence of numbers \"[1, 1/2, 1/3, 1/4, ..., 1/100]\"."],
    Content "Try to calculate \"1 + (1/2)^2 + (1/3)^2 + (1/4)^2 + ... + (1/100)^2\".\nIn fact, \"1 + (1/2)^2 + (1/3)^2 + (1/4)^2 + ...\" converges to \"pi * pi / 6\".\nRemember that we can convert a rational number to a floating-point number with \"rtof\"."
     ["rtof (2 / 3)"]
     [],
    Content "This is the end of this section.\nPlease play freely or proceed to the next section.\nThank you for enjoying our tutorial!"
     []
     []
    ],
  Section "Basics of functional programming"
   [
    Content "We can bind a value to a variable using \":=\" (not \"=\")."
     ["def x := 10", "x", "def y := 1 + x", "y"]
     [],
    Content "We support recursive definitions.\nRecursive definitions enable us to define a list with infinitely many elements.\nThe \"::\" infix operator adds the first argument to the head of the second argument list."
     ["def ones := 1 :: ones", "take 100 ones", "def nats := 1 :: map (\\n -> n + 1) nats", "take 100 nats", "def odds := 1 :: map (\\n -> n + 2) odds", "take 100 odds"]
     ["Try to define the infinite list of even numbers like [2, 4, 6, 8, 10, ...]."],
    Content "Let's define functions and test them."
     ["def increment x := x + 1", "increment 10", "def avrage x y := (x + y) / 2", "average 10 20"]
     [],
    Content "We can change an infix operator to a prefix operator by enclosing the operator by \"()\".\nFor example, \"(+) 2 3\" is equivalent to \"2 + 3\"."
     ["(+) 2 3", "(/) 3 2"]
     [],
    Content "The \"foldl\" function gathers together all elements of the third argument list using the operator specified by the first argument.\nThe second argument is an initial value."
     ["foldl (+) 0 [1, 2, 3, 4, 5]", "foldl (*) 1 [1, 2, 3, 4, 5]", "def sum xs := foldl (+) 0 xs", "sum [1, 2, 3, 4, 5]"]
     ["Try to get the sum of from 1 to 100."],
    Content "We can compare numbers using functions, \"=\", \"<\", \"<=\", \">\", \">=\".\nThese functions return boolean values, \"True\" and \"False\".\nFunctions that return boolean values are called \"predicates\"."
     ["1 = 1", "1 < 1", "1 <= 1",  "1 > 1", "1 >= 1"]
     [],
    Content "Using the \"takeWhile\" function, we can get the prefix of the second argument list whose elements satisfy the predicate of the first argument.\n\"primes\" is a infinite list that contains all prime numbers."
     ["takeWhile (\\n -> n < 100) primes", "takeWhile (\\n -> n < 1000) primes"]
     [],
    Content "Using the \"filter\" function, we can extract all elements that satisfy the given predicate."
     ["take 100 (filter isEven nats)", "take 100 (filter isPrime nats)", "take 100 (filter (\\p -> (modulo p 4) = 1) primes)"]
     ["Try to enumerate the first 100 primes that are congruent to 3 modulo 4."],
    Content "We can create a tuple by enclosing objects by \"()\".\n\nNote that a tuple that consists of only one element is equal to that element itself."
     ["(1, 2)", "(1, 2, 3)", "(1)", "((1))"]
     [],
    Content "Using the \"zip\" function, we can combine two lists as follows."
     ["take 100 (zip nats nats)", "take 100 (zip primes primes)"]
     ["Try to generate the prime table as \"[(1, 2), (2, 3), (3, 5), (4, 7), (5, 11), ...]\"."],
    Content "Try to create a Fibonacci sequence \"[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...]\".\n\nHint:\n  Replace \"???\" in the following expression to a proper function.\n  def fibs := 1 :: 1 :: map ??? (zip fibs (tail fibs))"
     []
     [],
    Content "This is the end of this section.\nPlease play freely or proceed to the next section.\nThank you for enjoying our tutorial!"
     []
     []
    ],
  Section "Basics of pattern matching"
   [
    Content "Let's try pattern matching for a list.\nThe \"join\" pattern (++) divides a list into two lists.\nNote that the matchAll expression enumerates all the decompositions."
     ["matchAll [1, 2, 3]       as list integer with $hs ++ $ts -> (hs, ts)",
      "matchAll [1, 2, 3, 4, 5] as list integer with $hs ++ $ts -> (hs, ts)"]
     [],
    Content "Try another pattern constructor \"cons\" (::).\nThe \"cons\" pattern (::) divides a list into the head element and the rest.\n"
     ["matchAll [1, 2, 3]       as list integer with $x :: $xs -> (x ,xs)",
      "matchAll [1, 2, 3, 4, 5] as list integer with $x :: $xs -> (x, xs)"]
     [],
    Content "\"_\" is a wildcard and matches with any objects."
     ["matchAll [1, 2, 3]       as list integer with $x :: _ -> x",
      "matchAll [1, 2, 3, 4, 5] as list integer with $hs ++ _ -> hs"]
     [],
    Content "We can write non-linear patterns.\nA non-linear pattern is a pattern that allows multiple occurrences of the same variables in a pattern.\nA pattern that begins with \"#\" matches the target when it is equal with the evaluation result of the expression after \"#\"."
     ["matchAll [1, 1, 2, 3, 3, 2] as list integer with _ ++ $x :: #x :: _ -> x",
      "matchAll [1, 1, 2, 3, 3, 2] as list integer with _ ++ $x :: #(x + 1) :: _ -> x"]
     [],
    Content "Egison can handle pattern matching with infinitely many results.\nFor example, we can enumerate twin primes using pattern matching as follows."
     ["take 10 (matchAll primes as list integer with _ ++ $p :: #(p + 2) :: _ -> (p, p + 2))"]
     ["What is the 100th twin prime?"],
    Content "Try to enumerate the first 10 prime pairs whose form is (p, p + 6) like \"[(5, 11), (7, 13), (11, 17), (13, 19), (17, 23), ...]\"."
     []
     [],
    Content "A pattern that begins with \"!\" is called not-pattern.\nA not-pattern matches when the content of the not-pattern does not match the target."
     ["matchAll [1, 1, 2, 3, 3, 2] as list integer with _ ++ $x :: #x :: _ -> x",
      "matchAll [1, 1, 2, 3, 3, 2] as list integer with _ ++ $x :: !#x :: _ -> x"]
     [],
    Content "A pattern whose form is \"p1 & p2\" is called and-pattern.\nAn and-pattern is a pattern that matches the target if and only if both \"p1\" and \"p2\" matches.\nThe and-pattern in the following sample is used like an as-pattern."
     ["take 10 (matchAll primes as list integer with _ ++ $p :: (!#(p + 2) & $q) :: _ -> (p, q))"]
     [],
    Content "A pattern whose form is \"p1 | p2\" is called or-pattern.\nAn or-pattern matches with the target, if \"p1\" or \"p2\" matches the target.\nIn the following sample, we enumerate prime triplets."
     ["take 10 (matchAll primes as list integer with _ ++ $p :: ($m & (#(p + 2) | #(p + 4))) :: #(p + 6) :: _ -> (p, m, (p + 6)))"]
     ["What is the 20th prime triplet?"],
    Content "Try to enumerate the first 4 prime quadruples whose form is (p, p + 2, p + 6, p + 8) like \"[(5, 7, 11, 13), (11, 13, 17, 19), ...]\"."
     []
     [],
    Content "This is the end of this section.\nPlease play freely or proceed to the next section.\nThank you for enjoying our tutorial!"
     []
     []
    ],
  Section "Pattern matching for multisets and sets"
   [
    Content "We can pattern-match a list as a multiset or set.\nWe can change the interpretation of patterns by changing the matcher, the second argument of the matchAll expression.\nThe meaning of the cons pattern (::) is generalized to divide a collection into \"an\" element and the rest."
     ["matchAll [1, 2, 3] as list integer     with $x :: $xs -> (x, xs)",
      "matchAll [1, 2, 3] as multiset integer with $x :: $xs -> (x, xs)",
      "matchAll [1, 2, 3] as set integer      with $x :: $xs -> (x, xs)"]
     [],
    Content "Try another pattern constructor \"join\" (++).\nThe \"join\" pattern (++) divides a collection into two collections."
     ["matchAll [1, 2, 3, 4, 5] as list integer     with $xs ++ $ys -> (xs, ys)",
      "matchAll [1, 2, 3, 4, 5] as multiset integer with $xs ++ $ys -> (xs, ys)",
      "matchAll [1, 2, 3, 4, 5] as set integer      with $xs ++ $ys -> (xs, ys)"]
     [],
    Content "Try non-linear pattern matching for multiset."
     ["matchAll [1, 2, 1, 3, 2] as multiset integer with $x :: #x :: _ -> x",
      "matchAll [1, 2, 1, 3, 2] as multiset integer with $x :: #(x + 2) :: _ -> x",
      "matchAll [1, 2, 1, 3, 2] as multiset integer with $x :: !(#(x + 2) :: _) -> x"]
     [],
    Content "Pattern matching of Egison efficiently backtracks for non-linear patterns.\nFor example, all the following pattern-matching expressions are processed in O(n^2)."
     ["matchAll [1..30] as multiset integer with $x :: #x :: _ -> x",
      "matchAll [1..30] as multiset integer with $x :: #x :: #x :: _ -> x",
      "matchAll [1..30] as multiset integer with $x :: #x :: #x :: #x _ -> x"]
     [],
    Content "Egison is designed to enumerate all the infinitely many pattern-matching results.\nThe following samples enumerate all the pairs and triplets of natural numbers."
     ["matchAll nats as set integer with $x :: $y :: _ -> (x, y)",
      "matchAll nats as set integer with $x :: $y :: $z :: _ -> (x, y, z)"]
     [],
    Content "This is the end of this section.\nPlease play freely or proceed to the next section.\nThank you for enjoying our tutorial!"
     []
     []
    ],
  Section "Symbolic computation"
   [
    Content "Egison treats unbound variables as a symbol."
     ["x + 1",
      "x + x",
      "2 * x + y"]
     [],
    Content "Egison automatically expands an expression to the canonical form."
     ["(x + y) * (x + y)",
      "(x + y)^2",
      "(x + y)^3"]
     [],
    Content "Egison can handle complex numbers.\n\"i\" represents the imaginary unit."
     ["i * i",
      "(1 + i)^2",
      "(1 + i)^4"]
     [],
    Content "Egison can handle algebraic numbers such as \"sqrt 2\" and \"sqrt 3\"."
     ["sqrt 12",
      "sqrt 2 * sqrt 2",
      "sqrt 2 * sqrt 3",
      "(rt 3 2)^3"]
     [],
    Content "Egison can handle the trigonometric functions such as \"cos θ\" and \"sin θ\"."
     ["(cos θ)^2 + (sin θ)^2"]
     [],
    Content "Here are several samples for symbolic computation in Egison.\nPlease visit the link!\nhttps://www.egison.org/math/"
     [
      ]
     [],
    Content "This is the end of this section.\nPlease play freely or proceed to the next section.\nThank you for enjoying our tutorial!"
     []
     []
    ],
  Section "Differential geometry: tensor analysis"
   [
    Content "We can handle vectors.\nWe construct vectors with \"[| |]\"."
     ["[| 1, 2, 3 |]",
      "[| 1, 2, 3 |] + [| 1, 2, 3 |]"
      ]
     [],
    Content "We can append a symbolical index to vectors."
     ["[| 1, 2, 3 |]_i + [| 1, 2, 3 |]_i",
      "[| 1, 2, 3 |]_i + [| 1, 2, 3 |]_j"
      ]
     [],
    Content "The \".\" function is a function for multiplying tensors."
     ["[| 1, 2, 3 |]_i . [| 1, 2, 3 |]_i",
      "[| 1, 2, 3 |]_i . [| 1, 2, 3 |]_j"
      ]
     [],
    Content "We can handle both of superscripts (~) and subscripts(_).\nThe \".\" function supports Einstein summation notation."
     ["[| 1, 2, 3 |]~i . [| 1, 2, 3 |]_i"
      ]
     [],
    Content "Matrix is represented as a vector of vectors."
     ["[| [| 1, 2, 3 |], [| 10, 20, 30 |] |]"
      ]
     [],
    Content "Matrix multiplication is represented as follows using tensor index notation."
     ["[| [| a, b |], [| c, d |] |]~i_j . [| [| x, y |], [| z, w |] |]~j_k"
      ]
     [],
    Content "The function defined using scalar parameters (prepended by \"$\") are automatically mapped to each component of tensors."
     ["def min $x $y := if x < y then x else y",
      "min [| 1, 2, 3 |]_i [| 10, 20, 30 |]_i",
      "min [| 1, 2, 3 |]_i [| 10, 20, 30 |]_j"
      ]
     [],
    Content "The function defined using tensor parameters (prepended by \"%\") treats a tensor as a whole.\nIf we prepend "
     ["def det2 %X := X_1_1 * X_2_2 -  X_1_2 * X_2_1",
      "det2 [| [| 2, 1 |], [| 1, 2 |] |]",
      "det2 [| [| a, b |], [| c, d |] |]"
      ]
     [],
    Content "Here are several samples of tensor analysis in programming.\nPlease visit the link!\nhttps://www.egison.org/math/"
     [
      ]
     [],
    Content "This is the end of this section.\nPlease play freely or proceed to the next section.\nThank you for enjoying our tutorial!"
     []
     []
    ],
  Section "Differential geometry: differential forms"
   [
    Content "By default, the same indices are completed to each tensor of the arguments."
     ["[| 1, 2, 3 |] + [| 1, 2, 3 |] -- => [| 1, 2, 3 |]_t1 + [| 1, 2, 3 |]_t1"
      ]
     [],
    Content "When “!” is prepended to the function application, the different indices are completed to each tensor of the arguments."
     ["[| 1, 2, 3 |] !+ [| 1, 2, 3 |] -- => [| 1, 2, 3 |]_t1 + [| 1, 2, 3 |]_t2"
      ]
     [],
    Content "1-forms on Euclid space and Wedge product are represented as follows.\n\"!\" is effectively used in the definition of Wedge product."
     ["def dx := [| 1, 0, 0 |]",
      "def dy := [| 0, 1, 0 |]",
      "def dz := [| 0, 0, 1 |]",
      "def wedge %A %B := A !. B",
      "wedge dx dy"
      ]
     [],
    Content "The \"dfNormalize\" function converts a differential form to the antisymmetric tensor."
     ["wedge dx dy",
      "dfNormalize (wedge dx dy)"
      ]
     [],
    Content "Exterior derivative is defined as follows.\n\"!\" is effectively used in the definition of exterior derivative."
     ["def params := [| x, y, z |]",
      "def d %A := !((flip ∂/∂) params A)",
      "d (f x y z)",
      "d (d (f x y z))",
      "dfNormalize (d (d (f x y z)))"
      ]
     [],
    Content "Here are several samples for representing differential forms in programming.\nPlease visit the link!\nhttps://www.egison.org/math/"
     [
      ]
     [],
    Content "This is the end of our tutorial.\nThank you for enjoying our tutorial!\nPlease check our paper, manual and code for further reference!"
     []
     []
    ]

  ]