ProgTutorial/Package/Ind_Interface.thy
changeset 189 069d525f8f1d
parent 186 371e4375c994
child 212 ac01ddb285f6
equal deleted inserted replaced
188:8939b8fd8603 189:069d525f8f1d
       
     1 theory Ind_Interface
       
     2 imports "../Base" "../Parsing" Ind_Prelims Simple_Inductive_Package
       
     3 begin
       
     4 
       
     5 section {* Parsing and Typing the Specification *}
       
     6 
       
     7 text {* 
       
     8   To be able to write down the specification in Isabelle, we have to introduce
       
     9   a new command (see Section~\ref{sec:newcommand}).  As the keyword for the
       
    10   new command we chose \simpleinductive{}. In the package we want to support
       
    11   some ``advanced'' features: First, we want that the package can cope with
       
    12   specifications inside locales. For example it should be possible to declare
       
    13 *}
       
    14 
       
    15 locale rel =
       
    16   fixes R :: "'a \<Rightarrow> 'a \<Rightarrow> bool"
       
    17 
       
    18 text {*
       
    19   and then define the transitive closure and the accessible part as follows:
       
    20 *}
       
    21 
       
    22 
       
    23 simple_inductive (in rel) 
       
    24   trcl' 
       
    25 where
       
    26   base: "trcl' x x"
       
    27 | step: "trcl' x y \<Longrightarrow> R y z \<Longrightarrow> trcl' x z"
       
    28 
       
    29 simple_inductive (in rel) 
       
    30   accpart'
       
    31 where
       
    32   accpartI: "(\<And>y. R y x \<Longrightarrow> accpart' y) \<Longrightarrow> accpart' x"
       
    33 
       
    34 text {* 
       
    35   Second, we want that the user can specify fixed parameters.
       
    36   Remember in the previous section we stated that the user can give the 
       
    37   specification for the transitive closure of a relation @{text R} as 
       
    38 *}
       
    39 
       
    40 simple_inductive
       
    41   trcl\<iota>\<iota> :: "('a \<Rightarrow> 'a \<Rightarrow> bool) \<Rightarrow> 'a \<Rightarrow> 'a \<Rightarrow> bool"
       
    42 where
       
    43   base: "trcl\<iota>\<iota> R x x"
       
    44 | step: "trcl\<iota>\<iota> R x y \<Longrightarrow> R y z \<Longrightarrow> trcl\<iota>\<iota> R x z"
       
    45 
       
    46 text {*
       
    47   Note that there is no locale given in this specification---the parameter
       
    48   @{text "R"} therefore needs to be included explicitly in @{term trcl\<iota>\<iota>}, but
       
    49   stays fixed throughout the specification. The problem with this way of
       
    50   stating the specification for the transitive closure is that it derives the
       
    51   following induction principle.
       
    52 
       
    53   \begin{center}\small
       
    54   \mprset{flushleft}
       
    55   \mbox{\inferrule{
       
    56              @{thm_style prem1  trcl\<iota>\<iota>.induct[where P=P, where z=R, where za=x, where zb=y]}\\\\
       
    57              @{thm_style prem2  trcl\<iota>\<iota>.induct[where P=P, where z=R, where za=x, where zb=y]}\\\\
       
    58              @{thm_style prem3  trcl\<iota>\<iota>.induct[where P=P, where z=R, where za=x, where zb=y]}}
       
    59             {@{thm_style concl  trcl\<iota>\<iota>.induct[where P=P, where z=R, where za=x, where zb=y]}}}  
       
    60   \end{center}
       
    61 
       
    62   But this does not correspond to the induction principle we derived by hand, which
       
    63   was
       
    64   
       
    65   \begin{center}\small
       
    66   \mprset{flushleft}
       
    67   \mbox{\inferrule{
       
    68              @{thm_style prem1  trcl_induct[no_vars]}\\\\
       
    69              @{thm_style prem2  trcl_induct[no_vars]}\\\\
       
    70              @{thm_style prem3  trcl_induct[no_vars]}}
       
    71             {@{thm_style concl  trcl_induct[no_vars]}}}  
       
    72   \end{center}
       
    73 
       
    74   The difference is that in the one derived by hand the relation @{term R} is not
       
    75   a parameter of the proposition @{term P} to be proved and it is also not universally
       
    76   qunatified in the second and third premise. The point is that the parameter @{term R}
       
    77   stays fixed thoughout the definition and we do not want to regard it as an ``ordinary''
       
    78   argument of the transitive closure, but one that can be freely instantiated. 
       
    79   In order to recognise such parameters, we have to extend the specification
       
    80   to include a mechanism to state fixed parameters. The user should be able
       
    81   to write 
       
    82 
       
    83 *}
       
    84 
       
    85 simple_inductive
       
    86   trcl'' for R :: "'a \<Rightarrow> 'a \<Rightarrow> bool"
       
    87 where
       
    88   base: "trcl'' R x x"
       
    89 | step: "trcl'' R x y \<Longrightarrow> R y z \<Longrightarrow> trcl'' R x z"
       
    90 
       
    91 simple_inductive
       
    92   accpart'' for R :: "'a \<Rightarrow> 'a \<Rightarrow> bool"
       
    93 where
       
    94   accpartI: "(\<And>y. R y x \<Longrightarrow> accpart'' R y) \<Longrightarrow> accpart'' R x"
       
    95 
       
    96 text {*
       
    97   \begin{figure}[t]
       
    98   \begin{isabelle}
       
    99   \railnontermfont{\rmfamily\itshape}
       
   100   \railterm{simpleinductive,where,for}
       
   101   \railalias{simpleinductive}{\simpleinductive{}}
       
   102   \railalias{where}{\isacommand{where}}
       
   103   \railalias{for}{\isacommand{for}}
       
   104   \begin{rail}
       
   105   simpleinductive target? fixes (for fixes)? \\
       
   106   (where (thmdecl? prop + '|'))?
       
   107   ;
       
   108   \end{rail}
       
   109   \end{isabelle}
       
   110   \caption{A railroad diagram describing the syntax of \simpleinductive{}. 
       
   111   The \emph{target} indicates an optional locale; the \emph{fixes} are an 
       
   112   \isacommand{and}-separated list of names for the inductive predicates (they
       
   113   can also contain typing- and syntax anotations); similarly the \emph{fixes} 
       
   114   after \isacommand{for} to indicate fixed parameters; \emph{prop} stands for a 
       
   115   introduction rule with an optional theorem declaration (\emph{thmdecl}).
       
   116   \label{fig:railroad}}
       
   117   \end{figure}
       
   118 *}
       
   119 
       
   120 text {*
       
   121   This leads directly to the railroad diagram shown in
       
   122   Figure~\ref{fig:railroad} for the syntax of \simpleinductive{}. This diagram
       
   123   more or less translates directly into the parser:
       
   124 
       
   125   @{ML_chunk [display,gray] parser}
       
   126 
       
   127   which we described in Section~\ref{sec:parsingspecs}. If we feed into the 
       
   128   parser the string (which corresponds to our definition of @{term even} and 
       
   129   @{term odd}):
       
   130 
       
   131   @{ML_response [display,gray]
       
   132 "let
       
   133   val input = filtered_input
       
   134      (\"even and odd \" ^  
       
   135       \"where \" ^
       
   136       \"  even0[intro]: \\\"even 0\\\" \" ^ 
       
   137       \"| evenS[intro]: \\\"odd n \<Longrightarrow> even (Suc n)\\\" \" ^ 
       
   138       \"| oddS[intro]:  \\\"even n \<Longrightarrow> odd (Suc n)\\\"\")
       
   139 in
       
   140   parse spec_parser input
       
   141 end"
       
   142 "(([(even, NONE, NoSyn), (odd, NONE, NoSyn)],
       
   143      [((even0,\<dots>), \"\\^E\\^Ftoken\\^Eeven 0\\^E\\^F\\^E\"),
       
   144       ((evenS,\<dots>), \"\\^E\\^Ftoken\\^Eodd n \<Longrightarrow> even (Suc n)\\^E\\^F\\^E\"),
       
   145       ((oddS,\<dots>), \"\\^E\\^Ftoken\\^Eeven n \<Longrightarrow> odd (Suc n)\\^E\\^F\\^E\")]), [])"}
       
   146 *}
       
   147 
       
   148 
       
   149 text {*
       
   150   then we get back a locale (in this case @{ML NONE}), the predicates (with type
       
   151   and syntax annotations), the parameters (similar as the predicates) and
       
   152   the specifications of the introduction rules. 
       
   153 
       
   154 
       
   155 
       
   156   This is all the information we
       
   157   need for calling the package and setting up the keyword. The latter is
       
   158   done in Lines 6 and 7 in the code below.
       
   159 
       
   160   @{ML_chunk [display,gray,linenos] syntax}
       
   161   
       
   162   We call @{ML OuterSyntax.command} with the kind-indicator @{ML
       
   163   OuterKeyword.thy_decl} since the package does not need to open up any goal
       
   164   state (see Section~\ref{sec:newcommand}). Note that the predicates and
       
   165   parameters are at the moment only some ``naked'' variables: they have no
       
   166   type yet (even if we annotate them with types) and they are also no defined
       
   167   constants yet (which the predicates will eventually be).  In Lines 1 to 4 we
       
   168   gather the information from the parser to be processed further. The locale
       
   169   is passed as argument to the function @{ML
       
   170   Toplevel.local_theory}.\footnote{FIXME Is this already described?} The other
       
   171   arguments, i.e.~the predicates, parameters and intro rule specifications,
       
   172   are passed to the function @{ML add_inductive in SimpleInductivePackage}
       
   173   (Line 4).
       
   174 
       
   175   We now come to the second subtask of the package, namely transforming the 
       
   176   parser output into some internal datastructures that can be processed further. 
       
   177   Remember that at the moment the introduction rules are just strings, and even
       
   178   if the predicates and parameters can contain some typing annotations, they
       
   179   are not yet in any way reflected in the introduction rules. So the task of
       
   180   @{ML add_inductive in SimpleInductivePackage} is to transform the strings
       
   181   into properly typed terms. For this it can use the function 
       
   182   @{ML read_spec in Specification}. This function takes some constants
       
   183   with possible typing annotations and some rule specifications and attempts to
       
   184   find a type according to the given type constraints and the type constraints
       
   185   by the surrounding (local theory). However this function is a bit
       
   186   too general for our purposes: we want that each introduction rule has only 
       
   187   name (for example @{text even0} or @{text evenS}), if a name is given at all.
       
   188   The function @{ML read_spec in Specification} however allows more
       
   189   than one rule. Since it is quite convenient to rely on this function (instead of
       
   190   building your own) we just quick ly write a wrapper function that translates
       
   191   between our specific format and the general format expected by 
       
   192   @{ML read_spec in Specification}. The code of this wrapper is as follows:
       
   193 
       
   194   @{ML_chunk [display,gray,linenos] read_specification}
       
   195 
       
   196   It takes a list of constants, a list of rule specifications and a local theory 
       
   197   as input. Does the transformation of the rule specifications in Line 3; calls
       
   198   the function and transforms the now typed rule specifications back into our
       
   199   format and returns the type parameter and typed rule specifications. 
       
   200 
       
   201 
       
   202    @{ML_chunk [display,gray,linenos] add_inductive}
       
   203 
       
   204 
       
   205   In order to add a new inductive predicate to a theory with the help of our
       
   206   package, the user must \emph{invoke} it. For every package, there are
       
   207   essentially two different ways of invoking it, which we will refer to as
       
   208   \emph{external} and \emph{internal}. By external invocation we mean that the
       
   209   package is called from within a theory document. In this case, the
       
   210   specification of the inductive predicate, including type annotations and
       
   211   introduction rules, are given as strings by the user. Before the package can
       
   212   actually make the definition, the type and introduction rules have to be
       
   213   parsed. In contrast, internal invocation means that the package is called by
       
   214   some other package. For example, the function definition package
       
   215   calls the inductive definition package to define the
       
   216   graph of the function. However, it is not a good idea for the function
       
   217   definition package to pass the introduction rules for the function graph to
       
   218   the inductive definition package as strings. In this case, it is better to
       
   219   directly pass the rules to the package as a list of terms, which is more
       
   220   robust than handling strings that are lacking the additional structure of
       
   221   terms. These two ways of invoking the package are reflected in its ML
       
   222   programming interface, which consists of two functions:
       
   223 
       
   224 
       
   225   @{ML_chunk [display,gray] SIMPLE_INDUCTIVE_PACKAGE}
       
   226 *}
       
   227 
       
   228 text {*
       
   229   (FIXME: explain Binding.binding; Attrib.binding somewhere else)
       
   230 
       
   231 
       
   232   The function for external invocation of the package is called @{ML
       
   233   add_inductive in SimpleInductivePackage}, whereas the one for internal
       
   234   invocation is called @{ML add_inductive_i in SimpleInductivePackage}. Both
       
   235   of these functions take as arguments the names and types of the inductive
       
   236   predicates, the names and types of their parameters, the actual introduction
       
   237   rules and a \emph{local theory}.  They return a local theory containing the
       
   238   definition and the induction principle as well introduction rules. 
       
   239 
       
   240   Note that @{ML add_inductive_i in SimpleInductivePackage} expects
       
   241   the types of the predicates and parameters to be specified using the
       
   242   datatype @{ML_type typ} of Isabelle's logical framework, whereas @{ML
       
   243   add_inductive in SimpleInductivePackage} expects them to be given as
       
   244   optional strings. If no string is given for a particular predicate or
       
   245   parameter, this means that the type should be inferred by the
       
   246   package. 
       
   247 
       
   248 
       
   249   Additional \emph{mixfix syntax} may be associated with the
       
   250   predicates and parameters as well. Note that @{ML add_inductive_i in
       
   251   SimpleInductivePackage} does not allow mixfix syntax to be associated with
       
   252   parameters, since it can only be used for parsing.\footnote{FIXME: why ist it there then?} 
       
   253   The names of the
       
   254   predicates, parameters and rules are represented by the type @{ML_type
       
   255   Binding.binding}. Strings can be turned into elements of the type @{ML_type
       
   256   Binding.binding} using the function @{ML [display] "Binding.name : string ->
       
   257   Binding.binding"} Each introduction rule is given as a tuple containing its
       
   258   name, a list of \emph{attributes} and a logical formula. Note that the type
       
   259   @{ML_type Attrib.binding} used in the list of introduction rules is just a
       
   260   shorthand for the type @{ML_type "Binding.binding * Attrib.src list"}.  The
       
   261   function @{ML add_inductive_i in SimpleInductivePackage} expects the formula
       
   262   to be specified using the datatype @{ML_type term}, whereas @{ML
       
   263   add_inductive in SimpleInductivePackage} expects it to be given as a string.
       
   264   An attribute specifies additional actions and transformations that should be
       
   265   applied to a theorem, such as storing it in the rule databases used by
       
   266   automatic tactics like the simplifier. The code of the package, which will
       
   267   be described in the following section, will mostly treat attributes as a
       
   268   black box and just forward them to other functions for storing theorems in
       
   269   local theories.  The implementation of the function @{ML add_inductive in
       
   270   SimpleInductivePackage} for external invocation of the package is quite
       
   271   simple. Essentially, it just parses the introduction rules and then passes
       
   272   them on to @{ML add_inductive_i in SimpleInductivePackage}:
       
   273 
       
   274   @{ML_chunk [display] add_inductive}
       
   275 
       
   276   For parsing and type checking the introduction rules, we use the function
       
   277   
       
   278   @{ML [display] "Specification.read_specification:
       
   279   (Binding.binding * string option * mixfix) list ->  (*{variables}*)
       
   280   (Attrib.binding * string list) list ->  (*{rules}*)
       
   281   local_theory ->
       
   282   (((Binding.binding * typ) * mixfix) list *
       
   283    (Attrib.binding * term list) list) *
       
   284   local_theory"}
       
   285 *}
       
   286 
       
   287 text {*
       
   288   During parsing, both predicates and parameters are treated as variables, so
       
   289   the lists \verb!preds_syn! and \verb!params_syn! are just appended
       
   290   before being passed to @{ML read_spec in Specification}. Note that the format
       
   291   for rules supported by @{ML read_spec in Specification} is more general than
       
   292   what is required for our package. It allows several rules to be associated
       
   293   with one name, and the list of rules can be partitioned into several
       
   294   sublists. In order for the list \verb!intro_srcs! of introduction rules
       
   295   to be acceptable as an input for @{ML read_spec in Specification}, we first
       
   296   have to turn it into a list of singleton lists. This transformation
       
   297   has to be reversed later on by applying the function
       
   298   @{ML [display] "the_single: 'a list -> 'a"}
       
   299   to the list \verb!specs! containing the parsed introduction rules.
       
   300   The function @{ML read_spec in Specification} also returns the list \verb!vars!
       
   301   of predicates and parameters that contains the inferred types as well.
       
   302   This list has to be chopped into the two lists \verb!preds_syn'! and
       
   303   \verb!params_syn'! for predicates and parameters, respectively.
       
   304   All variables occurring in a rule but not in the list of variables passed to
       
   305   @{ML read_spec in Specification} will be bound by a meta-level universal
       
   306   quantifier.
       
   307 *}
       
   308 
       
   309 text {*
       
   310   Finally, @{ML read_specification in Specification} also returns another local theory,
       
   311   but we can safely discard it. As an example, let us look at how we can use this
       
   312   function to parse the introduction rules of the @{text trcl} predicate:
       
   313 
       
   314   @{ML_response [display]
       
   315 "Specification.read_specification
       
   316   [(Binding.name \"trcl\", NONE, NoSyn),
       
   317    (Binding.name \"r\", SOME \"'a \<Rightarrow> 'a \<Rightarrow> bool\", NoSyn)]
       
   318   [((Binding.name \"base\", []), [\"trcl r x x\"]),
       
   319    ((Binding.name \"step\", []), [\"trcl r x y \<Longrightarrow> r y z \<Longrightarrow> trcl r x z\"])]
       
   320   @{context}"
       
   321 "((\<dots>,
       
   322   [(\<dots>,
       
   323     [Const (\"all\", \<dots>) $ Abs (\"x\", TFree (\"'a\", \<dots>),
       
   324        Const (\"Trueprop\", \<dots>) $
       
   325          (Free (\"trcl\", \<dots>) $ Free (\"r\", \<dots>) $ Bound 0 $ Bound 0))]),
       
   326    (\<dots>,
       
   327     [Const (\"all\", \<dots>) $ Abs (\"x\", TFree (\"'a\", \<dots>),
       
   328        Const (\"all\", \<dots>) $ Abs (\"y\", TFree (\"'a\", \<dots>),
       
   329          Const (\"all\", \<dots>) $ Abs (\"z\", TFree (\"'a\", \<dots>),
       
   330            Const (\"==>\", \<dots>) $
       
   331              (Const (\"Trueprop\", \<dots>) $
       
   332                (Free (\"trcl\", \<dots>) $ Free (\"r\", \<dots>) $ Bound 2 $ Bound 1)) $
       
   333              (Const (\"==>\", \<dots>) $ \<dots> $ \<dots>))))])]),
       
   334  \<dots>)
       
   335 : (((Binding.binding * typ) * mixfix) list *
       
   336    (Attrib.binding * term list) list) * local_theory"}
       
   337 
       
   338   In the list of variables passed to @{ML read_specification in Specification}, we have
       
   339   used the mixfix annotation @{ML NoSyn} to indicate that we do not want to associate any
       
   340   mixfix syntax with the variable. Moreover, we have only specified the type of \texttt{r},
       
   341   whereas the type of \texttt{trcl} is computed using type inference.
       
   342   The local variables \texttt{x}, \texttt{y} and \texttt{z} of the introduction rules
       
   343   are turned into bound variables with the de Bruijn indices,
       
   344   whereas \texttt{trcl} and \texttt{r} remain free variables.
       
   345 
       
   346 *}
       
   347 
       
   348 text {*
       
   349 
       
   350   \paragraph{Parsers for theory syntax}
       
   351 
       
   352   Although the function @{ML add_inductive in SimpleInductivePackage} parses terms and types, it still
       
   353   cannot be used to invoke the package directly from within a theory document.
       
   354   In order to do this, we have to write another parser. Before we describe
       
   355   the process of writing parsers for theory syntax in more detail, we first
       
   356   show some examples of how we would like to use the inductive definition
       
   357   package.
       
   358 
       
   359 
       
   360   The definition of the transitive closure should look as follows:
       
   361 *}
       
   362 
       
   363 ML {* SpecParse.opt_thm_name *}
       
   364 
       
   365 text {*
       
   366 
       
   367   A proposition can be parsed using the function @{ML prop in OuterParse}.
       
   368   Essentially, a proposition is just a string or an identifier, but using the
       
   369   specific parser function @{ML prop in OuterParse} leads to more instructive
       
   370   error messages, since the parser will complain that a proposition was expected
       
   371   when something else than a string or identifier is found.
       
   372   An optional locale target specification of the form \isa{(\isacommand{in}\ $\ldots$)}
       
   373   can be parsed using @{ML opt_target in OuterParse}.
       
   374   The lists of names of the predicates and parameters, together with optional
       
   375   types and syntax, are parsed using the functions @{ML "fixes" in OuterParse}
       
   376   and @{ML for_fixes in OuterParse}, respectively.
       
   377   In addition, the following function from @{ML_struct SpecParse} for parsing
       
   378   an optional theorem name and attribute, followed by a delimiter, will be useful:
       
   379   
       
   380   \begin{table}
       
   381   @{ML "opt_thm_name:
       
   382   string -> Attrib.binding parser" in SpecParse}
       
   383   \end{table}
       
   384 
       
   385   We now have all the necessary tools to write the parser for our
       
   386   \isa{\isacommand{simple{\isacharunderscore}inductive}} command:
       
   387   
       
   388  
       
   389   Once all arguments of the command have been parsed, we apply the function
       
   390   @{ML add_inductive in SimpleInductivePackage}, which yields a local theory
       
   391   transformer of type @{ML_type "local_theory -> local_theory"}. Commands in
       
   392   Isabelle/Isar are realized by transition transformers of type
       
   393   @{ML_type [display] "Toplevel.transition -> Toplevel.transition"}
       
   394   We can turn a local theory transformer into a transition transformer by using
       
   395   the function
       
   396 
       
   397   @{ML [display] "Toplevel.local_theory : string option ->
       
   398   (local_theory -> local_theory) ->
       
   399   Toplevel.transition -> Toplevel.transition"}
       
   400  
       
   401   which, apart from the local theory transformer, takes an optional name of a locale
       
   402   to be used as a basis for the local theory. 
       
   403 
       
   404   (FIXME : needs to be adjusted to new parser type)
       
   405 
       
   406   {\it
       
   407   The whole parser for our command has type
       
   408   @{text [display] "OuterLex.token list ->
       
   409   (Toplevel.transition -> Toplevel.transition) * OuterLex.token list"}
       
   410   which is abbreviated by @{text OuterSyntax.parser_fn}. The new command can be added
       
   411   to the system via the function
       
   412   @{text [display] "OuterSyntax.command :
       
   413   string -> string -> OuterKeyword.T -> OuterSyntax.parser_fn -> unit"}
       
   414   which imperatively updates the parser table behind the scenes. }
       
   415 
       
   416   In addition to the parser, this
       
   417   function takes two strings representing the name of the command and a short description,
       
   418   as well as an element of type @{ML_type OuterKeyword.T} describing which \emph{kind} of
       
   419   command we intend to add. Since we want to add a command for declaring new concepts,
       
   420   we choose the kind @{ML "OuterKeyword.thy_decl"}. Other kinds include
       
   421   @{ML "OuterKeyword.thy_goal"}, which is similar to @{ML thy_decl in OuterKeyword},
       
   422   but requires the user to prove a goal before making the declaration, or
       
   423   @{ML "OuterKeyword.diag"}, which corresponds to a purely diagnostic command that does
       
   424   not change the context. For example, the @{ML thy_goal in OuterKeyword} kind is used
       
   425   by the \isa{\isacommand{function}} command \cite{Krauss-IJCAR06}, which requires the user
       
   426   to prove that a given set of equations is non-overlapping and covers all cases. The kind
       
   427   of the command should be chosen with care, since selecting the wrong one can cause strange
       
   428   behaviour of the user interface, such as failure of the undo mechanism.
       
   429 *}
       
   430 
       
   431 text {*
       
   432   Note that the @{text trcl} predicate has two different kinds of parameters: the
       
   433   first parameter @{text R} stays \emph{fixed} throughout the definition, whereas
       
   434   the second and third parameter changes in the ``recursive call''. This will
       
   435   become important later on when we deal with fixed parameters and locales. 
       
   436 
       
   437 
       
   438  
       
   439   The purpose of the package we show next is that the user just specifies the
       
   440   inductive predicate by stating some introduction rules and then the packages
       
   441   makes the equivalent definition and derives from it the needed properties.
       
   442 *}
       
   443 
       
   444 text {*
       
   445   From a high-level perspective the package consists of 6 subtasks:
       
   446 
       
   447 
       
   448 
       
   449 *}
       
   450 
       
   451 
       
   452 (*<*)
       
   453 end
       
   454 (*>*)