]> Devoid-pointer.net GitWeb - Nine-Q.git/commitdiff
- Add preliminary implementation of Solubility Suite
authorMichal Malý <madcatxster@devoid-pointer.net>
Mon, 24 Nov 2014 13:54:03 +0000 (14:54 +0100)
committerMichal Malý <madcatxster@devoid-pointer.net>
Mon, 24 Nov 2014 13:54:03 +0000 (14:54 +0100)
- Introduce Auto_Lock (similar to C++11 std::lock_guard)

21 files changed:
bin/templates/face_index.html
bin/templates/face_solubility_c_f_ks_diff.html [new file with mode: 0644]
bin/templates/face_solubility_c_f_ks_shared.html [new file with mode: 0644]
bin/templates/face_solubility_ks_f_g_v.html [new file with mode: 0644]
bin/templates/face_solubility_params.html [new file with mode: 0644]
bin/templates/face_solubility_submit.html [new file with mode: 0644]
bin/templates/face_solubility_v_f_g_ks.html [new file with mode: 0644]
src/face_generators/face_generator.adb
src/face_generators/face_generator.ads
src/formatting_helpers.adb
src/formatting_helpers.ads
src/global_types.adb
src/global_types.ads
src/handlers/handler_start.adb
src/problem_generators/problem_generator-acidobazic_suite.adb
src/problem_generators/problem_generator-solubility_suite.adb [new file with mode: 0644]
src/problem_generators/problem_generator.adb
src/problem_generators/problem_generator.ads
src/problem_generators/problem_generator_syswides.ads
src/problem_manager.adb
src/problem_manager.ads

index 5f29ae9a3bd57b232a0a93672d11a0571de98c23..f1320be9c996c424e41479646298ffd77e8abca2 100644 (file)
@@ -1,3 +1,4 @@
   <a class="main_navlink" href="/start?problem_category=ACIDOBAZIC">pH jednosytné kyseliny/báze</a>
