From 874bcb674aab2604580ef920ca8ffd6417a0b156 Mon Sep 17 00:00:00 2001 From: Chris Sangwin Date: Thu, 18 Jan 2024 13:59:14 +0000 Subject: [PATCH 1/9] Add in a proof_opt command to denote optional lines in a proof. --- doc/en/Proof/Proof_CAS_library.md | 5 +++++ stack/maxima/contrib/prooflib.mac | 33 ++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/doc/en/Proof/Proof_CAS_library.md b/doc/en/Proof/Proof_CAS_library.md index f9bf0ea0f88..ff4b52a6a1c 100644 --- a/doc/en/Proof/Proof_CAS_library.md +++ b/doc/en/Proof/Proof_CAS_library.md @@ -2,6 +2,10 @@ STACK provides libraries to represent and manage lines of a text-based proof in a tree structure. This page is reference documentation for these CAS libraries. For examples of how to use these see the topics page on using [Parson's problems](../Topics/Parsons.md). +To use these functions you have to [load an optional library](../Authoring/Inclusions.md) into each question. + +E.g. `stack_include_contrib("prooflib.mac")` will include the library published in the master branch on github, which will be at or just ahead of an official release. + ## Proof construction functions, and their manipulation Proofs are represented as "proof construction functions" with arguments. For example, an if and only if proof would be represented as `proof_iff(A,B)`, where both `A` and `B` are sub-proofs. Proof construction functions don't typically modify their arguments, but some proof construction functions have simplification properties. For example `proof_iff(A,B)` is normally equivalent to `proof_iff(B,A)`. @@ -10,6 +14,7 @@ STACK supports the following types of proof construction functions. The followi * `proof()`: general, unspecified proof. * `proof_c()`: general proof, with commutative arguments. Typically each argument will be another proof block type. +* `proof_opt()`: steps in a proof which are optional. It assumes a single step. Wrap each optional step individually. The following represent particular types of proof. diff --git a/stack/maxima/contrib/prooflib.mac b/stack/maxima/contrib/prooflib.mac index d3b9ea7e7db..c4444582791 100644 --- a/stack/maxima/contrib/prooflib.mac +++ b/stack/maxima/contrib/prooflib.mac @@ -41,6 +41,7 @@ tap:proof_display(proof_ans, proof_steps); /* */ /* proof() - general, unspecified proof */ /* proof_c() - general proof, with commutative arguments */ +/* proof_opt() - proof_opt() */ /* */ /* proof_iff() - if any only if */ /* proof_cases() - proof by exhaustive cases, the first element */ @@ -53,7 +54,7 @@ tap:proof_display(proof_ans, proof_steps); /* Please update Proof/Proof_CAS_library.md with new types. */ /* Note, "proof" is assumed to come first in this list, as we use "rest" below for other types. */ -proof_types:[proof, proof_c, proof_iff, proof_cases, proof_ind]; +proof_types:[proof, proof_c, proof_opt, proof_iff, proof_cases, proof_ind]; proofp(ex) := block( if atom(ex) then true, @@ -62,10 +63,14 @@ proofp(ex) := block( ); s_test_case(proofp(proof(1,2,3)), true); +s_test_case(proofp(proof_iff(1,2)), true); s_test_case(proofp(sin(x)), false); proof_validatep(ex) := block( if atom(ex) then return(true), + if op(ex) = proof_opt then + if not(is(length(args(ex)) = 1)) then return(false) + else return(all_listp(proof_validatep, args(ex))), if op(ex) = proof_iff then if not(is(length(args(ex)) = 2)) then return(false) else return(all_listp(proof_validatep, args(ex))), @@ -80,12 +85,15 @@ proof_validatep(ex) := block( s_test_case(proof_validatep(proof(1,2,3)), true); s_test_case(proof_validatep(proof(1,2,proof(4,5,6))), true); s_test_case(proof_validatep(proof(1,2,proof_iff(4,5))), true); +/* proof_opt must have exactly one sub-proof. */ +s_test_case(proof_validatep(proof(1,2,proof_opt(4,5))), false); /* proof_iff must have exactly two sub-proofs. */ s_test_case(proof_validatep(proof(1,2,proof_iff(4))), false); s_test_case(proof_validatep(proof(1,2,proof_iff(4,5,6))), false); /* proof_ind must have exactly four sub-proofs. */ s_test_case(proof_validatep(proof_ind(1,proof(2,3),proof(4,5),6)), true); s_test_case(proof_validatep(proof_ind(1,proof(2,3),proof(4,5))), false); +s_test_case(proof_validatep(proof(1,proof_opt(2),proof_iff(4,5))), true); /* Is this a type of proof which can reorder its arguments? */ proof_commutep(ex):=block( @@ -102,12 +110,14 @@ s_test_case(proof_flatten(proof_iff(proof(A,B),proof(C))), proof(A,B,C)); s_test_case(proof_flatten(proof_c(proof(A,proof(B,C)),proof(D))), proof(A,B,C,D)); /* - * Create a normalised proof tree. + * Create a normalised proof tree. * To establish equivalence of proof trees we compare the normalised form. * This basically sorts and "simplifies" its arguments. + * We also remove the proof_opt tag. */ proof_normal(ex) := block( if atom(ex) then return(ex), + if op(ex) = proof_opt then return(first(args(ex))), /* Only sort arguments to types of proof which commute. */ if proof_commutep(ex) then return(apply(op(ex), sort(map(proof_normal, args(ex))))), /* Some proof types have subsets of arguments which commute. */ @@ -120,6 +130,7 @@ s_test_case(proof_normal(proof_c(B,A,D,C)), proof_c(A,B,C,D)); s_test_case(proof_normal(proof_iff(B,A)), proof_iff(A,B)); s_test_case(proof_normal(proof_ind(D,C,B,A)), proof_ind(D,B,C,A)); s_test_case(proof_normal(proof_cases(D,C,B,A)), proof_cases(D,A,B,C)); +s_test_case(proof_normal(proof_iff(proof_c(proof_opt(C),A), B)), proof_iff(proof_c(A,C),B)); /******************************************************************/ /* */ @@ -132,13 +143,15 @@ s_test_case(proof_normal(proof_cases(D,C,B,A)), proof_cases(D,A,B,C)); */ proof_alternatives(ex):=block([p1,p2], p2:proof_one_alternatives(ex), - do (p1:p2, p2:proof_one_distrib(p1), if is(p1=p2) then return(proof_ensure_list(p2))) + do (p1:p2, p2:proof_one_distrib(p1), if is(p1=p2) then return(map(proof_remove_nullproof, proof_ensure_list(p2)))) ); proof_one_alternatives(pr) := block( if atom(pr) then return(pr), if proof_commutep(pr) then return(apply(pf_one, map(lambda([ex], apply(op(pr), map(proof_one_alternatives, ex))), listify(permutations(args(pr)))))), /* In a proof by exhaustive cases the first element is fixed. */ + if op(pr)=proof_opt then return(pf_one(first(pr), nullproof)), + /* In a proof by exhaustive cases the first element is fixed. */ if op(pr)=proof_cases then return(apply(pf_one, map(lambda([ex], apply(op(pr), append([first(args(pr))], map(proof_one_alternatives, ex)))), listify(permutations(rest(args(pr))))))), /* In a proof by induction cases the first element and last elents are fixed. */ if op(pr) = proof_ind then return(apply(pf_one, map(lambda([ex], apply(op(pr), append([first(args(pr))], @@ -165,14 +178,20 @@ proof_one_distrib(ex):= block([_a,_i,_l], apply(pf_one, map(lambda([ex2], block([_aa], _aa:copy(args(ex)), _aa[_i]:_a[ex2], return(apply(op(ex),_aa)))), _l)) ); -proof_ensure_list(ex):= if listp(ex) then return(ex) else return([ex]); +proof_ensure_list(ex):= if listp(ex) then ex else [ex]; + +proof_remove_nullproof(ex):= block( + if atom(ex) then return(ex), + if freeof(nullproof, ex) then return(ex), + apply(op(ex), map(proof_remove_nullproof, sublist(args(ex), lambda([ex2], not(is(ex2=nullproof)))))) +); s_test_case(proof_alternatives(proof(A,B,C,D)), [proof(A,B,C,D)]); s_test_case(proof_alternatives(proof_c(A,B)), [proof_c(A,B),proof_c(B,A)]); s_test_case(proof_alternatives(proof_iff(A,B)), [proof_iff(A,B),proof_iff(B,A)]); s_test_case(proof_alternatives(proof_ind(A,B,C,D)), [proof_ind(A,B,C,D),proof_ind(A,C,B,D)]); s_test_case(proof_alternatives(proof_cases(A,B,C)), [proof_cases(A,B,C),proof_cases(A,C,B)]); - +s_test_case(proof_alternatives(proof_iff(proof(proof_opt(A), B),C)), [proof_iff(proof(A,B),C),proof_iff(proof(B),C),proof_iff(C,proof(A,B)),proof_iff(C,proof(B))]); /******************************************************************/ /* */ @@ -234,6 +253,8 @@ proof_getstep(k, pf) := block([keylist], proof_disp_replacesteps(ex, proof_steps) := block( if integerp(ex) or stringp(ex) then return(proof_getstep(ex, proof_steps)), if atom(ex) then return(sconcat("Error: the following atom does not index a step: ", string(ex))), + /* Flatten any optional steps now. */ + if is(op(ex)=proof_opt) then return(proof_disp_replacesteps(first(args(ex)), proof_steps)), apply(op(ex), map(lambda([ex2], proof_disp_replacesteps(ex2, proof_steps)), args(ex))) ); @@ -299,6 +320,8 @@ proof_line_html(pfs) := block([st], proof_disp_replacesteps_html(ex, proof_steps) := block( if integerp(ex) or stringp(ex) then return(proof_getstep_html(ex, proof_steps)), if atom(ex) then return(sconcat("Error: the following atom does not index a step: ", string(ex))), + /* Flatten any optional steps now. */ + if is(op(ex)=proof_opt) then return(proof_disp_replacesteps(first(args(ex)), proof_steps)), apply(op(ex), map(lambda([ex2], proof_disp_replacesteps_html(ex2, proof_steps)), args(ex))) ); From d742b5d28b467f4296e8198f656cee54eedd627b Mon Sep 17 00:00:00 2001 From: Chris Sangwin Date: Mon, 29 Jan 2024 14:21:11 +0000 Subject: [PATCH 2/9] Update the version number for the v4.5.0-hf2 release (Javascript bug). --- doc/en/Developer/Development_history.md | 5 +++++ stack/maxima/stackmaxima.mac | 2 +- version.php | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/en/Developer/Development_history.md b/doc/en/Developer/Development_history.md index d2a4531aeaf..c27da2bfd5d 100644 --- a/doc/en/Developer/Development_history.md +++ b/doc/en/Developer/Development_history.md @@ -2,6 +2,11 @@ For current and future plans, see [Development track](Development_track.md) and [Future plans](Future_plans.md). +## Version 4.5.0-hf2 + +Fix critical bug in Javascript. +Released January 2024. + ## Version 4.5.0 Released December 2023. diff --git a/stack/maxima/stackmaxima.mac b/stack/maxima/stackmaxima.mac index 62e829d225f..bf12cc89601 100644 --- a/stack/maxima/stackmaxima.mac +++ b/stack/maxima/stackmaxima.mac @@ -3331,4 +3331,4 @@ is_lang(code):=ev(is(%_STACK_LANG=code),simp=true)$ /* Stack expects some output with the version number the output happens at */ /* maximalocal.mac after additional library loading */ -stackmaximaversion:2023121100$ +stackmaximaversion:2024012900$ diff --git a/version.php b/version.php index 6486ce6effa..0921da8dc14 100644 --- a/version.php +++ b/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2023121100; +$plugin->version = 2024012900; $plugin->requires = 2020061500; $plugin->cron = 0; $plugin->component = 'qtype_stack'; From b2d014cc70c08700b9ab91f2a7b9a1afc292c674 Mon Sep 17 00:00:00 2001 From: Mat-Ge <128398586+Mat-Ge@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:21:12 +0100 Subject: [PATCH 3/9] four typos --- doc/en/Authoring/JSXGraph.md | 4 ++-- doc/en/Authoring/Tidy_Tool.md | 2 +- doc/en/Developer/Development_history.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/en/Authoring/JSXGraph.md b/doc/en/Authoring/JSXGraph.md index 8a1a4b879e3..61f87cf014d 100644 --- a/doc/en/Authoring/JSXGraph.md +++ b/doc/en/Authoring/JSXGraph.md @@ -187,9 +187,9 @@ You should check the sample questions about JSXGraph binding for examples of the Starting from version 4.3 there are three functions for dealing with pairs of points. Basically, if you want to represent vectors, lines or circles or anything that can be defined with just two points. `stack_jxg.bind_point_dual(inputRef, point1, point2)` will store the positions of the points into a single input as a list of lists, `stack_jxg.bind_point_relative(inputRef, point1, point2)` will also generate a list but in it the second point is represented relative to the first, and finally `stack_jxg.bind_point_direction(inputRef, point1, point2)` will provide the first point as coordinates and the second point as an angle and distance from the first. -Starting from 4.4 there is only one new bind function `stack_jxg.bind_list_of(inputRef, list)` which takes a list of points and/or sliders and stores it into a single input. It only works if he size or order of the list does not change during page loads, however the list can change its shape for variants of the question. The primary use target for this are the vertices of polygons, but one can probably come up with something else as well, it does work as a quick and dirty way of storing the whole graph state if the graph can be defined just by points and sliders. +Starting from 4.4 there is only one new bind function `stack_jxg.bind_list_of(inputRef, list)` which takes a list of points and/or sliders and stores it into a single input. It only works if the size or order of the list does not change during page loads, however the list can change its shape for variants of the question. The primary use target for this are the vertices of polygons, but one can probably come up with something else as well, it does work as a quick and dirty way of storing the whole graph state if the graph can be defined just by points and sliders. -There are also two new functions realted to dealing with groups of objects and matching inputs. For situations where the answer consists of multiple elements and it is possible that not all get moved one can use `stack_jxg.define_group(list)` which takes a list of points and/or sliders and makes it so that touching any one of them will trigger them all to be considered as touched and thus generates inputs. There is also `stack_jxg.starts_moved(object)` which takes a point or a slider and marks it as touched from the start, this may be of use if the graph is an optional part and the actual grading depends of other parts or if one wants to use PRT feedback as a way for describing the status of the graph and needs the objects to be transferred onto the CAS side without interaction from the student. +There are also two new functions related to dealing with groups of objects and matching inputs. For situations where the answer consists of multiple elements and it is possible that not all get moved one can use `stack_jxg.define_group(list)` which takes a list of points and/or sliders and makes it so that touching any one of them will trigger them all to be considered as touched and thus generates inputs. There is also `stack_jxg.starts_moved(object)` which takes a point or a slider and marks it as touched from the start, this may be of use if the graph is an optional part and the actual grading depends of other parts or if one wants to use PRT feedback as a way for describing the status of the graph and needs the objects to be transferred onto the CAS side without interaction from the student. ## Convenience tools for generating lists of values. diff --git a/doc/en/Authoring/Tidy_Tool.md b/doc/en/Authoring/Tidy_Tool.md index 15cecffeed3..827a956214f 100644 --- a/doc/en/Authoring/Tidy_Tool.md +++ b/doc/en/Authoring/Tidy_Tool.md @@ -1,4 +1,4 @@ -g## Tidy Tool +# Tidy Tool STACK potential response trees, and other parts of questions, can easily get messy. You may have one long question and delete some inputs, or duplicate a question and use it as a template but have to heavily edit it. Another example is to add nodes in an existing PRT and place these nodes between existing nodes. The numbering won't be in order and it will make it harder to follow the PRT logic. diff --git a/doc/en/Developer/Development_history.md b/doc/en/Developer/Development_history.md index c27da2bfd5d..13374b944ff 100644 --- a/doc/en/Developer/Development_history.md +++ b/doc/en/Developer/Development_history.md @@ -66,7 +66,7 @@ Major re-working of Javascript in STACK. Specifically 1. STACK-JS a VLE agnostic JavaScript system that moves all script execution into sandbox iframes and restricts the things those scripts can do outside that sandbox. Basically, replaces the `[[jsxgraph]]`-block and provides ways for doing other scripting. 2. Initial implementation of the `[[reveal]]`-block (#570) using the STACK-JS system. -3. Various related blocks like `[[iframe]]`, `[[javascript]]`, `[[style]`, `[[script]]`, and `[[cors]]` +3. Various related blocks like `[[iframe]]`, `[[javascript]]`, `[[style]]`, `[[script]]`, and `[[cors]]` 4. This version does not yet forbide all JavaScript outside STACK-JS, but do prepare future updates to do so and start migrating existing scripts into STACK-JS. These changes are significant and we strongly recommned you test all affected questions. From 9c340e6ca3cee24e3042ff3d5919822d190bf881 Mon Sep 17 00:00:00 2001 From: Chris Sangwin Date: Thu, 29 Feb 2024 16:23:35 +0000 Subject: [PATCH 4/9] Update links in the docs (WIP). --- doc/en/AbInitio/Authoring_quick_start_1.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/en/AbInitio/Authoring_quick_start_1.md b/doc/en/AbInitio/Authoring_quick_start_1.md index dff41fcbd26..e8c63f959b1 100644 --- a/doc/en/AbInitio/Authoring_quick_start_1.md +++ b/doc/en/AbInitio/Authoring_quick_start_1.md @@ -10,11 +10,11 @@ The authoring quick start guide shows you how to write STACK questions. Part 1 We assume the following: 1. You have access to a course with STACK installed. -2. You are familiar with simple \(\LaTeX\) formatting for mathematics. Some basic examples are provided in the [CASText](/Authoring/CASText.md) documentation. +2. You are familiar with simple \(\LaTeX\) formatting for mathematics. Some basic examples are provided in the [CASText](../Authoring/CASText.md) documentation. ## Creating a minimal STACK question -Go to your Course, navigate to the [question bank](/Moodle/Question_bank.md) and create a new question with the "STACK" question type. +Go to your Course, navigate to the [question bank](../Moodle/Question_bank.md) and create a new question with the "STACK" question type. There are lots of fields, but only a few are compulsory: @@ -23,7 +23,7 @@ There are lots of fields, but only a few are compulsory: 3. The teacher's "model answer" (inside "Input: ans1" on a default question), 4. A test of "correctness". -By default a new question automatically has one [input](/Authoring/Inputs.md), and one algorithm to test correctness of the answer. +By default a new question automatically has one [input](../Authoring/Inputs.md), and one algorithm to test correctness of the answer. ### Question name ### @@ -47,7 +47,7 @@ Notes: ## Input: ans1 -Scroll down: there will be an [inputs](/Authoring/Inputs.md) section of the editing form. Click on the header `Input: ans1` to reveal the relevant settings. +Scroll down: there will be an [inputs](../Authoring/Inputs.md) section of the editing form. Click on the header `Input: ans1` to reveal the relevant settings. For a minimal question, we must specify the _model answer_. Let this be @@ -57,16 +57,16 @@ Notes 1. The student's response is stored in the answer variable `ans1`. 2. The model answer must be a syntactically valid expression in CAS (Maxima) syntax, not LaTeX. This means multiplication must be explicitly specified, using `*`. -3. [Inputs](/Authoring/Inputs.md) can have a variety of types selected by the _Input type_ drop-down menu. The _Algebraic input_ is the default, and what we need here. +3. [Inputs](../Authoring/Inputs.md) can have a variety of types selected by the _Input type_ drop-down menu. The _Algebraic input_ is the default, and what we need here. 4. A question can have many inputs for multiple parts. These are discussed later in a later part. ## Assessing correctness of a response - the Potential Response Tree (PRT) Next we have to decide if the student's answer is correct. -To grade the student's response, we need to determine its mathematical properties using an algorithm known as a [potential response tree](/Authoring/Potential_response_trees.md). +To grade the student's response, we need to determine its mathematical properties using an algorithm known as a [potential response tree](../Authoring/Potential_response_trees.md). -By default, a new question contains one [potential response tree](/Authoring/Potential_response_trees.md) called `prt1`. Feedback generated by the tree replaces the tag `[[feedback:prt1]]` at the appropriate time. +By default, a new question contains one [potential response tree](../Authoring/Potential_response_trees.md) called `prt1`. Feedback generated by the tree replaces the tag `[[feedback:prt1]]` at the appropriate time. ### Configuring a potential response node @@ -79,8 +79,8 @@ A potential response tree is a non-empty acyclic directed graph of _potential re Each branch can then * Assign/update the score, -* Assign formative [feedback](/Authoring/Feedback.md) to the student, -* Leave an [answer note](/Authoring/Potential_response_trees.md#Answer_note) for statistical [reporting](/Authoring/Reporting.md) purposes, +* Assign formative [feedback](../Authoring/Feedback.md) to the student, +* Leave an [answer note](../Authoring/Potential_response_trees.md#Answer_note) for statistical [reporting](../Authoring/Reporting.md) purposes, * Continue to the next potential response node, or end the process with `[stop]`. Let us configure the first node to determine if the student has differentiated correctly. @@ -128,7 +128,7 @@ First is "validation", and normally servers have "instant validation" enabled. The second stage executes when a valid expression is entered, and this evaluates the potential response tree to assess the student's answer. -This two-stage process is a unique and essential feature of STACK. There are lots of options for validation to help the student. For example, in the above, all example expressions have strict syntax. Here we used expressions like `3*(x-1)^2`, with `*` symbols to denote multiplication. You could choose to let students type in expressions like `3(x-1)^2` and accept implied multiplication. Note, however, that teacher input will always have to be strict to avoid ambiguity. Documentation on these options is given in the [inputs](/Authoring/Inputs.md) section. +This two-stage process is a unique and essential feature of STACK. There are lots of options for validation to help the student. For example, in the above, all example expressions have strict syntax. Here we used expressions like `3*(x-1)^2`, with `*` symbols to denote multiplication. You could choose to let students type in expressions like `3(x-1)^2` and accept implied multiplication. Note, however, that teacher input will always have to be strict to avoid ambiguity. Documentation on these options is given in the [inputs](../Authoring/Inputs.md) section. # Next step # From b4136712b00dc3940d6be10101284cd657fd0406 Mon Sep 17 00:00:00 2001 From: Chris Sangwin Date: Thu, 29 Feb 2024 16:54:17 +0000 Subject: [PATCH 5/9] Fix broken back-links in the docs. --- doc/en/AbInitio/Authoring_quick_start_2.md | 4 ++-- doc/en/AbInitio/Authoring_quick_start_3.md | 6 +++--- doc/en/AbInitio/Authoring_quick_start_4.md | 2 +- doc/en/AbInitio/Authoring_quick_start_5.md | 2 +- doc/en/AbInitio/Authoring_quick_start_8.md | 12 ++++++------ doc/en/AbInitio/index.md | 2 +- doc/en/Authoring/Potential_response_trees.md | 2 +- doc/en/Authoring/Sample_questions.md | 2 +- doc/en/Authoring/index.md | 8 ++++---- doc/en/CAS/Simplification.md | 2 +- doc/en/index.md | 2 +- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/doc/en/AbInitio/Authoring_quick_start_2.md b/doc/en/AbInitio/Authoring_quick_start_2.md index fb78fab4931..a9cba7b6014 100644 --- a/doc/en/AbInitio/Authoring_quick_start_2.md +++ b/doc/en/AbInitio/Authoring_quick_start_2.md @@ -20,7 +20,7 @@ Let us focus on the problem of finding \(\int 3(x-1)^{-4} \mathrm{d}x\). Create ### Using question variables -The next steps would be to add question text, and then to add the teacher's answer `-1*(x-1)^(-3)+c` to the `model answer` field and the `potential response tree`. However, the expression and model answer will normally be referred to more than once, so it is usually easiest to assign them to "question variables" using the optional [question variables](/Authoring/Variables.md#Question_variables) field. +The next steps would be to add question text, and then to add the teacher's answer `-1*(x-1)^(-3)+c` to the `model answer` field and the `potential response tree`. However, the expression and model answer will normally be referred to more than once, so it is usually easiest to assign them to "question variables" using the optional [question variables](../Authoring/Variables.md#Question_variables) field. Add the following to the question variables @@ -38,7 +38,7 @@ Now it will be a lot faster to fill out the rest of the question. Add the follow Find \(\int{@exp@} \mathrm{d}x\) [[input:ans1]] [[validation:ans1]] -Notice that we have defined a local variable `exp`, and used the value of this in the Question text. There is a difference between mathematics enclosed between `\(..\)` symbols and `{@..@}` symbols. All the text-based fields in the question, including feedback, are [CAS text](/Authoring/CASText.md). This is HTML into which mathematics can be inserted. LaTeX is placed between `\(..\)`s, and CAS expressions (including your variables) between matching `{@..@}` symbols. The CAS expressions are evaluated in the context of the question variables and displayed as LaTeX. +Notice that we have defined a local variable `exp`, and used the value of this in the Question text. There is a difference between mathematics enclosed between `\(..\)` symbols and `{@..@}` symbols. All the text-based fields in the question, including feedback, are [CAS text](../Authoring/CASText.md). This is HTML into which mathematics can be inserted. LaTeX is placed between `\(..\)`s, and CAS expressions (including your variables) between matching `{@..@}` symbols. The CAS expressions are evaluated in the context of the question variables and displayed as LaTeX. Since we have used `{@exp@}` here, the user will not see a \(exp\) on the screen when the question is instantiated, but the _displayed value_ of `exp`: \(\frac{3}{(x-1)^{-4}}\) diff --git a/doc/en/AbInitio/Authoring_quick_start_3.md b/doc/en/AbInitio/Authoring_quick_start_3.md index 8f8b23a1d60..e4d5265a448 100644 --- a/doc/en/AbInitio/Authoring_quick_start_3.md +++ b/doc/en/AbInitio/Authoring_quick_start_3.md @@ -15,7 +15,7 @@ Try previewing this question and typing in `-1*(x-1)^(-3)+c`. The system should ## Answer test: Int -We will need to edit the potential response tree to use a better [answer test](/Authoring/Answer_Tests/Calculus.md). Return to the page "Editing a STACK question". Find your potential response tree settings, click on the drop-down menu where we selected `AlgEquiv` and select `Int` from the list. Type `x` (the variable) into the Test options setting. Now press the `[Save changes and continue editing]` button and once more click the preview button. We have just selected a special [answer test](/Authoring/Answer_Tests/Calculus.md) for dealing with integration questions. +We will need to edit the potential response tree to use a better [answer test](../Authoring/Answer_Tests/Calculus.md). Return to the page "Editing a STACK question". Find your potential response tree settings, click on the drop-down menu where we selected `AlgEquiv` and select `Int` from the list. Type `x` (the variable) into the Test options setting. Now press the `[Save changes and continue editing]` button and once more click the preview button. We have just selected a special [answer test](../Authoring/Answer_Tests/Calculus.md) for dealing with integration questions. The _Int_ answer test will accept any variable name for the constant of integration. For example, try typing in `-(x-1)^(-3)+k`. The system should accept this as correct. It will also give standard feedback if the student forgets a constant of integration or accidentally differentiates instead. To try this, type `-12*(x-1)^(-5)`. If you don't want students to see the automatic feedback, select the _Quiet_ option in the potential response node. @@ -38,7 +38,7 @@ For each mistake we think students might make, we can create an answer test. For ![Adding a new node](../../content/add_new_node.png) -Go to the potential response tree and click `[Add another node]` . Then under Node 1's `True` branch change `Next` to `Node 2`. If we enter Node 2, we know the student has the correct answer and just need to establish if it is factored or not. To establish this we need to use the [FacForm answer test](/Authoring/Answer_Tests/index.md). This tests both that SAns and TAns are equivalent, and that SAns is factored. In this case we already know that the student's answer is equivalent to the teacher's answer (using *Int*'s better tailored algorithm). Hence we can just test the student's answer against itself. +Go to the potential response tree and click `[Add another node]` . Then under Node 1's `True` branch change `Next` to `Node 2`. If we enter Node 2, we know the student has the correct answer and just need to establish if it is factored or not. To establish this we need to use the [FacForm answer test](../Authoring/Answer_Tests/index.md). This tests both that SAns and TAns are equivalent, and that SAns is factored. In this case we already know that the student's answer is equivalent to the teacher's answer (using *Int*'s better tailored algorithm). Hence we can just test the student's answer against itself. Update the form so that Node 2 has @@ -59,7 +59,7 @@ FacForm gives automatic feedback, but if you want to write your own you can set Your answer is not factored. Well done for getting the correct answer, but remember that there is no need to expand out the brackets. ``` -You can continue to expand your potential response tree, checking for as many common mistakes as you would like to. See the [documentation](/Authoring/Answer_Tests/index.md) for information on more answer tests. +You can continue to expand your potential response tree, checking for as many common mistakes as you would like to. See the [documentation](../Authoring/Answer_Tests/index.md) for information on more answer tests. ## Adding general feedback diff --git a/doc/en/AbInitio/Authoring_quick_start_4.md b/doc/en/AbInitio/Authoring_quick_start_4.md index 2b911008ce2..506d416571a 100644 --- a/doc/en/AbInitio/Authoring_quick_start_4.md +++ b/doc/en/AbInitio/Authoring_quick_start_4.md @@ -21,7 +21,7 @@ ta: int(exp,x)+c; We defined two local variables `exp` and `ta`, and used these values in other places such as the question text, input and potential response tree. -We are now in a position to generate a random question. To do this, modify the [question variables](/Authoring/Variables.md#Question_variables) to be +We are now in a position to generate a random question. To do this, modify the [question variables](../Authoring/Variables.md#Question_variables) to be ``` a1 : 1+rand(6); diff --git a/doc/en/AbInitio/Authoring_quick_start_5.md b/doc/en/AbInitio/Authoring_quick_start_5.md index 7b752f95f80..8e7e40673c5 100644 --- a/doc/en/AbInitio/Authoring_quick_start_5.md +++ b/doc/en/AbInitio/Authoring_quick_start_5.md @@ -68,7 +68,7 @@ We are testing that if we multiply by \(-nn+1\) instead of dividing, we should b You will see that not all deployed versions pass all tests, and if you click on a variant that failed a test, you will see why! Essentially, when \(nn=2\), \(-nn+1=-1\) multiplication and division are equivalent. Essentially, these random variants are "easier" than the others. This illustrates another key use of question tests - ensuring that all variants are the same difficulty and test the knowledge they are supposed to. In light of this, you may want to change `nn` again to ` 3+rand(4)` . Now all variants should pass all question tests. -Quality control is essential, and more information is given in the page on [testing](/Authoring/Testing.md). +Quality control is essential, and more information is given in the page on [testing](../Authoring/Testing.md). ## Aside: forbidden words diff --git a/doc/en/AbInitio/Authoring_quick_start_8.md b/doc/en/AbInitio/Authoring_quick_start_8.md index c2d0462d290..09850107cca 100644 --- a/doc/en/AbInitio/Authoring_quick_start_8.md +++ b/doc/en/AbInitio/Authoring_quick_start_8.md @@ -96,10 +96,10 @@ You should now be able to work with quizzes in Moodle. This concludes the authoring quick start guide. The STACK documentation is comprehensive, and there are many things you might want to look at next. For example, you can -- learn about more [input types](/Authoring/Inputs.md), -- learn about more [answer tests](/Authoring/Answer_Tests/index.md), -- add [plots](../Plots/Plots.md) to your [CASText](/Authoring/CASText.md) fields, -- add support for [multiple languages](/Authoring/Languages.md), -- learn about using [equivalence reasoning](/Authoring/Equivalence_reasoning.md), -- read about [Curve sketching](/Topics/Curve_sketching.md). +- learn about more [input types](../Authoring/Inputs.md), +- learn about more [answer tests](../Authoring/Answer_Tests/index.md), +- add [plots](../Plots/Plots.md) to your [CASText](../Authoring/CASText.md) fields, +- add support for [multiple languages](../Authoring/Languages.md), +- learn about using [equivalence reasoning](../Authoring/Equivalence_reasoning.md), +- read about [Curve sketching](../Topics/Curve_sketching.md). - look at more information on [Maxima](../CAS/index.md), particularly the Maxima documentation if you are not very familiar with Maxima's syntax and function names. A graphical Maxima interface like [wxMaxima](http://andrejv.github.com/wxmaxima/) can also be very helpful for finding the appropriate Maxima commands easily. diff --git a/doc/en/AbInitio/index.md b/doc/en/AbInitio/index.md index 176c1e714d3..84438d247e3 100644 --- a/doc/en/AbInitio/index.md +++ b/doc/en/AbInitio/index.md @@ -13,4 +13,4 @@ Those new to STACK will probably prefer to begin with the [authoring quick start ## See also -* [Authoring](/Authoring/index.md) +* [Authoring](../Authoring/index.md) diff --git a/doc/en/Authoring/Potential_response_trees.md b/doc/en/Authoring/Potential_response_trees.md index fee478e5d74..8218ada9051 100644 --- a/doc/en/Authoring/Potential_response_trees.md +++ b/doc/en/Authoring/Potential_response_trees.md @@ -1,6 +1,6 @@ # Potential response trees -The potential response tree is the algorithm which establishes the mathematical properties of the student's answer and assigns outcomes. For examples of how to use this, see the entry on [improving feedback](/AbInitio/Authoring_quick_start_3.md) in the quick start guide. +The potential response tree is the algorithm which establishes the mathematical properties of the student's answer and assigns outcomes. For examples of how to use this, see the entry on [improving feedback](../AbInitio/Authoring_quick_start_3.md) in the quick start guide. ## When is the tree used? ## diff --git a/doc/en/Authoring/Sample_questions.md b/doc/en/Authoring/Sample_questions.md index 368b3e6320c..9146f07e87c 100644 --- a/doc/en/Authoring/Sample_questions.md +++ b/doc/en/Authoring/Sample_questions.md @@ -38,7 +38,7 @@ This question creates two random matrices and asks students to multiply them tog Given a complex number \(z=ae^{ib}\) determine \(|z^{n}|\) and \(\arg(z^{n})\). Where \(a\), \(b\) and \(n\) are randomly generated numbers. -See the [authoring quick start 7](/AbInitio/Authoring_quick_start_7.md). +See the [authoring quick start 7](../AbInitio/Authoring_quick_start_7.md). ### `test_5_cubic-spline` ### diff --git a/doc/en/Authoring/index.md b/doc/en/Authoring/index.md index 4aff6eb6b9b..1d07ef01875 100644 --- a/doc/en/Authoring/index.md +++ b/doc/en/Authoring/index.md @@ -62,7 +62,7 @@ The authoring documentation also covers topics on: If you cannot find documentation on the topic you are looking for, it may be located in the [CAS](../CAS/index.md) section of the documentation. This includes documentation on working with Maxima in a question, and so covers topics like -* [Inequalities](/CAS/Inequalities.md), -* [Randomisation](/CAS/Random.md), -* [Plotting graphs../Plots/Plots.md), -* [Simplification](/CAS/Simplification.md). +* [Inequalities](../CAS/Inequalities.md), +* [Randomisation](../CAS/Random.md), +* [Plotting graphs](../Plots/Plots.md), +* [Simplification](../CAS/Simplification.md). diff --git a/doc/en/CAS/Simplification.md b/doc/en/CAS/Simplification.md index 38afe46a300..254f094dd1e 100644 --- a/doc/en/CAS/Simplification.md +++ b/doc/en/CAS/Simplification.md @@ -283,7 +283,7 @@ See the page on [propositional logic](../Topics/Propositional_Logic.md). Some further examples are given elsewhere: * Matrix examples in [showing working](Matrix.md#Showing-working). -* An example of a question with `simp:false` is discussed in [authoring quick start 7](/AbInitio/Authoring_quick_start_7.md). +* An example of a question with `simp:false` is discussed in [authoring quick start 7](../AbInitio/Authoring_quick_start_7.md). * Generating [random algebraic expressions](Random.md) which need to be "gathered and sorted". Note also that [question tests](../Authoring/Testing.md#Simplification) do not simplify test inputs. diff --git a/doc/en/index.md b/doc/en/index.md index a653c2a3b99..0867581b74b 100644 --- a/doc/en/index.md +++ b/doc/en/index.md @@ -20,7 +20,7 @@ This is the official user documentation for STACK. Documentation for question authors, including: * An [authoring quick start guide](Authoring/Authoring_quick_start.md), which can be used in conjunction with the "Getting started with STACK" [guide](../content/2019-STACK-Guide.pdf). -* Information on various [authoring topics](Authoring/index.md), such as [answer tests](Authoring/Answer_Tests/index.md), [inputs](/Authoring/Inputs.md), [potential response trees](Authoring/Potential_response_trees.md) and [translations](Authoring/Languages.md). +* Information on various [authoring topics](Authoring/index.md), such as [answer tests](Authoring/Answer_Tests/index.md), [inputs](Authoring/Inputs.md), [potential response trees](Authoring/Potential_response_trees.md) and [translations](Authoring/Languages.md). * [Frequently asked questions](Authoring/Author_FAQ.md). * Information on using the [CAS Maxima](CAS/index.md) for question authoring, including [randomisation](CAS/Random.md), [plotting graphs](Plots/index.md), and expression [simplification](CAS/Simplification.md). From a2b4257d01dc3e4c94d6e0afdd2d18251f5c76bf Mon Sep 17 00:00:00 2001 From: Chris Sangwin Date: Fri, 8 Mar 2024 07:35:19 +0000 Subject: [PATCH 6/9] Workaround to issue #1129. TODO: error trapping in user contributed functions. --- stack/maxima/contrib/vectorcalculus.mac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stack/maxima/contrib/vectorcalculus.mac b/stack/maxima/contrib/vectorcalculus.mac index e41351c22bc..9570f3b6e52 100644 --- a/stack/maxima/contrib/vectorcalculus.mac +++ b/stack/maxima/contrib/vectorcalculus.mac @@ -24,7 +24,7 @@ /* Calculate the divergence of a vector-valued function */ /****************************************************************/ div(u, vars):= block([div_vec], - if not(listp(vars)) or emptyp(vars) then error("div: the second argument must be a list of variables."), + /* TODO: error trapping: if not(listp(vars)) or emptyp(vars) then error("div: the second argument must be a list of variables."), */ if matrixp(u) then funcs: list_matrix_entries(u) else funcs: flatten(u), /* TODO: confirm div should always simplify? */ div_vec: map(lambda([ex], ev(diff(funcs[ex],vars[ex]), simp)), ev(makelist(ii,ii,1,length(vars)), simp)), @@ -39,7 +39,7 @@ s_test_case(div(matrix([x^2*cos(y),y^3]),[x,y]), 2*x*cos(y)+3*y^2); /* Calculate the curl of a vector-valued function */ /****************************************************************/ curl(u,vars):= block([cux, cuy, cuz], - if not(listp(vars)) or emptyp(vars) then error("curl: the second argument must be a list of 3 variables."), + /* TODO: error trapping: if not(listp(vars)) or emptyp(vars) then error("curl: the second argument must be a list of 3 variables."), */ if matrixp(u) then [ux,uy,uz]: list_matrix_entries(u) else [ux,uy,uz]: flatten(u), cux: diff(uz,vars[2]) - diff(uy,vars[3]), cuy: diff(ux,vars[3]) - diff(uz,vars[1]), From 8039ff2c760bb9b28fd15b839770b146571e0f3d Mon Sep 17 00:00:00 2001 From: shinuito <26803867+shinuito@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:30:34 +0000 Subject: [PATCH 7/9] Update Differential_equations.md Add an example to the docs of manipulating Laplace Transforms with Maxima. --- doc/en/Topics/Differential_equations.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/en/Topics/Differential_equations.md b/doc/en/Topics/Differential_equations.md index ed78f9b6df4..3887325fbe2 100644 --- a/doc/en/Topics/Differential_equations.md +++ b/doc/en/Topics/Differential_equations.md @@ -67,6 +67,17 @@ Further examples and documentation are given in the [Maxima manual](http://maxim Note that by default STACK changes the value of Maxima's `logabs` variable. This changes the way \(1/x\) is integrated. If you want the default behaviour of Maxima you will need to restore `logabs:false` in the question variables. +### Laplace Transforms ### + +Constant coefficient ODEs can also be manipulated in STACK using Laplace Transforms. An example of a second-order constant coefficient differential equation is given below with initial conditions set and the result of the Laplace Transform is stored. + + ode: 5*'diff(x(t),t,2)-4*'diff(x(t),t)+7*x(t)=0; + sol: solve(laplace(ode,t,s), 'laplace(x(t), t, s)); + sol: rhs(sol[1]); + sol: subst([x(0)=-1,diff(x(t), t)=0],sol); + +The `laplace` command will Laplace Transform the ode (more information in maxima docs [here](https://maxima.sourceforge.io/docs/manual/maxima_104.html#index-laplace)), but it will still be in terms of the Laplace Transform of `x(t)`, which is symbolic. The `solve` command then solves the algebraic equation for this symbolic Laplace Transformed function, and on the right-hand side of the equals sign, the desired answer is obtained using the `rhs` command. Lastly, the initial conditions need to be specified for `x(t)`. The Laplace Transform symbolically specifies values for `x(0)` and `x'(0)` and these can be replaced with the `subst` command as shown above. + ## Randomly generating ODE problems ## When randomly generating questions we could easily generate an ODE which cannot be solved in closed form, so that in particular using ode2 may be problematic. @@ -366,4 +377,4 @@ Further examples are ## See also -[Maxima reference topics](index.md#reference) \ No newline at end of file +[Maxima reference topics](index.md#reference) From c62c03dfefea21ab3e9701e16a6925f0a313aafa Mon Sep 17 00:00:00 2001 From: Luke Longworth <34358809+LukeLongworth@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:34:02 +1300 Subject: [PATCH 8/9] Update validators.mac Added `validate_interval_syntax`, a way to give more detailed feedback for students who are new to real intervals and try to input `[]` or `()`. --- stack/maxima/contrib/validators.mac | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/stack/maxima/contrib/validators.mac b/stack/maxima/contrib/validators.mac index 46a2a89057a..24da703ef29 100644 --- a/stack/maxima/contrib/validators.mac +++ b/stack/maxima/contrib/validators.mac @@ -57,3 +57,21 @@ s_test_case(validate_all_one_letter_variables(1), ""); s_test_case(validate_all_one_letter_variables((A*x+B)/(x^2+1) + C/x), ""); s_test_case(validate_all_one_letter_variables((Ax+B)/(x^2+1) + C/x), "Only single-character variable names are permitted in this input. Perhaps you forgot to use an asterisk (*) somewhere, or perhaps you used a Greek letter."); s_test_case(validate_all_one_letter_variables((theta*x+B)/(x^2+1) + C/x), "Only single-character variable names are permitted in this input. Perhaps you forgot to use an asterisk (*) somewhere, or perhaps you used a Greek letter."); + +/* This provides more detailed feedback for students who try to enter fully closed or open intervals using [] or () instead of cc(a,b) or oo(a,b). */ +/* It is intended for early courses where students might be new to using this written notation and STACK. */ +/* This does not work well with "Check type of response" turned on, and provides slightly awkward feedback when students take a union of multiple intervals with incorrect syntax. */ +validate_interval_syntax(ex):= block( + if ev(listp(ex),simp) then return(sconcat("To give a closed interval, use cc(",first(args(ex)),",",second(args(ex)),"), not [",first(args(ex)),",",second(args(ex)),"]. ")) + else if ev(ntuplep(ex),simp) then return(sconcat("To give an open interval, use oo(",first(args(ex)),",",second(args(ex)),"), not [",first(args(ex)),",",second(args(ex)),"]. ")) + else if is(safe_op(ex)="%union") then apply(sconcat, map(validate_interval_syntax, args(ex))) + else return("") +); + +s_test_case(validate_interval_syntax(cc(1,2)), ""); +s_test_case(validate_interval_syntax(oo(1,2)), ""); +s_test_case(validate_interval_syntax(%union(cc(1,2),oo(2,3))), ""); +s_test_case(validate_interval_syntax([1,2]), "To give a closed interval, use cc(1,2), not [1,2]. "); +s_test_case(validate_interval_syntax(ntuple(1,2)), "To give an open interval, use oo(1,2), not [1,2]. "); +s_test_case(validate_interval_syntax(%union([1,2],ntuple(2,3))), "To give a closed interval, use cc(1,2), not [1,2]. To give an open interval, use oo(1,2), not [1,2]. "); +s_test_case(validate_interval_syntax(%union([1,2],%union(oo(1,2),[2,3]))), "To give a closed interval, use cc(1,2), not [1,2]. To give a closed interval, use cc(2,3), not [2,3]. "); From 333911a5021071e2058c4f185e8182275810ca66 Mon Sep 17 00:00:00 2001 From: Luke Longworth <34358809+LukeLongworth@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:41:50 +1300 Subject: [PATCH 9/9] Update validators.mac Fixed a typo in the `validate_interval_syntax` feedback; it previously used square brackets for an open interval --- stack/maxima/contrib/validators.mac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stack/maxima/contrib/validators.mac b/stack/maxima/contrib/validators.mac index 24da703ef29..ede1dc9d564 100644 --- a/stack/maxima/contrib/validators.mac +++ b/stack/maxima/contrib/validators.mac @@ -63,7 +63,7 @@ s_test_case(validate_all_one_letter_variables((theta*x+B)/(x^2+1) + C/x), "Only /* This does not work well with "Check type of response" turned on, and provides slightly awkward feedback when students take a union of multiple intervals with incorrect syntax. */ validate_interval_syntax(ex):= block( if ev(listp(ex),simp) then return(sconcat("To give a closed interval, use cc(",first(args(ex)),",",second(args(ex)),"), not [",first(args(ex)),",",second(args(ex)),"]. ")) - else if ev(ntuplep(ex),simp) then return(sconcat("To give an open interval, use oo(",first(args(ex)),",",second(args(ex)),"), not [",first(args(ex)),",",second(args(ex)),"]. ")) + else if ev(ntuplep(ex),simp) then return(sconcat("To give an open interval, use oo(",first(args(ex)),",",second(args(ex)),"), not (",first(args(ex)),",",second(args(ex)),"). ")) else if is(safe_op(ex)="%union") then apply(sconcat, map(validate_interval_syntax, args(ex))) else return("") ); @@ -72,6 +72,6 @@ s_test_case(validate_interval_syntax(cc(1,2)), ""); s_test_case(validate_interval_syntax(oo(1,2)), ""); s_test_case(validate_interval_syntax(%union(cc(1,2),oo(2,3))), ""); s_test_case(validate_interval_syntax([1,2]), "To give a closed interval, use cc(1,2), not [1,2]. "); -s_test_case(validate_interval_syntax(ntuple(1,2)), "To give an open interval, use oo(1,2), not [1,2]. "); -s_test_case(validate_interval_syntax(%union([1,2],ntuple(2,3))), "To give a closed interval, use cc(1,2), not [1,2]. To give an open interval, use oo(1,2), not [1,2]. "); +s_test_case(validate_interval_syntax(ntuple(1,2)), "To give an open interval, use oo(1,2), not (1,2). "); +s_test_case(validate_interval_syntax(%union([1,2],ntuple(2,3))), "To give a closed interval, use cc(1,2), not [1,2]. To give an open interval, use oo(1,2), not (1,2). "); s_test_case(validate_interval_syntax(%union([1,2],%union(oo(1,2),[2,3]))), "To give a closed interval, use cc(1,2), not [1,2]. To give a closed interval, use cc(2,3), not [2,3]. ");