Description of subtopic is here.
.This is sample subtopic with dummy content related to Fractions.
Fractions represent equal parts of a whole or a collection. Fraction of a whole: When we divide a whole into equal parts, each part is a fraction of the whole. A fraction has two parts. The number on the top of the line is called the numerator. It tells how many equal parts of the whole or collection are taken. The number below the line is called the denominator. It shows the total divisible number of equal parts the whole into or the total number of equal parts which are there in a collection."
+ "html": "
Description of subtopic is here.
.Fractions represent equal parts of a whole or a collection. Fraction of a whole: When we divide a whole into equal parts, each part is a fraction of the whole. A fraction has two parts. The number on the top of the line is called the numerator. It tells how many equal parts of the whole or collection are taken. The number below the line is called the denominator. It shows the total divisible number of equal parts the whole into or the total number of equal parts which are there in a collection. Also, here's the equation for a line using in-line LaTeX:
Description of subtopic is here.
.This is sample subtopic with dummy content related to Fractions.
Fractions represent equal parts of a whole or a collection. Fraction of a whole: When we divide a whole into equal parts, each part is a fraction of the whole. A fraction has two parts. The number on the top of the line is called the numerator. It tells how many equal parts of the whole or collection are taken. The number below the line is called the denominator. It shows the total divisible number of equal parts the whole into or the total number of equal parts which are there in a collection."
+ html: "
Description of subtopic is here.
.Fractions represent equal parts of a whole or a collection. Fraction of a whole: When we divide a whole into equal parts, each part is a fraction of the whole. A fraction has two parts. The number on the top of the line is called the numerator. It tells how many equal parts of the whole or collection are taken. The number below the line is called the denominator. It shows the total divisible number of equal parts the whole into or the total number of equal parts which are there in a collection. Also, here's the equation for a line using in-line LaTeX:
Correct! Eagles can sustain flight.
"
},
- "labelled_as_correct": false,
+ "labelled_as_correct": true,
"param_changes": [],
"refresher_exploration_id": "",
"missing_prerequisite_skill_id": ""
@@ -421,7 +421,7 @@
"content_id": "feedback_1",
"html": "Correct! Eagles can sustain flight.
"
content_id: "feedback_1"
}
+ labelled_as_correct: true
}
rule_specs {
input {
@@ -656,6 +658,7 @@ states {
html: "",
+ "correctness_feedback_enabled": false,
+ "version": 0,
+ "record_playthrough_probability": 0.0,
+ "exploration": {
+ "init_state_name": "NumericExpressionInput.MatchesExactlyWith",
+ "param_changes": [],
+ "param_specs": {},
+ "states": {
+ "NumericExpressionInput.MatchesExactlyWith": {
+ "content": {
+ "content_id": "content",
+ "html": "What numeric expression represents one plus two with no reordering allowed? Note: divisions are treated as fractions for this state.
"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "NumericExpressionInput",
+ "customization_args": {
+ "placeholder": {
+ "value": {
+ "content_id": "ca_placeholder_0",
+ "unicode_str": "Input a numeric expression."
+ }
+ },
+ "useFractionForDivision": {
+ "value": true
+ }
+ },
+ "answer_groups": [{
+ "rule_specs": [{
+ "rule_type": "MatchesExactlyWith",
+ "inputs": {
+ "x": "1 + 2"
+ }
+ }],
+ "outcome": {
+ "dest": "NumericExpressionInput.MatchesUpToTrivialManipulations",
+ "feedback": {
+ "content_id": "feedback_1",
+ "html": "Correct!
"
+ },
+ "labelled_as_correct": true,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "tagged_skill_misconception_id": ""
+ }],
+ "default_outcome": {
+ "dest": "NumericExpressionInput.MatchesExactlyWith",
+ "feedback": {
+ "content_id": "default_outcome",
+ "html": "That answer isn't correct. Try again.
"
+ },
+ "labelled_as_correct": false,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "feedback_1": {},
+ "content": {},
+ "default_outcome": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " correto! p>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
صحيح! p>"},
+ "needs_update": false
+ }
+ },
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Que expressão numérica representa uma mais duas sem reordenação permitida? Nota: As divisões são tratadas como frações para este estado. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
ما هو التعبير الرقمي يمثل واحد زائد اثنين دون إعادة ترتيب المسموح به؟ ملاحظة: يتم التعامل مع الانقسامات ككسور لهذه الولاية. p>"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Essa resposta não está correta. Tente novamente. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
تلك الإجابة غير صحيحة. حاول مرة أخرى. p>"},
+ "needs_update": false
+ }
+ },
+ "ca_placeholder_0": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "Insira uma expressão numérica."},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "إدخال تعبير رقمي."},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ },
+ "NumericExpressionInput.MatchesUpToTrivialManipulations": {
+ "content": {
+ "content_id": "content",
+ "html": "
What numeric expression represents one plus two? Note that commutative and associative reordering is allowed.
"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "NumericExpressionInput",
+ "customization_args": {},
+ "answer_groups": [{
+ "rule_specs": [{
+ "rule_type": "MatchesUpToTrivialManipulations",
+ "inputs": {
+ "x": "1 + 2"
+ }
+ }],
+ "outcome": {
+ "dest": "NumericExpressionInput.IsEquivalentTo",
+ "feedback": {
+ "content_id": "feedback_1",
+ "html": "Correct!
"
+ },
+ "labelled_as_correct": true,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "tagged_skill_misconception_id": ""
+ }],
+ "default_outcome": {
+ "dest": "NumericExpressionInput.MatchesUpToTrivialManipulations",
+ "feedback": {
+ "content_id": "default_outcome",
+ "html": "That answer isn't correct. Try again.
"
+ },
+ "labelled_as_correct": false,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "feedback_1": {},
+ "content": {},
+ "default_outcome": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " correto! p>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
صحيح! p>"},
+ "needs_update": false
+ }
+ },
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Que expressão numérica representa um mais dois? Note que a reordenação comutativa e associativa é permitida. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
ما هو التعبير الرقمي يمثل واحد زائد اثنين؟ لاحظ أن إعادة ترتيب التوزيع والزملية مسموح بها. p>"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Essa resposta não está correta. Tente novamente. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
تلك الإجابة غير صحيحة. حاول مرة أخرى. p>"},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ },
+ "NumericExpressionInput.IsEquivalentTo": {
+ "content": {
+ "content_id": "content",
+ "html": "
What numeric expression represents one plus two? Note that any equivalent expression is allowed.
"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "NumericExpressionInput",
+ "customization_args": {},
+ "answer_groups": [{
+ "rule_specs": [{
+ "rule_type": "IsEquivalentTo",
+ "inputs": {
+ "x": "1 + 2"
+ }
+ }],
+ "outcome": {
+ "dest": "AlgebraicExpressionInput.MatchesExactlyWith",
+ "feedback": {
+ "content_id": "feedback_1",
+ "html": "Correct!
"
+ },
+ "labelled_as_correct": true,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "tagged_skill_misconception_id": ""
+ }],
+ "default_outcome": {
+ "dest": "NumericExpressionInput.IsEquivalentTo",
+ "feedback": {
+ "content_id": "default_outcome",
+ "html": "That answer isn't correct. Try again.
"
+ },
+ "labelled_as_correct": false,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "feedback_1": {},
+ "content": {},
+ "default_outcome": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " correto! p>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
صحيح! p>"},
+ "needs_update": false
+ }
+ },
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Que expressão numérica representa um mais dois? Observe que qualquer expressão equivalente é permitida. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
ما هو التعبير الرقمي يمثل واحد زائد اثنين؟ لاحظ أن أي تعبير معادل مسموح به. p>"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Essa resposta não está correta. Tente novamente.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " تلك الإجابة غير صحيحة. حاول مرة أخرى. p>"},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ },
+ "AlgebraicExpressionInput.MatchesExactlyWith": {
+ "content": {
+ "content_id": "content",
+ "html": "
What algebraic expression represents the product of (x+1)(x-2)? Note: divisions are treated as fractions for this state.
"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "AlgebraicExpressionInput",
+ "customization_args": {
+ "useFractionForDivision": {
+ "value": true
+ },
+ "customOskLetters": {
+ "value": ["x"]
+ }
+ },
+ "answer_groups": [{
+ "rule_specs": [{
+ "rule_type": "MatchesExactlyWith",
+ "inputs": {
+ "x": "x^2 - x - 2"
+ }
+ }],
+ "outcome": {
+ "dest": "AlgebraicExpressionInput.MatchesUpToTrivialManipulations",
+ "feedback": {
+ "content_id": "feedback_1",
+ "html": "Correct!
"
+ },
+ "labelled_as_correct": true,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "tagged_skill_misconception_id": ""
+ }],
+ "default_outcome": {
+ "dest": "AlgebraicExpressionInput.MatchesExactlyWith",
+ "feedback": {
+ "content_id": "default_outcome",
+ "html": "That answer isn't correct. Try again.
"
+ },
+ "labelled_as_correct": false,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "feedback_1": {},
+ "content": {},
+ "default_outcome": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " correto! p>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
صحيح! p>"},
+ "needs_update": false
+ }
+ },
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Qual expressão algébrica representa o produto de (x + 1) (x-2)? Nota: As divisões são tratadas como frações para este estado. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
ما هو التعبير الجبري يمثل نتاج (x + 1) (x-2)؟ ملاحظة: يتم التعامل مع الانقسامات ككسور لهذه الولاية. p>"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Essa resposta não está correta. Tente novamente. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
هذه الإجابة غير صحيحة. حاول مرة أخرى. p>"},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ },
+ "AlgebraicExpressionInput.MatchesUpToTrivialManipulations": {
+ "content": {
+ "content_id": "content",
+ "html": "
What algebraic expression represents the product of (x+1)(x-2)? Note that commutative and associative reordering is allowed.
"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "AlgebraicExpressionInput",
+ "customization_args": {
+ "customOskLetters": {
+ "value": ["x"]
+ }
+ },
+ "answer_groups": [{
+ "rule_specs": [{
+ "rule_type": "MatchesUpToTrivialManipulations",
+ "inputs": {
+ "x": "x^2 - x - 2"
+ }
+ }],
+ "outcome": {
+ "dest": "AlgebraicExpressionInput.IsEquivalentTo",
+ "feedback": {
+ "content_id": "feedback_1",
+ "html": "Correct!
"
+ },
+ "labelled_as_correct": true,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "tagged_skill_misconception_id": ""
+ }],
+ "default_outcome": {
+ "dest": "AlgebraicExpressionInput.MatchesUpToTrivialManipulations",
+ "feedback": {
+ "content_id": "default_outcome",
+ "html": "That answer isn't correct. Try again.
"
+ },
+ "labelled_as_correct": false,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "feedback_1": {},
+ "content": {},
+ "default_outcome": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " correto! p>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
صحيح! p>"},
+ "needs_update": false
+ }
+ },
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Qual expressão algébrica representa o produto de (x + 1) (x-2)? Note que a reordenação comutativa e associativa é permitida. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
ما هو التعبير الجبري يمثل نتاج (x + 1) (x-2)؟ لاحظ أن إعادة ترتيب التوزيع والزملية مسموح بها. p>"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Essa resposta não está correta. Tente novamente. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
تلك الإجابة غير صحيحة. حاول مرة أخرى. p>"},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ },
+ "AlgebraicExpressionInput.IsEquivalentTo": {
+ "content": {
+ "content_id": "content",
+ "html": "
What algebraic expression represents the product of (x+1)(x-2)? Note that any equivalent expression is allowed.
"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "AlgebraicExpressionInput",
+ "customization_args": {
+ "customOskLetters": {
+ "value": ["x"]
+ }
+ },
+ "answer_groups": [{
+ "rule_specs": [{
+ "rule_type": "IsEquivalentTo",
+ "inputs": {
+ "x": "x^2 - x - 2"
+ }
+ }],
+ "outcome": {
+ "dest": "MathEquationInput.MatchesExactlyWith",
+ "feedback": {
+ "content_id": "feedback_1",
+ "html": "Correct!
"
+ },
+ "labelled_as_correct": true,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "tagged_skill_misconception_id": ""
+ }],
+ "default_outcome": {
+ "dest": "AlgebraicExpressionInput.IsEquivalentTo",
+ "feedback": {
+ "content_id": "default_outcome",
+ "html": "That answer isn't correct. Try again.
"
+ },
+ "labelled_as_correct": false,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "feedback_1": {},
+ "content": {},
+ "default_outcome": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " correto! p>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
صحيح! p>"},
+ "needs_update": false
+ }
+ },
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Qual expressão algébrica representa o produto de (x + 1) (x-2)? Observe que qualquer expressão equivalente é permitida. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
ما هو التعبير الجبري يمثل نتاج (x + 1) (x-2)؟ لاحظ أن أي تعبير معادل مسموح به. p>"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Essa resposta não está correta. Tente novamente. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
تلك الإجابة غير صحيحة. حاول مرة أخرى. p>"},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ },
+ "MathEquationInput.MatchesExactlyWith": {
+ "content": {
+ "content_id": "content",
+ "html": "
What algebraic equation represents the quantity of two y and (x+1)(x-2)? Note: divisions are treated as fractions for this state.
"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "MathEquationInput",
+ "customization_args": {
+ "useFractionForDivision": {
+ "value": true
+ },
+ "customOskLetters": {
+ "value": ["x", "y"]
+ }
+ },
+ "answer_groups": [{
+ "rule_specs": [{
+ "rule_type": "MatchesExactlyWith",
+ "inputs": {
+ "x": "2y = x^2 - x - 2"
+ }
+ }],
+ "outcome": {
+ "dest": "MathEquationInput.MatchesUpToTrivialManipulations",
+ "feedback": {
+ "content_id": "feedback_1",
+ "html": "Correct!
"
+ },
+ "labelled_as_correct": true,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "tagged_skill_misconception_id": ""
+ }],
+ "default_outcome": {
+ "dest": "MathEquationInput.MatchesExactlyWith",
+ "feedback": {
+ "content_id": "default_outcome",
+ "html": "That answer isn't correct. Try again.
"
+ },
+ "labelled_as_correct": false,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "feedback_1": {},
+ "content": {},
+ "default_outcome": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " correto! p>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
صحيح! p>"},
+ "needs_update": false
+ }
+ },
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Que equação algébrica representa a quantidade de dois y e (x + 1) (x-2)? Observação: as divisões são tratadas como frações para este estado.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " ما هي المعادلة الجبرية تمثل كمية اثنين من y و (x + 1) (x-2)؟ ملاحظة: يتم التعامل مع الانقسامات ككسور لهذه الولاية. p>"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Essa resposta não está correta. Tente novamente. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
تلك الإجابة غير صحيحة. حاول مرة أخرى. p>"},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ },
+ "MathEquationInput.MatchesUpToTrivialManipulations": {
+ "content": {
+ "content_id": "content",
+ "html": "
What algebraic equation represents the quantity of two y and (x+1)(x-2)? Note that commutative and associative reordering is allowed.
"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "MathEquationInput",
+ "customization_args": {
+ "customOskLetters": {
+ "value": ["x", "y"]
+ }
+ },
+ "answer_groups": [{
+ "rule_specs": [{
+ "rule_type": "MatchesUpToTrivialManipulations",
+ "inputs": {
+ "x": "2y = x^2 - x - 2"
+ }
+ }],
+ "outcome": {
+ "dest": "MathEquationInput.IsEquivalentTo",
+ "feedback": {
+ "content_id": "feedback_1",
+ "html": "Correct!
"
+ },
+ "labelled_as_correct": true,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "tagged_skill_misconception_id": ""
+ }],
+ "default_outcome": {
+ "dest": "MathEquationInput.MatchesUpToTrivialManipulations",
+ "feedback": {
+ "content_id": "default_outcome",
+ "html": "That answer isn't correct. Try again.
"
+ },
+ "labelled_as_correct": false,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "feedback_1": {},
+ "content": {},
+ "default_outcome": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " correto! p>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
صحيح! p>"},
+ "needs_update": false
+ }
+ },
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Qual equação algébrica representa a quantidade de dois y e (x + 1) (x-2)? Note que a reordenação comutativa e associativa é permitida. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
ما المعادلة الجبرية التي تمثل كمية اثنين y و (x + 1) (x-2)؟ لاحظ أنه يُسمح بإعادة الترتيب التبادلي والرابطي. p>"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Essa resposta não está correta. Tente novamente. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
تلك الإجابة غير صحيحة. حاول مرة أخرى. p>"},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ },
+ "MathEquationInput.IsEquivalentTo": {
+ "content": {
+ "content_id": "content",
+ "html": "
What algebraic equation represents the quantity of two y and (x+1)(x-2)? Note that any equivalent expression is allowed, including reordering around the equals sign.
"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "MathEquationInput",
+ "customization_args": {
+ "customOskLetters": {
+ "value": ["x", "y"]
+ }
+ },
+ "answer_groups": [{
+ "rule_specs": [{
+ "rule_type": "IsEquivalentTo",
+ "inputs": {
+ "x": "2y = x^2 - x - 2"
+ }
+ }],
+ "outcome": {
+ "dest": "End",
+ "feedback": {
+ "content_id": "feedback_1",
+ "html": "Correct!
"
+ },
+ "labelled_as_correct": true,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "tagged_skill_misconception_id": ""
+ }],
+ "default_outcome": {
+ "dest": "MathEquationInput.IsEquivalentTo",
+ "feedback": {
+ "content_id": "default_outcome",
+ "html": "That answer isn't correct. Try again.
"
+ },
+ "labelled_as_correct": false,
+ "param_changes": [],
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
+ },
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "feedback_1": {},
+ "content": {},
+ "default_outcome": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " correto! p>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
صحيح! p>"},
+ "needs_update": false
+ }
+ },
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Qual equação algébrica representa a quantidade de dois y e (x + 1) (x-2)? Observe que qualquer expressão equivalente é permitida, incluindo reordenando em torno do sinal de igual. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
ما هي المعادلة الجبرية تمثل كمية اثنين من y و (x + 1) (x-2)؟ لاحظ أن أي تعبير معادل مسموح به، بما في ذلك إعادة ترتيب حول علامة التساوي. p>"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "
Essa resposta não está correta. Tente novamente. P>"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "
تلك الإجابة غير صحيحة. حاول مرة أخرى. p>"},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ },
+ "End": {
+ "content": {
+ "content_id": "content",
+ "html": "Congratulations, you have finished!"
+ },
+ "param_changes": [],
+ "interaction": {
+ "id": "EndExploration",
+ "customization_args": {
+ "recommendedExplorationIds": {
+ "value": []
+ }
+ },
+ "answer_groups": [],
+ "default_outcome": null,
+ "hints": [],
+ "solution": null
+ },
+ "classifier_model_id": "",
+ "recorded_voiceovers": {
+ "voiceovers_mapping": {
+ "content": {}
+ }
+ },
+ "written_translations": {
+ "translations_mapping": {
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : "Parabéns, você terminou!"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "تهانينا، لقد انتهيت!"},
+ "needs_update": false
+ }
+ }
+ }
+ },
+ "solicit_answer_details": false,
+ "next_content_id_index": -1
+ }
+ },
+ "objective": "Demonstrate math interactions.",
+ "language_code": "en",
+ "correctness_feedback_enabled": false,
+ "title": "Math Expressions"
+ }
+}
diff --git a/domain/src/main/assets/test_exp_id_5.textproto b/domain/src/main/assets/test_exp_id_5.textproto
new file mode 100644
index 00000000000..8a72003adc9
--- /dev/null
+++ b/domain/src/main/assets/test_exp_id_5.textproto
@@ -0,0 +1,1107 @@
+id: "test_exp_id_5"
+states {
+ key: "NumericExpressionInput.MatchesExactlyWith"
+ value {
+ name: "NumericExpressionInput.MatchesExactlyWith"
+ recorded_voiceovers {
+ key: "feedback_1"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "default_outcome"
+ value {
+ }
+ }
+ content {
+ html: "
What numeric expression represents one plus two with no reordering allowed? Note: divisions are treated as fractions for this state.
"
+ content_id: "content"
+ }
+ written_translations {
+ key: "feedback_1"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " correto! p>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\265\330\255\331\212\330\255! p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Que express\303\243o num\303\251rica representa uma mais duas sem reordena\303\247\303\243o permitida? Nota: As divis\303\265es s\303\243o tratadas como fra\303\247\303\265es para este estado. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\331\205\330\247 \331\207\331\210 \330\247\331\204\330\252\330\271\330\250\331\212\330\261 \330\247\331\204\330\261\331\202\331\205\331\212 \331\212\331\205\330\253\331\204 \331\210\330\247\330\255\330\257 \330\262\330\247\330\246\330\257 \330\247\330\253\331\206\331\212\331\206 \330\257\331\210\331\206 \330\245\330\271\330\247\330\257\330\251 \330\252\330\261\330\252\331\212\330\250 \330\247\331\204\331\205\330\263\331\205\331\210\330\255 \330\250\331\207\330\237 \331\205\331\204\330\247\330\255\330\270\330\251: \331\212\330\252\331\205 \330\247\331\204\330\252\330\271\330\247\331\205\331\204 \331\205\330\271 \330\247\331\204\330\247\331\206\331\202\330\263\330\247\331\205\330\247\330\252 \331\203\331\203\330\263\331\210\330\261 \331\204\331\207\330\260\331\207 \330\247\331\204\331\210\331\204\330\247\331\212\330\251. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "default_outcome"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Essa resposta n\303\243o est\303\241 correta. Tente novamente. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\252\331\204\331\203 \330\247\331\204\330\245\330\254\330\247\330\250\330\251 \330\272\331\212\330\261 \330\265\330\255\331\212\330\255\330\251. \330\255\330\247\331\210\331\204 \331\205\330\261\330\251 \330\243\330\256\330\261\331\211. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "ca_placeholder_0"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "Insira uma express\303\243o num\303\251rica."
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "\330\245\330\257\330\256\330\247\331\204 \330\252\330\271\330\250\331\212\330\261 \330\261\331\202\331\205\331\212."
+ }
+ }
+ }
+ }
+ interaction {
+ id: "NumericExpressionInput"
+ answer_groups {
+ outcome {
+ dest_state_name: "NumericExpressionInput.MatchesUpToTrivialManipulations"
+ feedback {
+ html: "
Correct!
"
+ content_id: "feedback_1"
+ }
+ labelled_as_correct: true
+ }
+ rule_specs {
+ input {
+ key: "x"
+ value {
+ math_expression: "1 + 2"
+ }
+ }
+ rule_type: "MatchesExactlyWith"
+ }
+ }
+ default_outcome {
+ dest_state_name: "NumericExpressionInput.MatchesExactlyWith"
+ feedback {
+ html: "That answer isn\'t correct. Try again.
"
+ content_id: "default_outcome"
+ }
+ }
+ customization_args {
+ key: "placeholder"
+ value {
+ custom_schema_value {
+ subtitled_unicode {
+ unicode_str: "Input a numeric expression."
+ content_id: "ca_placeholder_0"
+ }
+ }
+ }
+ }
+ customization_args {
+ key: "useFractionForDivision"
+ value {
+ bool_value: true
+ }
+ }
+ }
+ }
+}
+states {
+ key: "NumericExpressionInput.MatchesUpToTrivialManipulations"
+ value {
+ name: "NumericExpressionInput.MatchesUpToTrivialManipulations"
+ recorded_voiceovers {
+ key: "feedback_1"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "default_outcome"
+ value {
+ }
+ }
+ content {
+ html: "What numeric expression represents one plus two? Note that commutative and associative reordering is allowed.
"
+ content_id: "content"
+ }
+ written_translations {
+ key: "feedback_1"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " correto! p>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\265\330\255\331\212\330\255! p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Que express\303\243o num\303\251rica representa um mais dois? Note que a reordena\303\247\303\243o comutativa e associativa \303\251 permitida. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\331\205\330\247 \331\207\331\210 \330\247\331\204\330\252\330\271\330\250\331\212\330\261 \330\247\331\204\330\261\331\202\331\205\331\212 \331\212\331\205\330\253\331\204 \331\210\330\247\330\255\330\257 \330\262\330\247\330\246\330\257 \330\247\330\253\331\206\331\212\331\206\330\237 \331\204\330\247\330\255\330\270 \330\243\331\206 \330\245\330\271\330\247\330\257\330\251 \330\252\330\261\330\252\331\212\330\250 \330\247\331\204\330\252\331\210\330\262\331\212\330\271 \331\210\330\247\331\204\330\262\331\205\331\204\331\212\330\251 \331\205\330\263\331\205\331\210\330\255 \330\250\331\207\330\247. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "default_outcome"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Essa resposta n\303\243o est\303\241 correta. Tente novamente. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\252\331\204\331\203 \330\247\331\204\330\245\330\254\330\247\330\250\330\251 \330\272\331\212\330\261 \330\265\330\255\331\212\330\255\330\251. \330\255\330\247\331\210\331\204 \331\205\330\261\330\251 \330\243\330\256\330\261\331\211. p>"
+ }
+ }
+ }
+ }
+ interaction {
+ id: "NumericExpressionInput"
+ answer_groups {
+ outcome {
+ dest_state_name: "NumericExpressionInput.IsEquivalentTo"
+ feedback {
+ html: "
Correct!
"
+ content_id: "feedback_1"
+ }
+ labelled_as_correct: true
+ }
+ rule_specs {
+ input {
+ key: "x"
+ value {
+ math_expression: "1 + 2"
+ }
+ }
+ rule_type: "MatchesUpToTrivialManipulations"
+ }
+ }
+ default_outcome {
+ dest_state_name: "NumericExpressionInput.MatchesUpToTrivialManipulations"
+ feedback {
+ html: "That answer isn\'t correct. Try again.
"
+ content_id: "default_outcome"
+ }
+ }
+ }
+ }
+}
+states {
+ key: "NumericExpressionInput.IsEquivalentTo"
+ value {
+ name: "NumericExpressionInput.IsEquivalentTo"
+ recorded_voiceovers {
+ key: "feedback_1"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "default_outcome"
+ value {
+ }
+ }
+ content {
+ html: "What numeric expression represents one plus two? Note that any equivalent expression is allowed.
"
+ content_id: "content"
+ }
+ written_translations {
+ key: "feedback_1"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " correto! p>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\265\330\255\331\212\330\255! p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Que express\303\243o num\303\251rica representa um mais dois? Observe que qualquer express\303\243o equivalente \303\251 permitida. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\331\205\330\247 \331\207\331\210 \330\247\331\204\330\252\330\271\330\250\331\212\330\261 \330\247\331\204\330\261\331\202\331\205\331\212 \331\212\331\205\330\253\331\204 \331\210\330\247\330\255\330\257 \330\262\330\247\330\246\330\257 \330\247\330\253\331\206\331\212\331\206\330\237 \331\204\330\247\330\255\330\270 \330\243\331\206 \330\243\331\212 \330\252\330\271\330\250\331\212\330\261 \331\205\330\271\330\247\330\257\331\204 \331\205\330\263\331\205\331\210\330\255 \330\250\331\207. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "default_outcome"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Essa resposta n\303\243o est\303\241 correta. Tente novamente.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \330\252\331\204\331\203 \330\247\331\204\330\245\330\254\330\247\330\250\330\251 \330\272\331\212\330\261 \330\265\330\255\331\212\330\255\330\251. \330\255\330\247\331\210\331\204 \331\205\330\261\330\251 \330\243\330\256\330\261\331\211. p>"
+ }
+ }
+ }
+ }
+ interaction {
+ id: "NumericExpressionInput"
+ answer_groups {
+ outcome {
+ dest_state_name: "AlgebraicExpressionInput.MatchesExactlyWith"
+ feedback {
+ html: "
Correct!
"
+ content_id: "feedback_1"
+ }
+ labelled_as_correct: true
+ }
+ rule_specs {
+ input {
+ key: "x"
+ value {
+ math_expression: "1 + 2"
+ }
+ }
+ rule_type: "IsEquivalentTo"
+ }
+ }
+ default_outcome {
+ dest_state_name: "NumericExpressionInput.IsEquivalentTo"
+ feedback {
+ html: "That answer isn\'t correct. Try again.
"
+ content_id: "default_outcome"
+ }
+ }
+ }
+ }
+}
+states {
+ key: "AlgebraicExpressionInput.MatchesExactlyWith"
+ value {
+ name: "AlgebraicExpressionInput.MatchesExactlyWith"
+ recorded_voiceovers {
+ key: "feedback_1"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "default_outcome"
+ value {
+ }
+ }
+ content {
+ html: "What algebraic expression represents the product of (x+1)(x-2)? Note: divisions are treated as fractions for this state.
"
+ content_id: "content"
+ }
+ written_translations {
+ key: "feedback_1"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " correto! p>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\265\330\255\331\212\330\255! p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Qual express\303\243o alg\303\251brica representa o produto de (x + 1) (x-2)? Nota: As divis\303\265es s\303\243o tratadas como fra\303\247\303\265es para este estado. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\331\205\330\247 \331\207\331\210 \330\247\331\204\330\252\330\271\330\250\331\212\330\261 \330\247\331\204\330\254\330\250\330\261\331\212 \331\212\331\205\330\253\331\204 \331\206\330\252\330\247\330\254 (x + 1) (x-2)\330\237 \331\205\331\204\330\247\330\255\330\270\330\251: \331\212\330\252\331\205 \330\247\331\204\330\252\330\271\330\247\331\205\331\204 \331\205\330\271 \330\247\331\204\330\247\331\206\331\202\330\263\330\247\331\205\330\247\330\252 \331\203\331\203\330\263\331\210\330\261 \331\204\331\207\330\260\331\207 \330\247\331\204\331\210\331\204\330\247\331\212\330\251. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "default_outcome"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Essa resposta n\303\243o est\303\241 correta. Tente novamente. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\331\207\330\260\331\207 \330\247\331\204\330\245\330\254\330\247\330\250\330\251 \330\272\331\212\330\261 \330\265\330\255\331\212\330\255\330\251. \330\255\330\247\331\210\331\204 \331\205\330\261\330\251 \330\243\330\256\330\261\331\211. p>"
+ }
+ }
+ }
+ }
+ interaction {
+ id: "AlgebraicExpressionInput"
+ answer_groups {
+ outcome {
+ dest_state_name: "AlgebraicExpressionInput.MatchesUpToTrivialManipulations"
+ feedback {
+ html: "
Correct!
"
+ content_id: "feedback_1"
+ }
+ labelled_as_correct: true
+ }
+ rule_specs {
+ input {
+ key: "x"
+ value {
+ math_expression: "x^2 - x - 2"
+ }
+ }
+ rule_type: "MatchesExactlyWith"
+ }
+ }
+ default_outcome {
+ dest_state_name: "AlgebraicExpressionInput.MatchesExactlyWith"
+ feedback {
+ html: "That answer isn\'t correct. Try again.
"
+ content_id: "default_outcome"
+ }
+ }
+ customization_args {
+ key: "useFractionForDivision"
+ value {
+ bool_value: true
+ }
+ }
+ customization_args {
+ key: "customOskLetters"
+ value {
+ schema_object_list {
+ schema_object {
+ normalized_string: "x"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+states {
+ key: "AlgebraicExpressionInput.MatchesUpToTrivialManipulations"
+ value {
+ name: "AlgebraicExpressionInput.MatchesUpToTrivialManipulations"
+ recorded_voiceovers {
+ key: "feedback_1"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "default_outcome"
+ value {
+ }
+ }
+ content {
+ html: "What algebraic expression represents the product of (x+1)(x-2)? Note that commutative and associative reordering is allowed.
"
+ content_id: "content"
+ }
+ written_translations {
+ key: "feedback_1"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " correto! p>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\265\330\255\331\212\330\255! p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Qual express\303\243o alg\303\251brica representa o produto de (x + 1) (x-2)? Note que a reordena\303\247\303\243o comutativa e associativa \303\251 permitida. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\331\205\330\247 \331\207\331\210 \330\247\331\204\330\252\330\271\330\250\331\212\330\261 \330\247\331\204\330\254\330\250\330\261\331\212 \331\212\331\205\330\253\331\204 \331\206\330\252\330\247\330\254 (x + 1) (x-2)\330\237 \331\204\330\247\330\255\330\270 \330\243\331\206 \330\245\330\271\330\247\330\257\330\251 \330\252\330\261\330\252\331\212\330\250 \330\247\331\204\330\252\331\210\330\262\331\212\330\271 \331\210\330\247\331\204\330\262\331\205\331\204\331\212\330\251 \331\205\330\263\331\205\331\210\330\255 \330\250\331\207\330\247. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "default_outcome"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Essa resposta n\303\243o est\303\241 correta. Tente novamente. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\252\331\204\331\203 \330\247\331\204\330\245\330\254\330\247\330\250\330\251 \330\272\331\212\330\261 \330\265\330\255\331\212\330\255\330\251. \330\255\330\247\331\210\331\204 \331\205\330\261\330\251 \330\243\330\256\330\261\331\211. p>"
+ }
+ }
+ }
+ }
+ interaction {
+ id: "AlgebraicExpressionInput"
+ answer_groups {
+ outcome {
+ dest_state_name: "AlgebraicExpressionInput.IsEquivalentTo"
+ feedback {
+ html: "
Correct!
"
+ content_id: "feedback_1"
+ }
+ labelled_as_correct: true
+ }
+ rule_specs {
+ input {
+ key: "x"
+ value {
+ math_expression: "x^2 - x - 2"
+ }
+ }
+ rule_type: "MatchesUpToTrivialManipulations"
+ }
+ }
+ default_outcome {
+ dest_state_name: "AlgebraicExpressionInput.MatchesUpToTrivialManipulations"
+ feedback {
+ html: "That answer isn\'t correct. Try again.
"
+ content_id: "default_outcome"
+ }
+ }
+ customization_args {
+ key: "customOskLetters"
+ value {
+ schema_object_list {
+ schema_object {
+ normalized_string: "x"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+states {
+ key: "AlgebraicExpressionInput.IsEquivalentTo"
+ value {
+ name: "AlgebraicExpressionInput.IsEquivalentTo"
+ recorded_voiceovers {
+ key: "feedback_1"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "default_outcome"
+ value {
+ }
+ }
+ content {
+ html: "What algebraic expression represents the product of (x+1)(x-2)? Note that any equivalent expression is allowed.
"
+ content_id: "content"
+ }
+ written_translations {
+ key: "feedback_1"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " correto! p>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\265\330\255\331\212\330\255! p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Qual express\303\243o alg\303\251brica representa o produto de (x + 1) (x-2)? Observe que qualquer express\303\243o equivalente \303\251 permitida. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\331\205\330\247 \331\207\331\210 \330\247\331\204\330\252\330\271\330\250\331\212\330\261 \330\247\331\204\330\254\330\250\330\261\331\212 \331\212\331\205\330\253\331\204 \331\206\330\252\330\247\330\254 (x + 1) (x-2)\330\237 \331\204\330\247\330\255\330\270 \330\243\331\206 \330\243\331\212 \330\252\330\271\330\250\331\212\330\261 \331\205\330\271\330\247\330\257\331\204 \331\205\330\263\331\205\331\210\330\255 \330\250\331\207. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "default_outcome"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Essa resposta n\303\243o est\303\241 correta. Tente novamente. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\252\331\204\331\203 \330\247\331\204\330\245\330\254\330\247\330\250\330\251 \330\272\331\212\330\261 \330\265\330\255\331\212\330\255\330\251. \330\255\330\247\331\210\331\204 \331\205\330\261\330\251 \330\243\330\256\330\261\331\211. p>"
+ }
+ }
+ }
+ }
+ interaction {
+ id: "AlgebraicExpressionInput"
+ answer_groups {
+ outcome {
+ dest_state_name: "MathEquationInput.MatchesExactlyWith"
+ feedback {
+ html: "
Correct!
"
+ content_id: "feedback_1"
+ }
+ labelled_as_correct: true
+ }
+ rule_specs {
+ input {
+ key: "x"
+ value {
+ math_expression: "x^2 - x - 2"
+ }
+ }
+ rule_type: "IsEquivalentTo"
+ }
+ }
+ default_outcome {
+ dest_state_name: "AlgebraicExpressionInput.IsEquivalentTo"
+ feedback {
+ html: "That answer isn\'t correct. Try again.
"
+ content_id: "default_outcome"
+ }
+ }
+ customization_args {
+ key: "customOskLetters"
+ value {
+ schema_object_list {
+ schema_object {
+ normalized_string: "x"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+states {
+ key: "MathEquationInput.MatchesExactlyWith"
+ value {
+ name: "MathEquationInput.MatchesExactlyWith"
+ recorded_voiceovers {
+ key: "feedback_1"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "default_outcome"
+ value {
+ }
+ }
+ content {
+ html: "What algebraic equation represents the quantity of two y and (x+1)(x-2)? Note: divisions are treated as fractions for this state.
"
+ content_id: "content"
+ }
+ written_translations {
+ key: "feedback_1"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " correto! p>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\265\330\255\331\212\330\255! p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Que equa\303\247\303\243o alg\303\251brica representa a quantidade de dois y e (x + 1) (x-2)? Observa\303\247\303\243o: as divis\303\265es s\303\243o tratadas como fra\303\247\303\265es para este estado.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \331\205\330\247 \331\207\331\212 \330\247\331\204\331\205\330\271\330\247\330\257\331\204\330\251 \330\247\331\204\330\254\330\250\330\261\331\212\330\251 \330\252\331\205\330\253\331\204 \331\203\331\205\331\212\330\251 \330\247\330\253\331\206\331\212\331\206 \331\205\331\206 y \331\210 (x + 1) (x-2)\330\237 \331\205\331\204\330\247\330\255\330\270\330\251: \331\212\330\252\331\205 \330\247\331\204\330\252\330\271\330\247\331\205\331\204 \331\205\330\271 \330\247\331\204\330\247\331\206\331\202\330\263\330\247\331\205\330\247\330\252 \331\203\331\203\330\263\331\210\330\261 \331\204\331\207\330\260\331\207 \330\247\331\204\331\210\331\204\330\247\331\212\330\251. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "default_outcome"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Essa resposta n\303\243o est\303\241 correta. Tente novamente. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\252\331\204\331\203 \330\247\331\204\330\245\330\254\330\247\330\250\330\251 \330\272\331\212\330\261 \330\265\330\255\331\212\330\255\330\251. \330\255\330\247\331\210\331\204 \331\205\330\261\330\251 \330\243\330\256\330\261\331\211. p>"
+ }
+ }
+ }
+ }
+ interaction {
+ id: "MathEquationInput"
+ answer_groups {
+ outcome {
+ dest_state_name: "MathEquationInput.MatchesUpToTrivialManipulations"
+ feedback {
+ html: "
Correct!
"
+ content_id: "feedback_1"
+ }
+ labelled_as_correct: true
+ }
+ rule_specs {
+ input {
+ key: "x"
+ value {
+ math_expression: "2y = x^2 - x - 2"
+ }
+ }
+ rule_type: "MatchesExactlyWith"
+ }
+ }
+ default_outcome {
+ dest_state_name: "MathEquationInput.MatchesExactlyWith"
+ feedback {
+ html: "That answer isn\'t correct. Try again.
"
+ content_id: "default_outcome"
+ }
+ }
+ customization_args {
+ key: "useFractionForDivision"
+ value {
+ bool_value: true
+ }
+ }
+ customization_args {
+ key: "customOskLetters"
+ value {
+ schema_object_list {
+ schema_object {
+ normalized_string: "x"
+ }
+ schema_object {
+ normalized_string: "y"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+states {
+ key: "MathEquationInput.MatchesUpToTrivialManipulations"
+ value {
+ name: "MathEquationInput.MatchesUpToTrivialManipulations"
+ recorded_voiceovers {
+ key: "feedback_1"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "default_outcome"
+ value {
+ }
+ }
+ content {
+ html: "What algebraic equation represents the quantity of two y and (x+1)(x-2)? Note that commutative and associative reordering is allowed.
"
+ content_id: "content"
+ }
+ written_translations {
+ key: "feedback_1"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " correto! p>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\265\330\255\331\212\330\255! p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Qual equa\303\247\303\243o alg\303\251brica representa a quantidade de dois y e (x + 1) (x-2)? Note que a reordena\303\247\303\243o comutativa e associativa \303\251 permitida. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\331\205\330\247 \330\247\331\204\331\205\330\271\330\247\330\257\331\204\330\251 \330\247\331\204\330\254\330\250\330\261\331\212\330\251 \330\247\331\204\330\252\331\212 \330\252\331\205\330\253\331\204 \331\203\331\205\331\212\330\251 \330\247\330\253\331\206\331\212\331\206 y \331\210 (x + 1) (x-2)\330\237 \331\204\330\247\330\255\330\270 \330\243\331\206\331\207 \331\212\331\217\330\263\331\205\330\255 \330\250\330\245\330\271\330\247\330\257\330\251 \330\247\331\204\330\252\330\261\330\252\331\212\330\250 \330\247\331\204\330\252\330\250\330\247\330\257\331\204\331\212 \331\210\330\247\331\204\330\261\330\247\330\250\330\267\331\212. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "default_outcome"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Essa resposta n\303\243o est\303\241 correta. Tente novamente. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\252\331\204\331\203 \330\247\331\204\330\245\330\254\330\247\330\250\330\251 \330\272\331\212\330\261 \330\265\330\255\331\212\330\255\330\251. \330\255\330\247\331\210\331\204 \331\205\330\261\330\251 \330\243\330\256\330\261\331\211. p>"
+ }
+ }
+ }
+ }
+ interaction {
+ id: "MathEquationInput"
+ answer_groups {
+ outcome {
+ dest_state_name: "MathEquationInput.IsEquivalentTo"
+ feedback {
+ html: "
Correct!
"
+ content_id: "feedback_1"
+ }
+ labelled_as_correct: true
+ }
+ rule_specs {
+ input {
+ key: "x"
+ value {
+ math_expression: "2y = x^2 - x - 2"
+ }
+ }
+ rule_type: "MatchesUpToTrivialManipulations"
+ }
+ }
+ default_outcome {
+ dest_state_name: "MathEquationInput.MatchesUpToTrivialManipulations"
+ feedback {
+ html: "That answer isn\'t correct. Try again.
"
+ content_id: "default_outcome"
+ }
+ }
+ customization_args {
+ key: "customOskLetters"
+ value {
+ schema_object_list {
+ schema_object {
+ normalized_string: "x"
+ }
+ schema_object {
+ normalized_string: "y"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+states {
+ key: "MathEquationInput.IsEquivalentTo"
+ value {
+ name: "MathEquationInput.IsEquivalentTo"
+ recorded_voiceovers {
+ key: "feedback_1"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ recorded_voiceovers {
+ key: "default_outcome"
+ value {
+ }
+ }
+ content {
+ html: "What algebraic equation represents the quantity of two y and (x+1)(x-2)? Note that any equivalent expression is allowed, including reordering around the equals sign.
"
+ content_id: "content"
+ }
+ written_translations {
+ key: "feedback_1"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " correto! p>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\265\330\255\331\212\330\255! p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Qual equa\303\247\303\243o alg\303\251brica representa a quantidade de dois y e (x + 1) (x-2)? Observe que qualquer express\303\243o equivalente \303\251 permitida, incluindo reordenando em torno do sinal de igual. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\331\205\330\247 \331\207\331\212 \330\247\331\204\331\205\330\271\330\247\330\257\331\204\330\251 \330\247\331\204\330\254\330\250\330\261\331\212\330\251 \330\252\331\205\330\253\331\204 \331\203\331\205\331\212\330\251 \330\247\330\253\331\206\331\212\331\206 \331\205\331\206 y \331\210 (x + 1) (x-2)\330\237 \331\204\330\247\330\255\330\270 \330\243\331\206 \330\243\331\212 \330\252\330\271\330\250\331\212\330\261 \331\205\330\271\330\247\330\257\331\204 \331\205\330\263\331\205\331\210\330\255 \330\250\331\207\330\214 \330\250\331\205\330\247 \331\201\331\212 \330\260\331\204\331\203 \330\245\330\271\330\247\330\257\330\251 \330\252\330\261\330\252\331\212\330\250 \330\255\331\210\331\204 \330\271\331\204\330\247\331\205\330\251 \330\247\331\204\330\252\330\263\330\247\331\210\331\212. p>"
+ }
+ }
+ }
+ }
+ written_translations {
+ key: "default_outcome"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "
Essa resposta n\303\243o est\303\241 correta. Tente novamente. P>"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "
\330\252\331\204\331\203 \330\247\331\204\330\245\330\254\330\247\330\250\330\251 \330\272\331\212\330\261 \330\265\330\255\331\212\330\255\330\251. \330\255\330\247\331\210\331\204 \331\205\330\261\330\251 \330\243\330\256\330\261\331\211. p>"
+ }
+ }
+ }
+ }
+ interaction {
+ id: "MathEquationInput"
+ answer_groups {
+ outcome {
+ dest_state_name: "End"
+ feedback {
+ html: "
Correct!
"
+ content_id: "feedback_1"
+ }
+ labelled_as_correct: true
+ }
+ rule_specs {
+ input {
+ key: "x"
+ value {
+ math_expression: "2y = x^2 - x - 2"
+ }
+ }
+ rule_type: "IsEquivalentTo"
+ }
+ }
+ default_outcome {
+ dest_state_name: "MathEquationInput.IsEquivalentTo"
+ feedback {
+ html: "That answer isn\'t correct. Try again.
"
+ content_id: "default_outcome"
+ }
+ }
+ customization_args {
+ key: "customOskLetters"
+ value {
+ schema_object_list {
+ schema_object {
+ normalized_string: "x"
+ }
+ schema_object {
+ normalized_string: "y"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+states {
+ key: "End"
+ value {
+ name: "End"
+ recorded_voiceovers {
+ key: "content"
+ value {
+ }
+ }
+ content {
+ html: "Congratulations, you have finished!"
+ content_id: "content"
+ }
+ written_translations {
+ key: "content"
+ value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: "Parab\303\251ns, voc\303\252 terminou!"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "\330\252\331\207\330\247\331\206\331\212\331\206\330\247\330\214 \331\204\331\202\330\257 \330\247\331\206\330\252\331\207\331\212\330\252!"
+ }
+ }
+ }
+ }
+ interaction {
+ id: "EndExploration"
+ customization_args {
+ key: "recommendedExplorationIds"
+ value {
+ schema_object_list {
+ }
+ }
+ }
+ }
+ }
+}
+init_state_name: "NumericExpressionInput.MatchesExactlyWith"
+objective: "Demonstrate math interactions."
+title: "Math Expressions"
+language_code: "en"
diff --git a/domain/src/main/assets/test_story_id_0.json b/domain/src/main/assets/test_story_id_0.json
index 7532008bedd..9475413db61 100644
--- a/domain/src/main/assets/test_story_id_0.json
+++ b/domain/src/main/assets/test_story_id_0.json
@@ -23,6 +23,18 @@
"outline": "",
"title": "Image Region Selection Exploration",
"acquired_skill_ids": []
+ }, {
+ "description": "Test the different math expression/equation interactions.",
+ "exploration_id": "test_exp_id_5",
+ "destination_node_ids": [],
+ "thumbnail_filename": "",
+ "outline_is_finalized": true,
+ "id": "test_exp_id_5",
+ "prerequisite_skill_ids": [],
+ "thumbnail_bg_color": "#d68f78",
+ "outline": "Test the different math expression/equation interactions.",
+ "title": "Math Expressions",
+ "acquired_skill_ids": []
}],
"story_title": "First Story",
"story_description": ""
diff --git a/domain/src/main/assets/test_story_id_0.textproto b/domain/src/main/assets/test_story_id_0.textproto
index 17fc668ea32..53ddcce942a 100644
--- a/domain/src/main/assets/test_story_id_0.textproto
+++ b/domain/src/main/assets/test_story_id_0.textproto
@@ -18,3 +18,11 @@ chapters {
}
title: "Image Region Selection Exploration"
}
+chapters {
+ exploration_id: "test_exp_id_5"
+ chapter_thumbnail {
+ background_color_rgb: 14061432
+ }
+ title: "Math Expressions"
+ description: "Test the different math expression/equation interactions."
+}
diff --git a/domain/src/main/assets/test_topic_id_0.json b/domain/src/main/assets/test_topic_id_0.json
index 26a2aa5ccb4..a9115679060 100644
--- a/domain/src/main/assets/test_topic_id_0.json
+++ b/domain/src/main/assets/test_topic_id_0.json
@@ -3,7 +3,7 @@
"thumbnail_bg_color": "#b378f1",
"description": "",
"title": "First Story",
- "node_titles": ["Prototype Exploration", "Image Region Selection Exploration"],
+ "node_titles": ["Prototype Exploration", "Image Region Selection Exploration", "Math Expressions"],
"thumbnail_filename": "",
"published": true,
"id": "test_story_id_0"
diff --git a/domain/src/main/java/org/oppia/android/domain/audio/AudioPlayerController.kt b/domain/src/main/java/org/oppia/android/domain/audio/AudioPlayerController.kt
index 8e4e8c14f67..756177c31ef 100644
--- a/domain/src/main/java/org/oppia/android/domain/audio/AudioPlayerController.kt
+++ b/domain/src/main/java/org/oppia/android/domain/audio/AudioPlayerController.kt
@@ -40,7 +40,7 @@ class AudioPlayerController @Inject constructor(
) {
inner class AudioMutableLiveData :
- MutableLiveData>(AsyncResult.pending()) {
+ MutableLiveData>(AsyncResult.Pending()) {
override fun onActive() {
super.onActive()
audioLock.withLock {
@@ -128,17 +128,17 @@ class AudioPlayerController @Inject constructor(
completed = true
stopUpdatingSeekBar()
playProgress?.value =
- AsyncResult.success(PlayProgress(PlayStatus.COMPLETED, 0, duration))
+ AsyncResult.Success(PlayProgress(PlayStatus.COMPLETED, 0, duration))
}
mediaPlayer.setOnPreparedListener {
prepared = true
duration = it.duration
playProgress?.value =
- AsyncResult.success(PlayProgress(PlayStatus.PREPARED, 0, duration))
+ AsyncResult.Success(PlayProgress(PlayStatus.PREPARED, 0, duration))
}
mediaPlayer.setOnErrorListener { _, what, extra ->
playProgress?.value =
- AsyncResult.failed(
+ AsyncResult.Failure(
AudioPlayerException("Audio Player put in error state with what: $what and extra: $extra")
)
releaseMediaPlayer()
@@ -186,7 +186,7 @@ class AudioPlayerController @Inject constructor(
exceptionsController.logNonFatalException(e)
oppiaLogger.e("AudioPlayerController", "Failed to set data source for media player", e)
}
- playProgress?.value = AsyncResult.pending()
+ playProgress?.value = AsyncResult.Pending()
}
/**
@@ -212,7 +212,7 @@ class AudioPlayerController @Inject constructor(
check(prepared) { "Media Player not in a prepared state" }
if (mediaPlayer.isPlaying) {
playProgress?.value =
- AsyncResult.success(
+ AsyncResult.Success(
PlayProgress(PlayStatus.PAUSED, mediaPlayer.currentPosition, duration)
)
mediaPlayer.pause()
@@ -239,7 +239,7 @@ class AudioPlayerController @Inject constructor(
val position = if (completed) 0 else mediaPlayer.currentPosition
completed = false
playProgress?.postValue(
- AsyncResult.success(
+ AsyncResult.Success(
PlayProgress(PlayStatus.PLAYING, position, mediaPlayer.duration)
)
)
diff --git a/domain/src/main/java/org/oppia/android/domain/audio/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/audio/BUILD.bazel
index 440876b4862..c7141fc8c91 100644
--- a/domain/src/main/java/org/oppia/android/domain/audio/BUILD.bazel
+++ b/domain/src/main/java/org/oppia/android/domain/audio/BUILD.bazel
@@ -33,7 +33,7 @@ kt_android_library(
deps = [
"//data/src/main/java/org/oppia/android/data/persistence:cache_store",
"//domain/src/main/java/org/oppia/android/domain/oppialogger:oppia_logger",
- "//model:topic_java_proto_lite",
+ "//model/src/main/proto:topic_java_proto_lite",
"//third_party:javax_inject_javax_inject",
"//utility/src/main/java/org/oppia/android/util/data:data_provider",
],
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/AnswerClassificationController.kt b/domain/src/main/java/org/oppia/android/domain/classify/AnswerClassificationController.kt
index 7281ff50485..979b8a28fc6 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/AnswerClassificationController.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/AnswerClassificationController.kt
@@ -42,7 +42,7 @@ class AnswerClassificationController @Inject constructor(
interaction.defaultOutcome,
interactionClassifier,
interaction.id,
- writtenTranslationContext
+ ClassificationContext(writtenTranslationContext, interaction.customizationArgsMap)
)
}
@@ -54,7 +54,7 @@ class AnswerClassificationController @Inject constructor(
defaultOutcome: Outcome,
interactionClassifier: InteractionClassifier,
interactionId: String,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): ClassificationResult {
for (answerGroup in answerGroups) {
for (ruleSpec in answerGroup.ruleSpecsList) {
@@ -65,7 +65,7 @@ class AnswerClassificationController @Inject constructor(
" has: ${interactionClassifier.getRuleTypes()}"
}
try {
- if (ruleClassifier.matches(answer, ruleSpec.inputMap, writtenTranslationContext)) {
+ if (ruleClassifier.matches(answer, ruleSpec.inputMap, classificationContext)) {
// Explicit classification matched.
return if (!answerGroup.hasTaggedSkillMisconception()) {
ClassificationResult.OutcomeOnly(answerGroup.outcome)
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/classify/BUILD.bazel
index 6b7d7c7fe59..7c523f15e2f 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/BUILD.bazel
+++ b/domain/src/main/java/org/oppia/android/domain/classify/BUILD.bazel
@@ -12,15 +12,28 @@ kt_android_library(
],
visibility = ["//:oppia_api_visibility"],
deps = [
+ ":classification_context",
":classification_result",
":interaction_classifier",
- "//model:exploration_java_proto_lite",
- "//model:interaction_object_java_proto_lite",
- "//model:translation_java_proto_lite",
+ "//model/src/main/proto:exploration_java_proto_lite",
+ "//model/src/main/proto:interaction_object_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
"//third_party:javax_inject_javax_inject",
],
)
+kt_android_library(
+ name = "classification_context",
+ srcs = [
+ "ClassificationContext.kt",
+ ],
+ visibility = ["//:oppia_api_visibility"],
+ deps = [
+ "//model/src/main/proto:exploration_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
+ ],
+)
+
kt_android_library(
name = "classification_result",
srcs = [
@@ -28,7 +41,7 @@ kt_android_library(
],
visibility = ["//:oppia_api_visibility"],
deps = [
- "//model:exploration_java_proto_lite",
+ "//model/src/main/proto:exploration_java_proto_lite",
],
)
@@ -38,6 +51,7 @@ kt_android_library(
"GenericInteractionClassifier.kt",
],
deps = [
+ ":classification_context",
":interaction_classifier",
":rule_classifier",
],
@@ -77,8 +91,9 @@ kt_android_library(
],
visibility = ["//:__subpackages__"],
deps = [
- "//model:interaction_object_java_proto_lite",
- "//model:translation_java_proto_lite",
+ ":classification_context",
+ "//model/src/main/proto:interaction_object_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
],
)
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/ClassificationContext.kt b/domain/src/main/java/org/oppia/android/domain/classify/ClassificationContext.kt
new file mode 100644
index 00000000000..5d2ba3b88be
--- /dev/null
+++ b/domain/src/main/java/org/oppia/android/domain/classify/ClassificationContext.kt
@@ -0,0 +1,19 @@
+package org.oppia.android.domain.classify
+
+import org.oppia.android.app.model.SchemaObject
+import org.oppia.android.app.model.WrittenTranslationContext
+
+/**
+ * Represents the context provided to classifiers when they're classifying an answer.
+ *
+ * This object provides context for the interaction and learner settings to help classifiers
+ * properly categorize and process answers.
+ *
+ * @property writtenTranslationContext the [WrittenTranslationContext] currently used by the learner
+ * @property customizationArgs the customization arguments defined by the current interaction
+ */
+data class ClassificationContext(
+ val writtenTranslationContext: WrittenTranslationContext =
+ WrittenTranslationContext.getDefaultInstance(),
+ val customizationArgs: Map = mapOf()
+)
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/InteractionsModule.kt b/domain/src/main/java/org/oppia/android/domain/classify/InteractionsModule.kt
index 4175bda19eb..3dec6a50142 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/InteractionsModule.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/InteractionsModule.kt
@@ -4,13 +4,16 @@ import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
+import org.oppia.android.domain.classify.rules.AlgebraicExpressionInputRules
import org.oppia.android.domain.classify.rules.ContinueRules
import org.oppia.android.domain.classify.rules.DragDropSortInputRules
import org.oppia.android.domain.classify.rules.FractionInputRules
import org.oppia.android.domain.classify.rules.ImageClickInputRules
import org.oppia.android.domain.classify.rules.ItemSelectionInputRules
+import org.oppia.android.domain.classify.rules.MathEquationInputRules
import org.oppia.android.domain.classify.rules.MultipleChoiceInputRules
import org.oppia.android.domain.classify.rules.NumberWithUnitsRules
+import org.oppia.android.domain.classify.rules.NumericExpressionInputRules
import org.oppia.android.domain.classify.rules.NumericInputRules
import org.oppia.android.domain.classify.rules.RatioExpressionInputRules
import org.oppia.android.domain.classify.rules.TextInputRules
@@ -107,4 +110,32 @@ class InteractionsModule {
): InteractionClassifier {
return GenericInteractionClassifier(ruleClassifiers)
}
+
+ @Provides
+ @IntoMap
+ @StringKey("NumericExpressionInput")
+ fun provideNumericExpressionInputInteractionClassifier(
+ @NumericExpressionInputRules ruleClassifiers: Map
+ ): InteractionClassifier {
+ return GenericInteractionClassifier(ruleClassifiers)
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("AlgebraicExpressionInput")
+ fun provideAlgebraicExpressionInputInteractionClassifier(
+ @AlgebraicExpressionInputRules ruleClassifiers:
+ Map
+ ): InteractionClassifier {
+ return GenericInteractionClassifier(ruleClassifiers)
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("MathEquationInput")
+ fun provideMathEquationInputInteractionClassifier(
+ @MathEquationInputRules ruleClassifiers: Map
+ ): InteractionClassifier {
+ return GenericInteractionClassifier(ruleClassifiers)
+ }
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/RuleClassifier.kt b/domain/src/main/java/org/oppia/android/domain/classify/RuleClassifier.kt
index f4fba3fc301..973b97e4fb2 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/RuleClassifier.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/RuleClassifier.kt
@@ -1,7 +1,6 @@
package org.oppia.android.domain.classify
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
/** An answer classifier for a specific interaction rule. */
interface RuleClassifier {
@@ -12,6 +11,6 @@ interface RuleClassifier {
fun matches(
answer: InteractionObject,
inputs: Map,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/classify/rules/BUILD.bazel
index 95880ffd7c6..f581741e245 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/BUILD.bazel
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/BUILD.bazel
@@ -15,8 +15,8 @@ kt_android_library(
deps = [
":rule_classifier_provider",
"//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
- "//model:interaction_object_java_proto_lite",
- "//model:translation_java_proto_lite",
+ "//model/src/main/proto:interaction_object_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
"//third_party:javax_inject_javax_inject",
],
)
@@ -30,7 +30,7 @@ kt_android_library(
visibility = ["//:__subpackages__"],
deps = [
"//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
- "//model:translation_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
"//third_party:javax_inject_javax_inject",
],
)
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/GenericRuleClassifier.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/GenericRuleClassifier.kt
index 95f81ee1e9b..ed6efda5b4a 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/GenericRuleClassifier.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/GenericRuleClassifier.kt
@@ -1,7 +1,7 @@
package org.oppia.android.domain.classify.rules
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import javax.inject.Inject
@@ -15,15 +15,15 @@ import javax.inject.Inject
*/
// TODO(#1580): Re-restrict access using Bazel visibilities
class GenericRuleClassifier constructor(
- val expectedAnswerObjectType: InteractionObject.ObjectTypeCase,
- val orderedExpectedParameterTypes: LinkedHashMap<
+ private val expectedAnswerObjectType: InteractionObject.ObjectTypeCase,
+ private val orderedExpectedParameterTypes: LinkedHashMap<
String, InteractionObject.ObjectTypeCase>,
- val matcherDelegate: MatcherDelegate
+ private val matcherDelegate: MatcherDelegate
) : RuleClassifier {
override fun matches(
answer: InteractionObject,
inputs: Map,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
check(answer.objectTypeCase == expectedAnswerObjectType) {
"Expected answer to be of type ${expectedAnswerObjectType.name} " +
@@ -34,7 +34,7 @@ class GenericRuleClassifier constructor(
.map { (parameterName, expectedObjectType) ->
retrieveInputObject(parameterName, expectedObjectType, inputs)
}
- return matcherDelegate.matches(answer, parameterInputs, writtenTranslationContext)
+ return matcherDelegate.matches(answer, parameterInputs, classificationContext)
}
private fun retrieveInputObject(
@@ -58,7 +58,7 @@ class GenericRuleClassifier constructor(
* Returns whether the validated and extracted answer matches the expectations per the
* specification of this classifier.
*/
- fun matches(answer: T, writtenTranslationContext: WrittenTranslationContext): Boolean
+ fun matches(answer: T, classificationContext: ClassificationContext): Boolean
}
interface SingleInputMatcher {
@@ -66,7 +66,7 @@ class GenericRuleClassifier constructor(
* Returns whether the validated and extracted answer matches the single validated and extracted
* input parameter per the specification of this classifier.
*/
- fun matches(answer: T, input: T, writtenTranslationContext: WrittenTranslationContext): Boolean
+ fun matches(answer: T, input: T, classificationContext: ClassificationContext): Boolean
}
interface MultiTypeSingleInputMatcher {
@@ -74,11 +74,7 @@ class GenericRuleClassifier constructor(
* Returns whether the validated and extracted answer matches the single validated and extracted
* input parameter per the specification of this classifier.
*/
- fun matches(
- answer: AT,
- input: IT,
- writtenTranslationContext: WrittenTranslationContext
- ): Boolean
+ fun matches(answer: AT, input: IT, classificationContext: ClassificationContext): Boolean
}
interface MultiTypeDoubleInputMatcher {
@@ -90,7 +86,7 @@ class GenericRuleClassifier constructor(
answer: AT,
firstInput: ITF,
secondInput: ITS,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean
}
@@ -103,7 +99,7 @@ class GenericRuleClassifier constructor(
answer: T,
firstInput: T,
secondInput: T,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean
}
@@ -112,7 +108,7 @@ class GenericRuleClassifier constructor(
abstract fun matches(
answer: InteractionObject,
inputs: List,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean
class NoInputMatcherDelegate(
@@ -122,10 +118,10 @@ class GenericRuleClassifier constructor(
override fun matches(
answer: InteractionObject,
inputs: List,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
check(inputs.isEmpty())
- return matcher.matches(extractObject(answer), writtenTranslationContext)
+ return matcher.matches(extractObject(answer), classificationContext)
}
}
@@ -136,11 +132,11 @@ class GenericRuleClassifier constructor(
override fun matches(
answer: InteractionObject,
inputs: List,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
check(inputs.size == 1)
return matcher.matches(
- extractObject(answer), extractObject(inputs.first()), writtenTranslationContext
+ extractObject(answer), extractObject(inputs.first()), classificationContext
)
}
}
@@ -153,11 +149,11 @@ class GenericRuleClassifier constructor(
override fun matches(
answer: InteractionObject,
inputs: List,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
check(inputs.size == 1)
return matcher.matches(
- extractAnswerObject(answer), extractInputObject(inputs.first()), writtenTranslationContext
+ extractAnswerObject(answer), extractInputObject(inputs.first()), classificationContext
)
}
}
@@ -169,14 +165,14 @@ class GenericRuleClassifier constructor(
override fun matches(
answer: InteractionObject,
inputs: List,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
check(inputs.size == 2)
return matcher.matches(
extractObject(answer),
extractObject(inputs[0]),
extractObject(inputs[1]),
- writtenTranslationContext
+ classificationContext
)
}
}
@@ -190,14 +186,14 @@ class GenericRuleClassifier constructor(
override fun matches(
answer: InteractionObject,
inputs: List,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
check(inputs.size == 2)
return matcher.matches(
extractAnswerObject(answer),
extractFirstParamObject(inputs[0]),
extractSecondParamObject(inputs[1]),
- writtenTranslationContext
+ classificationContext
)
}
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/InteractionObjectTypeExtractorRepository.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/InteractionObjectTypeExtractorRepository.kt
index 9b72e5dea69..48bec5b8f5c 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/InteractionObjectTypeExtractorRepository.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/InteractionObjectTypeExtractorRepository.kt
@@ -76,6 +76,7 @@ class InteractionObjectTypeExtractorRepository @Inject constructor() {
createMapping(InteractionObject::getListOfSetsOfTranslatableHtmlContentIds)
ObjectTypeCase.TRANSLATABLE_SET_OF_NORMALIZED_STRING ->
createMapping(InteractionObject::getTranslatableSetOfNormalizedString)
+ ObjectTypeCase.MATH_EXPRESSION -> createMapping(InteractionObject::getMathExpression)
ObjectTypeCase.OBJECTTYPE_NOT_SET -> createMapping { error("Invalid object type") }
}
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/RuleQualifiers.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/RuleQualifiers.kt
index 18b9d9faed7..ec08463b944 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/RuleQualifiers.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/RuleQualifiers.kt
@@ -2,42 +2,93 @@ package org.oppia.android.domain.classify.rules
import javax.inject.Qualifier
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the continue interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the
+ * continue interaction.
+ */
@Qualifier
annotation class ContinueRules
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the fraction input interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the
+ * fraction input interaction.
+ */
@Qualifier
annotation class FractionInputRules
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the item selection interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the item
+ * selection interaction.
+ */
@Qualifier
annotation class ItemSelectionInputRules
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the multiple choice interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the
+ * multiple choice interaction.
+ */
@Qualifier
annotation class MultipleChoiceInputRules
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the number with units interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the number
+ * with units interaction.
+ */
@Qualifier
annotation class NumberWithUnitsRules
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the text input interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the text
+ * input interaction.
+ */
@Qualifier
annotation class TextInputRules
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the numeric input interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the
+ * numeric input interaction.
+ */
@Qualifier
annotation class NumericInputRules
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the drag drop sort input interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the drag
+ * drop sort input interaction.
+ */
@Qualifier
annotation class DragDropSortInputRules
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the image click input interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the image
+ * click input interaction.
+ */
@Qualifier
annotation class ImageClickInputRules
-/** Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the ratio input interaction. */
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the ratio
+ * input interaction.
+ */
@Qualifier
annotation class RatioExpressionInputRules
+
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the
+ * numeric expression input interaction.
+ */
+@Qualifier
+annotation class NumericExpressionInputRules
+
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the
+ * algebraic expression input interaction.
+ */
+@Qualifier
+annotation class AlgebraicExpressionInputRules
+
+/**
+ * Corresponds to [org.oppia.android.domain.classify.RuleClassifier]s that can be used by the
+ * math equation input interaction.
+ */
+@Qualifier
+annotation class MathEquationInputRules
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputIsEquivalentToRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputIsEquivalentToRuleClassifierProvider.kt
new file mode 100644
index 00000000000..fb4f9bf3c8a
--- /dev/null
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputIsEquivalentToRuleClassifierProvider.kt
@@ -0,0 +1,78 @@
+package org.oppia.android.domain.classify.rules.algebraicexpressioninput
+
+import org.oppia.android.app.model.InteractionObject
+import org.oppia.android.app.model.Polynomial
+import org.oppia.android.domain.classify.ClassificationContext
+import org.oppia.android.domain.classify.RuleClassifier
+import org.oppia.android.domain.classify.rules.GenericRuleClassifier
+import org.oppia.android.domain.classify.rules.RuleClassifierProvider
+import org.oppia.android.util.logging.ConsoleLogger
+import org.oppia.android.util.math.MathExpressionParser.Companion.MathParsingResult
+import org.oppia.android.util.math.MathExpressionParser.Companion.parseAlgebraicExpression
+import org.oppia.android.util.math.isApproximatelyEqualTo
+import org.oppia.android.util.math.toPolynomial
+import javax.inject.Inject
+
+/**
+ * Provider for a classifier that determines whether an algebraic expression is mathematically
+ * equivalent to the creator-specific expression defined as the input to this interaction.
+ *
+ * Note that both expressions are assumed and parsed as polynomials.
+ *
+ * See this class's tests for a list of supported cases (both for matching and not matching).
+ */
+class AlgebraicExpressionInputIsEquivalentToRuleClassifierProvider @Inject constructor(
+ private val classifierFactory: GenericRuleClassifier.Factory,
+ private val consoleLogger: ConsoleLogger
+) : RuleClassifierProvider, GenericRuleClassifier.SingleInputMatcher {
+ override fun createRuleClassifier(): RuleClassifier {
+ return classifierFactory.createSingleInputClassifier(
+ expectedObjectType = InteractionObject.ObjectTypeCase.MATH_EXPRESSION,
+ inputParameterName = "x",
+ matcher = this
+ )
+ }
+
+ override fun matches(
+ answer: String,
+ input: String,
+ classificationContext: ClassificationContext
+ ): Boolean {
+ val allowedVariables = classificationContext.extractAllowedVariables()
+ val answerExpression = parsePolynomial(answer, allowedVariables) ?: return false
+ val inputExpression = parsePolynomial(input, allowedVariables) ?: return false
+ return answerExpression.isApproximatelyEqualTo(inputExpression)
+ }
+
+ private fun parsePolynomial(rawExpression: String, allowedVariables: List): Polynomial? {
+ return when (val expResult = parseAlgebraicExpression(rawExpression, allowedVariables)) {
+ is MathParsingResult.Success -> {
+ expResult.result.toPolynomial().also {
+ if (it == null) {
+ consoleLogger.w(
+ "AlgebraExpEquivalent", "Expression is not a supported polynomial: $rawExpression."
+ )
+ }
+ }
+ }
+ is MathParsingResult.Failure -> {
+ consoleLogger.e(
+ "AlgebraExpEquivalent",
+ "Encountered expression that failed parsing. Expression: $rawExpression." +
+ " Failure: ${expResult.error}."
+ )
+ null
+ }
+ }
+ }
+
+ private companion object {
+ private fun ClassificationContext.extractAllowedVariables(): List {
+ return customizationArgs["customOskLetters"]
+ ?.schemaObjectList
+ ?.schemaObjectList
+ ?.map { it.normalizedString }
+ ?: listOf()
+ }
+ }
+}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputMatchesExactlyWithRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputMatchesExactlyWithRuleClassifierProvider.kt
new file mode 100644
index 00000000000..c35ba68b7f2
--- /dev/null
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputMatchesExactlyWithRuleClassifierProvider.kt
@@ -0,0 +1,71 @@
+package org.oppia.android.domain.classify.rules.algebraicexpressioninput
+
+import org.oppia.android.app.model.InteractionObject
+import org.oppia.android.app.model.MathExpression
+import org.oppia.android.domain.classify.ClassificationContext
+import org.oppia.android.domain.classify.RuleClassifier
+import org.oppia.android.domain.classify.rules.GenericRuleClassifier
+import org.oppia.android.domain.classify.rules.RuleClassifierProvider
+import org.oppia.android.util.logging.ConsoleLogger
+import org.oppia.android.util.math.MathExpressionParser.Companion.MathParsingResult
+import org.oppia.android.util.math.MathExpressionParser.Companion.parseAlgebraicExpression
+import org.oppia.android.util.math.isApproximatelyEqualTo
+import javax.inject.Inject
+
+/**
+ * Provider for a classifier that determines whether an algebraic expression is exactly equal to the
+ * creator-specific expression defined as the input to this interaction, including any parenthetical
+ * groups in the expressions and operand order.
+ *
+ * See this class's tests for a list of supported cases (both for matching and not matching).
+ */
+class AlgebraicExpressionInputMatchesExactlyWithRuleClassifierProvider @Inject constructor(
+ private val classifierFactory: GenericRuleClassifier.Factory,
+ private val consoleLogger: ConsoleLogger
+) : RuleClassifierProvider, GenericRuleClassifier.SingleInputMatcher {
+ override fun createRuleClassifier(): RuleClassifier {
+ return classifierFactory.createSingleInputClassifier(
+ expectedObjectType = InteractionObject.ObjectTypeCase.MATH_EXPRESSION,
+ inputParameterName = "x",
+ matcher = this
+ )
+ }
+
+ override fun matches(
+ answer: String,
+ input: String,
+ classificationContext: ClassificationContext
+ ): Boolean {
+ val allowedVariables = classificationContext.extractAllowedVariables()
+ val answerExpression = parseExpression(answer, allowedVariables) ?: return false
+ val inputExpression = parseExpression(input, allowedVariables) ?: return false
+ return answerExpression.isApproximatelyEqualTo(inputExpression)
+ }
+
+ private fun parseExpression(
+ rawExpression: String,
+ allowedVariables: List
+ ): MathExpression? {
+ return when (val expResult = parseAlgebraicExpression(rawExpression, allowedVariables)) {
+ is MathParsingResult.Success -> expResult.result
+ is MathParsingResult.Failure -> {
+ consoleLogger.e(
+ "AlgebraExpMatchesExact",
+ "Encountered expression that failed parsing. Expression: $rawExpression." +
+ " Failure: ${expResult.error}."
+ )
+ null
+ }
+ }
+ }
+
+ private companion object {
+ private fun ClassificationContext.extractAllowedVariables(): List {
+ return customizationArgs["customOskLetters"]
+ ?.schemaObjectList
+ ?.schemaObjectList
+ ?.map { it.normalizedString }
+ ?: listOf()
+ }
+ }
+}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputMatchesUpToTrivialManipulationsRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputMatchesUpToTrivialManipulationsRuleClassifierProvider.kt
new file mode 100644
index 00000000000..27a02bfdd54
--- /dev/null
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputMatchesUpToTrivialManipulationsRuleClassifierProvider.kt
@@ -0,0 +1,75 @@
+package org.oppia.android.domain.classify.rules.algebraicexpressioninput
+
+import org.oppia.android.app.model.ComparableOperation
+import org.oppia.android.app.model.InteractionObject
+import org.oppia.android.domain.classify.ClassificationContext
+import org.oppia.android.domain.classify.RuleClassifier
+import org.oppia.android.domain.classify.rules.GenericRuleClassifier
+import org.oppia.android.domain.classify.rules.RuleClassifierProvider
+import org.oppia.android.util.logging.ConsoleLogger
+import org.oppia.android.util.math.MathExpressionParser.Companion.MathParsingResult
+import org.oppia.android.util.math.MathExpressionParser.Companion.parseAlgebraicExpression
+import org.oppia.android.util.math.isApproximatelyEqualTo
+import org.oppia.android.util.math.toComparableOperation
+import javax.inject.Inject
+
+/**
+ * Provider for a classifier that determines whether an algebraic expression is equal to the
+ * creator-specific expression defined as the input to this interaction, with some manipulations.
+ *
+ * 'Trivial manipulations' indicates rearranging any operands for commutative operations, or changes
+ * in resolution order (i.e. associative) without changing the meaning of the expression.
+ *
+ * See this class's tests for a list of supported cases (both for matching and not matching).
+ */
+class AlgebraicExpressionInputMatchesUpToTrivialManipulationsRuleClassifierProvider
+@Inject constructor(
+ private val classifierFactory: GenericRuleClassifier.Factory,
+ private val consoleLogger: ConsoleLogger
+) : RuleClassifierProvider, GenericRuleClassifier.SingleInputMatcher {
+ override fun createRuleClassifier(): RuleClassifier {
+ return classifierFactory.createSingleInputClassifier(
+ expectedObjectType = InteractionObject.ObjectTypeCase.MATH_EXPRESSION,
+ inputParameterName = "x",
+ matcher = this
+ )
+ }
+
+ override fun matches(
+ answer: String,
+ input: String,
+ classificationContext: ClassificationContext
+ ): Boolean {
+ val allowedVariables = classificationContext.extractAllowedVariables()
+ val answerExpression = parseComparableOperation(answer, allowedVariables) ?: return false
+ val inputExpression = parseComparableOperation(input, allowedVariables) ?: return false
+ return answerExpression.isApproximatelyEqualTo(inputExpression)
+ }
+
+ private fun parseComparableOperation(
+ rawExpression: String,
+ allowedVariables: List
+ ): ComparableOperation? {
+ return when (val expResult = parseAlgebraicExpression(rawExpression, allowedVariables)) {
+ is MathParsingResult.Success -> expResult.result.toComparableOperation()
+ is MathParsingResult.Failure -> {
+ consoleLogger.e(
+ "AlgebraExpTrivialManips",
+ "Encountered expression that failed parsing. Expression: $rawExpression." +
+ " Failure: ${expResult.error}."
+ )
+ null
+ }
+ }
+ }
+
+ private companion object {
+ private fun ClassificationContext.extractAllowedVariables(): List {
+ return customizationArgs["customOskLetters"]
+ ?.schemaObjectList
+ ?.schemaObjectList
+ ?.map { it.normalizedString }
+ ?: listOf()
+ }
+ }
+}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputModule.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputModule.kt
new file mode 100644
index 00000000000..9923355076f
--- /dev/null
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputModule.kt
@@ -0,0 +1,39 @@
+package org.oppia.android.domain.classify.rules.algebraicexpressioninput
+
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+import org.oppia.android.domain.classify.RuleClassifier
+import org.oppia.android.domain.classify.rules.AlgebraicExpressionInputRules
+
+/**
+ * Module that binds rule classifiers corresponding to the algebraic expression input interaction.
+ */
+@Module
+class AlgebraicExpressionInputModule {
+ @Provides
+ @IntoMap
+ @StringKey("MatchesExactlyWith")
+ @AlgebraicExpressionInputRules
+ internal fun provideAlgebraicExpressionInputMatchesExactlyWithRuleClassifier(
+ classifierProvider: AlgebraicExpressionInputMatchesExactlyWithRuleClassifierProvider
+ ): RuleClassifier = classifierProvider.createRuleClassifier()
+
+ @Provides
+ @IntoMap
+ @StringKey("MatchesUpToTrivialManipulations")
+ @AlgebraicExpressionInputRules
+ internal fun provideAlgebraicExpressionInputMatchesUpToTrivialManipulationsRuleClassifier(
+ classifierProvider:
+ AlgebraicExpressionInputMatchesUpToTrivialManipulationsRuleClassifierProvider
+ ): RuleClassifier = classifierProvider.createRuleClassifier()
+
+ @Provides
+ @IntoMap
+ @StringKey("IsEquivalentTo")
+ @AlgebraicExpressionInputRules
+ internal fun provideAlgebraicExpressionInputIsEquivalentToRuleClassifier(
+ classifierProvider: AlgebraicExpressionInputIsEquivalentToRuleClassifierProvider
+ ): RuleClassifier = classifierProvider.createRuleClassifier()
+}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/BUILD.bazel
new file mode 100644
index 00000000000..c59365719f5
--- /dev/null
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/BUILD.bazel
@@ -0,0 +1,47 @@
+"""
+Classifiers for the 'AlgebraicExpressionInput' interaction.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library")
+
+kt_android_library(
+ name = "algebraic_expression_input_providers",
+ srcs = [
+ "AlgebraicExpressionInputIsEquivalentToRuleClassifierProvider.kt",
+ "AlgebraicExpressionInputMatchesExactlyWithRuleClassifierProvider.kt",
+ "AlgebraicExpressionInputMatchesUpToTrivialManipulationsRuleClassifierProvider.kt",
+ ],
+ visibility = ["//domain:domain_testing_visibility"],
+ deps = [
+ ":dagger",
+ "//domain/src/main/java/org/oppia/android/domain/classify:classification_context",
+ "//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
+ "//domain/src/main/java/org/oppia/android/domain/classify/rules:generic_rule_classifier",
+ "//domain/src/main/java/org/oppia/android/domain/classify/rules:rule_classifier_provider",
+ "//model/src/main/proto:exploration_java_proto_lite",
+ "//model/src/main/proto:interaction_object_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
+ "//third_party:javax_inject_javax_inject",
+ "//utility/src/main/java/org/oppia/android/util/locale:oppia_locale",
+ "//utility/src/main/java/org/oppia/android/util/logging:console_logger",
+ "//utility/src/main/java/org/oppia/android/util/math:extensions",
+ "//utility/src/main/java/org/oppia/android/util/math:math_expression_parser",
+ ],
+)
+
+kt_android_library(
+ name = "algebraic_expression_input_rule_module",
+ srcs = [
+ "AlgebraicExpressionInputModule.kt",
+ ],
+ visibility = ["//:oppia_prod_module_visibility"],
+ deps = [
+ ":algebraic_expression_input_providers",
+ ":dagger",
+ "//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
+ "//domain/src/main/java/org/oppia/android/domain/classify/rules:rule_classifier_provider",
+ ],
+)
+
+dagger_rules()
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/BUILD.bazel
index dfed4bcfc94..5dadd526a23 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/BUILD.bazel
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/BUILD.bazel
@@ -15,12 +15,13 @@ kt_android_library(
],
deps = [
":dagger",
+ "//domain/src/main/java/org/oppia/android/domain/classify:classification_context",
"//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
"//domain/src/main/java/org/oppia/android/domain/classify/rules:generic_rule_classifier",
"//domain/src/main/java/org/oppia/android/domain/classify/rules:rule_classifier_provider",
"//domain/src/main/java/org/oppia/android/domain/util:extensions",
- "//model:interaction_object_java_proto_lite",
- "//model:translation_java_proto_lite",
+ "//model/src/main/proto:interaction_object_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
"//third_party:javax_inject_javax_inject",
],
)
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXAtPositionYClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXAtPositionYClassifierProvider.kt
index 72affc7d851..b6388896d97 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXAtPositionYClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXAtPositionYClassifierProvider.kt
@@ -5,7 +5,7 @@ import org.oppia.android.app.model.InteractionObject.ObjectTypeCase.NON_NEGATIVE
import org.oppia.android.app.model.InteractionObject.ObjectTypeCase.TRANSLATABLE_HTML_CONTENT_ID
import org.oppia.android.app.model.ListOfSetsOfTranslatableHtmlContentIds
import org.oppia.android.app.model.TranslatableHtmlContentId
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -45,7 +45,7 @@ class DragDropSortInputHasElementXAtPositionYClassifierProvider @Inject construc
answer: ListOfContentIdSets1,
firstInput: ContentId1,
secondInput: Int,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
// Note that the '1' returned here is to have consistency with the web platform: matched indexes
// start at 1 rather than 0 to make the indexes more human friendly.
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXBeforeElementYClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXBeforeElementYClassifierProvider.kt
index 41fa56e9bcd..2c79702d5c9 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXBeforeElementYClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputHasElementXBeforeElementYClassifierProvider.kt
@@ -4,7 +4,7 @@ import org.oppia.android.app.model.InteractionObject.ObjectTypeCase.LIST_OF_SETS
import org.oppia.android.app.model.InteractionObject.ObjectTypeCase.TRANSLATABLE_HTML_CONTENT_ID
import org.oppia.android.app.model.ListOfSetsOfTranslatableHtmlContentIds
import org.oppia.android.app.model.TranslatableHtmlContentId
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -44,7 +44,7 @@ class DragDropSortInputHasElementXBeforeElementYClassifierProvider @Inject const
answer: ListOfContentIdSets2,
firstInput: ContentId2,
secondInput: ContentId2,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
val answerSets = answer.contentIdListsList.map { it.getContentIdSet() }
return answerSets.indexOfFirst {
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputIsEqualToOrderingClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputIsEqualToOrderingClassifierProvider.kt
index 18b8335d51c..60fdbd5d66d 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputIsEqualToOrderingClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputIsEqualToOrderingClassifierProvider.kt
@@ -3,7 +3,7 @@ package org.oppia.android.domain.classify.rules.dragAndDropSortInput
import org.oppia.android.app.model.InteractionObject.ObjectTypeCase.LIST_OF_SETS_OF_TRANSLATABLE_HTML_CONTENT_IDS
import org.oppia.android.app.model.ListOfSetsOfTranslatableHtmlContentIds
import org.oppia.android.app.model.SetOfTranslatableHtmlContentIds
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -34,7 +34,7 @@ class DragDropSortInputIsEqualToOrderingClassifierProvider @Inject constructor(
override fun matches(
answer: ListOfSetsOfTranslatableHtmlContentIds,
input: ListOfSetsOfTranslatableHtmlContentIds,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean = areListOfSetsOfHtmlStringsEqual(answer, input)
/**
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputIsEqualToOrderingWithOneItemAtIncorrectPositionClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputIsEqualToOrderingWithOneItemAtIncorrectPositionClassifierProvider.kt
index acf28424dfe..64dcae58082 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputIsEqualToOrderingWithOneItemAtIncorrectPositionClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput/DragDropSortInputIsEqualToOrderingWithOneItemAtIncorrectPositionClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.dragAndDropSortInput
import org.oppia.android.app.model.InteractionObject.ObjectTypeCase.LIST_OF_SETS_OF_TRANSLATABLE_HTML_CONTENT_IDS
import org.oppia.android.app.model.ListOfSetsOfTranslatableHtmlContentIds
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -33,7 +33,7 @@ class DragDropSortInputIsEqualToOrderingWithOneItemAtIncorrectPositionClassifier
override fun matches(
answer: ListOfSetsOfTranslatableHtmlContentIds,
input: ListOfSetsOfTranslatableHtmlContentIds,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
val answerStringSets = answer.contentIdListsList
val inputStringSets = input.contentIdListsList
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/BUILD.bazel
index 0de2f2b01a3..5da32a18e7c 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/BUILD.bazel
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/BUILD.bazel
@@ -21,12 +21,13 @@ kt_android_library(
],
deps = [
":dagger",
+ "//domain/src/main/java/org/oppia/android/domain/classify:classification_context",
"//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
"//domain/src/main/java/org/oppia/android/domain/classify/rules:generic_rule_classifier",
"//domain/src/main/java/org/oppia/android/domain/classify/rules:rule_classifier_provider",
"//domain/src/main/java/org/oppia/android/domain/util:extensions",
- "//model:interaction_object_java_proto_lite",
- "//model:translation_java_proto_lite",
+ "//model/src/main/proto:interaction_object_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
"//third_party:javax_inject_javax_inject",
],
)
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasDenominatorEqualToRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasDenominatorEqualToRuleClassifierProvider.kt
index 7467770aaee..1331c2d000c 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasDenominatorEqualToRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasDenominatorEqualToRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -31,7 +31,7 @@ class FractionInputHasDenominatorEqualToRuleClassifierProvider @Inject construct
override fun matches(
answer: Fraction,
input: Int,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
return answer.denominator == input
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasFractionalPartExactlyEqualToRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasFractionalPartExactlyEqualToRuleClassifierProvider.kt
index 5daf835e119..e8e75bce46d 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasFractionalPartExactlyEqualToRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasFractionalPartExactlyEqualToRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -31,7 +31,7 @@ class FractionInputHasFractionalPartExactlyEqualToRuleClassifierProvider
override fun matches(
answer: Fraction,
input: Fraction,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
return answer.numerator == input.numerator && answer.denominator == input.denominator
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasIntegerPartEqualToRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasIntegerPartEqualToRuleClassifierProvider.kt
index cdf8425ec19..81b953e27ca 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasIntegerPartEqualToRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasIntegerPartEqualToRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -31,7 +31,7 @@ class FractionInputHasIntegerPartEqualToRuleClassifierProvider @Inject construct
override fun matches(
answer: Fraction,
input: Int,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
return answer.wholeNumber == input
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasNoFractionalPartRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasNoFractionalPartRuleClassifierProvider.kt
index 30b63ee8020..d47a4ea43cd 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasNoFractionalPartRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasNoFractionalPartRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -28,7 +28,7 @@ class FractionInputHasNoFractionalPartRuleClassifierProvider @Inject constructor
override fun matches(
answer: Fraction,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
return answer.numerator == 0
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasNumeratorEqualToRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasNumeratorEqualToRuleClassifierProvider.kt
index ac3da4c4d15..fa56ec057b5 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasNumeratorEqualToRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputHasNumeratorEqualToRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -31,7 +31,7 @@ class FractionInputHasNumeratorEqualToRuleClassifierProvider @Inject constructor
override fun matches(
answer: Fraction,
input: Int,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
return answer.numerator == input
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsEquivalentToAndInSimplestFormRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsEquivalentToAndInSimplestFormRuleClassifierProvider.kt
index 76ed9cb0f72..44a2b074447 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsEquivalentToAndInSimplestFormRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsEquivalentToAndInSimplestFormRuleClassifierProvider.kt
@@ -2,13 +2,13 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
-import org.oppia.android.domain.util.approximatelyEquals
-import org.oppia.android.domain.util.toFloat
-import org.oppia.android.domain.util.toSimplestForm
+import org.oppia.android.util.math.isApproximatelyEqualTo
+import org.oppia.android.util.math.toDouble
+import org.oppia.android.util.math.toSimplestForm
import javax.inject.Inject
/**
@@ -34,8 +34,9 @@ class FractionInputIsEquivalentToAndInSimplestFormRuleClassifierProvider
override fun matches(
answer: Fraction,
input: Fraction,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
- return answer.toFloat().approximatelyEquals(input.toFloat()) && answer == input.toSimplestForm()
+ return answer.toDouble().isApproximatelyEqualTo(input.toDouble()) &&
+ answer == input.toSimplestForm()
}
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsEquivalentToRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsEquivalentToRuleClassifierProvider.kt
index d7f8c597461..d37e0e51acb 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsEquivalentToRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsEquivalentToRuleClassifierProvider.kt
@@ -2,12 +2,12 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
-import org.oppia.android.domain.util.approximatelyEquals
-import org.oppia.android.domain.util.toFloat
+import org.oppia.android.util.math.isApproximatelyEqualTo
+import org.oppia.android.util.math.toDouble
import javax.inject.Inject
/**
@@ -32,8 +32,8 @@ class FractionInputIsEquivalentToRuleClassifierProvider @Inject constructor(
override fun matches(
answer: Fraction,
input: Fraction,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
- return answer.toFloat().approximatelyEquals(input.toFloat())
+ return answer.toDouble().isApproximatelyEqualTo(input.toDouble())
}
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsExactlyEqualToRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsExactlyEqualToRuleClassifierProvider.kt
index 628824681c2..eaf41d88008 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsExactlyEqualToRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsExactlyEqualToRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -30,7 +30,7 @@ class FractionInputIsExactlyEqualToRuleClassifierProvider @Inject constructor(
override fun matches(
answer: Fraction,
input: Fraction,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
return answer == input
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsGreaterThanRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsGreaterThanRuleClassifierProvider.kt
index 740ab078eee..05d4936965c 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsGreaterThanRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsGreaterThanRuleClassifierProvider.kt
@@ -2,11 +2,11 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
-import org.oppia.android.domain.util.toFloat
+import org.oppia.android.util.math.toDouble
import javax.inject.Inject
/**
@@ -31,8 +31,8 @@ class FractionInputIsGreaterThanRuleClassifierProvider @Inject constructor(
override fun matches(
answer: Fraction,
input: Fraction,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
- return answer.toFloat() > input.toFloat()
+ return answer.toDouble() > input.toDouble()
}
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsLessThanRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsLessThanRuleClassifierProvider.kt
index 3fbf98e8fac..b4e0fde2e20 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsLessThanRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput/FractionInputIsLessThanRuleClassifierProvider.kt
@@ -2,11 +2,11 @@ package org.oppia.android.domain.classify.rules.fractioninput
import org.oppia.android.app.model.Fraction
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
-import org.oppia.android.domain.util.toFloat
+import org.oppia.android.util.math.toDouble
import javax.inject.Inject
/**
@@ -31,8 +31,8 @@ class FractionInputIsLessThanRuleClassifierProvider @Inject constructor(
override fun matches(
answer: Fraction,
input: Fraction,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
- return answer.toFloat() < input.toFloat()
+ return answer.toDouble() < input.toDouble()
}
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput/BUILD.bazel
index a6edda847b8..eb216ecc013 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput/BUILD.bazel
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput/BUILD.bazel
@@ -12,12 +12,13 @@ kt_android_library(
],
deps = [
":dagger",
+ "//domain/src/main/java/org/oppia/android/domain/classify:classification_context",
"//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
"//domain/src/main/java/org/oppia/android/domain/classify/rules:generic_rule_classifier",
"//domain/src/main/java/org/oppia/android/domain/classify/rules:rule_classifier_provider",
"//domain/src/main/java/org/oppia/android/domain/util:extensions",
- "//model:interaction_object_java_proto_lite",
- "//model:translation_java_proto_lite",
+ "//model/src/main/proto:interaction_object_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
"//third_party:javax_inject_javax_inject",
],
)
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput/ImageClickInputIsInRegionRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput/ImageClickInputIsInRegionRuleClassifierProvider.kt
index 3504fdaa68e..4f13484722f 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput/ImageClickInputIsInRegionRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput/ImageClickInputIsInRegionRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.imageClickInput
import org.oppia.android.app.model.ClickOnImage
import org.oppia.android.app.model.InteractionObject
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -31,7 +31,7 @@ class ImageClickInputIsInRegionRuleClassifierProvider @Inject constructor(
override fun matches(
answer: ClickOnImage,
input: String,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
return answer.clickedRegionsList.indexOf(input) != -1
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/BUILD.bazel
index 0827ee61f10..b1ae3ef0a1f 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/BUILD.bazel
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/BUILD.bazel
@@ -15,12 +15,13 @@ kt_android_library(
],
deps = [
":dagger",
+ "//domain/src/main/java/org/oppia/android/domain/classify:classification_context",
"//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
"//domain/src/main/java/org/oppia/android/domain/classify/rules:generic_rule_classifier",
"//domain/src/main/java/org/oppia/android/domain/classify/rules:rule_classifier_provider",
"//domain/src/main/java/org/oppia/android/domain/util:extensions",
- "//model:interaction_object_java_proto_lite",
- "//model:translation_java_proto_lite",
+ "//model/src/main/proto:interaction_object_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
"//third_party:javax_inject_javax_inject",
],
)
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputContainsAtLeastOneOfRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputContainsAtLeastOneOfRuleClassifierProvider.kt
index f434bdd263c..f84e30b9b32 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputContainsAtLeastOneOfRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputContainsAtLeastOneOfRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.itemselectioninput
import org.oppia.android.app.model.InteractionObject
import org.oppia.android.app.model.SetOfTranslatableHtmlContentIds
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -32,6 +32,6 @@ class ItemSelectionInputContainsAtLeastOneOfRuleClassifierProvider @Inject const
override fun matches(
answer: SetOfTranslatableHtmlContentIds,
input: SetOfTranslatableHtmlContentIds,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean = answer.getContentIdSet().intersect(input.getContentIdSet()).isNotEmpty()
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputDoesNotContainAtLeastOneOfRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputDoesNotContainAtLeastOneOfRuleClassifierProvider.kt
index 5dbe1330c19..e3d306c9afc 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputDoesNotContainAtLeastOneOfRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputDoesNotContainAtLeastOneOfRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.itemselectioninput
import org.oppia.android.app.model.InteractionObject
import org.oppia.android.app.model.SetOfTranslatableHtmlContentIds
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -33,6 +33,6 @@ class ItemSelectionInputDoesNotContainAtLeastOneOfRuleClassifierProvider
override fun matches(
answer: SetOfTranslatableHtmlContentIds,
input: SetOfTranslatableHtmlContentIds,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean = answer.getContentIdSet().intersect(input.getContentIdSet()).isEmpty()
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputEqualsRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputEqualsRuleClassifierProvider.kt
index c447df24546..4227906dbaf 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputEqualsRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputEqualsRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.itemselectioninput
import org.oppia.android.app.model.InteractionObject
import org.oppia.android.app.model.SetOfTranslatableHtmlContentIds
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -32,6 +32,6 @@ class ItemSelectionInputEqualsRuleClassifierProvider @Inject constructor(
override fun matches(
answer: SetOfTranslatableHtmlContentIds,
input: SetOfTranslatableHtmlContentIds,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean = answer.getContentIdSet() == input.getContentIdSet()
}
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputIsProperSubsetOfRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputIsProperSubsetOfRuleClassifierProvider.kt
index ba14381872a..c7a35286733 100644
--- a/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputIsProperSubsetOfRuleClassifierProvider.kt
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput/ItemSelectionInputIsProperSubsetOfRuleClassifierProvider.kt
@@ -2,7 +2,7 @@ package org.oppia.android.domain.classify.rules.itemselectioninput
import org.oppia.android.app.model.InteractionObject
import org.oppia.android.app.model.SetOfTranslatableHtmlContentIds
-import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.domain.classify.ClassificationContext
import org.oppia.android.domain.classify.RuleClassifier
import org.oppia.android.domain.classify.rules.GenericRuleClassifier
import org.oppia.android.domain.classify.rules.RuleClassifierProvider
@@ -32,7 +32,7 @@ class ItemSelectionInputIsProperSubsetOfRuleClassifierProvider @Inject construct
override fun matches(
answer: SetOfTranslatableHtmlContentIds,
input: SetOfTranslatableHtmlContentIds,
- writtenTranslationContext: WrittenTranslationContext
+ classificationContext: ClassificationContext
): Boolean {
val answerSet = answer.getContentIdSet()
val inputSet = input.getContentIdSet()
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/mathequationinput/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/classify/rules/mathequationinput/BUILD.bazel
new file mode 100644
index 00000000000..0a70ea19348
--- /dev/null
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/mathequationinput/BUILD.bazel
@@ -0,0 +1,47 @@
+"""
+Classifiers for the 'MathEquationInput' interaction.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library")
+
+kt_android_library(
+ name = "math_equation_input_providers",
+ srcs = [
+ "MathEquationInputIsEquivalentToRuleClassifierProvider.kt",
+ "MathEquationInputMatchesExactlyWithRuleClassifierProvider.kt",
+ "MathEquationInputMatchesUpToTrivialManipulationsRuleClassifierProvider.kt",
+ ],
+ visibility = ["//domain:domain_testing_visibility"],
+ deps = [
+ ":dagger",
+ "//domain/src/main/java/org/oppia/android/domain/classify:classification_context",
+ "//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
+ "//domain/src/main/java/org/oppia/android/domain/classify/rules:generic_rule_classifier",
+ "//domain/src/main/java/org/oppia/android/domain/classify/rules:rule_classifier_provider",
+ "//model/src/main/proto:exploration_java_proto_lite",
+ "//model/src/main/proto:interaction_object_java_proto_lite",
+ "//model/src/main/proto:translation_java_proto_lite",
+ "//third_party:javax_inject_javax_inject",
+ "//utility/src/main/java/org/oppia/android/util/locale:oppia_locale",
+ "//utility/src/main/java/org/oppia/android/util/logging:console_logger",
+ "//utility/src/main/java/org/oppia/android/util/math:extensions",
+ "//utility/src/main/java/org/oppia/android/util/math:math_expression_parser",
+ ],
+)
+
+kt_android_library(
+ name = "math_equation_input_rule_module",
+ srcs = [
+ "MathEquationInputModule.kt",
+ ],
+ visibility = ["//:oppia_prod_module_visibility"],
+ deps = [
+ ":dagger",
+ ":math_equation_input_providers",
+ "//domain/src/main/java/org/oppia/android/domain/classify:rule_classifier",
+ "//domain/src/main/java/org/oppia/android/domain/classify/rules:rule_classifier_provider",
+ ],
+)
+
+dagger_rules()
diff --git a/domain/src/main/java/org/oppia/android/domain/classify/rules/mathequationinput/MathEquationInputIsEquivalentToRuleClassifierProvider.kt b/domain/src/main/java/org/oppia/android/domain/classify/rules/mathequationinput/MathEquationInputIsEquivalentToRuleClassifierProvider.kt
new file mode 100644
index 00000000000..1bcbdcf21f6
--- /dev/null
+++ b/domain/src/main/java/org/oppia/android/domain/classify/rules/mathequationinput/MathEquationInputIsEquivalentToRuleClassifierProvider.kt
@@ -0,0 +1,103 @@
+package org.oppia.android.domain.classify.rules.mathequationinput
+
+import org.oppia.android.app.model.InteractionObject
+import org.oppia.android.app.model.Polynomial
+import org.oppia.android.domain.classify.ClassificationContext
+import org.oppia.android.domain.classify.RuleClassifier
+import org.oppia.android.domain.classify.rules.GenericRuleClassifier
+import org.oppia.android.domain.classify.rules.RuleClassifierProvider
+import org.oppia.android.util.logging.ConsoleLogger
+import org.oppia.android.util.math.MathExpressionParser.Companion.MathParsingResult
+import org.oppia.android.util.math.MathExpressionParser.Companion.parseAlgebraicEquation
+import org.oppia.android.util.math.isApproximatelyEqualTo
+import org.oppia.android.util.math.minus
+import org.oppia.android.util.math.sort
+import org.oppia.android.util.math.toPolynomial
+import org.oppia.android.util.math.unaryMinus
+import javax.inject.Inject
+
+/**
+ * Provider for a classifier that determines whether a math equation expression is mathematically
+ * equivalent to the creator-specific equation defined as the input to this interaction.
+ *
+ * Note that both equations are assumed and parsed as polynomial equations. Furthermore, this
+ * classifier allows the two sides of the equations to be rearranged in any way on either side of
+ * the '=' sign (but they can't be multiples of each other).
+ *
+ * See this class's tests for a list of supported cases (both for matching and not matching).
+ */
+class MathEquationInputIsEquivalentToRuleClassifierProvider @Inject constructor(
+ private val classifierFactory: GenericRuleClassifier.Factory,
+ private val consoleLogger: ConsoleLogger
+) : RuleClassifierProvider, GenericRuleClassifier.SingleInputMatcher