test/Tools/isac/BridgeJEdit/Test_VSCode_Example.thy
author wneuper <Walther.Neuper@jku.at>
Mon, 01 Jan 2024 11:31:16 +0100
changeset 60789 8fa678b678e8
parent 60782 e797d1bdfe37
permissions -rw-r--r--
Doc/Specify_Phase 4: start use antiquotations from isar-ref
     1 theory Test_VSCode_Example
     2   imports "Isac.Build_Isac" (*"Isac.Calculation"*)
     3 (*keywords "Example" :: thy_decl  -- the active definition is in Calculation.thy*)
     4 begin
     5 
     6 text \<open>
     7   Tests here in BridgePIDE are already based on Build_Isac.thy bypassing $ISABELLE_ISAC_TEST.
     8   TODO: eliminate "Isac." above on eliminating session in $ISABELLE_ISAC_TEST.
     9 \<close>
    10 
    11 subsection \<open>Development\<close>
    12 text \<open>
    13   Here we develop the semantics of \<open>Example\<close> as test
    14   --- build Outer_Syntax Example n:
    15   and save intermediate states as separate tests in test/../preliminary.sml
    16   as soon as some stable state is achieved.
    17 
    18   This way we make all \<open>Example\<close> to \<open>Example\<close> step by step 
    19   and finally rename all again to \<open>Example\<close>.
    20 \<close> ML \<open>
    21 val ((in_thy, thy_id_pos), (in_pbl, probl_id_pos), (in_met, meth_id_pos)) =
    22   (("__", Position.none), ( "__/__", Position.none), ("__/__", Position.none))
    23 val in_ = (in_thy, in_pbl, in_met)
    24 val spec_ as (spec_thy, spec_pbl, spec_met) = References.empty
    25 val o_ as (o_thy, o_pbl, o_met) =
    26   ("Diff_App", ["univariate_calculus", "Optimisation"], ["Optimisation", "by_univariate_calculus"])
    27 \<close> ML \<open>
    28 \<close> ML \<open> (** preliminary handling of References **)
    29 \<close> ML \<open> (* assumption:  we read the Specfication as a whole *)
    30 (*/-------------------- state of src/../preliminary.sml at that time  -------------------------\*)
    31 fun re_eval_all ((*dummyarg*)) = ("re_eval_all if new_thy"; ((*dummyarg*)))
    32 fun re_eval_pbl ((*dummyarg*)) = ("re_eval_pbl"; ((*dummyarg*)))
    33 fun re_eval_met ((*dummyarg*)) = ("re_eval_met"; ((*dummyarg*)))
    34 ;
    35 re_eval_all: unit -> unit;
    36 (*
    37   switch_pbl_met IS MISSING! Greater changes are to expect with respective introduction.
    38   In the meanwhile we just \<open>re_eval\<close> the \<open>I_Model\<close> of \<open>Problem\<close>;
    39   in case this is complete, we automatically complete the \<open>I_Model\<close> of \<open>MethodC\<close>
    40   without displaying the respective \<open>P_Model\<close> and immediately start solve-phase by \<open>Apply_Method\<close>;
    41 *)
    42 fun re_eval (input_thy, input_pbl, input_met) (spec_thy, spec_pbl, spec_met) =
    43   if spec_thy <> input_thy
    44     then re_eval_all ()
    45     else if spec_pbl <> input_pbl
    46       then re_eval_pbl ()
    47       else if spec_met <> input_met
    48         then re_eval_met ()
    49         else ("nothing to re-evaluate"; ((*dummyarg*)))
    50 ;
    51 re_eval: References.T -> References.T -> unit;
    52 (*\-------------------- state of src/../preliminary.sml at that time  -------------------------/*)
    53 \<close> ML \<open> (*for building code copy here
    54          calculation.sml --- build Outer_Syntax Example 3 ff*)
    55 \<close> ML \<open>
    56 \<close> ML \<open>
    57 (*/--------------------- the active definition is in Calculation.thy -------------------------\* )
    58 val _ =
    59   Outer_Syntax.command \<^command_keyword>\<open>Example_test\<close>
    60     "prepare ISAC problem type and register it to Knowledge Store"
    61     (Parse.name -- Parse.!!! ( \<^keyword>\<open>Specification\<close> -- \<^keyword>\<open>:\<close> --
    62          \<^keyword>\<open>Model\<close> -- \<^keyword>\<open>:\<close>  |-- Problem.parse_pos_model_input --
    63            (\<^keyword>\<open>References\<close> -- \<^keyword>\<open>:\<close> |-- Parse.!!! References.parse_input_pos
    64              (** )-- ( \<^keyword>\<open>Solution\<close> -- keyword>\<open>:\<close>)( ** )WHAT IS WRONG HEREWITH?( **)) ) >>
    65       (fn (example_id, (model_input,
    66           ((thy_id, thy_id_pos), ((probl_id, probl_id_pos), (meth_id, meth_id_pos)) ) )) =>
    67         Toplevel.theory (fn thy =>
    68           let
    69             val state =
    70               case the_data thy of
    71                   CTbasic.EmptyPtree => Preliminary.init_ctree thy example_id
    72                 | state =>
    73                   let
    74                     val {origin = (o_model, o_ref, _), spec = spec_ref, probl, meth, ctxt, ...} =
    75                       CTbasic.get_obj I state [(*Pos todo*)] |> CTbasic.rep_specify_data
    76                     val thy_id' = References.for_parse thy_id (#1 spec_ref) (#1 o_ref)
    77                     val (model, where_) = Model_Pattern.parse_pos_empty ctxt model_input (*+check syntax*)
    78 (*reminder for Template.show ----------------------------------------------------------------\*)
    79                     val ((_, _, _), (_, pbl_id as ["univariate_calculus", "Optimisation"], _)) =
    80                       References.for_eval_model (thy_id, probl_id, meth_id) spec_ref o_ref
    81                     val {model = model_patt, where_ = where_ctree, where_rls, ...} =
    82                       Problem.from_store ctxt pbl_id;
    83 
    84 (*/----- ERROR: mk_env not reasonable for "Inc Constants [] [__=__, __=__]"-------------\
    85   ------------------------------------------------vvvvvvvvvvvvvvvvvvvvv-----------------------* )
    86                     val (_, env_eval) = Pre_Conds.sep_variants_envs_OLD model_patt probl
    87                     (*------------------- must be completed first -----------------^^^^^*)
    88 
    89                     val _ =
    90                       case I_Model.is_complete_OLD ctxt model_patt where_rls where_ctree probl of
    91                         NONE => writeln "I_Model.is_complete_OLD --> NONE"
    92                       | SOME variants =>
    93                         writeln ("I_Model.is_complete_OLD --> SOME " ^ LibraryC.ints2str' variants)
    94 ( *\----- ERROR: mk_env not reasonable for "Inc Constants [] [__=__, __=__]"-------------/*)
    95 (*reminder for Template.show ----------------------------------------------------------------/*)
    96 
    97 (*reminder for Pre_Conds.check ---------------------------------------------------------\* )
    98 val (true, [(true, (Const ("Orderings.ord_class.less", _) $ Const ("Groups.zero_class.zero", _) $
    99   Free ("r", _), _))]) = Pre_Conds.check ctxt where_rls where_ env_eval;
   100 ( *reminder for Pre_Conds.check ---------------------------------------------------------/*)
   101 (**)
   102                     val ((_, _, _), (_, pbl_id as ["univariate_calculus", "Optimisation"], _)) =
   103                         References.for_eval_model (thy_id, probl_id, meth_id) spec_ref o_ref
   104                     val _ = re_eval References.empty References.empty
   105 (**)
   106                   in
   107                     Preliminary.update_state thy
   108                       (example_id, (model_input, (thy_id, probl_id, meth_id))) (the_data thy)
   109                   end
   110           in set_data state thy end)));
   111 ( *\--------------------- the active definition is in Calculation.thy -------------------------/*)
   112 \<close> ML \<open>
   113 \<close> ML \<open> (*Example below*)
   114 \<close>
   115 
   116 subsection \<open>Mirror from src/../VSCode_Example.thy for testing purposes\<close>
   117 
   118 subsubsection \<open>Complete Specification at once\<close>
   119 
   120 text \<open>
   121   Just a test, that this works also in $ISABELLE_ISAC
   122 \<close>
   123 Example "Diff_App-No.123a" (*complete Specification*)
   124   Specification:
   125     Model:
   126       Given: \<open>Constants [r = 7]\<close>
   127       Where: \<open>0 < r\<close>
   128       Find:  \<open>Maximum A\<close> \<open>AdditionalValues [u, v]\<close>
   129       Relate: \<open>Extremum (A = 2 * u * v - u \<up> 2)\<close> 
   130         \<open>SideConditions [(u / 2) \<up> 2 + (2 / v) \<up> 2 = r \<up> 2]\<close>
   131     References:
   132       Theory_Ref: "Diff_App"
   133   (*\<Otimes>*) Problem_Ref: "univariate_calculus/Optimisation"
   134   (*\<Odot>*) Method_Ref: "Optimisation/by_univariate_calculus"
   135 (*Solution:*)
   136 
   137 text \<open>
   138   Check complete \<open>Model\<close>, infer empty \<open>References\<close> from data hidden in "Diff_App-No.123a"
   139 \<close>
   140 (*
   141 Example: no syntax check of terms, thus OK.
   142 Example: proper syntax check of terms with Model_Pattern.parse_pos,
   143 *)
   144 (** )Example
   145 ( **)Example(**) "Diff_App-No.123a" (*complete Model, empty References*)
   146   Specification:
   147     Model:
   148       Given: \<open>Constants [r = 7]\<close>
   149       Where: \<open>0 < r\<close>
   150       Find:  \<open>Maximum A\<close> \<open>AdditionalValues [u, v]\<close>
   151       Relate: \<open>Extremum (A = 2 * u * v - u \<up> 2)\<close> 
   152         \<open>SideConditions [(u / 2) \<up> 2 + (2 / v) \<up> 2 = r \<up> 2]\<close>
   153     References: 
   154       Theory_Ref: "__"
   155   (*\<Odot>*) Problem_Ref: "__/__"
   156   (*\<Otimes>*) Method_Ref: "__/__"
   157 (*Solution:*)
   158 
   159 subsubsection \<open>Empty Specification ready for input by students\<close>
   160 text \<open>
   161   In order to help the student (similar to a personal instructur helping a beginner)
   162   the \<open>Specification\<close> is given as a template,
   163   where so-called \<open>descriptor\<close>s give hints about the input requested
   164   (in native language!) and delimiters are (partially) given.
   165   
   166   For the same reason \<open>Where\<close> is already filled; most beginners are 
   167   not aware of "where_-conditions", i.e. of preconditions.
   168 \<close>
   169  (*all empty*)
   170 
   171 Example "Diff_App-No.123a"
   172   Specification:
   173     Model:
   174       Given: \<open>Constants [__=__, __=__]\<close>
   175       Where: \<open>0 < r\<close>
   176       Find:  \<open>Maximum __\<close> \<open>AdditionalValues [__, __]\<close>
   177       Relate: \<open>Extremum (__=__)\<close> 
   178         \<open>SideConditions [__=__, __=__]\<close>
   179     References: 
   180       Theory_Ref: "__"
   181   (*\<Odot>*) Problem_Ref: "__/__"
   182   (*\<Otimes>*) Method_Ref: "__/__"
   183 (*Solution:*)
   184 
   185 subsubsection \<open>Start Example with a CAS_Cmd\<close>
   186 text \<open>
   187   The student wants to use Isabelle/Isac like an Algebra System and issues a CAS_Cmd.
   188   The CAS_Cmd triggers implicit Specification (which usually is not requested 
   189   in case of CAS_Cmd).
   190 
   191   This user-requirement is not reflected so far. Three possible solutions are sketched below.
   192 \<close>
   193 (*----------------------------------------*  )
   194 Example ""
   195   Specification (\<open>Simplify (2*a + 3*a)\<close>):
   196   Solution:
   197     \<open>2*a + 3*a\<close>
   198     :
   199 (  *-----------------------------------------*)
   200 
   201 (*----------------------------------------*  )
   202 Example \<open>Simplify (2*a + 3*a)\<close>
   203   Specification:
   204   Solution:
   205     \<open>2*a + 3*a\<close>
   206     :
   207 (  *----------------------------------------*)
   208 
   209 (*----------------------------------------*  )
   210 \<open>Simplify (2*a + 3*a)\<close>
   211   Specification:
   212   Solution:
   213     \<open>2*a + 3*a\<close>
   214     :
   215 (  *----------------------------------------*)
   216 
   217 subsubsection \<open>Specification step by step\<close>
   218 
   219 text \<open>
   220   In an electronic textbook there shall be references to examples given, for instance,
   221   by \<open>Example "Diff_App-No.123a"\<close>. Such a reference shall be easily "activated" 
   222   to start a Specification + Solution at the spot ?and?or? to open a separate window.
   223 
   224   Activating the reference \<open>Example "Diff_App-No.123a"\<close> shall result in a threefold alternative,
   225   where (2)=(3) holds for the parsers:
   226   (1) the Example presents a "normal problem" with a Specification (preferred by engineers),
   227     see above subsubsection \<open>Empty Specification\<close>,
   228   (2) the Example presents a "normal problem" without a Specification (preferred in high-school):
   229     |Example "Diff_App-No.123a"
   230     |  Specification:
   231     |  Solution:
   232     |    ..first formula as given by the specifid Method..
   233   (3) the Example presents a "CAS_Cmd":
   234     |Example \<open>Simplify (2*a + 3*a)\<close>
   235     |  Specification:
   236     |  Solution:
   237     |    solve (x + 1 = 2, x)
   238   \<open>Specification\<close> is always visible in order to remind of the existence.
   239   \<open>Outer_Syntax.command \<^command_keyword>\<open>Example\<close>\<close> shall be able to parse all three cases
   240   (while still skipping considerations about \<open>Specification\<close> and sub-problems).
   241 
   242   Below is the Example with case (1):
   243   * The lines \<open>text --- remove this line and make content Isar input---\\<close>
   244     and      ---------- remove this line and make content Isar input---/
   245     will be deleted after correct implementation
   246   * The \<open>ML \<open>blocks\<close>\<close> in between show, that ISAC is able of "next step guidance"; 
   247     these can be deleted as well.
   248   * This Example also shows the toggle between \<open>\<Otimes>\<close> and \<open>\<Odot>\<close>.
   249 \<close> 
   250 
   251 text \<open>
   252   # 1: ISAC starts the Example by presenting an "empty" Specification.
   253 
   254   "Presenting" should mean: by "activation" of Example the system presents
   255   the empty Specification as a template, where the items give hints on the input format.
   256 
   257   The field \<open>Where\<close> is already filled, but editable and shows missing input by \<open>False\<close>.
   258 \<close>
   259 ML \<open>
   260 \<close> ML \<open> (* # 1: At the first call of \<open>Example\<close> ISAC initialises a calculation *)
   261 val p = ([], Pos.Pbl);
   262 val pt = Preliminary.update_state @{theory} (*here we have Ctree.EmptyPtree and thus call init_ctree*)
   263   ("Diff_App-No.123a", 
   264   ( [("#Given", [("Constants [r = 7]", Position.none)]),
   265      ("#Where", [("0 < r", Position.none)]), 
   266      ("#Find", [("Maximum A", Position.none), ("AdditionalValues [u, v]", Position.none)]), 
   267      ("#Relate", [("Extremum A = 2 * u * v - u \<up> 2", Position.none), 
   268         ("SideConditions [((u::real) / 2) \<up> 2 + (2 / v) \<up> 2 = r \<up> 2]", Position.none)])
   269     ],
   270     ("Diff_App", "univariate_calculus/Optimisation", "Optimisation/by_univariate_calculus")
   271   )
   272 ) CTbasic.EmptyPtree;
   273 val ptp = (pt, p);
   274 \<close>
   275 Example "Diff_App-No.123a" (*all empty step 0*)
   276   Specification:
   277     Model:
   278       Given: \<open>Constants [__=__, __=__]\<close>
   279       Where: \<open>0 < r\<close>
   280       Find:  \<open>Maximum __\<close> \<open>AdditionalValues [__, __]\<close>
   281       Relate: \<open>Extremum (__=__)\<close> 
   282         \<open>SideConditions [__=__, __=__]\<close>
   283     References: 
   284       Theory_Ref: "__"
   285   (*\<Odot>*) Problem_Ref: "__/__"
   286   (*\<Otimes>*) Method_Ref: "__/__"
   287 (*Solution:*)
   288 
   289 text \<open>
   290   # 2: Somewhere in the Model, the user inputs a next item, which is marked "correct" by ISAC.
   291 
   292   By this input Where: \<open>0 < r\<close> is not rendered \<open>False\<close> anymore.
   293 \<close> 
   294 ML \<open> (* # 2: Somewhere in the Model, the user inputs a next item *)
   295 (*/---Step.specify_do_next still works with Ctree, not Ctree-------------------* )
   296 val ("ok", ([(Tactic.Add_Given "Constants [r = 7]", _, _)], _, ptp))
   297   = Step.specify_do_next ptp;
   298 ( *\---Step.specify_do_next still works with Ctree, not Ctree-------------------*)
   299 \<close>
   300 Example "Diff_App-No.123a" (*step 1: <Constants [r = 7]>*)
   301   Specification:
   302     Model:
   303       Given: \<open>Constants [r = 7]\<close>
   304       Where: \<open>0 < r\<close>
   305       Find:  \<open>Maximum __\<close> \<open>AdditionalValues [__, __]\<close>
   306       Relate: \<open>Extremum (__=__)\<close> \<open>SideConditions [__=__, __=__]\<close>
   307     References:
   308       Theory_Ref: "__"
   309   (*\<Odot>*) Problem_Ref: "__/__"
   310   (*\<Otimes>*) Method_Ref: "__/__"
   311 (*Solution:*)
   312 
   313 text \<open>
   314   # 3: The user inputs a next item somewhere in the \<open>Specification\<close>;
   315        Since this item requests for a list, \<open>AdditionalValues\<close> is marked "incomplete":
   316 \<close> 
   317 ML \<open> (* 3: ISAC suggests a next item within a term list *)
   318 (*/---Step.specify_do_next still works with Ctree, not Ctree-------------------* )
   319 val ("ok", ([(Tactic.Add_Find "Maximum A", _, _)], _, ptp))
   320   = Step.specify_do_next ptp;
   321 ( *\---Step.specify_do_next still works with Ctree, not Ctree-------------------*)
   322 \<close>
   323 Example "Diff_App-No.123a" (*step 2: AdditionalValues [u]*)
   324   Specification:
   325     Model:
   326       Given: \<open>Constants [r = 7]\<close>
   327       Where: \<open>0 < r\<close>
   328       Find:  \<open>Maximum __\<close> \<open>AdditionalValues [u]\<close>
   329       Relate: \<open>Extremum (__=__)\<close> \<open>SideConditions [__=__, __=__]\<close>
   330     References:
   331       Theory_Ref: "__"
   332   (*\<Odot>*) Problem_Ref: "__/__"
   333   (*\<Otimes>*) Method_Ref: "__/__"
   334 (*Solution:*)
   335 
   336 text \<open>
   337   # 4: The user inputs a next item, and the item \<open>AdditionalValues\<close> is marked "correct":
   338 \<close> 
   339 ML \<open> (* # 4: ISAC suggests a next step on request, even a next element within a list *)
   340 (*/---Step.specify_do_next still works with Ctree, not Ctree-------------------* )
   341 val ("ok", ([(Tactic.Add_Find "AdditionalValues [u]", _, _)], _, ptp))
   342   = Step.specify_do_next ptp;
   343 ( *\---Step.specify_do_next still works with Ctree, not Ctree-------------------*)
   344 \<close>
   345 
   346 Example "Diff_App-No.123a" (*step 3: A.Values [u, v]*)
   347   Specification:
   348     Model:
   349       Given: \<open>Constants [r = 7]\<close>
   350       Where: \<open>0 < r\<close>
   351       Find:  \<open>Maximum __\<close> \<open>AdditionalValues [u, v]\<close>
   352       Relate: \<open>Extremum (__=__)\<close> \<open>SideConditions [__=__, __=__]\<close>
   353     References:
   354       Theory_Ref: "__"
   355   (*\<Odot>*) Problem_Ref: "__/__"
   356   (*\<Otimes>*) Method_Ref: "__/__"
   357 (*Solution:*)
   358 
   359 text \<open>
   360   # 5: Input to \<open>Problem_Ref\<close> is according to the (hidden) formalisation:
   361 \<close> 
   362 ML \<open> (* # 5: Input to \<open>Problem_Ref\<close> might change the \<open>Model\<close> *)
   363 \<close>
   364 Example "Diff_App-No.123a" (*step 4: (wrong) Problem_Ref input*)
   365   Specification:
   366     Model:
   367       Given: \<open>Constants [r = 7]\<close>
   368       Where: \<open>0 < r\<close>
   369       Find:  \<open>Maximum __\<close> \<open>AdditionalValues [u, v]\<close>
   370       Relate: \<open>Extremum (__=__)\<close> \<open>SideConditions [__=__, __=__]\<close>
   371     References:
   372       Theory_Ref: "__"
   373   (*\<Odot>*) Problem_Ref: "univariate_calculus/Optimisation" (*"Biegelinien/einfache" not in teststore*)
   374   (*\<Otimes>*) Method_Ref: "__/__"
   375 (*Solution:*)
   376 
   377 text \<open>
   378   # 6: Input to \<open>Method_Ref\<close> changes the \<open>Model\<close> to the respective \<open>Method\<close>;
   379        There is also a feature to change the \<open>Model\<close> according to 
   380        direct manipulation of \<open>\<Otimes>\<close> and \<open>\<Odot>\<close>.
   381 \<close> 
   382 ML \<open> (* # 6: Input to \<open>Method_Ref\<close> changes the \<open>Model\<close> to the respective \<open>Method\<close> *)
   383 \<close>
   384 Example "Diff_App-No.123a" (*step 5: Method_Ref updated*)
   385   Specification:
   386     Model:      (* FIXME: lookup model of method *)
   387       Given: \<open>Constants [r = 7]\<close>
   388       Where: \<open>0 < r\<close>
   389       Find:  \<open>Maximum __\<close> \<open>AdditionalValues [u, v]\<close>
   390       Relate: \<open>Extremum (__=__)\<close> \<open>SideConditions [__]\<close>
   391     References:
   392       Theory_Ref: "__"
   393   (*\<Odot>*) Problem_Ref: "univariate_calculus/Optimisation"
   394   (*\<Otimes>*) Method_Ref: "Optimisation/by_univariate_calculus"
   395 (*Solution:*)
   396 
   397 text \<open>
   398   # 7: xxxxx:
   399 \<close> 
   400 ML \<open> (* # 7: user inputs value of Method_Ref, which shows the respective Model *)
   401 \<close>
   402 
   403 subsubsection \<open>Specification with Model for the Method_Ref\<close>
   404 
   405 text \<open>
   406   Observe the (still outcommented) toggles \<Odot>\<Otimes> changed according to the Reference
   407 \<close>
   408 Example "Diff_App-No.123a" (*Specification of the Method_Ref*)
   409   Specification:
   410     Model:        
   411       Given: \<open>Constants [r = 7]\<close> \<open>Maximum A\<close> \<open>AdditionalValues [u, v]\<close>
   412         \<open>Extremum (A = 2 * u * v - u \<up> 2)\<close>
   413         \<open>SideConditions [(u / 2) \<up> 2 + (2 / v) \<up> 2 = r \<up> 2]\<close>
   414         \<open>FunctionVariable u\<close> \<open>Domain {0 <..< r}\<close> \<open>ErrorBound (\<epsilon> = 0)\<close> 
   415       Where: \<open>0 < r\<close>
   416       Find: \<open>Results (A, u, v)\<close>
   417       Relate:
   418     References:
   419       Theory_Ref: "Diff_App"
   420   (*\<Odot>*) Problem_Ref: "univariate_calculus/Optimisation"
   421   (*\<Otimes>*) Method_Ref: "Optimisation/by_univariate_calculus"
   422 (*Solution:*)
   423 
   424 text \<open> --- remove this line and make content Isar input---\
   425 ---------- remove this line and make content Isar input---/
   426 \<close>
   427 
   428 text \<open>
   429   # 8: xxxxx:
   430 \<close> 
   431 ML \<open> (* # 8: ISAC suggests a next step on request *)
   432 \<close>
   433 text \<open> --- remove this line and make content Isar input---\
   434 ---------- remove this line and make content Isar input---/
   435 \<close>
   436 
   437 text \<open>
   438   # 9: xxxxx:
   439 \<close> 
   440 ML \<open> (* # 9: ISAC suggests a next step on request *)
   441 \<close>
   442 text \<open> --- remove this line and make content Isar input---\
   443 ---------- remove this line and make content Isar input---/
   444 \<close>
   445 
   446 ML \<open>
   447 \<close> ML \<open>
   448 \<close> ML \<open>
   449 \<close>
   450 
   451 end