+  <a class="main_navlink" href="/start?problem_category=SOLUBILITY">Srážecí rovnováhy</a>
 
 
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 (file)
index 0000000..a4ddf51
--- /dev/null
@@ -0,0 +1,23 @@
+
+  <div class="backgrounded_block">
+    <div class="caption_v1">Zadání:</div>
+    <div class="assignment_text">Jaká je koncentrace látky X<span class="subscript">@_X_STOCHIO_@</span>Z<span class="subscript">@_Z_STOCHIO_@</span> o K<span class="subscript">s</span> = @_KS_INT_@,@_KS_DEC_@&nbsp;.&nbsp;10<span class="exponent">@_KS_EXP_@</span> která je rozpuštěna v roztoku, který obsahuje uni-univalentní elektrolyt o koncetraci @_EC_INT_@,@_EC_DEC_@&nbsp;.&nbsp;10<span class="exponent">@_EC_EXP_@</span>&nbsp;mol/dm<span class="exponent">3</span>, který nemá s danou látkou žádný společný iont?</div>
+    @_SUBMIT_FORM_@
+    </div>
+    @_ANSWER_SECTION_@
+
+    <div class="backgrounded_block">
+      <span class="expand_section" onclick="show_hide_item('hints_section', 'hints_button', 'block', '/images/arrow_right.png', '/images/arrow_down.png')">
+       <img class="expand_section" id="hints_button" src="/images/arrow_right.png" alt="Zobraz sekci" title="Zobraz sekci" />
+      </span>
+      <div class="caption_v1">Nápověda:</div>
+      <div class="assignment_hint" id="hints_section">
+       @_HINTS_SECTION_@
+      </div>
+    </div>
+
+    <div class="backgrounded_block">
+      <div class="caption_v1">Parametry příkladů:</div>
+      @_PARAMETERS_FORM_@ 
+    </div>
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 (file)
index 0000000..20fac37
--- /dev/null
@@ -0,0 +1,23 @@
+
+  <div class="backgrounded_block">
+    <div class="caption_v1">Zadání:</div>
+    <div class="assignment_text">Jaká je koncentrace látky X<span class="subscript">@_X_STOCHIO_@</span>Z<span class="subscript">@_Z_STOCHIO_@</span> o K<span class="subscript">s</span> = @_KS_INT_@,@_KS_DEC_@&nbsp;.&nbsp;10<span class="exponent">@_KS_EXP_@</span> která je rozpuštěna v roztoku, který obsahuje zcela rozpuštěnou látku A<span class="subscript">@_X_STOCHIO_@</span>Z o koncentraci @_EC_INT_@,@_EC_DEC_@&nbsp;.&nbsp;10<span class="exponent">@_EC_EXP_@</span>&nbsp;mol/dm<span class="exponent">3</span>. Anion obou látek je stejný.</div>
+    @_SUBMIT_FORM_@
+    </div>
+    @_ANSWER_SECTION_@
+
+    <div class="backgrounded_block">
+      <span class="expand_section" onclick="show_hide_item('hints_section', 'hints_button', 'block', '/images/arrow_right.png', '/images/arrow_down.png')">
+       <img class="expand_section" id="hints_button" src="/images/arrow_right.png" alt="Zobraz sekci" title="Zobraz sekci" />
+      </span>
+      <div class="caption_v1">Nápověda:</div>
+      <div class="assignment_hint" id="hints_section">
+       @_HINTS_SECTION_@
+      </div>
+    </div>
+
+    <div class="backgrounded_block">
+      <div class="caption_v1">Parametry příkladů:</div>
+      @_PARAMETERS_FORM_@ 
+    </div>
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 (file)
index 0000000..288eca9
--- /dev/null
@@ -0,0 +1,23 @@
+
+  <div class="backgrounded_block">
+    <div class="caption_v1">Zadání:</div>
+    <div class="assignment_text">Jaké je K<span class="subscript">s</span> látky X<span class="subscript">@_X_STOCHIO_@</span>Z<span class="subscript">@_Z_STOCHIO_@</span> o molární hmotnosti MW=@_MOLAR_MASS_INT_@,@_MOLAR_MASS_DEC_@&nbsp;g/mol, jejíž @_SAMPLE_WEIGHT_INT_@,@_SAMPLE_WEIGHT_DEC_@&nbsp;g se zcela rozpustí v @_SAMPLE_VOLUME_INT_@,@_SAMPLE_VOLUME_DEC_@&nbsp;.&nbsp;10<span class="exponent">@_SAMPLE_VOLUME_EXP_@</span>&nbsp;dm<span class="exponent">3</span></div>
+    @_SUBMIT_FORM_@
+    </div>
+    @_ANSWER_SECTION_@
+
+    <div class="backgrounded_block">
+      <span class="expand_section" onclick="show_hide_item('hints_section', 'hints_button', 'block', '/images/arrow_right.png', '/images/arrow_down.png')">
+       <img class="expand_section" id="hints_button" src="/images/arrow_right.png" alt="Zobraz sekci" title="Zobraz sekci" />
+      </span>
+      <div class="caption_v1">Nápověda:</div>
+      <div class="assignment_hint" id="hints_section">
+       @_HINTS_SECTION_@
+      </div>
+    </div>
+
+    <div class="backgrounded_block">
+      <div class="caption_v1">Parametry příkladů:</div>
+      @_PARAMETERS_FORM_@ 
+    </div>
diff --git a/bin/templates/face_solubility_params.html b/bin/templates/face_solubility_params.html
new file mode 100644 (file)
index 0000000..86852e2
--- /dev/null
@@ -0,0 +1,18 @@
+      <form class="problem_form" method="post" action="/next_problem" enctype="multipart/form-data">
+       <div class="form_line">
+         <label class="form_label_sol_param" for="@_PARAMETER_IONIC_STRENGTH_@">Uvažovat iontovou sílu:</label>
+         <input type="checkbox" name="@_PARAMETER_IONIC_STRENGTH_@" value="True" @_PARAMETER_IONIC_STRENGTH_CHECKED_@ />
+       </div>
+       <div class="form_line">
+         <label class="form_label_sol_param" for="@_PARAMETER_PROBLEM_SUBTYPE_@">Typ příkladu:</label>
+         <select name="@_PARAMETER_PROBLEM_SUBTYPE_@">
+           <option value="@_V_FROM_G_KS_@" @_V_FROM_G_KS_SELECTED_@>Objem potřebný k rozpuštění, zadáno Ks, G, MW</option>
+           <option value="@_KS_FROM_G_V_@" @_KS_FROM_G_V_SELECTED_@>Součin rozpustnosti, zadnáno G, V, MW</option>
+           <option value="@_C_FROM_KS_DIFFERENT_IONS_@" @_C_FROM_KS_DIFFERENT_IONS_SELECTED_@>Koncentrace rozpuštené látky v přítomnosti indiferentního elektrolytu</option>
+           <option value="@_C_FROM_KS_SHARED_ION_@" @_C_FROM_KS_SHARED_ION_SELECTED_@>Koncentrace rozpuštěné látky v přítomnosti elektrolytu se stejným aniontem</option>
+         </select>
+       </div>
+       <div class="form_line">
+         <input type="submit" name="next_problem" value="Další příklad" />
+       </div>
+      </form>
diff --git a/bin/templates/face_solubility_submit.html b/bin/templates/face_solubility_submit.html
new file mode 100644 (file)
index 0000000..c1fec58
--- /dev/null
@@ -0,0 +1,9 @@
+      <form class="problem_form" method="post" action="/check_answer" enctype="multipart/form-data">
+       <div class="form_line">
+         <label class="form_label_sol" for="ANSWER_NUM">Výsledek:</label>
+         <input class="form_input_sol" type="text" name="ANSWER_NUM" maxlength="128" value="" />
+       </div>
+       <div class="form_line">
+         <input type="submit" name="send_answer" value="Zkontrolovat" />
+       </div>
+      </form>
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 (file)
index 0000000..1bbe36d
--- /dev/null
@@ -0,0 +1,23 @@
+
+  <div class="backgrounded_block">
+    <div class="caption_v1">Zadání:</div>
+    <div class="assignment_text">V jakém objemu se rozpustí @_SAMPLE_WEIGHT_INT_@,@_SAMPLE_WEIGHT_DEC_@&nbsp;g látky X<span class="subscript">@_X_STOCHIO_@</span>Z<span class="subscript">@_Z_STOCHIO_@</span> o K<span class="subscript">s</span>=@_KS_INT_@,@_KS_DEC_@&nbsp;.&nbsp;10<span class="exponent">@_KS_EXP_@</span> a molekulové hmotnosti MW=@_MOLAR_MASS_INT_@,@_MOLAR_MASS_DEC_@&nbsp;g/mol?</div>
+    @_SUBMIT_FORM_@
+    </div>
+    @_ANSWER_SECTION_@
+
+    <div class="backgrounded_block">
+      <span class="expand_section" onclick="show_hide_item('hints_section', 'hints_button', 'block', '/images/arrow_right.png', '/images/arrow_down.png')">
+       <img class="expand_section" id="hints_button" src="/images/arrow_right.png" alt="Zobraz sekci" title="Zobraz sekci" />
+      </span>
+      <div class="caption_v1">Nápověda:</div>
+      <div class="assignment_hint" id="hints_section">
+       @_HINTS_SECTION_@
+      </div>
+    </div>
+
+    <div class="backgrounded_block">
+      <div class="caption_v1">Parametry příkladů:</div>
+      @_PARAMETERS_FORM_@ 
+    </div>
index 6790ad8da6b2c14e981db100aac59189b7992f3e..0f9c75108a370c05ff0328e7cddd22bdbf64acc1 100644 (file)
@@ -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, "&lt;&nbsp;" & 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;
index 3a264351e4acb6c9832075646e3793be47a828db..a758854a635bb23f89a14a8cd09bb139d238de53 100644 (file)
@@ -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";
index 313032f19cd2e1f460564c94eaf20ebbfbb6ac99..8163dde5495ceb206bf6f0920d5d1240b3ab5856 100644 (file)
@@ -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;
index ba0128d2df24560f21ed67d4c50bb3517ee3c43e..c7011ae0a71840dc1e60e5609037169af16eef16 100644 (file)
@@ -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;
index ab6ccbac8fa26c734dff8beebf6d7c555826f9c6..2480115d620897498b67c0e565d32fad187ae36d 100644 (file)
@@ -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);
index 6d18c20be77055bffe83b95e5782a625b3272102..7e42f3916ac13aba031da07ef3d908ed4f8e6500 100644 (file)
@@ -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;
index d4d9defebda9a3c2bf291ee72a1bfccacc4f3811..cd7008e4eb4b8532b5f0976899ae0e2943109a78 100644 (file)
@@ -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
index 8dba22e3536357a9dc4ee6ede39298a3b3312d75..21f6c3b86e5f99e8298bf54a00d69f06f1543936 100644 (file)
@@ -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 (file)
index 0000000..84be063
--- /dev/null
@@ -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<span class=""subscript""s</span> = ");
+      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 => "&nbsp;.&nbsp;10<span class=""exponent"">"); Append_UB_Text(Source => Message, New_Item => Ans_Exp_Str); Append_UB_Text(Source => Message, New_Item => "</span>&nbsp;");
+
+    case P_Subtype is
+      when V_FROM_G_KS =>
+       Append_UB_Text(Source => Message, New_Item => "dm<span class=""exponent"">3</span>");
+      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/dm<span class=""exponent"">3</span>");
+    end case;
+  end Get_Correct_Answer_String;
+    
+
+end Solubility_Suite;
index 16d19609c184e276f3b51d936a518a961a1debe2..97c3d4803dc9379880c34ecac71bd1d8f50c9456 100644 (file)
@@ -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;
index c98e408c061bf7689935208c77eefe642458acc9..16bafd64f460fe8f2eacc4890eced645fe01dda8 100644 (file)
@@ -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;
index 86da2475a7a5f0d296faf0f75518aa5b2ece75df..f058304a06bffad76730f153a76f59e9a144c99a 100644 (file)
@@ -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;
index d7bb1fdc706aa913f5a020ca3e2e8cf50efe007d..91d3fcf26ac2dff1059d9ae31b59b23b6165962f 100644 (file)
@@ -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;
index f717374254b069133ed302e64001c62b4bde75ba..57cfed52e85ee44185aad18c0bd0b114f623c920 100644 (file)
@@ -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;