From: Michal Malý Date: Mon, 24 Nov 2014 13:54:03 +0000 (+0100) Subject: - Add preliminary implementation of Solubility Suite X-Git-Url: https://gitweb.devoid-pointer.net/?a=commitdiff_plain;h=3eb3d198618786cb51a2460b7c4acd9cf96e9b30;p=Nine-Q.git - Add preliminary implementation of Solubility Suite - Introduce Auto_Lock (similar to C++11 std::lock_guard) --- diff --git a/bin/templates/face_index.html b/bin/templates/face_index.html index 5f29ae9..f1320be 100644 --- a/bin/templates/face_index.html +++ b/bin/templates/face_index.html @@ -1,3 +1,4 @@ pH jednosytné kyseliny/báze + Srážecí rovnováhy diff --git a/bin/templates/face_solubility_c_f_ks_diff.html b/bin/templates/face_solubility_c_f_ks_diff.html new file mode 100644 index 0000000..a4ddf51 --- /dev/null +++ b/bin/templates/face_solubility_c_f_ks_diff.html @@ -0,0 +1,23 @@ + +
+
Zadání:
+
Jaká je koncentrace látky X@_X_STOCHIO_@Z@_Z_STOCHIO_@ o Ks = @_KS_INT_@,@_KS_DEC_@ . 10@_KS_EXP_@ která je rozpuÅ¡těna v roztoku, který obsahuje uni-univalentní elektrolyt o koncetraci @_EC_INT_@,@_EC_DEC_@ . 10@_EC_EXP_@ mol/dm3, který nemá s danou látkou žádný společný iont?
+ @_SUBMIT_FORM_@ +
+ + @_ANSWER_SECTION_@ + +
+ + Zobraz sekci + +
Nápověda:
+
+ @_HINTS_SECTION_@ +
+
+ +
+
Parametry příkladů:
+ @_PARAMETERS_FORM_@ +
diff --git a/bin/templates/face_solubility_c_f_ks_shared.html b/bin/templates/face_solubility_c_f_ks_shared.html new file mode 100644 index 0000000..20fac37 --- /dev/null +++ b/bin/templates/face_solubility_c_f_ks_shared.html @@ -0,0 +1,23 @@ + +
+
Zadání:
+
Jaká je koncentrace látky X@_X_STOCHIO_@Z@_Z_STOCHIO_@ o Ks = @_KS_INT_@,@_KS_DEC_@ . 10@_KS_EXP_@ která je rozpuÅ¡těna v roztoku, který obsahuje zcela rozpuÅ¡těnou látku A@_X_STOCHIO_@Z o koncentraci @_EC_INT_@,@_EC_DEC_@ . 10@_EC_EXP_@ mol/dm3. Anion obou látek je stejný.
+ @_SUBMIT_FORM_@ +
+ + @_ANSWER_SECTION_@ + +
+ + Zobraz sekci + +
Nápověda:
+
+ @_HINTS_SECTION_@ +
+
+ +
+
Parametry příkladů:
+ @_PARAMETERS_FORM_@ +
diff --git a/bin/templates/face_solubility_ks_f_g_v.html b/bin/templates/face_solubility_ks_f_g_v.html new file mode 100644 index 0000000..288eca9 --- /dev/null +++ b/bin/templates/face_solubility_ks_f_g_v.html @@ -0,0 +1,23 @@ + +
+
Zadání:
+
Jaké je Ks látky X@_X_STOCHIO_@Z@_Z_STOCHIO_@ o molární hmotnosti MW=@_MOLAR_MASS_INT_@,@_MOLAR_MASS_DEC_@ g/mol, jejíž @_SAMPLE_WEIGHT_INT_@,@_SAMPLE_WEIGHT_DEC_@ g se zcela rozpustí v @_SAMPLE_VOLUME_INT_@,@_SAMPLE_VOLUME_DEC_@ . 10@_SAMPLE_VOLUME_EXP_@ dm3
+ @_SUBMIT_FORM_@ +
+ + @_ANSWER_SECTION_@ + +
+ + Zobraz sekci + +
Nápověda:
+
+ @_HINTS_SECTION_@ +
+
+ +
+
Parametry příkladů:
+ @_PARAMETERS_FORM_@ +
diff --git a/bin/templates/face_solubility_params.html b/bin/templates/face_solubility_params.html new file mode 100644 index 0000000..86852e2 --- /dev/null +++ b/bin/templates/face_solubility_params.html @@ -0,0 +1,18 @@ +
+
+ + +
+
+ + +
+
+ +
+
diff --git a/bin/templates/face_solubility_submit.html b/bin/templates/face_solubility_submit.html new file mode 100644 index 0000000..c1fec58 --- /dev/null +++ b/bin/templates/face_solubility_submit.html @@ -0,0 +1,9 @@ +
+
+ + +
+
+ +
+
diff --git a/bin/templates/face_solubility_v_f_g_ks.html b/bin/templates/face_solubility_v_f_g_ks.html new file mode 100644 index 0000000..1bbe36d --- /dev/null +++ b/bin/templates/face_solubility_v_f_g_ks.html @@ -0,0 +1,23 @@ + +
+
Zadání:
+
V jakém objemu se rozpustí @_SAMPLE_WEIGHT_INT_@,@_SAMPLE_WEIGHT_DEC_@ g látky X@_X_STOCHIO_@Z@_Z_STOCHIO_@ o Ks=@_KS_INT_@,@_KS_DEC_@ . 10@_KS_EXP_@ a molekulové hmotnosti MW=@_MOLAR_MASS_INT_@,@_MOLAR_MASS_DEC_@ g/mol?
+ @_SUBMIT_FORM_@ +
+ + @_ANSWER_SECTION_@ + +
+ + Zobraz sekci + +
Nápověda:
+
+ @_HINTS_SECTION_@ +
+
+ +
+
Parametry příkladů:
+ @_PARAMETERS_FORM_@ +
diff --git a/src/face_generators/face_generator.adb b/src/face_generators/face_generator.adb index 6790ad8..0f9c751 100644 --- a/src/face_generators/face_generator.adb +++ b/src/face_generators/face_generator.adb @@ -1,9 +1,10 @@ -with AWS.Templates; +with Ada.Strings.Unbounded; package body Face_Generator is function Generate_Index_Face(HTML: out HTML_Code) return Boolean is use AWS.Templates; + Temp: HTML_Code; Translations_Hdr: Translate_Set; begin @@ -43,8 +44,10 @@ package body Face_Generator is declare Problem_Type_Str: constant String := Assignment.Element(PROBLEM_TYPE_KEY); begin - if Problem_Type_Str = Problem_Type'Image(Acidobazic) then + if Problem_Type_Str = PROBLEM_TYPE_ACIDOBAZIC then return Generate_Face_Acidobazic(Assignment, Answer_Message, Answer_Code, Parameters, HTML); + elsif Problem_Type_Str = PROBLEM_TYPE_SOLUBILITY then + return Generate_Face_Solubility(Assignment, Answer_Message, Answer_Code, Parameters, HTML); else return False; end if; @@ -52,6 +55,31 @@ package body Face_Generator is end Generate_Face_With_Answer; -- BEGIN: Private functions + + procedure Add_Answer_Section(Translations: in out AWS.Templates.Translate_Set; Answer_Message: in UB_Text; + AR: in Problem_Generator_Syswides.Answer_RetCode) is + use AWS.Templates; + use Problem_Generator_Syswides; + + Temp: HTML_Code; + Translations_Answer: Translate_Set; + begin + case AR is + when Correct_Answer => + Insert(Translations_Answer, Assoc(ANSWER_KIND_KEY, ANSWER_KIND_GOOD)); + Insert(Translations_Answer, Assoc(ANSWER_MESSAGE_KEY, UB_Text_To_Fixed_String(Answer_Message))); + Temp := Parse(Filename => "templates/answer_section.html", Translations => Translations_Answer); + Insert(Translations, Assoc(ANSWER_SECTION_KEY, HTML_To_Fixed_String(Temp))); + when Wrong_Answer | Malformed_Answer => + Insert(Translations_Answer, Assoc(ANSWER_KIND_KEY, ANSWER_KIND_BAD)); + Insert(Translations_Answer, Assoc(ANSWER_MESSAGE_KEY, UB_Text_To_Fixed_String(Answer_Message))); + Temp := Parse(Filename => "templates/answer_section.html", Translations => Translations_Answer); + Insert(Translations, Assoc(ANSWER_SECTION_KEY, HTML_To_Fixed_String(Temp))); + when others => + Insert(Translations, Assoc(ANSWER_SECTION_KEY, "")); + end case; + end Add_Answer_Section; + function Generate_Face_Acidobazic(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map; Answer_Message: in UB_Text; Answer_Code: in Problem_Generator_Syswides.Answer_RetCode; @@ -62,7 +90,6 @@ package body Face_Generator is use Problem_Generator_Syswides.Assignment_Info; use Problem_Generator_Syswides.Parameters_Info; - Translations_Answer: Translate_Set; Translations_Hdr: Translate_Set; Translations: Translate_Set; Temp: HTML_Code; @@ -94,20 +121,7 @@ package body Face_Generator is Insert(Translations, Assoc("PARAMETER_NO_BOTH_SIMPLIFICATIONS_CHECKED", "checked")); end if; - case Answer_Code is - when Correct_Answer => - Insert(Translations_Answer, Assoc(ANSWER_KIND_KEY, ANSWER_KIND_GOOD)); - Insert(Translations_Answer, Assoc(ANSWER_MESSAGE_KEY, UB_Text_To_Fixed_String(Answer_Message))); - Temp := Parse(Filename => "templates/answer_section.html", Translations => Translations_Answer); - Insert(Translations, Assoc(ANSWER_SECTION_KEY, HTML_To_Fixed_String(Temp))); - when Wrong_Answer | Malformed_Answer => - Insert(Translations_Answer, Assoc(ANSWER_KIND_KEY, ANSWER_KIND_BAD)); - Insert(Translations_Answer, Assoc(ANSWER_MESSAGE_KEY, UB_Text_To_Fixed_String(Answer_Message))); - Temp := Parse(Filename => "templates/answer_section.html", Translations => Translations_Answer); - Insert(Translations, Assoc(ANSWER_SECTION_KEY, HTML_To_Fixed_String(Temp))); - when others => - Insert(Translations, Assoc(ANSWER_SECTION_KEY, "")); - end case; + Add_Answer_Section(Translations, Answer_Message, Answer_Code); -- Generate hints if Assignment.Element(Acidobazic_Suite.PKX_KEY) = Acidobazic_Suite.PKX_PKA_TEXT then @@ -127,4 +141,195 @@ package body Face_Generator is return True; end Generate_Face_Acidobazic; + + function Generate_Face_Solubility(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map; + Answer_Message: in UB_Text; + Answer_Code: in Problem_Generator_Syswides.Answer_RetCode; + Parameters: in Problem_Generator_Syswides.Parameters_Info.Map; + HTML: out HTML_Code) return Boolean is + use Ada.Strings.Unbounded; + use AWS.Templates; + use Problem_Generator_Syswides; + use Problem_Generator_Syswides.Assignment_Info; + use Problem_Generator_Syswides.Parameters_Info; + + Translations_Hdr: Translate_Set; + Translations_Params: Translate_Set; + Translations: Translate_Set; + Temp: HTML_Code; + Params_Code: HTML_Code; + + P_Subtype: UB_Text; + begin + Insert(Translations_Hdr, Assoc(HEADER_CAPTION_KEY, "< " & Solubility_Suite.PROBLEM_NAME_READABLE)); + Insert(Translations_Hdr, Assoc(META_EXPIRE_NOW_KEY, META_EXPIRE_NOW_TEXT)); + Insert(Translations_Hdr, Assoc(META_NO_CACHE_KEY, META_NO_CACHE_TEXT)); + HTML := Parse(Filename => "templates/header.html", Translations => Translations_Hdr); + -- Add JavaScripts + Temp := Parse(Filename => "scripts/expand_collapse.js", Cached => True); + Append_HTML(Source => HTML, New_Item => Temp); + + Add_Answer_Section(Translations, Answer_Message, Answer_Code); + + Temp := Parse(Filename => "templates/face_solubility_submit.html", Cached => True); + Insert(Translations, Assoc("SUBMIT_FORM", HTML_To_Fixed_String(Temp))); + + -- Add parameters section + Insert(Translations_Params, Assoc(Solubility_Suite.PARAMETER_IONIC_STRENGTH_KEY, Solubility_Suite.PARAMETER_IONIC_STRENGTH_KEY)); + if Parameters.Find(Solubility_Suite.PARAMETER_IONIC_STRENGTH_KEY) /= Parameters_Info.No_Element then + Insert(Translations_Params, Assoc("PARAMETER_IONIC_STRENGTH_CHECKED", "checked=""checked""")); + end if; + if Parameters.Find(Solubility_Suite.PARAMETER_PROBLEM_SUBTYPE_KEY) = Parameters_Info.No_Element then + -- This parameter must be always present + return False; + end if; + P_Subtype := To_UB_Text(Parameters.Element(Solubility_Suite.PARAMETER_PROBLEM_SUBTYPE_KEY)); + Insert(Translations_Params, Assoc(Solubility_Suite.PARAMETER_PROBLEM_SUBTYPE_KEY, Solubility_Suite.PARAMETER_PROBLEM_SUBTYPE_KEY)); + Insert(Translations_Params, Assoc(Solubility_Suite.PROBLEM_SUBTYPE_V_FROM_G_KS, Solubility_Suite.PROBLEM_SUBTYPE_V_FROM_G_KS)); + Insert(Translations_Params, Assoc(Solubility_Suite.PROBLEM_SUBTYPE_KS_FROM_G_V, Solubility_Suite.PROBLEM_SUBTYPE_KS_FROM_G_V)); + Insert(Translations_Params, Assoc(Solubility_Suite.PROBLEM_SUBTYPE_C_FROM_KS_DIFFERENT_IONS, Solubility_Suite.PROBLEM_SUBTYPE_C_FROM_KS_DIFFERENT_IONS)); + Insert(Translations_Params, Assoc(Solubility_Suite.PROBLEM_SUBTYPE_C_FROM_KS_SHARED_ION, Solubility_Suite.PROBLEM_SUBTYPE_C_FROM_KS_SHARED_ION)); + Insert(Translations_Params, Assoc(UB_Text_To_Fixed_String(P_Subtype) & "_SELECTED", "selected=""selected""")); + -- Load and parse parameters template + Params_Code := Parse(Filename => "templates/face_solubility_params.html", Translations => Translations_Params); + -- Put the processed parameters template into the face + Insert(Translations, Assoc("PARAMETERS_FORM", HTML_To_Fixed_String(Params_Code))); + + + if P_Subtype = Solubility_Suite.PROBLEM_SUBTYPE_V_FROM_G_KS then + -- Check that we have all necessary fields in the assignment + if Assignment.Find(Solubility_Suite.X_STOCHIO_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.Z_STOCHIO_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.KS_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.KS_DEC_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.KS_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.SAMPLE_WEIGHT_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.SAMPLE_WEIGHT_DEC_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.MOLAR_MASS_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.MOLAR_MASS_DEC_KEY) = Assignment_Info.No_Element then + return False; + end if; + + Insert(Translations, Assoc(Solubility_Suite.X_STOCHIO_KEY, Assignment.Element(Solubility_Suite.X_STOCHIO_KEY))); + Insert(Translations, Assoc(Solubility_Suite.Z_STOCHIO_KEY, Assignment.Element(Solubility_Suite.Z_STOCHIO_KEY))); + Insert(Translations, Assoc(Solubility_Suite.KS_INT_KEY, Assignment.Element(Solubility_Suite.KS_INT_KEY))); + Insert(Translations, Assoc(Solubility_Suite.KS_DEC_KEY, Assignment.Element(Solubility_Suite.KS_DEC_KEY))); + Insert(Translations, Assoc(Solubility_Suite.KS_EXP_KEY, Assignment.Element(Solubility_Suite.KS_EXP_KEY))); + Insert(Translations, Assoc(Solubility_Suite.SAMPLE_WEIGHT_INT_KEY, Assignment.Element(Solubility_Suite.SAMPLE_WEIGHT_INT_KEY))); + Insert(Translations, Assoc(Solubility_Suite.SAMPLE_WEIGHT_DEC_KEY, Assignment.Element(Solubility_Suite.SAMPLE_WEIGHT_DEC_KEY))); + Insert(Translations, Assoc(Solubility_Suite.MOLAR_MASS_INT_KEY, Assignment.Element(Solubility_Suite.MOLAR_MASS_INT_KEY))); + Insert(Translations, Assoc(Solubility_Suite.MOLAR_MASS_DEC_KEY, Assignment.Element(Solubility_Suite.MOLAR_MASS_DEC_KEY))); + + Temp := Parse(Filename => "templates/face_solubility_v_f_g_ks.html", Translations => Translations); + Append_HTML(Source => HTML, New_Item => Temp); + elsif P_Subtype = Solubility_Suite.PROBLEM_SUBTYPE_KS_FROM_G_V then + -- Check that we have all necessary fields in the assignment + if Assignment.Find(Solubility_Suite.X_STOCHIO_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.Z_STOCHIO_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.SAMPLE_VOLUME_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.SAMPLE_VOLUME_DEC_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.SAMPLE_VOLUME_EXP_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.SAMPLE_WEIGHT_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.SAMPLE_WEIGHT_DEC_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.MOLAR_MASS_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.MOLAR_MASS_DEC_KEY) = Assignment_Info.No_Element then + return False; + end if; + + Insert(Translations, Assoc(Solubility_Suite.X_STOCHIO_KEY, Assignment.Element(Solubility_Suite.X_STOCHIO_KEY))); + Insert(Translations, Assoc(Solubility_Suite.Z_STOCHIO_KEY, Assignment.Element(Solubility_Suite.Z_STOCHIO_KEY))); + Insert(Translations, Assoc(Solubility_Suite.SAMPLE_VOLUME_INT_KEY, Assignment.Element(Solubility_Suite.SAMPLE_VOLUME_INT_KEY))); + Insert(Translations, Assoc(Solubility_Suite.SAMPLE_VOLUME_DEC_KEY, Assignment.Element(Solubility_Suite.SAMPLE_VOLUME_DEC_KEY))); + Insert(Translations, Assoc(Solubility_Suite.SAMPLE_VOLUME_EXP_KEY, Assignment.Element(Solubility_Suite.SAMPLE_VOLUME_EXP_KEY))); + Insert(Translations, Assoc(Solubility_Suite.SAMPLE_WEIGHT_INT_KEY, Assignment.Element(Solubility_Suite.SAMPLE_WEIGHT_INT_KEY))); + Insert(Translations, Assoc(Solubility_Suite.SAMPLE_WEIGHT_DEC_KEY, Assignment.Element(Solubility_Suite.SAMPLE_WEIGHT_DEC_KEY))); + Insert(Translations, Assoc(Solubility_Suite.MOLAR_MASS_INT_KEY, Assignment.Element(Solubility_Suite.MOLAR_MASS_INT_KEY))); + Insert(Translations, Assoc(Solubility_Suite.MOLAR_MASS_DEC_KEY, Assignment.Element(Solubility_Suite.MOLAR_MASS_DEC_KEY))); + + Temp := Parse(Filename => "templates/face_solubility_ks_f_g_v.html", Translations => Translations); + Append_HTML(Source => HTML, New_Item => Temp); + elsif P_Subtype = Solubility_Suite.PROBLEM_SUBTYPE_C_FROM_KS_DIFFERENT_IONS or P_Subtype = Solubility_Suite.PROBLEM_SUBTYPE_C_FROM_KS_SHARED_ION then + -- Check that we have all necessary fields in the assignment + if Assignment.Find(Solubility_Suite.X_STOCHIO_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.Z_STOCHIO_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.EC_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.EC_DEC_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.EC_EXP_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.KS_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.KS_DEC_KEY) = Assignment_Info.No_Element then + return False; + end if; + if Assignment.Find(Solubility_Suite.KS_INT_KEY) = Assignment_Info.No_Element then + return False; + end if; + + Insert(Translations, Assoc(Solubility_Suite.X_STOCHIO_KEY, Assignment.Element(Solubility_Suite.X_STOCHIO_KEY))); + Insert(Translations, Assoc(Solubility_Suite.Z_STOCHIO_KEY, Assignment.Element(Solubility_Suite.Z_STOCHIO_KEY))); + Insert(Translations, Assoc(Solubility_Suite.EC_INT_KEY, Assignment.Element(Solubility_Suite.EC_INT_KEY))); + Insert(Translations, Assoc(Solubility_Suite.EC_DEC_KEY, Assignment.Element(Solubility_Suite.EC_DEC_KEY))); + Insert(Translations, Assoc(Solubility_Suite.EC_EXP_KEY, Assignment.Element(Solubility_Suite.EC_EXP_KEY))); + Insert(Translations, Assoc(Solubility_Suite.KS_INT_KEY, Assignment.Element(Solubility_Suite.KS_INT_KEY))); + Insert(Translations, Assoc(Solubility_Suite.KS_DEC_KEY, Assignment.Element(Solubility_Suite.KS_DEC_KEY))); + Insert(Translations, Assoc(Solubility_Suite.KS_EXP_KEY, Assignment.Element(Solubility_Suite.KS_EXP_KEY))); + + if P_Subtype = Solubility_Suite.PROBLEM_SUBTYPE_C_FROM_KS_DIFFERENT_IONS then + Temp := Parse(Filename => "templates/face_solubility_c_f_ks_diff.html", Translations => Translations); + else + Temp := Parse(Filename => "templates/face_solubility_c_f_ks_shared.html", Translations => Translations); + end if; + Append_HTML(Source => HTML, New_Item => Temp); + else + return False; + end if; + + Temp := Parse(Filename => "templates/footer.html"); + Append_HTML(Source => HTML, New_Item => Temp); + + return True; + end Generate_Face_Solubility; + end Face_Generator; diff --git a/src/face_generators/face_generator.ads b/src/face_generators/face_generator.ads index 3a26435..a758854 100644 --- a/src/face_generators/face_generator.ads +++ b/src/face_generators/face_generator.ads @@ -1,5 +1,6 @@ with Global_Types; with Problem_Generator_Syswides; +with AWS.Templates; use Global_Types; package Face_Generator is @@ -16,12 +17,21 @@ package Face_Generator is HTML: out HTML_Code) return Boolean; private + procedure Add_Answer_Section(Translations: in out AWS.Templates.Translate_Set; Answer_Message: in UB_Text; + AR: in Problem_Generator_Syswides.Answer_RetCode); + function Generate_Face_Acidobazic(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map; Answer_Message: in UB_Text; Answer_Code: in Problem_Generator_Syswides.Answer_RetCode; Parameters: in Problem_Generator_Syswides.Parameters_Info.Map; HTML: out HTML_Code) return Boolean; + function Generate_Face_Solubility(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map; + Answer_Message: in UB_Text; + Answer_Code: in Problem_Generator_Syswides.Answer_RetCode; + Parameters: in Problem_Generator_Syswides.Parameters_Info.Map; + HTML: out HTML_Code) return Boolean; + HEADER_CAPTION_KEY: constant String := "HEADER_CAPTION"; HINTS_SECTION_KEY: constant String := "HINTS_SECTION"; META_EXPIRE_NOW_KEY: constant String := "META_EXPIRE_NOW"; diff --git a/src/formatting_helpers.adb b/src/formatting_helpers.adb index 313032f..8163dde 100644 --- a/src/formatting_helpers.adb +++ b/src/formatting_helpers.adb @@ -4,6 +4,49 @@ with Ada.Strings.Fixed; package body Formatting_Helpers is + function Get_Decimal_Part_Flt(Num: in FH_Float) return FH_Float is + package FHEF is new Ada.Numerics.Generic_Elementary_Functions(FH_Float); + use FHEF; + + Log_Arg_Floored: FH_Float; + begin + Log_Arg_Floored := FH_Float'Floor(FHEF.Log(Base => 10.0, X => Num)); + + return ((10.0 ** Log_Arg_Floored) - 1.0) / Log_Arg_Floored; + end Get_Decimal_Part_Flt; + + function Get_Integer_Part_Int(Num: in FH_Float) return Integer is + package FHEF is new Ada.Numerics.Generic_Elementary_Functions(FH_Float); + use FHEF; + + Log_Arg_Floored: FH_Float; + Scaled: FH_Float; + begin + Log_Arg_Floored := FH_Float'Floor(FHEF.Log(Base => 10.0, X => Num)); + Scaled := Num / Log_Arg_Floored; + + return Integer(Scaled - Get_Decimal_Part_Flt(Num)); + end Get_Integer_Part_Int; + + function String_To_Float(S: in String) return FH_Float is + F: FH_Float; + Idx: Natural; + SS: String := S; + begin + -- Replace "," with "." as decimal seaprator + Idx := Ada.Strings.Fixed.Index(Source => SS, Pattern => ",", From => 1); + if Idx > 0 then + Ada.Strings.Fixed.Replace_Slice(Source => SS, Low => Idx, High => Idx, By => "."); + end if; + + begin + return FH_Float'Value(S); + exception + when Constraint_Error => + raise Constraint_Error; + end; + end String_To_Float; + function Round_To_Valid_Nums(Num: in FH_Float; Decimals: in FH_Float) return FH_Float is package FHEF is new Ada.Numerics.Generic_Elementary_Functions(FH_Float); use FHEF; diff --git a/src/formatting_helpers.ads b/src/formatting_helpers.ads index ba0128d..c7011ae 100644 --- a/src/formatting_helpers.ads +++ b/src/formatting_helpers.ads @@ -5,6 +5,9 @@ generic type FH_Float is digits <>; package Formatting_Helpers is + function Get_Decimal_Part_Flt(Num: in FH_Float) return FH_Float; + function Get_Integer_Part_Int(Num: in FH_Float) return Integer; + function String_To_Float(S: in String) return FH_Float; function Round_To_Valid_Nums(Num: in FH_Float; Decimals: FH_Float) return FH_Float; procedure Split_Integer_Decimal_Strs(Num: in FH_Float; Decimals: in FH_Float; Integer_Part: out UB_Text; Decimal_Part: out UB_Text); procedure Split_Integer_Decimal_Exponent_Strs(Num: in FH_Float; Decimals: in FH_Float; Integer_Part: out UB_Text; Decimal_Part: out UB_Text; diff --git a/src/global_types.adb b/src/global_types.adb index ab6ccba..2480115 100644 --- a/src/global_types.adb +++ b/src/global_types.adb @@ -10,6 +10,11 @@ package body Global_Types is Ada.Strings.Unbounded.Append(Source => Source, New_Item => New_Item); end Append_UB_Text; + procedure Append_UB_Text(Source: in out UB_Text; New_Item: in String) is + begin + Ada.Strings.Unbounded.Append(Source => Source, New_Item => To_UB_Text(New_Item)); + end Append_UB_Text; + function HTML_To_Fixed_String(HTML: in HTML_Code) return String is begin return Ada.Strings.Unbounded.To_String(HTML); diff --git a/src/global_types.ads b/src/global_types.ads index 6d18c20..7e42f39 100644 --- a/src/global_types.ads +++ b/src/global_types.ads @@ -9,6 +9,7 @@ package Global_Types is procedure Append_HTML(Source: in out HTML_Code; New_Item: in HTML_Code); procedure Append_UB_Text(Source: in out UB_Text; New_Item: in UB_Text); + procedure Append_UB_Text(Source: in out UB_Text; New_Item: in String); function HTML_To_Fixed_String(HTML: in HTML_Code) return String; function To_HTML_Code(S: in String) return HTML_Code; function To_UB_Text(S: in String) return UB_Text; diff --git a/src/handlers/handler_start.adb b/src/handlers/handler_start.adb index d4d9def..cd7008e 100644 --- a/src/handlers/handler_start.adb +++ b/src/handlers/handler_start.adb @@ -29,9 +29,11 @@ package body Handler_Start is Success: Boolean; begin if Raw_Problem_Category = Problem_Manager.Problem_Category'Image(Problem_Manager.Acidobazic) then - P_Cat := Problem_Manager.Acidobazic; + P_Cat := Problem_Manager.Acidobazic; + elsif Raw_Problem_Category = Problem_Manager.Problem_Category'Image(Problem_Manager.Solubility) then + P_Cat := Problem_Manager.Solubility; else - return AWS.Response.URL(Location => "/"); + return AWS.Response.URL(Location => "/"); end if; -- Register new UID if necessary and create a first problem diff --git a/src/problem_generators/problem_generator-acidobazic_suite.adb b/src/problem_generators/problem_generator-acidobazic_suite.adb index 8dba22e..21f6c3b 100644 --- a/src/problem_generators/problem_generator-acidobazic_suite.adb +++ b/src/problem_generators/problem_generator-acidobazic_suite.adb @@ -2,7 +2,7 @@ with Ada.Numerics.Discrete_Random; with Ada.Numerics.Float_Random; with Ada.Numerics.Generic_Elementary_Functions; with Ada.Strings.Fixed; ---with Ada.Text_IO; +with Ada.Text_IO; with Formatting_Helpers; separate(Problem_Generator) @@ -26,10 +26,12 @@ package body Acidobazic_Suite is package FH is new Formatting_Helpers(pH_Float); use Answer_Info; + Guard: Auto_Lock.LC; pH: pH_Float; pH_Answered: pH_Float; begin - Problem.Lock_State; + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; pH := Calculate_Solution(Problem); -- Verify answer data @@ -37,7 +39,6 @@ package body Acidobazic_Suite is return Malformed_Answer; end if; if Answer.Find(ANSWER_SIMPLIFICATION_KEY) = Answer_Info.No_Element then - Problem.Unlock_State; return Malformed_Answer; end if; @@ -54,7 +55,6 @@ package body Acidobazic_Suite is exception when Constraint_Error => Message := To_UB_Text("Nesprávně zadaná hodnota pH"); - Problem.Unlock_State; return Malformed_Answer; end; @@ -65,7 +65,6 @@ package body Acidobazic_Suite is Ada.Text_IO.Put_Line("SS : " & Simplification_Str & " SI : " & Simplification'Image(Problem.Simpl)); if Simplification_Str /= Simplification'Image(Problem.Simpl) then Message := To_UB_Text("Nesprávné zanedbání"); - Problem.Unlock_State; return Wrong_Answer; end if; end; @@ -76,7 +75,6 @@ package body Acidobazic_Suite is if pH_Answered - (Precision * 5.0) < pH and pH_Answered + (Precision * 5.0) > pH then Message := To_UB_Text("Správná odpověď"); - Problem.Unlock_State; return Correct_Answer; else declare @@ -91,22 +89,22 @@ package body Acidobazic_Suite is Append_UB_Text(Source => Message, New_Item => To_UB_Text(",")); Append_UB_Text(Source => Message, New_Item => Dec_S); Append_UB_Text(Source => Message, New_Item => To_UB_Text(")")); - Problem.Unlock_State; return Wrong_Answer; end; end if; end Check_Answer; function Get_Assignment(Problem: in out Acidobazic_Problem; Assignment: in out Assignment_Info.Map) return Boolean is + Guard: Auto_Lock.LC; C: Assignment_Info.Cursor; Success: Boolean; pKx: pH_Float; begin - Problem.Lock_State; + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; Assignment.Insert(PROBLEM_TYPE_KEY, PROBLEM_TYPE_ACIDOBAZIC, C, Success); if Success = False then - Problem.Unlock_State; return False; end if; case Problem.Subst_Type is @@ -130,7 +128,6 @@ package body Acidobazic_Suite is Assignment.Insert(PKX_VALUE_INT_KEY, UB_Text_To_Fixed_String(Int_S), C, Success); Assignment.Insert(PKX_VALUE_DEC_KEY, UB_Text_To_Fixed_String(Dec_S), C, Success); if Success = False then - Problem.Unlock_State; return False; end if; end; @@ -148,41 +145,41 @@ package body Acidobazic_Suite is Assignment.Insert(CONCENTRATION_DEC_KEY, UB_Text_To_Fixed_String(Dec_S), C, Success); Assignment.Insert(CONCENTRATION_EXP_KEY, UB_Text_To_Fixed_String(Exp_S), C, Success); if Success = False then - Problem.Unlock_State; return False; end if; end; - Problem.Unlock_State; return True; end Get_Assignment; function Get_Parameters(Problem: in out Acidobazic_Problem; Parameters: out Parameters_Info.Map) return Boolean is + Guard: Auto_Lock.LC; C: Parameters_Info.Cursor; Success: Boolean; begin - Problem.Lock_State; + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; + if Problem.Parameters.No_Both_Simplifications then Parameters.Insert(PARAMETER_NO_BOTH_SIMPLIFICATIONS_KEY, "True", C, Success); - Problem.Unlock_State; return Success; end if; - Problem.Unlock_State; return True; end Get_Parameters; procedure New_Problem(Problem: in out Acidobazic_Problem) is - package Random_Substance_Type_Gen is new Ada.Numerics.Discrete_Random(Result_Subtype => Substance_Type); package Random_Dissoc_Type_Gen is new Ada.Numerics.Discrete_Random(Result_Subtype => Dissociation_Constant_Type); + Guard: Auto_Lock.LC; DCT_G: Random_Dissoc_Type_Gen.Generator; ST_G: Random_Substance_Type_Gen.Generator; cX_Min: pH_Float; cX_Max: pH_Float; begin - Problem.Lock_State; + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; -- Dissociation constant type (pKa or pKb) Random_Dissoc_Type_Gen.Reset(Gen => DCT_G); Problem.DCT := Random_Dissoc_Type_Gen.Random(Gen => DCT_G); @@ -218,20 +215,22 @@ package body Acidobazic_Suite is Calculate_Concentration_Limits(cX_Min, cX_Max, Problem.Kx, Problem.Simpl); Problem.cX := Random_cX(cX_Min, cX_Max); - Problem.Unlock_State; end New_Problem; function Set_Parameters(Problem: in out Acidobazic_Problem; Parameters: in Parameters_Info.Map) return Boolean is use Parameters_Info; + + Guard: Auto_Lock.LC; begin - Problem.Lock_State; + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; + if Parameters.Find(PARAMETER_NO_BOTH_SIMPLIFICATIONS_KEY) = Parameters_Info.No_Element then Problem.Parameters.No_Both_Simplifications := False; else Problem.Parameters.No_Both_Simplifications := True; end if; - Problem.Unlock_State; return True; end Set_Parameters; -- END: Inherited functions diff --git a/src/problem_generators/problem_generator-solubility_suite.adb b/src/problem_generators/problem_generator-solubility_suite.adb new file mode 100644 index 0000000..84be063 --- /dev/null +++ b/src/problem_generators/problem_generator-solubility_suite.adb @@ -0,0 +1,592 @@ +with Ada.Numerics.Discrete_Random; +with Ada.Numerics.Float_Random; +with Ada.Numerics.Generic_Elementary_Functions; +with Ada.Strings.Fixed; +with Ada.Text_IO; +with Formatting_Helpers; + +separate(Problem_Generator) + +package body Solubility_Suite is + + -- BEGIN: Inherited funcions + function Create return access Solubility_Problem is + Problem: access Solubility_Problem; + Parameters: Solubility_Parameters; + begin + Parameters := (Ionic_Strength => False, + P_Subtype => V_FROM_G_KS); + + Problem := new Solubility_Problem; + Problem.Parameters := Parameters; + return Problem; + end; + + function Check_Answer(Problem: in out Solubility_Problem; Answer: in Answer_Info.Map; Message: out UB_Text) return Answer_RetCode is + package FH is new Formatting_Helpers(SS_Float); + use Answer_Info; + use FH; + + Guard: Auto_Lock.LC; + AF: SS_Float; + Int_Part_Got: Integer; + Int_Part_Calcd: Integer; + Dec_Part_Got: SS_Float; + Dec_Part_Calcd: SS_Float; + begin + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; + -- Check that there is a valid answer in the map + if Answer.Find(ANSWER_NUM_KEY) = Answer_Info.No_Element then + return No_Answer; + end if; + + begin + AF := String_To_Float(Answer.Element(ANSWER_NUM_KEY)); + exception + when Constraint_Error => + Message := To_UB_Text("Nesprávně zadaná odpověď"); + return Malformed_Answer; + end; + + Int_Part_Got := Get_Integer_Part_Int(AF); + Int_Part_Calcd := Get_Integer_Part_Int(Problem.Answer_Num); + if Int_Part_Got /= Int_Part_Calcd then + Get_Correct_Answer_String(Message, Problem.Parameters.P_Subtype, Problem.Answer_Num); + return Wrong_Answer; + end if; + + Dec_Part_Got := Get_Decimal_Part_Flt(AF); + Dec_Part_Calcd := Get_Decimal_Part_Flt(Problem.Answer_Num); + + if Dec_Part_Calcd + PRECISION < Dec_Part_Got and Dec_Part_Calcd - PRECISION > Dec_Part_Got then + return Correct_Answer; + else + Get_Correct_Answer_String(Message, Problem.Parameters.P_Subtype, Problem.Answer_Num); + return Wrong_Answer; + end if; + end Check_Answer; + + function Get_Assignment(Problem: in out Solubility_Problem; Assignment: in out Assignment_Info.Map) return Boolean is + package FH is new Formatting_Helpers(SS_Float); + use FH; + + Guard: Auto_Lock.LC; + begin + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; + Assignment.Insert(PROBLEM_TYPE_KEY, PROBLEM_TYPE_SOLUBILITY); + Assignment.Insert(PROBLEM_SUBTYPE_KEY, Problem_Subtype'Image(Problem.Parameters.P_Subtype)); + + -- Stochiometry + if (Problem.Prob_Data.M > 1.0) then + Assignment.Insert(X_STOCHIO_KEY, Stochiometric_Count'Image(Stochiometric_Count(Problem.Prob_Data.M))); + else + Assignment.Insert(X_STOCHIO_KEY, ""); + end if; + if (Problem.Prob_Data.N > 1.0) then + Assignment.Insert(Z_STOCHIO_KEY, Stochiometric_Count'Image(Stochiometric_Count(Problem.Prob_Data.N))); + else + Assignment.Insert(Z_STOCHIO_KEY, ""); + end if; + + case Problem.Parameters.P_Subtype is + when V_FROM_G_KS => + declare + G_Str_Int: UB_Text; + G_Str_Dec: UB_Text; + Ks_Str_Int: UB_Text; + Ks_Str_Dec: UB_Text; + Ks_Str_Exp: UB_Text; + MW_Str_Int: UB_Text; + MW_Str_Dec: UB_Text; + begin + Split_Integer_Decimal_Strs(Problem.Prob_Data.V_G, DECIMALS, G_Str_Int, G_Str_Dec); + Split_Integer_Decimal_Strs(Problem.Prob_Data.V_MW, DECIMALS_MW, MW_Str_Int, MW_Str_Dec); + Split_Integer_Decimal_Exponent_Strs(Problem.Prob_Data.V_Ks, DECIMALS, Ks_Str_Int, Ks_Str_Dec, Ks_Str_Exp); + + Assignment.Insert(SAMPLE_WEIGHT_INT_KEY, UB_Text_To_Fixed_String(G_Str_Int)); + Assignment.Insert(SAMPLE_WEIGHT_DEC_KEY, UB_Text_To_Fixed_String(G_Str_Dec)); + Assignment.Insert(MOLAR_MASS_INT_KEY, UB_Text_To_Fixed_String(MW_Str_Int)); + Assignment.Insert(MOLAR_MASS_DEC_KEY, UB_Text_To_Fixed_String(MW_Str_Dec)); + Assignment.Insert(KS_INT_KEY, UB_Text_To_Fixed_String(Ks_Str_Int)); + Assignment.Insert(KS_DEC_KEY, UB_Text_To_Fixed_String(Ks_Str_Dec)); + Assignment.Insert(KS_EXP_KEY, UB_Text_To_Fixed_String(Ks_Str_Exp)); + + return True; + end; + when KS_FROM_G_V => + declare + G_Str_Int: UB_Text; + G_Str_Dec: UB_Text; + MW_Str_Int: UB_Text; + MW_Str_Dec: UB_Text; + V_Str_Int: UB_Text; + V_Str_Dec: UB_Text; + V_Str_Exp: UB_Text; + begin + Split_Integer_Decimal_Strs(Problem.Prob_Data.Ks_G, DECIMALS, G_Str_Int, G_Str_Dec); + Split_Integer_Decimal_Strs(Problem.Prob_Data.Ks_MW, DECIMALS_MW, MW_Str_Int, MW_Str_Dec); + Split_Integer_Decimal_Exponent_Strs(Problem.Prob_Data.Ks_V, DECIMALS, V_Str_Int, V_Str_Dec, V_Str_Exp); + + Assignment.Insert(SAMPLE_WEIGHT_INT_KEY, UB_Text_To_Fixed_String(G_Str_Int)); + Assignment.Insert(SAMPLE_WEIGHT_DEC_KEY, UB_Text_To_Fixed_String(G_Str_Dec)); + Assignment.Insert(MOLAR_MASS_INT_KEY, UB_Text_To_Fixed_String(MW_Str_Int)); + Assignment.Insert(MOLAR_MASS_DEC_KEY, UB_Text_To_Fixed_String(MW_Str_Dec)); + Assignment.Insert(SAMPLE_VOLUME_INT_KEY, UB_Text_To_Fixed_String(V_Str_Int)); + Assignment.Insert(SAMPLE_VOLUME_DEC_KEY, UB_Text_To_Fixed_String(V_Str_Dec)); + Assignment.Insert(SAMPLE_VOLUME_EXP_KEY, UB_Text_To_Fixed_String(V_Str_Exp)); + return True; + end; + when C_FROM_KS_DIFFERENT_IONS | C_FROM_KS_SHARED_ION => + declare + EC_Str_Int: UB_Text; + EC_Str_Dec: UB_Text; + EC_Str_Exp: UB_Text; + Ks_Str_Int: UB_Text; + Ks_Str_Dec: UB_Text; + Ks_Str_Exp: UB_Text; + begin + Split_Integer_Decimal_Exponent_Strs(Problem.Prob_Data.C_EC, DECIMALS, EC_Str_Int, EC_Str_Dec, EC_Str_Exp); + Split_Integer_Decimal_Exponent_Strs(Problem.Prob_Data.C_Ks, DECIMALS, Ks_Str_Int, Ks_Str_Dec, Ks_Str_Exp); + + Assignment.Insert(EC_INT_KEY, UB_Text_To_Fixed_String(EC_Str_Int)); + Assignment.Insert(EC_DEC_KEY, UB_Text_To_Fixed_String(EC_Str_Dec)); + Assignment.Insert(EC_EXP_KEY, UB_Text_To_Fixed_String(EC_Str_Exp)); + Assignment.Insert(KS_INT_KEY, UB_Text_To_Fixed_String(Ks_Str_Int)); + Assignment.Insert(KS_DEC_KEY, UB_Text_To_Fixed_String(Ks_Str_Dec)); + Assignment.Insert(KS_EXP_KEY, UB_Text_To_Fixed_String(Ks_Str_Exp)); + return True; + end; + end case; + end Get_Assignment; + + function Get_Parameters(Problem: in out Solubility_Problem; Parameters: out Parameters_Info.Map) return Boolean is + Guard: Auto_Lock.LC; + C: Parameters_Info.Cursor; + Success: Boolean; + begin + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; + + if Problem.Parameters.Ionic_Strength then + Parameters.Insert(PARAMETER_IONIC_STRENGTH_KEY, "True", C, Success); + if Success = False then + return False; + end if; + end if; + + Parameters.Insert(PARAMETER_PROBLEM_SUBTYPE_KEY, Problem_Subtype'Image(Problem.Parameters.P_Subtype), C, Success); + + return Success; + end Get_Parameters; + + procedure New_Problem(Problem: in out Solubility_Problem) is + package FH is new Formatting_Helpers(SS_Float); + package Random_Stochio_Count is new Ada.Numerics.Discrete_Random(Stochiometric_Count); + use FH; + use Ada.Numerics.Float_Random; + + Stochio_RGen: Random_Stochio_Count.Generator; + Float_RGen: Generator; + + Guard: Auto_Lock.LC; + M: Stochiometric_Count; -- Number of cations + N: Stochiometric_Count; -- Number of anions + Answer_Num: SS_Float; -- Answer to this problem + Prob_Data: Solubility_Problem_Data; -- Assignment data + begin + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; + -- Generate MW + Reset(Gen => Float_RGen); + + -- Generate stochiometry of the molecul + Random_Stochio_Count.Reset(Gen => Stochio_RGen); + M := Random_Stochio_Count.Random(Gen => Stochio_RGen); + N := Random_Stochio_Count.Random(Gen => Stochio_RGen); + + -- Reduce molecule stochiometry to the lowest terms + declare + High: Stochiometric_Count := Stochiometric_Count'Max(M, N); + Low: constant Stochiometric_Count := Stochiometric_Count'Min(M, N); + Modulus: Natural; + begin + Modulus := High mod Low; + if Modulus = 0 then + High := High / Low; + if M > N then + M := High; + N := 1; + else + N := High; + M := 1; + end if; + end if; + end; + + -- Generate the rest of the data based on the problem subtype and calulate the solution + case Problem.Parameters.P_Subtype is + when V_FROM_G_KS => + declare + MOLECULE_WEIGHT_RANGE: constant SS_Float := MOLECULE_WEIGHT_MAX - MOLECULE_WEIGHT_MIN; + SAMPLE_WEIGHT_RANGE: constant SS_Float := SAMPLE_WEIGHT_MAX - SAMPLE_WEIGHT_MIN; + G: SS_Float; -- Sample weight in grams + Ks: SS_Float; -- Solubility product + MW: SS_Float; -- Molar mass + begin + -- Generate sample weight + G := (SS_Float(Random(Gen => Float_RGen)) * SAMPLE_WEIGHT_RANGE) + SAMPLE_WEIGHT_MIN; + G := Round_To_Valid_Nums(G, DECIMALS); + Ks := Round_To_Valid_Nums(Generate_Solubility_Product, DECIMALS); + MW := (SS_Float(Random(Gen => Float_RGen)) * MOLECULE_WEIGHT_RANGE) + MOLECULE_WEIGHT_MIN; + MW := Round_To_Valid_Nums(MW, DECIMALS_MW); + Prob_Data := (Option => V_FROM_G_KS, + M => SS_Float(M), N => SS_Float(N), + V_G => G, V_Ks => Ks, V_MW => MW); + Answer_Num := Calculate_Sample_Volume(Prob_Data, Problem.Parameters.Ionic_Strength); + end; + when KS_FROM_G_V => + declare + MOLECULE_WEIGHT_RANGE: constant SS_Float := MOLECULE_WEIGHT_MAX - MOLECULE_WEIGHT_MIN; + SAMPLE_WEIGHT_RANGE: constant SS_Float := SAMPLE_WEIGHT_MAX - SAMPLE_WEIGHT_MIN; + G: SS_Float; -- Sample weight in grams + MW: SS_Float; -- Molar mass + V: SS_Float; -- Sample volume in dm3 + begin + G := (SS_Float(Random(Gen => Float_RGen)) * SAMPLE_WEIGHT_RANGE) + SAMPLE_WEIGHT_MIN; + G := Round_To_Valid_Nums(G, DECIMALS); + MW := (SS_Float(Random(Gen => Float_RGen)) * MOLECULE_WEIGHT_RANGE) + MOLECULE_WEIGHT_MIN; + MW := Round_To_Valid_Nums(MW, DECIMALS_MW); + V := Round_To_Valid_Nums(Generate_Sample_Volume, DECIMALS); + Prob_Data := (Option => KS_FROM_G_V, + M => SS_Float(M), N => SS_Float(N), + Ks_G => G, Ks_MW => MW, Ks_V => V); + Answer_Num := Calculate_Solubility_Product(Prob_Data, Problem.Parameters.Ionic_Strength); + end; + when C_FROM_KS_DIFFERENT_IONS => + declare + EC: SS_Float; -- Concentration of the other electrolyte if mol/dm3 which does *not* share an ion with the target electrolyte + Ks: SS_Float; -- Solubility product + begin + EC := Round_To_Valid_Nums(Generate_Electrolyte_Concentration, DECIMALS); + Ks := Round_To_Valid_Nums(Generate_Solubility_Product, DECIMALS); + Prob_Data := (Option => C_FROM_KS_DIFFERENT_IONS, + M => SS_Float(M), N => SS_Float(N), + C_EC => EC, C_Ks => Ks); + Answer_Num := Calculate_Sat_Concentration_Different(Prob_Data, Problem.Parameters.Ionic_Strength); + end; + when C_FROM_KS_SHARED_ION => + declare + EC: SS_Float; -- Concentration of the other electrolyte if mol/dm3 which *does* share an ion with the target electrolyte + Ks: SS_Float; -- Solubility product + begin + EC := Round_To_Valid_Nums(Generate_Electrolyte_Concentration, DECIMALS); + Ks := Round_To_Valid_Nums(Generate_Solubility_Product, DECIMALS); + Prob_Data := (Option => C_FROM_KS_SHARED_ION, + M => SS_Float(M), N => SS_Float(N), + C_EC => EC, C_Ks => Ks); + Answer_Num := Calculate_Sat_Concentration_Shared(Prob_Data, Problem.Parameters.Ionic_Strength); + end; + end case; + + -- Set assignment data + Problem.Prob_Data := Prob_Data; + Problem.Answer_Num := Answer_Num; + end New_Problem; + + function Set_Parameters(Problem: in out Solubility_Problem; Parameters: in Parameters_Info.Map) return Boolean is + use Parameters_Info; + + Guard: Auto_Lock.LC; + begin + Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access); + Guard.Lock; + -- Change ionic strength settings + if Parameters.Find(PARAMETER_IONIC_STRENGTH_KEY) /= Parameters_Info.No_Element then + Problem.Parameters.Ionic_Strength := True; + else + Problem.Parameters.Ionic_Strength := False; + end if; + + -- Change problem subtype + if Parameters.Find(PARAMETER_PROBLEM_SUBTYPE_KEY) /= Parameters_Info.No_Element then + declare + PS_Str: constant String := Parameters.Element(PARAMETER_PROBLEM_SUBTYPE_KEY); + begin + if PS_Str = PROBLEM_SUBTYPE_V_FROM_G_KS then + Problem.Parameters.P_Subtype := V_FROM_G_KS; + elsif PS_Str = PROBLEM_SUBTYPE_KS_FROM_G_V then + Problem.Parameters.P_Subtype := KS_FROM_G_V; + elsif PS_Str = PROBLEM_SUBTYPE_C_FROM_KS_DIFFERENT_IONS then + Problem.Parameters.P_Subtype := C_FROM_KS_DIFFERENT_IONS; + elsif PS_Str = PROBLEM_SUBTYPE_C_FROM_KS_SHARED_ION then + Problem.Parameters.P_Subtype := C_FROM_KS_SHARED_ION; + else + raise Constraint_Error; + end if; + end; + else + -- This parameter must be always present + return False; + end if; + + return True; + end Set_Parameters; + + -- END: Inherited functions + + -- BEGIN: Private functions + + function Calculate_Activity_Coefficient(Charge: in SS_Float; Ionic_Strength: in SS_Float) return SS_Float is + package MEF is new Ada.Numerics.Generic_Elementary_Functions(SS_Float); + use MEF; + + Z_Squared: constant SS_Float := Charge ** 2.0; + IS_Sqrt: constant SS_Float := Sqrt(Ionic_Strength); + Log_AC: SS_Float; + begin + Log_AC := (0.509 * Z_Squared * IS_Sqrt) / (1.0 + IS_Sqrt); + return 10.0 ** (-Log_AC); + end Calculate_Activity_Coefficient; + + function Calculate_Ionic_Strength(Ions: in Ion_List) return SS_Float is + package MEF is new Ada.Numerics.Generic_Elementary_Functions(SS_Float); + use MEF; + + I: SS_Float := 0.0; + begin + for Idx in Ions'Range loop + I := I + (Ions(Idx).Concentration * SS_Float((Ions(Idx).Charge ** 2))); + end loop; + + return 0.5 * I; + end Calculate_Ionic_Strength; + + function Calculate_Sample_Volume(Prob_Data: in Solubility_Problem_Data; Ionic_Strength: in Boolean) return SS_Float is + package SS_IO is new Ada.Text_IO.Float_IO(SS_Float); + + C: SS_Float; + begin + C := Calculate_Sat_Concentration(Prob_Data.M, Prob_Data.N, Prob_Data.V_Ks, 1.0, 1.0); + + if Ionic_Strength then + declare + I: SS_Float; -- Ionic strength in mol/dm3 + GM: SS_Float; -- Activity coefficent for the cation + GN: SS_Float; -- Activity coefficient for the anion + + Ions: Ion_List(1 .. 2); + begin + Ions(1) := (Concentration => C, Charge => Prob_Data.N); + Ions(2) := (Concentration => C, Charge => Prob_Data.M); + I := Calculate_Ionic_Strength(Ions); + + GM := Calculate_Activity_Coefficient(Prob_Data.N, I); + GN := Calculate_Activity_Coefficient(Prob_Data.M, I); + + Ada.Text_IO.Put_Line("IS, AC"); + SS_IO.Put(I); Ada.Text_IO.New_Line; + SS_IO.Put(GM); Ada.Text_IO.New_Line; + SS_IO.Put(GN); Ada.Text_IO.New_Line; + + C := Calculate_Sat_Concentration(Prob_Data.M, Prob_Data.N, Prob_Data.V_Ks, GM, GN); + end; + end if; + + return Prob_Data.V_G / (Prob_Data.V_MW * C); + end Calculate_Sample_Volume; + + function Calculate_Sat_Concentration(Cation_Count: in SS_Float; Anion_Count: in SS_Float; Ks: in SS_Float; + Cation_Gamma: in SS_Float; Anion_Gamma: in SS_Float) return SS_Float is + package MEF is new Ada.Numerics.Generic_Elementary_Functions(SS_Float); + use MEF; + + MPN: constant SS_Float := Cation_Count + Anion_Count; + MM: constant SS_Float := Cation_Count ** Cation_Count; + NN: constant SS_Float := Anion_Count ** Anion_Count; + begin + return (Ks / (MM * (Cation_Gamma ** Cation_Count) * NN * (Anion_Gamma ** Anion_Count))) ** (1.0 / MPN); + end Calculate_Sat_Concentration; + + function Calculate_Sat_Concentration_Different(Prob_Data: in Solubility_Problem_Data; Ionic_Strength: in Boolean) return SS_Float is + package MEF is new Ada.Numerics.Generic_Elementary_Functions(SS_Float); + use MEF; + + GM: SS_Float; + GN: SS_Float; + C: SS_Float; -- Sample concentration + I: SS_Float; -- Ionic strength of the other electrolyte in mol/dm3 + Ions_Other: Ion_List(1 .. 2); + begin + -- Calculate ionic strength of the other electrolyte + Ions_Other(1) := (Concentration => Prob_Data.C_EC, Charge => 1.0); + Ions_Other(2) := (Concentration => Prob_Data.C_EC, Charge => 1.0); + I := Calculate_Ionic_Strength(Ions_Other); + GM := Calculate_Activity_Coefficient(1.0, I); + GN := Calculate_Activity_Coefficient(1.0, I); + C := Calculate_Sat_Concentration(Prob_Data.M, Prob_Data.N, Prob_Data.C_Ks, GM, GN); + + if Ionic_Strength then + declare + I_All: SS_Float; -- Ionic strength of all ions in mol/dm3 + GM: SS_Float; -- Activity coefficent for the cation + GN: SS_Float; -- Activity coefficient for the anion + + Ions_All: Ion_List(1 .. 4); + begin + Ions_All(1) := (Concentration => Prob_Data.C_EC, Charge => 1.0); + Ions_All(2) := (Concentration => Prob_Data.C_EC, Charge => 1.0); + Ions_All(3) := (Concentration => C, Charge => Prob_Data.N); + Ions_All(4) := (Concentration => C, Charge => Prob_Data.M); + I_All := Calculate_Ionic_Strength(Ions_All); + + GM := Calculate_Activity_Coefficient(Prob_Data.M, I_All); + GN := Calculate_Activity_Coefficient(Prob_Data.N, I_All); + + C := Calculate_Sat_Concentration(Prob_Data.M, Prob_Data.N, Prob_Data.C_Ks, GM, GN); + end; + end if; + + return C; + end Calculate_Sat_Concentration_Different; + + function Calculate_Sat_Concentration_Shared(Prob_Data: in Solubility_Problem_Data; Ionic_Strength: in Boolean) return SS_Float is + package MEF is new Ada.Numerics.Generic_Elementary_Functions(SS_Float); + use MEF; + + GN: SS_Float := 1.0; -- Activity coefficient of the anion if the other electrolyte + EC_Pwrd: SS_Float; + R: SS_Float; + begin + EC_Pwrd := Prob_Data.C_EC ** Prob_Data.N; + + if Ionic_Strength then + declare + I_EC: SS_Float; -- Ionic strength of the ions from the other electrolyte + Ions: Ion_List(1 .. 2); + begin + Ions(1) := (Concentration => Prob_Data.C_EC, Charge => 1.0); + Ions(2) := (Concentration => Prob_Data.C_EC, Charge => Prob_Data.M); + I_EC := Calculate_Ionic_Strength(Ions); + + GN := Calculate_Activity_Coefficient(Prob_Data.M, I_EC); + end; + end if; + + R := Prob_Data.C_Ks / (EC_Pwrd * (GN ** Prob_Data.N)); + + return (R ** (1.0 / Prob_Data.M)) / Prob_Data.M; + end Calculate_Sat_Concentration_Shared; + + function Calculate_Solubility_Product(Prob_Data: in Solubility_Problem_Data; Ionic_Strength: in Boolean) return SS_Float is + package MEF is new Ada.Numerics.Generic_Elementary_Functions(SS_Float); + use MEF; + + M: constant SS_Float := Prob_Data.M; -- Stochiometry of the cation + N: constant SS_Float := Prob_Data.N; -- Stochiometry of the anion + C: SS_Float; -- Concentration of the sample + GM: SS_Float := 1.0; -- Activity coefficent for the cation + GN: SS_Float := 1.0; -- Activity coefficient for the anion + begin + C := Prob_Data.Ks_G / (Prob_Data.Ks_MW * Prob_Data.Ks_V); + + if Ionic_Strength then + declare + I: SS_Float; -- Ionic strength in mol/dm3 + + Ions: Ion_List(1 .. 2); + begin + Ions(1) := (Concentration => C, Charge => Prob_Data.N); -- Cation + Ions(2) := (Concentration => C, Charge => Prob_Data.M); -- Anion + I := Calculate_Ionic_Strength(Ions); + + GM := Calculate_Activity_Coefficient(M, I); -- Cation + GN := Calculate_Activity_Coefficient(N, I); -- Anion + end; + end if; + + return ((Prob_Data.M * C * GM) ** Prob_Data.M) * ((Prob_Data.N * C * GN) ** Prob_Data.N); + end Calculate_Solubility_Product; + + function Generate_Electrolyte_Concentration return SS_Float is + package MEF is new Ada.Numerics.Generic_Elementary_Functions(SS_Float); + use Ada.Numerics.Float_Random; + use MEF; + + CONC_RANGE: constant SS_Float := ELECTROLYTE_CONCENTRATION_LOG_MAX - ELECTROLYTE_CONCENTRATION_LOG_MIN; + + Float_RGen: Generator; + R: SS_Float; + begin + Reset(Gen => Float_RGen); + R := (SS_Float(Random(Gen => Float_RGen)) * CONC_RANGE) + ELECTROLYTE_CONCENTRATION_LOG_MIN; + + return 10.0 ** R; + end Generate_Electrolyte_Concentration; + + function Generate_Sample_Volume return SS_Float is + package MEF is new Ada.Numerics.Generic_Elementary_Functions(SS_Float); + use Ada.Numerics.Float_Random; + use MEF; + + VOL_RANGE: constant SS_Float := SAMPLE_VOLUME_LOG_MAX - SAMPLE_VOLUME_LOG_MIN; + Float_RGen: Generator; + R: SS_Float; + begin + Reset(Gen => Float_RGen); + R := (SS_Float(Random(Gen => Float_RGen)) * VOL_RANGE) + SAMPLE_VOLUME_LOG_MIN; + + return 10.0 ** R; + end Generate_Sample_Volume; + + function Generate_Solubility_Product return SS_Float is + package MEF is new Ada.Numerics.Generic_Elementary_Functions(SS_Float); + use Ada.Numerics.Float_Random; + use MEF; + + PKS_RANGE: constant SS_Float := PKS_MAX - PKS_MIN; + Float_RGen: Generator; + R: SS_Float; + begin + Reset(Gen => Float_RGen); + R := (SS_Float(Random(Gen => Float_RGen)) * PKS_RANGE) + PKS_MIN; + + return 10.0 ** (-R); + end Generate_Solubility_Product; + + procedure Get_Correct_Answer_String(Message: in out UB_Text; P_Subtype: in Problem_Subtype; Answer: in SS_Float) is + package FH is new Formatting_Helpers(SS_Float); + use FH; + + Ans_Int_Str: UB_Text; + Ans_Dec_Str: UB_Text; + Ans_Exp_Str: UB_Text; + begin + Split_Integer_Decimal_Exponent_Strs(Answer, DECIMALS, Ans_Int_Str, Ans_Dec_Str, Ans_Exp_Str); + Message := To_UB_Text("Å patná odpověď, hodnota správného výsledku je "); + + case P_Subtype is + when V_FROM_G_KS => + Append_UB_Text(Source => Message, New_Item => "V = "); + when KS_FROM_G_V => + Append_UB_Text(Source => Message, New_Item => "K = "); + when C_FROM_KS_DIFFERENT_IONS | C_FROM_KS_SHARED_ION => + Append_UB_Text(Source => Message, New_Item => "c = "); + end case; + + Append_UB_Text(Source => Message, New_Item => Ans_Int_Str); Append_UB_Text(Source => Message, New_Item => ","); Append_UB_Text(Source => Message, New_Item => Ans_Dec_Str); + Append_UB_Text(Source => Message, New_Item => " . 10"); Append_UB_Text(Source => Message, New_Item => Ans_Exp_Str); Append_UB_Text(Source => Message, New_Item => " "); + + case P_Subtype is + when V_FROM_G_KS => + Append_UB_Text(Source => Message, New_Item => "dm3"); + when KS_FROM_G_V => + null; + when C_FROM_KS_DIFFERENT_IONS | C_FROM_KS_SHARED_ION => + Append_UB_Text(Source => Message, New_Item => "mol/dm3"); + end case; + end Get_Correct_Answer_String; + + +end Solubility_Suite; diff --git a/src/problem_generators/problem_generator.adb b/src/problem_generators/problem_generator.adb index 16d1960..97c3d48 100644 --- a/src/problem_generators/problem_generator.adb +++ b/src/problem_generators/problem_generator.adb @@ -1,37 +1,53 @@ -with Ada.Text_IO; - package body Problem_Generator is protected body Problem_Mutex is entry Lock when Locked = False is begin Locked := True; - end Lock; - - procedure Unlock is + end Lock; + + entry Unlock when Locked is begin Locked := False; end Unlock; end Problem_Mutex; + + package body Auto_Lock is + procedure Init(This: in out LC; Mutex: Problem_Mutex_All_Access; Auto_Unlock: in Boolean := True) is + begin + This.Auto_Unlock := Auto_Unlock; + This.Mutex := Mutex; + end; + + procedure Lock(This: in out LC) is + begin + This.Mutex.Lock; + end Lock; + + procedure Unlock(This: in out LC) is + begin + This.Mutex.Unlock; + end Unlock; + + procedure Finalize(This: in out LC) is + begin + if This.Auto_Unlock then + This.Unlock; + end if; + end Finalize; + end Auto_Lock; function Get_Problem(P_Type: in Problem_Type) return access Chem_Problem'Class is begin case P_Type is when Acidobazic => return Acidobazic_Suite.Create; + when Solubility => + return Solubility_Suite.Create; end case; end Get_Problem; - procedure Lock_State(Problem: in out Chem_Problem) is - begin - Problem.Mutex.Lock; - end Lock_State; - - procedure Unlock_State(Problem: in out Chem_Problem) is - begin - Problem.Mutex.Unlock; - end Unlock_State; - package body Acidobazic_Suite is separate; + package body Solubility_Suite is separate; end Problem_Generator; diff --git a/src/problem_generators/problem_generator.ads b/src/problem_generators/problem_generator.ads index c98e408..16bafd6 100644 --- a/src/problem_generators/problem_generator.ads +++ b/src/problem_generators/problem_generator.ads @@ -1,5 +1,6 @@ with Global_Types; with Problem_Generator_Syswides; +with Ada.Finalization; use Global_Types; use Problem_Generator_Syswides; @@ -7,11 +8,12 @@ package Problem_Generator is protected type Problem_Mutex is entry Lock; - procedure Unlock; + entry Unlock; private Locked: Boolean := False; end Problem_Mutex; + type Problem_Mutex_All_Access is access all Problem_Mutex; type Chem_Problem is abstract tagged limited private; @@ -20,18 +22,32 @@ package Problem_Generator is function Get_Assignment(Problem: in out Chem_Problem; Assignment: in out Assignment_Info.Map) return Boolean is abstract; function Get_Parameters(Problem: in out Chem_Problem; Parameters: out Parameters_Info.Map) return Boolean is abstract; procedure New_Problem(Problem: in out Chem_Problem) is abstract; - procedure Lock_State(Problem: in out Chem_Problem); function Set_Parameters(Problem: in out Chem_Problem; Parameters: in Parameters_Info.Map) return Boolean is abstract; - procedure Unlock_State(Problem: in out Chem_Problem); function Get_Problem(P_Type: in Problem_Type) return access Chem_Problem'Class; private type Chem_Problem is abstract tagged limited record - Mutex: Problem_Mutex; + Mutex: aliased Problem_Mutex; end record; + package Auto_Lock is + type LC is new Ada.Finalization.Controlled with private; + procedure Init(This: in out LC; Mutex: Problem_Mutex_All_Access; Auto_Unlock: in Boolean := True); + procedure Lock(This: in out LC); + procedure Unlock(This: in out LC); + private + type LC is new Ada.Finalization.Controlled with + record + Auto_Unlock: Boolean; + Mutex: Problem_Mutex_All_Access; + end record; + overriding procedure Finalize(This: in out LC); + + Pragma Volatile(LC); + end Auto_Lock; + package Acidobazic_Suite is use Problem_Generator_Syswides.Acidobazic_Suite; @@ -94,4 +110,89 @@ private end Acidobazic_Suite; + package Solubility_Suite is + use Problem_Generator_Syswides.Solubility_Suite; + + type Solubility_Problem is new Problem_Generator.Chem_Problem with private; + -- Constructor + function Create return access Solubility_Problem; + -- Inherited + function Check_Answer(Problem: in out Solubility_Problem; Answer: in Answer_Info.Map; Message: out UB_Text) return Answer_RetCode; + procedure New_Problem(Problem: in out Solubility_Problem); + function Get_Assignment(Problem: in out Solubility_Problem; Assignment: in out Assignment_Info.Map) return Boolean; + function Get_Parameters(Problem: in out Solubility_Problem; Parameters: out Parameters_Info.Map) return Boolean; + function Set_Parameters(Problem: in out Solubility_Problem; Parameters: in Parameters_Info.Map) return Boolean; + + private + type SS_Float is digits 18; + subtype Stochiometric_Count is Positive range 1 .. 5; + + ELECTROLYTE_CONCENTRATION_LOG_MAX: constant SS_Float := 0.176; + ELECTROLYTE_CONCENTRATION_LOG_MIN: constant SS_Float := -4.0; + MOLECULE_WEIGHT_MAX: constant SS_Float := 450.0; + MOLECULE_WEIGHT_MIN: constant SS_Float := 45.0; + PKS_MAX: constant SS_Float := 54.0; + PKS_MIN: constant SS_Float := 10.0; + SAMPLE_VOLUME_LOG_MAX: constant SS_Float := 10.0; + SAMPLE_VOLUME_LOG_MIN: constant SS_Float := -0.3; + SAMPLE_WEIGHT_MAX: constant SS_Float := 1.5; + SAMPLE_WEIGHT_MIN: constant SS_Float := 0.1; + DECIMALS: constant SS_Float := 1.0E3; + DECIMALS_MW: constant SS_Float := 1.0E2; + PRECISION: constant SS_Float := 1.0E-2; + + type Ion is + record + Concentration: SS_Float; + Charge: SS_Float; + end record; + type Ion_List is Array (Positive range <>) of Ion; + + type Solubility_Parameters is + record + Ionic_Strength: Boolean; -- Correct for ionic strength in calculations + P_Subtype: Problem_Subtype; + end record; + + type Solubility_Problem_Data(Option: Problem_Subtype := V_FROM_G_KS) is + record + M: SS_Float; -- Number of cations + N: SS_Float; -- Number of anions + case Option is + when V_FROM_G_KS => + V_G: SS_Float; -- Sample mass in grams + V_Ks: SS_Float; -- Ks constant + V_MW: SS_Float; -- Molar mass of the sample + when KS_FROM_G_V => + Ks_G: SS_Float; -- Sample mass in grams + Ks_MW: SS_Float; -- Molar mass of the sample + Ks_V: SS_Float; -- Sample volume in dm3 + when C_FROM_KS_DIFFERENT_IONS | C_FROM_KS_SHARED_ION => + C_EC: SS_Float; -- Concentration of the other electrolyte in the solution + C_Ks: SS_Float; -- Ks constant + end case; + end record; + + type Solubility_Problem is new Problem_Generator.Chem_Problem with + record + Parameters: Solubility_Parameters; -- Parameters of the generator + Prob_Data: Solubility_Problem_Data; -- Data which make the assignment + Answer_Num: SS_Float; -- Answer to the problem + end record; + + function Calculate_Activity_Coefficient(Charge: in SS_Float; Ionic_Strength: in SS_Float) return SS_Float; + function Calculate_Ionic_Strength(Ions: in Ion_List) return SS_Float; + function Calculate_Sat_Concentration(Cation_Count: in SS_Float; Anion_Count: in SS_Float; Ks: in SS_Float; + Cation_Gamma: in SS_Float; Anion_Gamma: in SS_Float) return SS_Float; + function Calculate_Sat_Concentration_Different(Prob_Data: in Solubility_Problem_Data; Ionic_Strength: in Boolean) return SS_Float; + function Calculate_Sat_Concentration_Shared(Prob_Data: in Solubility_Problem_Data; Ionic_Strength: in Boolean) return SS_Float; + function Calculate_Sample_Volume(Prob_Data: in Solubility_Problem_Data; Ionic_Strength: in Boolean) return SS_Float; + function Calculate_Solubility_Product(Prob_Data: in Solubility_Problem_Data; Ionic_Strength: in Boolean) return SS_Float; + function Generate_Electrolyte_Concentration return SS_Float; + function Generate_Sample_Volume return SS_Float; + function Generate_Solubility_Product return SS_Float; + procedure Get_Correct_Answer_String(Message: in out UB_Text; P_Subtype: in Problem_Subtype; Answer: in SS_Float); + + end Solubility_Suite; + end Problem_Generator; diff --git a/src/problem_generators/problem_generator_syswides.ads b/src/problem_generators/problem_generator_syswides.ads index 86da247..f058304 100644 --- a/src/problem_generators/problem_generator_syswides.ads +++ b/src/problem_generators/problem_generator_syswides.ads @@ -3,7 +3,7 @@ with Ada.Containers.Indefinite_Ordered_Maps; package Problem_Generator_Syswides is type Answer_RetCode is (Invalid_Answer, No_Answer, Correct_Answer, Wrong_Answer, Malformed_Answer); - type Problem_Type is (Acidobazic); + type Problem_Type is (Acidobazic, Solubility); package Answer_Info is new Ada.Containers.Indefinite_Ordered_Maps(String, String); package Assignment_Info is new Ada.Containers.Indefinite_Ordered_Maps(String, String); package Parameters_Info is new Ada.Containers.Indefinite_Ordered_Maps(String, String); @@ -13,8 +13,13 @@ package Problem_Generator_Syswides is ANSWER_KIND_BAD: constant String := "answer_kind_bad"; ANSWER_MESSAGE_KEY: constant String := "ANSWER_MESSAGE"; ANSWER_SECTION_KEY: constant String := "ANSWER_SECTION"; + -- + PARAMETER_TRUE: constant String := "True"; + PARAMETER_FALSE: constant String := "False"; + -- PROBLEM_TYPE_KEY: constant String := "PROBLEM_TYPE"; PROBLEM_TYPE_ACIDOBAZIC: constant String := Problem_Type'Image(Acidobazic); + PROBLEM_TYPE_SOLUBILITY: constant String := Problem_Type'Image(Solubility); package Acidobazic_Suite is -- What effect to ignore in calculations? @@ -38,4 +43,36 @@ package Problem_Generator_Syswides is PARAMETER_NO_BOTH_SIMPLIFICATIONS_KEY: constant String := "PARAMETER_NO_BOTH_SIMPLIFICATIONS"; end Acidobazic_Suite; + package Solubility_Suite is + type Problem_Subtype is (V_FROM_G_KS, KS_FROM_G_V, C_FROM_KS_DIFFERENT_IONS, C_FROM_KS_SHARED_ION); + + PROBLEM_NAME_READABLE: constant String := "Srážecí rovnováhy"; + PROBLEM_SUBTYPE_KEY: constant String := "PROBLEM_SUBTYPE"; + PROBLEM_SUBTYPE_V_FROM_G_KS: constant String := Problem_Subtype'Image(V_FROM_G_KS); + PROBLEM_SUBTYPE_KS_FROM_G_V: constant String := Problem_Subtype'Image(KS_FROM_G_V); + PROBLEM_SUBTYPE_C_FROM_KS_DIFFERENT_IONS: constant String := Problem_Subtype'Image(C_FROM_KS_DIFFERENT_IONS); + PROBLEM_SUBTYPE_C_FROM_KS_SHARED_ION: constant String := Problem_Subtype'Image(C_FROM_KS_SHARED_ION); + ANSWER_NUM_KEY: constant String := "ANSWER_NUM"; + -- + EC_INT_KEY: constant String := "EC_INT"; + EC_DEC_KEY: constant String := "EC_DEC"; + EC_EXP_KEY: constant String := "EC_EXP"; + KS_INT_KEY: constant String := "KS_INT"; + KS_DEC_KEY: constant String := "KS_DEC"; + KS_EXP_KEY: constant String := "KS_EXP"; + MOLAR_MASS_INT_KEY: constant String := "MOLAR_MASS_INT"; + MOLAR_MASS_DEC_KEY: constant String := "MOLAR_MASS_DEC"; + SAMPLE_VOLUME_INT_KEY: constant String := "SAMPLE_VOLUME_INT"; + SAMPLE_VOLUME_DEC_KEY: constant String := "SAMPLE_VOLUME_DEC"; + SAMPLE_VOLUME_EXP_KEY: constant String := "SAMPLE_VOLUME_EXP"; + SAMPLE_WEIGHT_INT_KEY: constant String := "SAMPLE_WEIGHT_INT"; + SAMPLE_WEIGHT_DEC_KEY: constant String := "SAMPLE_WEIGHT_DEC"; + X_STOCHIO_KEY: constant String := "X_STOCHIO"; + Z_STOCHIO_KEY: constant String := "Z_STOCHIO"; + -- + PARAMETER_IONIC_STRENGTH_KEY: constant String := "PARAMETER_IONIC_STRENGTH"; + PARAMETER_PROBLEM_SUBTYPE_KEY: constant String := "PARAMETER_PROBLEM_SUBTYPE"; + + end Solubility_Suite; + end Problem_Generator_Syswides; diff --git a/src/problem_manager.adb b/src/problem_manager.adb index d7bb1fd..91d3fcf 100644 --- a/src/problem_manager.adb +++ b/src/problem_manager.adb @@ -49,6 +49,7 @@ package body Problem_Manager is if Success = False then return False; end if; + -- Get assignment Success := USD.Problem.Get_Assignment(Assignment); if Success = False then @@ -108,6 +109,8 @@ package body Problem_Manager is case P_Cat is when Acidobazic => New_USD.Problem := Problem_Generator.Get_Problem(Problem_Generator_Syswides.Acidobazic); + when Solubility => + New_USD.Problem := Problem_Generator.Get_Problem(Problem_Generator_Syswides.Solubility); when others => return False; end case; diff --git a/src/problem_manager.ads b/src/problem_manager.ads index f717374..57cfed5 100644 --- a/src/problem_manager.ads +++ b/src/problem_manager.ads @@ -7,7 +7,7 @@ with AWS.Session; use Global_Types; package Problem_Manager is - type Problem_Category is (Invalid, Acidobazic); + type Problem_Category is (Invalid, Acidobazic, Solubility); function Display_Checked_Answer(UID: in Unique_ID; Answer: in Problem_Generator_Syswides.Answer_Info.Map; HTML: out HTML_Code) return Boolean; function Display_New_Assignment(UID: in Unique_ID; HTML: out HTML_Code) return Boolean;