--- /dev/null
+
+<script type="text/javascript">
+
+function show_hide_item(element_id, button_id, disp_style, show_icon, hide_icon)
+{
+ elem = document.getElementById(element_id);
+ btn = document.getElementById(button_id);
+
+ if (elem.style.display == 'none') {
+ elem.style.display = disp_style;
+ btn.src = hide_icon;
+ } else {
+ elem.style.display = 'none';
+ btn.src = show_icon;
+ }
+}
+
+</script>
+
--- /dev/null
+* {
+ margin: 0;
+ padding: 1px;
+}
+
+.answer_kind_good {
+ color: #2a7c20;
+ font-size: 14pt;
+}
+
+.answer_kind_bad {
+ color: #fa0d0d;
+ font-size: 14pt;
+}
+
+.assignment_hint {
+ display: none;
+}
+
+.assignment_text {
+ font-size: 14pt;
+ margin-left: 32px;
+ margin-right: 32px;
+}
+
+.backgrounded_block {
+ background-color: #d4e5f4;
+
+ /* CSS3 only */
+ border-radius: 10px;
+ /* Pre CSS3 Mozilla */
+ -moz-border-radius: 10px;
+ /* Pre CSS3 Webkit */
+ -webkit-border-radius: 10px;
+
+ margin-bottom: 24px;
+ padding-top: 16px;
+
+ width: 100%;
+}
+
+.caption_v1 {
+ font-size: 16pt;
+ margin-left: 32px;
+ margin-right: 32px;
+ margin-bottom: 16px;
+}
+
+.emphasis {
+ font-style: italic;
+}
+
+.exponent {
+ font-size: 75%;
+ vertical-align: super;
+}
+
+.key_info {
+ font-weight: bold;
+}
+
+.problem_form {
+ margin-left: 32px;
+ margin-right: 32px;
+ margin-bottom: 32px;
+ padding-right: 0px;
+ padding-top: 16px;
+}
+
+ div.form_line {
+ padding-top: 8px;
+ }
+
+ .form_label_ac {
+ display: inline-block;
+ width: 128px;
+ }
+
+ .form_label_ac_param {
+ display: inline-block;
+ width: 320px;
+ }
+
+ .form_input_ac {
+ display: inline-block;
+ width: 128px;
+ margin-right: 0px;
+ padding-right: 0px;
+ }
+
+.subscript {
+ font-size: 75%;
+ vertical-align: sub;
+}
+
+a.back_link {
+ color: black;
+ text-decoration: none;
+}
+
+a.main_navlink {
+ text-align: center;
+ background-color: #d4e5f4;
+ color: black;
+ padding: 8px 8px 8px;
+ margin-top: 32px;
+ font-size: 22pt;
+ font-weight: bold;
+ text-decoration: none;
+ width: 420px;
+
+ /* CSS3 only */
+ border-radius: 4px;
+ /* Pre CSS3 Mozilla */
+ -moz-border-radius: 4px;
+ /* Pre CSS3 Webkit */
+ -webkit-border-radius: 4px;
+}
+
+a:hover.main_navlink {
+ background-color: #58acf4;
+}
+
+a:visited.main_navlink {
+ color: black;
+}
+
+a.footer_link {
+ color: black;
+ font-weight: normal;
+ text-decoration: none;
+}
+
+a:visited.footer_link {
+ color: black;
+ font-weight: normal;
+ text-decoration: none;
+}
+
+div.hint_block {
+ margin-left: 32px;
+ margin-right: 32px;
+ padding-top: 16px;
+ padding-bottom: 16px;
+}
+
+div.hint_caption {
+ font-size: 14pt;
+ margin-left: 16px;
+ margin-bottom: 16px;
+}
+
+img.expand_section {
+ width: 14pt;
+ height: 14pt;
+ padding: 3px;
+ border: 2px solid black;
+
+ /* CSS3 only */
+ border-radius: 4px;
+ /* Pre CSS3 Mozilla */
+ -moz-border-radius: 4px;
+ /* Pre CSS3 Webkit */
+ -webkit-border-radius: 4px;
+}
+
+img.math_formula {
+ height: 32px;
+ width: auto;
+ display: block;
+
+ margin-bottom: 16px;
+ margin-left: 32px;
+}
+
+img.math_formula_multiline {
+ height: 58px;
+ width: auto;
+ display: block;
+
+ margin-bottom: 32px;
+ margin-left: 32px;
+}
+
+
+span.expand_section {
+ float: right;
+ margin-right: 32px;
+}
+
+#container {
+ margin: 0 auto 0;
+ width: 1000px;
+ min-height: 100vh;
+ position: relative;
+}
+
+#content {
+ margin-top: 32px;
+ width: 100%;
+}
+
+#core_info {
+ display: table;
+ margin: 16px auto;
+}
+
+#footer {
+ background-color: #58acf4;
+ height: 48px;
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ margin-top: 32px;
+
+ /* CSS3 only */
+ border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+ /* Pre CSS3 Mozilla */
+ -moz-border-radius-topleft: 10px;
+ -moz-border-radius-topright: 10px;
+}
+
+#ignore_chart_ac {
+ width: 872px;
+ height: auto;
+
+ margin-bottom: 16px;
+ margin-left: 32px;
+}
+
+#page_caption {
+ background-color: #58acf4;
+
+ /* CSS3 only */
+ border-bottom-left-radius: 10px;
+ border-bottom-right-radius: 10px;
+ /* Pre CSS3 Mozilla */
+ -moz-border-radius-bottomleft: 10px;
+ -moz-border-radius-bottomright: 10px;
+
+ height: 96px;
+}
+
+#page_caption_text {
+ margin-top: 32px;
+ margin-left: 32px;
+ font-size: 24pt;
+}
--- /dev/null
+ <div class="backgrounded_block">
+ <div class="@_ANSWER_KIND_@">@_ANSWER_MESSAGE_@</div>
+ </div>
--- /dev/null
+
+ <div class="backgrounded_block">
+ <div class="caption_v1">Zadání:</div>
+ <div class="assignment_text">Vypočítejte pH <span class="key_info">@_SUBSTANCE_@</span> jejíž <span class="key_info">@_PKX_@ = @_PKX_VALUE_INT_@,@_PKX_VALUE_DEC_@</span> a koncentrace <span class="key_info">c = @_CONCENTRATION_INT_@,@_CONCENTRATION_DEC_@ . 10<span class="exponent">@_CONCENTRATION_EXP_@</span></span> mol/L.</div>
+ <form class="problem_form" method="post" action="/check_answer" enctype="multipart/form-data">
+ <div class="form_line">
+ <label class="form_label_ac" for="@_ANSWER_PH_@">pH:</label>
+ <input class="form_input_ac" type="text" name="@_ANSWER_PH_@" maxlength="128" value="" />
+ </div>
+ <div class="form_line">
+ <label class="form_label_ac" for="@_ANSWER_SIMPLIFICATION_@">Lze zanedbat:</label>
+ <select class="form_input_ac" name="@_ANSWER_SIMPLIFICATION_@">
+ <option value="@_ANSWER_OPTION_SIMPL_BOTH_@">Vše</option>
+ <option value="@_ANSWER_OPTION_SIMPL_ATPR_@">Autoprotolýzu</option>
+ <option value="@_ANSWER_OPTION_SIMPL_DISSOC_@">Úbytek disociací</option>
+ </select>
+ </div>
+ <div class="form_line">
+ <input type="submit" name="send_answer" value="Zkontrolovat" />
+ </div>
+ </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>
+ <form class="problem_form" method="post" action="/next_problem" enctype="multipart/form-data">
+ <div class="form_line">
+ <label class="form_label_ac_param" for="@_PARAMETER_NO_BOTH_SIMPLIFICATIONS_@">Pouze příklady, kde nelze zanedbat oba jevy:</label>
+ <input type="checkbox" name="@_PARAMETER_NO_BOTH_SIMPLIFICATIONS_@" value="True" @_PARAMETER_NO_BOTH_SIMPLIFICATIONS_CHECKED_@ />
+ </div>
+ <div class="form_line">
+ <input type="submit" name="next_problem" value="Další příklad" />
+ </div>
+ </form>
+ </div>
--- /dev/null
+ <a class="main_navlink" href="/start?problem_category=ACIDOBAZIC">pH jednosytné kyseliny/báze</a>
+
+
--- /dev/null
+ </div>
+ <div id="spacer"></div>
+ <div id="footer">
+ <span id="core_info">Powered by: <a class="footer_link" href="http://libre.adacore.com/tools/aws/">AWS</a> | <a class="footer_link" href="http://gitweb.devoid-pointer.net/?p=Nine-Q.git;a=summary">Nine-Q</a></span>
+ </div>
+ </div>
+</body>
+
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="cs" lang="cs">
+
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="content-language" content="cs" />
+ @_META_EXPIRE_NOW_@
+ @_META_NO_CACHE_@
+ <link rel="stylesheet" type="text/css" href="/main_stylesheet" />
+ <title>TGen_Prototype</title>
+</head>
+
+<body>
+ <div id="container">
+ <div id="page_caption">
+ <div id="page_caption_text"><a class="back_link" href="/">@_HEADER_CAPTION_@</a></div>
+ </div>
+ <div id="content">
--- /dev/null
+ <div class="hint_block">
+ <div class="hint_caption">
+ Definice pH:
+ </div>
+ <img class="math_formula" src="/images/ph_calc.png" alt="Definice pH - vztah" />
+ </div>
+ <div class="hint_block">
+ <div class="hint_caption">
+ Základní vztah pro koncentraci kationů H<span class="subscript">3</span>O, není-li uvažován vliv <span class="emphasis">autoprotolýzy</span> nebo <span class="emphasis">úbytku disociací</span>:
+ </div>
+ <img class="math_formula" src="/images/h3o_calc.png" alt="Základní vztah pro koncentraci [H3O+]" />
+ </div>
+
+ <div class="hint_block">
+ <div class="hint_caption">
+ V některých případech je nutné do základního vztahu pro výpočet koncentrace H<span class="subscript">3</span>O přidat korekce na vliv <span class="emphasis">autoprotolýzy</span> či <span class="emphasis">úbytku disociací</span>. Existují vztahy, z nichž můžeme určit, kdy je nutné který vliv uvažovat.
+ </div>
+ <div class="hint_caption">
+ Je-li splněna tato rovnice, lze zanedbat vliv <span class="emphasis">autoprotolýzy</span>:
+ </div>
+ <img class="math_formula_multiline" src="/images/ign_atpr_acid.png" alt="Ověření zanedbání autoprotolýzy - vztah" />
+ <div class="hint_caption">
+ Je-li splněna tato rovnice, lze zanedbat vliv <span class="emphasis">úbytku disociací</span>:
+ </div>
+ <img class="math_formula_multiline" src="/images/ign_dissoc_acid.png" alt="Ověření zanedbání autoprotolýzy - vztah" />
+ </div>
+
+ <div class="hint_block">
+ <div class="hint_caption">
+ Vztah pro koncentraci H<span class="subscript">3</span>O , je-li uvažována autoprotolýza:
+ </div>
+ <img class="math_formula" src="/images/h3o_calc_atpr_acid.png" alt="Vztah pro koncentraci H3O+ s autoprotolýzou" />
+ <div class="hint_caption">
+ Vztah pro koncentraci H<span class="subscript">3</span>O , je-li uvažován úbytek disociací:
+ </div>
+ <img class="math_formula_multiline" src="/images/h3o_calc_dissoc_acid.png" alt="Vztah pro koncentraci H3O+ s úbyktem disociací" />
+ </div>
+
+ <div class="hint_block">
+ <div class="hint_caption">
+ Grafické znázornění oblastí, kde je třeba uvažovat <span class="emphasis">autoprotolýzu</span> a <span class="emphasis">úbytek disociací:</span>
+ </div>
+ <img id="ignore_chart_ac" src="/images/ign_chart_acid.png" />
+ </div>
+
--- /dev/null
+ <div class="hint_block">
+ <div class="hint_caption">
+ Definice pOH:
+ </div>
+ <img class="math_formula" src="/images/poh_calc.png" alt="Definice pOH - vztah" />
+ </div>
+ <div class="hint_block">
+ <div class="hint_caption">
+ Základní vztah pro koncentraci aniontů <span class="exponent">-</span>OH, není-li uvažován vliv <span class="emphasis">autoprotolýzy</span> nebo <span class="emphasis">úbytku disociací</span>:
+ </div>
+ <img class="math_formula" src="/images/oh_calc.png" alt="Základní vztah pro koncentraci -OH" />
+ </div>
+
+ <div class="hint_block">
+ <div class="hint_caption">
+ V některých případech je nutné do základního vztahu pro výpočet koncentrace <span class="exponent">-</span>OH, přidat korekce na vliv <span class="emphasis">autoprotolýzy</span> či <span class="emphasis">úbytku disociací</span>. Existují vztahy, z nichž můžeme určit, kdy je nutné který vliv uvažovat.
+ </div>
+ <div class="hint_caption">
+ Je-li splněna tato rovnice, lze zanedbat vliv <span class="emphasis">autoprotolýzy</span>:
+ </div>
+ <img class="math_formula_multiline" src="/images/ign_atpr_base.png" alt="Ověření zanedbání autoprotolýzy - vztah" />
+ <div class="hint_caption">
+ Je-li splněna tato rovnice, lze zanedbat vliv <span class="emphasis">úbytku disociací</span>:
+ </div>
+ <img class="math_formula_multiline" src="/images/ign_dissoc_base.png" alt="Ověření zanedbání autoprotolýzy - vztah" />
+ </div>
+
+ <div class="hint_block">
+ <div class="hint_caption">
+ Vztah pro koncentraci <span class="exponent">-</span>OH, je-li uvažována autoprotolýza:
+ </div>
+ <img class="math_formula" src="/images/oh_calc_atpr_base.png" alt="Vztah pro koncentraci -OH s autoprotolýzou" />
+ <div class="hint_caption">
+ Vztah pro koncentraci <span class="exponent">-</span>OH, je-li uvažován úbytek disociací:
+ </div>
+ <img class="math_formula_multiline" src="/images/oh_calc_dissoc_base.png" alt="Vztah pro koncentraci -OH s úbyktem disociací" />
+ </div>
+
+ <div class="hint_block">
+ <div class="hint_caption">
+ Grafické znázornění oblastí, kde je třeba uvažovat <span class="emphasis">autoprotolýzu</span> a <span class="emphasis">úbytek disociací:</span>
+ </div>
+ <img id="ignore_chart_ac" src="/images/ign_chart_base.png" />
+ </div>
--- /dev/null
+with AWS.Templates;
+
+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
+ Insert(Translations_Hdr, Assoc(HEADER_CAPTION_KEY, "..."));
+ Temp := Parse(Filename => "templates/header.html", Translations => Translations_Hdr, Cached => True);
+ Append_HTML(Source => HTML, New_Item => Temp);
+
+ Temp := Parse(Filename => "templates/face_index.html", Cached => True);
+ Append_HTML(Source => HTML, New_Item => Temp);
+
+ Temp := Parse(Filename => "templates/footer.html", Cached => True);
+ Append_HTML(Source => HTML, New_Item => Temp);
+ return True;
+ end Generate_Index_Face;
+
+ function Generate_Face(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map; Parameters:
+ in Problem_Generator_Syswides.Parameters_Info.Map;
+ HTML: out HTML_Code) return Boolean is
+ begin
+ return Generate_Face_With_Answer(Assignment => Assignment, Parameters => Parameters, HTML => HTML,
+ Answer_Message => To_UB_Text(""), Answer_Code => Problem_Generator_Syswides.Invalid_Answer);
+ end Generate_Face;
+
+ function Generate_Face_With_Answer(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 Problem_Generator_Syswides;
+ use Problem_Generator_Syswides.Assignment_Info;
+
+ begin
+ if Assignment.Find(PROBLEM_TYPE_KEY) = Assignment_Info.No_Element then
+ return False;
+ end if;
+
+ declare
+ Problem_Type_Str: constant String := Assignment.Element(PROBLEM_TYPE_KEY);
+ begin
+ if Problem_Type_Str = Problem_Type'Image(Acidobazic) then
+ return Generate_Face_Acidobazic(Assignment, Answer_Message, Answer_Code, Parameters, HTML);
+ else
+ return False;
+ end if;
+ end;
+ end Generate_Face_With_Answer;
+
+ -- BEGIN: Private functions
+ 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 is
+ use AWS.Templates;
+ use Problem_Generator_Syswides;
+ 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;
+ begin
+ Insert(Translations_Hdr, Assoc(HEADER_CAPTION_KEY, "< " & Acidobazic_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);
+
+ Insert(Translations, Assoc(Acidobazic_Suite.CONCENTRATION_INT_KEY, Assignment.Element(Acidobazic_Suite.CONCENTRATION_INT_KEY)));
+ Insert(Translations, Assoc(Acidobazic_Suite.CONCENTRATION_DEC_KEY, Assignment.Element(Acidobazic_Suite.CONCENTRATION_DEC_KEY)));
+ Insert(Translations, Assoc(Acidobazic_Suite.CONCENTRATION_EXP_KEY, Assignment.Element(Acidobazic_Suite.CONCENTRATION_EXP_KEY)));
+ Insert(Translations, Assoc(Acidobazic_Suite.PKX_KEY, Assignment.Element(Acidobazic_Suite.PKX_KEY)));
+ Insert(Translations, Assoc(Acidobazic_Suite.PKX_VALUE_INT_KEY, Assignment.Element(Acidobazic_Suite.PKX_VALUE_INT_KEY)));
+ Insert(Translations, Assoc(Acidobazic_Suite.PKX_VALUE_DEC_KEY, Assignment.Element(Acidobazic_Suite.PKX_VALUE_DEC_KEY)));
+ Insert(Translations, Assoc(Acidobazic_Suite.SUBSTANCE_KEY, Assignment.Element(Acidobazic_Suite.SUBSTANCE_KEY)));
+ Insert(Translations, Assoc("ANSWER_PH", Acidobazic_Suite.ANSWER_PH_KEY));
+ Insert(Translations, Assoc("ANSWER_SIMPLIFICATION", Acidobazic_Suite.ANSWER_SIMPLIFICATION_KEY));
+ Insert(Translations, Assoc("ANSWER_OPTION_SIMPL_ATPR", Acidobazic_Suite.Simplification'Image(Acidobazic_Suite.Autoprotolysis)));
+ Insert(Translations, Assoc("ANSWER_OPTION_SIMPL_BOTH", Acidobazic_Suite.Simplification'Image(Acidobazic_Suite.Both)));
+ Insert(Translations, Assoc("ANSWER_OPTION_SIMPL_DISSOC", Acidobazic_Suite.Simplification'Image(Acidobazic_Suite.Dissociation)));
+ Insert(Translations, Assoc("PARAMETER_NO_BOTH_SIMPLIFICATIONS", Acidobazic_Suite.PARAMETER_NO_BOTH_SIMPLIFICATIONS_KEY));
+ if Parameters.Find(Acidobazic_Suite.PARAMETER_NO_BOTH_SIMPLIFICATIONS_KEY) = Parameters_Info.No_Element then
+ Insert(Translations, Assoc("PARAMETER_NO_BOTH_SIMPLIFICATIONS_CHECKED", ""));
+ else
+ 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;
+
+ -- Generate hints
+ if Assignment.Element(Acidobazic_Suite.PKX_KEY) = Acidobazic_Suite.PKX_PKA_TEXT then
+ Temp := Parse(Filename => "templates/hints_acidobazic_acid.html", Cached => True);
+ Insert(Translations, Assoc(HINTS_SECTION_KEY, Temp));
+ elsif Assignment.Element(Acidobazic_Suite.PKX_KEY) = Acidobazic_Suite.PKX_PKB_TEXT then
+ Temp := Parse(Filename => "templates/hints_acidobazic_base.html", Cached => True);
+ Insert(Translations, Assoc(HINTS_SECTION_KEY, Temp));
+ end if;
+
+ Temp := Parse(Filename => "templates/face_acidobazic.html", Translations => Translations);
+ Append_HTML(Source => HTML, New_Item => Temp);
+
+
+ Temp := Parse(Filename => "templates/footer.html");
+ Append_HTML(Source => HTML, New_Item => Temp);
+
+ return True;
+ end Generate_Face_Acidobazic;
+end Face_Generator;
--- /dev/null
+with Global_Types;
+with Problem_Generator_Syswides;
+
+use Global_Types;
+package Face_Generator is
+ function Generate_Index_Face(HTML: out HTML_Code) return Boolean;
+
+ function Generate_Face(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map;
+ Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
+ HTML: out HTML_Code) return Boolean;
+
+ function Generate_Face_With_Answer(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;
+
+private
+ 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;
+
+ HEADER_CAPTION_KEY: constant String := "HEADER_CAPTION";
+ HINTS_SECTION_KEY: constant String := "HINTS_SECTION";
+ META_EXPIRE_NOW_KEY: constant String := "META_EXPIRE_NOW";
+ META_EXPIRE_NOW_TEXT: constant String := "<meta http-equiv=""expires"" content=""0"" />";
+ META_NO_CACHE_KEY: constant String := "META_NO_CACHE";
+ META_NO_CACHE_TEXT: constant String := "<meta http-equiv=""cache-control"" content=""no-cache"" />";
+
+end Face_Generator;
--- /dev/null
+with Ada.Numerics.Generic_Elementary_Functions;
+with Ada.Strings.Fixed;
+--with Ada.Text_IO;
+
+package body Formatting_Helpers is
+
+ 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;
+
+ F_Log_Num: constant FH_Float := FH_Float'Floor(FHEF.Log(Base => 10.0, X => Num));
+ F_Log_Dec: constant FH_Float := FH_Float'Floor(FHEF.Log(Base => 10.0, X => Decimals));
+ Shift: FH_Float;
+ TNum: FH_Float;
+ begin
+ Shift := 10.0 ** (F_Log_Num - F_Log_Dec);
+ TNum := FH_Float'Rounding(Num / Shift) * Shift;
+
+ --Ada.Text_IO.Put_Line("FLN: " & FH_Float'Image(F_Log_Num) & " FLD: " & FH_Float'Image(F_Log_Dec) & " S: " & FH_Float'Image(Shift) & " Res: " & FH_Float'Image(TNum));
+ return TNum;
+ end Round_To_Valid_Nums;
+
+ procedure Split_Integer_Decimal_Strs(Num: in FH_Float; Decimals: in FH_Float; Integer_Part: out UB_Text; Decimal_Part: out UB_Text) is
+ I: Integer;
+ D: Integer;
+ begin
+ Split_Integer_Decimal_Ints(Num, Decimals, I, D);
+
+ Integer_Part := To_UB_Text(Integer'Image(I));
+ Get_Decimal_Part_Str(Decimal_Part, D, Decimals);
+
+ --Ada.Text_IO.Put_Line("I: " & Integer'Image(I) & " D: " & Integer'Image(D) & " Num: " & FH_Float'Image(Num));
+ end Split_Integer_Decimal_Strs;
+
+ procedure Split_Integer_Decimal_Exponent_Strs(Num: in FH_Float; Decimals: in FH_Float; Integer_Part: out UB_Text; Decimal_Part: out UB_Text;
+ Exponent_Part: out UB_Text) is
+ package FHEF is new Ada.Numerics.Generic_Elementary_Functions(FH_Float);
+ use FHEF;
+
+ FE: FH_Float;
+ TNum: FH_Float;
+ begin
+ --Ada.Text_IO.Put_Line("Num: " & FH_Float'Image(Num));
+ FE := FH_Float'Floor(FHEF.Log(Base => 10.0, X => Num));
+ --Ada.Text_IO.Put_Line("Exp: " & FH_Float'Image(FE));
+ TNum := Num / (10.0 ** FE);
+
+ Split_Integer_Decimal_Strs(TNum, Decimals, Integer_Part, Decimal_Part);
+ Append_UB_Text(Exponent_Part, To_UB_Text(Integer'Image(Integer(FE))));
+
+ end Split_Integer_Decimal_Exponent_Strs;
+
+ -- BEGIN: Private functions
+ procedure Get_Decimal_Part_Str(Decimal_Part: out UB_Text; D: in Integer; Decimals: in FH_Float) is
+ Pos: Integer := Integer(Decimals) / 10;
+ begin
+ while Pos > 1 loop
+ if D < Pos then
+ Append_UB_Text(Decimal_Part, To_UB_Text("0"));
+ else
+ exit;
+ end if;
+ Pos := Pos / 10;
+ end loop;
+
+ Append_UB_Text(Decimal_Part, To_UB_Text(Ada.Strings.Fixed.Trim(Integer'Image(D), Ada.Strings.Left)));
+ end Get_Decimal_Part_Str;
+
+ procedure Split_Integer_Decimal_Ints(Num: in FH_Float; Decimals: in FH_Float; I: out Integer; D: out Integer) is
+ begin
+ I := Integer(FH_Float'Floor(Num));
+ D := Integer(FH_Float'Rounding((Num - FH_Float(I)) * Decimals));
+ end Split_Integer_Decimal_Ints;
+
+end Formatting_Helpers;
--- /dev/null
+with Global_Types;
+
+use Global_Types;
+generic
+type FH_Float is digits <>;
+package Formatting_Helpers is
+
+ 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;
+ Exponent_Part: out UB_Text);
+
+private
+ procedure Get_Decimal_Part_Str(Decimal_Part: out UB_Text; D: Integer; Decimals: in FH_Float);
+ procedure Split_Integer_Decimal_Ints(Num: in FH_Float; Decimals: in FH_Float; I: out Integer; D: out Integer);
+
+end Formatting_Helpers;
--- /dev/null
+package body Global_Types is
+
+ procedure Append_HTML(Source: in out HTML_Code; New_Item: in HTML_Code) is
+ begin
+ Ada.Strings.Unbounded.Append(Source => Source, New_Item => New_Item);
+ end Append_HTML;
+
+ procedure Append_UB_Text(Source: in out UB_Text; New_Item: in UB_Text) is
+ begin
+ Ada.Strings.Unbounded.Append(Source => Source, New_Item => 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);
+ end HTML_To_Fixed_String;
+
+ function To_HTML_Code(S: in String) return HTML_Code is
+ begin
+ return Ada.Strings.Unbounded.To_Unbounded_String(S);
+ end To_HTML_Code;
+
+ function To_UB_Text(S: in String) return UB_Text is
+ begin
+ return Ada.Strings.Unbounded.To_Unbounded_String(S);
+ end To_UB_Text;
+
+ function UB_Text_To_Fixed_String(Text: in UB_Text) return String is
+ begin
+ return Ada.Strings.Unbounded.To_String(Text);
+ end UB_Text_To_Fixed_String;
+
+end Global_Types;
--- /dev/null
+with Ada.Containers;
+with Ada.Strings.Unbounded;
+
+package Global_Types is
+
+ subtype Unique_ID is Ada.Containers.Count_Type;
+ subtype HTML_Code is Ada.Strings.Unbounded.Unbounded_String;
+ subtype UB_Text is Ada.Strings.Unbounded.Unbounded_String;
+
+ 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);
+ 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;
+ function UB_Text_To_Fixed_String(Text: in UB_Text) return String;
+
+end Global_Types;
--- /dev/null
+with AWS.Containers.Tables;
+with AWS.Messages;
+with AWS.MIME;
+with AWS.Parameters;
+with AWS.Response;
+with AWS.Session;
+with AWS.Status;
+
+with Global_Types;
+with Problem_Manager;
+with Problem_Generator_Syswides;
+
+with Ada.Text_IO;
+
+use Global_Types;
+package body Handler_Check_Answer is
+
+ function Handle(Request: AWS.Status.Data) return AWS.Response.Data is
+ SID: constant AWS.Session.ID := AWS.Status.Session(Request);
+ Req_Type: constant AWS.Status.Request_Method := AWS.Status.Method(Request);
+
+ Raw_UID: constant String := AWS.Session.Get(SID, "UID");
+ UID: Unique_ID;
+ HTML: HTML_Code;
+ begin
+ Ada.Text_IO.Put_Line("Handling /check_answer");
+
+ case Req_Type is
+ when AWS.Status.POST =>
+ declare
+ use Problem_Generator_Syswides;
+ use Problem_Generator_Syswides.Answer_Info;
+
+ POST_Data: constant AWS.Parameters.List := AWS.Status.Parameters(Request);
+ Answer: Answer_Info.Map;
+ Success: Boolean;
+ begin
+ -- Get UID
+ Success := Problem_Manager.Get_UID(Raw_UID, UID);
+ if Success = False then
+ -- UID could not have been registered
+ -- TODO: Print some sensible error message, for now just redirect to index
+ Ada.Text_IO.Put_Line("UID has not been registered: " & Raw_UID);
+ return AWS.Response.URL(Location => "/");
+ end if;
+
+ for Idx in 1 .. POST_Data.Count loop
+ declare
+ C: Answer_Info.Cursor;
+ E: constant AWS.Containers.Tables.Element := POST_Data.Get(Idx);
+ Success: Boolean;
+ begin
+ Answer.Insert(E.Name, E.Value, C, Success);
+ -- TODO: Handle error
+ end;
+ end loop;
+
+ Success := Problem_Manager.Display_Checked_Answer(UID, Answer, HTML);
+ return AWS.Response.Build(Content_Type => AWS.MIME.Text_HTML,
+ Message_Body => HTML_To_Fixed_String(HTML),
+ Status_Code => AWS.Messages.S200);
+ end;
+ when others =>
+ Ada.Text_IO.Put_Line("Invalid request");
+ return AWS.Response.URL(Location => "/");
+ end case;
+ end Handle;
+
+ function Callback return AWS.Dispatchers.Callback.Handler is
+ begin
+ return AWS.Dispatchers.Callback.Create(Handle'Access);
+ end Callback;
+
+end Handler_Check_Answer;
--- /dev/null
+with AWS.Dispatchers.Callback;
+
+package Handler_Check_Answer is
+ function Callback return AWS.Dispatchers.Callback.Handler;
+end Handler_Check_Answer;
--- /dev/null
+with AWS.Messages;
+with AWS.MIME;
+with AWS.Response;
+with AWS.Status;
+with Face_Generator;
+with Global_Types;
+
+use Global_Types;
+package body Handler_Default is
+
+ function Handle(Request: AWS.Status.Data) return AWS.Response.Data is
+ HTML: HTML_Code;
+ Success: Boolean;
+ begin
+ Success := Face_Generator.Generate_Index_Face(HTML);
+ if Success = False then
+ return AWS.Response.Build(Content_Type => AWS.MIME.Text_HTML,
+ Message_Body => "Internal server error occured, we're sorry about that...",
+ Status_Code => AWS.Messages.S503);
+ else
+ return AWS.Response.Build(Content_Type => AWS.MIME.Text_HTML,
+ Message_Body => HTML_To_Fixed_String(HTML),
+ Status_Code => AWS.Messages.S200);
+ end if;
+ end Handle;
+
+ function Callback return AWS.Dispatchers.Callback.Handler is
+ begin
+ return AWS.Dispatchers.Callback.Create(Handle'Access);
+ end Callback;
+
+end Handler_Default;
--- /dev/null
+with AWS.Dispatchers.Callback;
+
+package Handler_Default is
+ function Callback return AWS.Dispatchers.Callback.Handler;
+end Handler_Default;
\ No newline at end of file
--- /dev/null
+with Ada.Strings.Fixed;
+with AWS.Messages;
+with AWS.MIME;
+with AWS.Response;
+with AWS.Status;
+with Ada.Text_IO;
+
+package body Handler_Images is
+
+ function File_Exists(Path: in String) return Boolean is
+ File: Ada.Text_IO.File_Type;
+ begin
+ begin
+ Ada.Text_IO.Open(File => File, Mode => Ada.Text_IO.In_File, Name => Path);
+ Ada.Text_IO.Close(File);
+ return True;
+ exception
+ when Ada.Text_IO.Name_Error =>
+ return False;
+ end;
+ end File_Exists;
+
+ function Handle(Request: AWS.Status.Data) return AWS.Response.Data is
+ use Ada.Strings.Fixed;
+
+ URI: constant String := AWS.Status.URI(Request);
+ Idx: Positive;
+ begin
+ Idx := Index(Source => URI, Pattern => "/", From => URI'Last, Going => Ada.Strings.Backward);
+ declare
+ Image_Path: constant String := "images/" & URI(Idx + 1 .. URI'Last);
+ begin
+ if File_Exists(Image_Path) = False then
+ return AWS.Response.Build(Content_Type => AWS.MIME.Text_HTML,
+ Message_Body => "",
+ Status_Code => AWS.Messages.S404);
+ else
+ return AWS.Response.File(Content_Type => AWS.MIME.Image_Png,
+ Filename => Image_Path,
+ Status_Code => AWS.Messages.S200);
+ end if;
+ end;
+ end Handle;
+
+ function Callback return AWS.Dispatchers.Callback.Handler is
+ begin
+ return AWS.Dispatchers.Callback.Create(Handle'Access);
+ end Callback;
+
+end Handler_Images;
--- /dev/null
+with AWS.Dispatchers.Callback;
+
+package Handler_Images is
+ function Callback return AWS.Dispatchers.Callback.Handler;
+end Handler_Images;
--- /dev/null
+with AWS.Containers.Tables;
+with AWS.Messages;
+with AWS.MIME;
+with AWS.Parameters;
+with AWS.Response;
+with AWS.Session;
+with AWS.Status;
+
+with Global_Types;
+with Problem_Generator_Syswides;
+with Problem_Manager;
+
+use Global_Types;
+
+package body Handler_Next_Problem is
+
+ function Handle(Request: AWS.Status.Data) return AWS.Response.Data is
+ SID: constant AWS.Session.ID := AWS.Status.Session(Request);
+
+ HTML: HTML_Code;
+ Raw_UID: constant String := AWS.Session.Get(SID, "UID");
+ Req_Method : constant AWS.Status.Request_Method := AWS.Status.Method(Request);
+ UID: Unique_ID;
+ begin
+ case Req_Method is
+ when AWS.Status.POST =>
+ declare
+ use Problem_Generator_Syswides;
+ use Problem_Generator_Syswides.Parameters_Info;
+
+ Problem_Parameters: Parameters_Info.Map;
+ POST_Data: constant AWS.Parameters.List := AWS.Status.Parameters(Request);
+ Success: Boolean;
+ begin
+ -- Get UID
+ Success := Problem_Manager.Get_UID(Raw_UID, UID);
+ if Success = False then
+ -- UID could not have been registered
+ -- TODO: Print some sensible error message, for now just redirect to index
+ return AWS.Response.URL(Location => "/");
+ end if;
+
+ -- UID OK, read problem parameters
+ for Idx in 1 .. POST_Data.Count loop
+ declare
+ C: Parameters_Info.Cursor;
+ E: constant AWS.Containers.Tables.Element := POST_Data.Get(Idx);
+ Success: Boolean;
+ begin
+ Problem_Parameters.Insert(E.Name, E.Value, C, Success);
+ -- TODO: Handle error
+ end;
+ end loop;
+
+ -- Display new problem
+ Success := Problem_Manager.Display_Next_Assignment(UID, Problem_Parameters, HTML);
+ if Success = False then
+ -- TODO: Handle error in a less reckless manner
+ return AWS.Response.URL(Location => "/");
+ end if;
+
+ return AWS.Response.Build(Content_Type => AWS.MIME.Text_HTML,
+ Message_Body => HTML_To_Fixed_String(HTML),
+ Status_Code => AWS.Messages.S200);
+ end;
+ when others =>
+ return AWS.Response.URL(Location => "/");
+ end case;
+ end Handle;
+
+ function Callback return AWS.Dispatchers.Callback.Handler is
+ begin
+ return AWS.Dispatchers.Callback.Create(Handle'Access);
+ end Callback;
+
+end Handler_Next_Problem;
--- /dev/null
+with AWS.Dispatchers.Callback;
+
+package Handler_Next_Problem is
+ function Callback return AWS.Dispatchers.Callback.Handler;
+end Handler_Next_Problem;
\ No newline at end of file
--- /dev/null
+with AWS.Messages;
+with AWS.MIME;
+with AWS.Response;
+with AWS.Session;
+with AWS.Status;
+with Global_Types;
+with Problem_Manager;
+
+--with Ada.Text_IO;
+
+use Global_Types;
+package body Handler_Start is
+
+ function Handle(Request: AWS.Status.Data) return AWS.Response.Data is
+ SID: constant AWS.Session.ID := AWS.Status.Session(Request);
+ Req_Type: constant AWS.Status.Request_Method := AWS.Status.Method(Request);
+
+ Raw_UID: constant String := AWS.Session.Get(SID, "UID");
+ HTML: HTML_Code;
+ begin
+ --Ada.Text_IO.Put_Line("Handling /start");
+
+ case Req_Type is
+ when AWS.Status.GET =>
+ declare
+ Raw_Problem_Category: constant String := AWS.Status.Parameter(Request, "problem_category");
+ P_Cat: Problem_Manager.Problem_Category;
+ UID: Unique_ID;
+ Success: Boolean;
+ begin
+ if Raw_Problem_Category = Problem_Manager.Problem_Category'Image(Problem_Manager.Acidobazic) then
+ P_Cat := Problem_Manager.Acidobazic;
+ else
+ return AWS.Response.URL(Location => "/");
+ end if;
+
+ -- Register new UID if necessary and create a first problem
+ Success := Problem_Manager.Get_UID(Raw_UID, UID);
+ if Success = False then
+ -- UID stored within this session is not valid, register a new one
+ Success := Problem_Manager.Register_UID(UID);
+ if Success = False then
+ -- UID could not have been registered
+ -- TODO: Print some sensible error message, for now just redirect to index
+ return AWS.Response.URL(Location => "/");
+ end if;
+ -- Save the new UID
+ AWS.Session.Set(SID, "UID", Unique_ID'Image(UID));
+ AWS.Session.Set_Callback(Problem_Manager.Session_Expired'Access);
+ end if;
+
+ -- We're all set, create a new problem
+ Success := Problem_Manager.Prepare_Problem(UID, P_Cat);
+ if Success = False then
+ -- Something went wrong when generating the problem
+ -- TODO: Print some sensible error message, for now just redirect to index
+ return AWS.Response.URL(Location => "/");
+ end if;
+
+ Success := Problem_Manager.Display_New_Assignment(UID, HTML);
+ if Success = False then
+ HTML := To_HTML_Code("Cannot display assignment");
+ end if;
+ return AWS.Response.Build(Content_Type => AWS.MIME.Text_HTML,
+ Message_Body => HTML_To_Fixed_String(HTML),
+ Status_Code => AWS.Messages.S200);
+ end;
+ when others =>
+ return AWS.Response.URL(Location => "/");
+ end case;
+ end Handle;
+
+ function Callback return AWS.Dispatchers.Callback.Handler is
+ begin
+ return AWS.Dispatchers.Callback.Create(Handle'Access);
+ end Callback;
+
+end Handler_Start;
--- /dev/null
+with AWS.Dispatchers.Callback;
+
+package Handler_Start is
+ function Callback return AWS.Dispatchers.Callback.Handler;
+end Handler_Start;
\ No newline at end of file
--- /dev/null
+with AWS.Messages;
+with AWS.MIME;
+with AWS.Templates;
+with AWS.Response;
+with AWS.Status;
+
+package body Handler_Styles is
+
+ function Main_Handle(Request: AWS.Status.Data) return AWS.Response.Data is
+ begin
+ return AWS.Response.Build(Content_Type => AWS.MIME.Text_CSS,
+ Message_Body => AWS.Templates.Parse(Filename => "styles/main.css", Cached => True),
+ Status_Code => AWS.Messages.S200);
+ end Main_Handle;
+
+ function Main_Callback return AWS.Dispatchers.Callback.Handler is
+ begin
+ return AWS.Dispatchers.Callback.Create(Main_Handle'Access);
+ end Main_Callback;
+
+end Handler_Styles;
--- /dev/null
+with AWS.Dispatchers.Callback;
+
+package Handler_Styles is
+ function Main_Callback return AWS.Dispatchers.Callback.Handler;
+end Handler_Styles;
+
--- /dev/null
+with Handler_Check_Answer;
+with Handler_Default;
+with Handler_Images;
+with Handler_Next_Problem;
+with Handler_Start;
+with Handler_Styles;
+
+package body Handlers is
+ function Get_Dispatchers return AWS.Services.Dispatchers.URI.Handler is
+ Handler: AWS.Services.Dispatchers.URI.Handler;
+ begin
+ Handler.Register_Default_Callback(Action => Handler_Default.Callback);
+ Handler.Register(URI => "/check_answer",
+ Action => Handler_Check_Answer.Callback);
+ Handler.Register(URI => "/images",
+ Action => Handler_Images.Callback,
+ Prefix => True);
+ Handler.Register(URI => "/next_problem",
+ Action => Handler_Next_Problem.Callback);
+ Handler.Register(URI => "/start",
+ Action => Handler_Start.Callback);
+ Handler.Register(URI => "/main_stylesheet",
+ Action => Handler_Styles.Main_Callback);
+
+ return Handler;
+ end Get_Dispatchers;
+end Handlers;
--- /dev/null
+with AWS.Services.Dispatchers.URI;
+
+package Handlers is
+ function Get_Dispatchers return AWS.Services.Dispatchers.URI.Handler;
+end Handlers;
\ No newline at end of file
--- /dev/null
+with Ada.Text_IO;
+
+with AWS.Config;
+with AWS.Config.Set;
+with AWS.Server;
+
+with Handlers;
+
+procedure Nine_Q is
+ Server_Config: AWS.Config.Object;
+ Web_Server: AWS.Server.HTTP;
+begin
+ Server_Config := AWS.Config.Get_Current;
+ AWS.Config.Set.Session(Server_Config, True);
+ AWS.Config.Set.Server_Host(Server_Config, "localhost");
+ AWS.Config.Set.Server_Port(Server_Config, 18400);
+
+ AWS.Config.Set.Session_Lifetime(1800.0);
+ AWS.Config.Set.Session_Cleanup_Interval(900.0);
+
+ Ada.Text_IO.Put_Line("Starting server...");
+ AWS.Server.Start(Web_Server => Web_Server,
+ Dispatcher => Handlers.Get_Dispatchers,
+ Config => Server_Config);
+
+ AWS.Server.Wait(AWS.Server.Q_Key_Pressed);
+ AWS.Server.Shutdown(Web_Server);
+
+end Nine_Q;
--- /dev/null
+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 Acidobazic_Suite is
+ -- BEGIN: Inherited functions
+ function Create return access Acidobazic_Problem is
+ Parameters: Acidobazic_Parameters;
+ Problem: access Acidobazic_Problem;
+ begin
+ Problem := new Acidobazic_Problem;
+
+ Parameters := (No_Both_Simplifications => False);
+ Problem.Parameters := Parameters;
+
+ return Problem;
+ end Create;
+
+ function Check_Answer(Problem: in out Acidobazic_Problem; Answer: in Answer_Info.Map;
+ Message: out UB_Text) return Answer_RetCode is
+ package FH is new Formatting_Helpers(pH_Float);
+ use Answer_Info;
+
+ pH: pH_Float;
+ pH_Answered: pH_Float;
+ begin
+ Problem.Lock_State;
+
+ pH := Calculate_Solution(Problem);
+ -- Verify answer data
+ if Answer.Find(ANSWER_PH_KEY) = Answer_Info.No_Element then
+ return Malformed_Answer;
+ end if;
+ if Answer.Find(ANSWER_SIMPLIFICATION_KEY) = Answer_Info.No_Element then
+ Problem.Unlock_State;
+ return Malformed_Answer;
+ end if;
+
+ declare
+ pH_Answered_S: String := Answer.Element(ANSWER_PH_KEY);
+ Idx: Natural;
+ begin
+ -- Replace "," with "." as decimal seaprator
+ Idx := Ada.Strings.Fixed.Index(Source => pH_Answered_S, Pattern => ",", From => 1);
+ if Idx > 0 then
+ Ada.Strings.Fixed.Replace_Slice(Source => pH_Answered_S, Low => Idx, High => Idx, By => ".");
+ end if;
+ pH_Answered := pH_Float'Value(pH_Answered_S);
+ exception
+ when Constraint_Error =>
+ Message := To_UB_Text("Nesprávně zadaná hodnota pH");
+ Problem.Unlock_State;
+ return Malformed_Answer;
+ end;
+
+ -- Check correctness of simplification
+ declare
+ Simplification_Str: constant String := Answer.Element(ANSWER_SIMPLIFICATION_KEY);
+ begin
+ 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;
+
+ -- Check correctness of the result
+ pH := FH.Round_To_Valid_Nums(pH, Decimals);
+ pH_Answered := FH.Round_To_Valid_Nums(pH_Answered, Decimals);
+
+ 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
+ package FH is new Formatting_Helpers(pH_Float);
+
+ Int_S: UB_Text;
+ Dec_S: UB_Text;
+ begin
+ FH.Split_Integer_Decimal_Strs(pH, Decimals, Int_S, Dec_S);
+ Message := To_UB_Text("Nesprávná odpověď. (pH vypočtené programem = ");
+ Append_UB_Text(Source => Message, New_Item => Int_S);
+ 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
+ C: Assignment_Info.Cursor;
+ Success: Boolean;
+ pKx: pH_Float;
+ begin
+ Problem.Lock_State;
+
+ 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
+ when Acid =>
+ Assignment.Insert(SUBSTANCE_KEY, "kyseliny", C, Success);
+ Assignment.Insert(PKX_KEY, PKX_PKA_TEXT, C, Success);
+ when Base =>
+ Assignment.Insert(SUBSTANCE_KEY, "báze", C, Success);
+ Assignment.Insert(PKX_KEY, PKX_PKB_TEXT, C, Success);
+ end case;
+
+ pKx := X_To_pX(Problem.Kx);
+ -- Print pKx in nn.nnn format
+ declare
+ package FH is new Formatting_Helpers(pH_Float);
+
+ Int_S: UB_Text;
+ Dec_S: UB_Text;
+ begin
+ FH.Split_Integer_Decimal_Strs(pKx, Decimals, Int_S, Dec_S);
+ 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;
+
+ -- Print concentration in N.nnn 10^n format
+ declare
+ package FH is new Formatting_Helpers(pH_Float);
+
+ Int_S: UB_Text;
+ Dec_S: UB_Text;
+ Exp_S: UB_Text;
+ begin
+ FH.Split_Integer_Decimal_Exponent_Strs(Problem.cX, Decimals, Int_S, Dec_S, Exp_S);
+ Assignment.Insert(CONCENTRATION_INT_KEY, UB_Text_To_Fixed_String(Int_S), C, Success);
+ 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
+ C: Parameters_Info.Cursor;
+ Success: Boolean;
+ begin
+ Problem.Lock_State;
+ 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);
+
+ 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;
+ -- Dissociation constant type (pKa or pKb)
+ Random_Dissoc_Type_Gen.Reset(Gen => DCT_G);
+ Problem.DCT := Random_Dissoc_Type_Gen.Random(Gen => DCT_G);
+
+ -- Substance type (acid or base)
+ Random_Substance_Type_Gen.Reset(Gen => ST_G);
+ Problem.Subst_Type := Random_Substance_Type_Gen.Random(Gen => ST_G);
+ -- What simplification to use
+ if (Problem.Parameters.No_Both_Simplifications = False) then
+ declare
+ package Random_Simplification_Gen is new Ada.Numerics.Discrete_Random(Result_Subtype => Simplification);
+ SIM_G: Random_Simplification_Gen.Generator;
+ begin
+ Random_Simplification_Gen.Reset(Gen => SIM_G);
+ Problem.Simpl := Random_Simplification_Gen.Random(Gen => SIM_G);
+
+ -- Random dissociation constant
+ Problem.Kx := Random_Kx;
+ end;
+ else
+ declare
+ subtype Enforced_Simplification is Simplification range Autoprotolysis .. Dissociation;
+ package Random_Simplification_Gen is new Ada.Numerics.Discrete_Random(Result_Subtype => Enforced_Simplification);
+ SIM_G: Random_Simplification_Gen.Generator;
+ begin
+ Random_Simplification_Gen.Reset(Gen => SIM_G);
+ Problem.Simpl := Random_Simplification_Gen.Random(Gen => SIM_G);
+
+ Problem.Kx := Random_Kx_Enforced(Problem.Simpl);
+ -- Generate dissociation constant that fits the enforced simplification mode
+ end;
+ end if;
+
+ 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;
+ begin
+ Problem.Lock_State;
+ 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
+
+ -- BEGIN: Private functions
+ function Autoprotolysis_Limit(Kx: in pH_Float) return pH_Float is
+ Ratio: constant pH_Float := 2.0E-13;
+ begin
+ return Ratio / Kx;
+ end Autoprotolysis_Limit;
+
+ function Dissociation_Limit(Kx: in pH_Float) return pH_Float is
+ Ratio_Squared: constant pH_Float := 0.0025;
+ begin
+ return Kx / Ratio_Squared;
+ end Dissociation_Limit;
+
+ procedure Calculate_Concentration_Limits(Min: out pH_Float; Max: out pH_Float; Kx: in pH_Float; S: in Simplification) is
+ begin
+ case S is
+ -- We are ignoring autoprotolysis but taking dissociation into account
+ when Autoprotolysis =>
+ Min := Autoprotolysis_Limit(Kx);
+ Max := Dissociation_Limit(Kx);
+ -- We are ignoring dissociation but taking autoprotolysis into account
+ when Dissociation =>
+ Min := Dissociation_Limit(Kx);
+ Max := Autoprotolysis_Limit(Kx);
+ when Both =>
+ declare
+ ATPR: constant pH_Float := Autoprotolysis_Limit(Kx);
+ Dissoc: constant pH_Float := Dissociation_Limit(Kx);
+ begin
+ Min := pH_Float'Max(ATPR, Dissoc);
+ Max := Min + MAX_CONCENTRATION_DIFF;
+ end;
+ end case;
+
+ -- Apply hard limit on minimum concentration, approximation we use does not work reliably below this limit
+ if Min < CONCENTRATION_HARD_MIN_LIMIT then
+ Min := CONCENTRATION_HARD_MIN_LIMIT;
+ else
+ Min := Correct_Up(Min);
+ end if;
+ Max := Correct_Down(Max);
+ --Ada.Text_IO.Put_Line("Min: " & pH_Float'Image(Min) & " Max: " & pH_Float'Image(Max) & " Simpl: " & Simplification'Image(S));
+ end Calculate_Concentration_Limits;
+
+ function Calculate_Solution(Problem: in Acidobazic_Problem) return pH_Float is
+ package MEF is new Ada.Numerics.Generic_Elementary_Functions(pH_Float);
+ use MEF;
+
+ package pH_Float_IO is new Ada.Text_IO.Float_IO(pH_Float);
+ use pH_Float_IO;
+ use Ada.Text_IO;
+ begin
+ -- DEBUG Dump
+ --Put("* Kx = "); Put(Problem.Kx);
+ --Put(" | cX = "); Put(Problem.cX);
+ --case Problem.Subst_Type is
+ -- when Acid =>
+ -- Put_Line(" | ACID");
+ -- when Base =>
+ -- Put_Line(" | BASE");
+ -- end case;
+ -- END DEBUG Dump
+
+ case Problem.Simpl is
+ -- We are ignoring everything
+ when Both =>
+ declare
+ pH: constant pH_Float := X_To_pX(Sqrt(Problem.Kx * Problem.cX));
+ begin
+ --Ada.Text_IO.Put_Line("Both simplifications");
+ case Problem.Subst_Type is
+ when Acid =>
+ return pH;
+ when Base =>
+ return 14.0 - pH;
+ end case;
+ end;
+ -- We are ignoring autoprotolysis and taking dissociation into account
+ when Autoprotolysis =>
+ declare
+ D: pH_Float;
+ X_1: pH_Float;
+ X_2: pH_Float;
+ pH: pH_Float;
+ begin
+ --Ada.Text_IO.Put_Line("Taking only dissociation into account");
+ -- Solve the quadratic equation
+ D := (Problem.Kx ** 2.0) + (4.0 * Problem.Kx * Problem.cX);
+ if D < 0.0 then
+ raise Constraint_Error;
+ end if;
+ D := MEF.Sqrt(D);
+ X_1 := (-Problem.Kx + D) / 2.0;
+ X_2 := (-Problem.Kx - D) / 2.0;
+ pH := X_To_pX(pH_Float'Max(X_1, X_2));
+ if Problem.Subst_Type = Base then
+ return 14.0 - pH;
+ else
+ return pH;
+ end if;
+ end;
+ -- We are ignoring dissociation and taking autoprotolysis into account
+ when Dissociation =>
+ declare
+ pH: constant pH_Float := X_To_pX(Sqrt(Problem.Kx * Problem.cX + K_W));
+ begin
+ --Ada.Text_IO.Put_Line("Taking only autoprotolysis into account");
+ case Problem.Subst_Type is
+ when Acid =>
+ return pH;
+ when Base =>
+ return 14.0 - pH;
+ end case;
+ end;
+ end case;
+
+ end Calculate_Solution;
+
+ function Correct_Down(Num: in pH_Float) return pH_Float is
+ package MEF is new Ada.Numerics.Generic_Elementary_Functions(pH_Float);
+ use MEF;
+
+ F: constant pH_Float := Correction_Exponent(Num);
+ begin
+ return Num - 10.0 ** F;
+ end Correct_Down;
+
+ function Correct_Up(Num: in pH_Float) return pH_Float is
+ package MEF is new Ada.Numerics.Generic_Elementary_Functions(pH_Float);
+ use MEF;
+
+ F: constant pH_Float := Correction_Exponent(Num);
+ begin
+ return Num + 10.0 ** F;
+ end Correct_Up;
+
+ function Correction_Exponent(Num: in pH_Float) return pH_Float is
+ package MEF is new Ada.Numerics.Generic_Elementary_Functions(pH_Float);
+ use MEF;
+
+ F_Log_Num: constant pH_Float := pH_Float'Floor(Log(Base => 10.0, X => Num));
+ F_Log_Dec: constant pH_Float := pH_Float'Floor(Log(Base => 10.0, X => Decimals));
+ begin
+ return F_Log_Num - F_Log_Dec;
+ end Correction_Exponent;
+
+ function pX_To_X(pX: in pH_Float) return pH_Float is
+ package MEF is new Ada.Numerics.Generic_Elementary_Functions(pH_Float);
+ use MEF;
+ begin
+ return 10.0 ** (-pX);
+ end pX_To_X;
+
+ function Random_cX(Min: in pH_Float; Max: in pH_Float) return pH_Float is
+ package FH is new Formatting_Helpers(pH_Float);
+ package MEF is new Ada.Numerics.Generic_Elementary_Functions(pH_Float);
+ use MEF;
+ use Ada.Numerics.Float_Random;
+
+ Seed: Generator;
+ Rand: Float;
+ cX_Rand: pH_Float;
+ cX: pH_Float;
+ Log_Min: constant pH_Float := Log(Base => 10.0, X => Min);
+ Log_Max: constant pH_Float := Log(Base => 10.0, X => Max);
+ Concentration_Scale: constant pH_Float := Log_Max - Log_Min;
+ begin
+ Reset(Gen => Seed);
+ Rand := Random(Gen => Seed);
+ cX_Rand := pH_Float(Rand);
+
+ cX := (Concentration_Scale * cX_Rand) + Log_Min;
+ cX := 10.0 ** cX;
+ return FH.Round_To_Valid_Nums(cX, Decimals);
+ end Random_cX;
+
+ function Random_Kx return pH_Float is
+ package FH is new Formatting_Helpers(pH_Float);
+ use Ada.Numerics.Float_Random;
+
+ type pH_Region is (Acidic, Basic);
+ package pH_Region_Random is new Ada.Numerics.Discrete_Random(Result_Subtype => pH_Region);
+
+ pH_Seed: pH_Region_Random.Generator;
+ Seed: Generator;
+ Region: pH_Region;
+ Rand: Float;
+ pH_Rand: pH_Float;
+ pKx: pH_Float;
+
+ Acid_Scale: constant pH_Float := Acidic_Max_pH - Acidic_Min_pH;
+ Base_Scale: constant pH_Float := Basic_Max_pH - Basic_Min_pH;
+
+ begin
+ pH_Region_Random.Reset(Gen => pH_Seed);
+ Region := pH_Region_Random.Random(Gen => pH_Seed);
+
+ Reset(Seed);
+ Rand := Random(Gen => Seed);
+ pH_Rand := pH_Float(Rand);
+
+ -- There is no need to normalize the scale because the generator already returns
+ -- a normalized range
+ case Region is
+ when Acidic =>
+ pKx := (Acid_Scale * pH_Rand) + Acidic_Min_pH;
+ when Basic =>
+ pKx := (Base_Scale * pH_Rand) + Basic_Min_pH;
+ end case;
+
+ pKx := FH.Round_To_Valid_Nums(pKx, Decimals);
+ return pX_To_X(pKx);
+ end Random_Kx;
+
+ function Random_Kx_Enforced(S: in Simplification) return pH_Float is
+ package FH is new Formatting_Helpers(pH_Float);
+ use Ada.Numerics.Float_Random;
+
+ Seed: Generator;
+ Rand: Float;
+ pH_Rand: pH_Float;
+ pKx: pH_Float;
+ begin
+ Reset(Seed);
+ Rand := Random(Seed);
+ pH_Rand := pH_Float(Rand);
+
+ case S is
+ -- Ignore autoprotolysis, enforce dissociation
+ when Autoprotolysis =>
+ declare
+ Scale: constant pH_Float := Acidic_Max_pH - Acidic_Min_pH;
+ begin
+ -- pKa and pKb between <2.0; 7.5> can overpower autoprotolysis and might require dissociation
+ pKx := (Scale * pH_Rand) + Acidic_Min_pH;
+ end;
+ -- Ignore dissociation, enforce autoprotolysis
+ when Dissociation =>
+ -- pKa and pKb between <7.6; 12> do not require dissociation but might require autoprotolysis
+ declare
+ Scale: constant pH_Float := Basic_Max_pH - Basic_Min_pH;
+ begin
+ pKx := (Scale * pH_Rand) + Basic_Min_pH;
+ end;
+ when others =>
+ raise Constraint_Error;
+ end case;
+
+ pKx := FH.Round_To_Valid_Nums(pKx, Decimals);
+ return pX_To_X(pKx);
+ end Random_Kx_Enforced;
+
+ function X_To_pX(X: in pH_Float) return pH_Float is
+ package MEF is new Ada.Numerics.Generic_Elementary_Functions(pH_Float);
+ begin
+ return MEF.Log(Base => 10.0, X => X) * (-1.0);
+ end X_To_pX;
+
+end Acidobazic_Suite;
--- /dev/null
+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
+ begin
+ Locked := False;
+ end Unlock;
+ end Problem_Mutex;
+
+ 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;
+ 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;
+
+end Problem_Generator;
--- /dev/null
+with Global_Types;
+with Problem_Generator_Syswides;
+
+use Global_Types;
+use Problem_Generator_Syswides;
+package Problem_Generator is
+
+ protected type Problem_Mutex is
+ entry Lock;
+ procedure Unlock;
+
+ private
+ Locked: Boolean := False;
+ end Problem_Mutex;
+
+ type Chem_Problem is abstract tagged limited private;
+
+ function Create return access Chem_Problem is abstract;
+ function Check_Answer(Problem: in out Chem_Problem; Answer: in Answer_Info.Map; Message: out UB_Text) return Answer_RetCode is abstract;
+ 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;
+ end record;
+
+ package Acidobazic_Suite is
+ use Problem_Generator_Syswides.Acidobazic_Suite;
+
+ type Acidobazic_Problem is new Problem_Generator.Chem_Problem with private;
+ -- Constructor
+ function Create return access Acidobazic_Problem;
+ -- Inherited
+ function Check_Answer(Problem: in out Acidobazic_Problem; Answer: in Answer_Info.Map; Message: out UB_Text) return Answer_RetCode;
+ procedure New_Problem(Problem: in out Acidobazic_Problem);
+ function Get_Assignment(Problem: in out Acidobazic_Problem; Assignment: in out Assignment_Info.Map) return Boolean;
+ function Get_Parameters(Problem: in out Acidobazic_Problem; Parameters: out Parameters_Info.Map) return Boolean;
+ function Set_Parameters(Problem: in out Acidobazic_Problem; Parameters: in Parameters_Info.Map) return Boolean;
+
+ private
+ type pH_Float is digits 15;
+ -- Present dissociation constant as pKa or pKb?
+ type Dissociation_Constant_Type is (pKa, pKb);
+ -- Is the substance acidic or basic?
+ type Substance_Type is (Acid, Base);
+
+ type Acidobazic_Parameters is
+ record
+ No_Both_Simplifications: Boolean;
+ end record;
+
+ function Autoprotolysis_Limit(Kx: in pH_Float) return pH_Float;
+ procedure Calculate_Concentration_Limits(Min: out pH_Float; Max: out pH_Float; Kx: in pH_Float; S: in Simplification);
+ function Calculate_Solution(Problem: in Acidobazic_Problem) return pH_Float;
+ function Correct_Down(Num: in pH_Float) return pH_Float;
+ function Correct_Up(Num: in pH_Float) return pH_Float;
+ function Correction_Exponent(Num: in pH_Float) return pH_Float;
+ function Dissociation_Limit(Kx: in pH_Float) return pH_Float;
+ function pX_To_X(pX: in pH_Float) return pH_Float;
+ function Random_cX(Min: in pH_Float; Max: in pH_Float) return pH_Float;
+ function Random_Kx return pH_Float;
+ function Random_Kx_Enforced(S: in Simplification) return pH_Float;
+ function X_To_pX(X: in pH_Float) return pH_Float;
+
+ type Acidobazic_Problem is new Problem_Generator.Chem_Problem with
+ record
+ Answer: pH_Float;
+ cX: pH_Float;
+ DCT: Dissociation_Constant_Type;
+ Kx: pH_Float;
+ Parameters: Acidobazic_Parameters;
+ Simpl: Simplification;
+ Subst_Type: Substance_Type;
+ end record;
+
+ Acidic_Min_pH: constant pH_Float := 1.75;
+ Acidic_Max_pH: constant pH_Float := 5.75;
+ Basic_Min_pH: constant pH_Float := 9.5;
+ Basic_Max_pH: constant pH_Float := 12.5;
+ K_W: constant pH_Float := 1.0E-14;
+ -- Maximum concentration can be only 1.5 mol/dm3 higher than minimum concentration
+ MAX_CONCENTRATION_DIFF: constant pH_Float := 0.75;
+ CONCENTRATION_HARD_MIN_LIMIT: constant pH_Float := 1.0E-6;
+ Decimals: constant pH_Float := 1.0E3;
+ Precision: constant pH_Float := 1.0E-3;
+
+ end Acidobazic_Suite;
+
+end Problem_Generator;
--- /dev/null
+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);
+ 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);
+
+ ANSWER_KIND_KEY: constant String := "ANSWER_KIND";
+ ANSWER_KIND_GOOD: constant String := "answer_kind_good";
+ ANSWER_KIND_BAD: constant String := "answer_kind_bad";
+ ANSWER_MESSAGE_KEY: constant String := "ANSWER_MESSAGE";
+ ANSWER_SECTION_KEY: constant String := "ANSWER_SECTION";
+ PROBLEM_TYPE_KEY: constant String := "PROBLEM_TYPE";
+ PROBLEM_TYPE_ACIDOBAZIC: constant String := Problem_Type'Image(Acidobazic);
+
+ package Acidobazic_Suite is
+ -- What effect to ignore in calculations?
+ type Simplification is (Autoprotolysis, Dissociation, Both);
+
+ PROBLEM_NAME_READABLE: constant String := "pH jednosytné kyseliny/báze";
+ CONCENTRATION_INT_KEY: constant String := "CONCENTRATION_INT";
+ CONCENTRATION_DEC_KEY: constant String := "CONCENTRATION_DEC";
+ CONCENTRATION_EXP_KEY: constant String := "CONCENTRATION_EXP";
+ PKX_KEY: constant String := "PKX";
+ PKX_PKA_TEXT: constant String := "pKa";
+ PKX_PKB_TEXT: constant String := "pKb";
+ PKX_VALUE_DEC_KEY: constant String := "PKX_VALUE_DEC";
+ PKX_VALUE_INT_KEY: constant String := "PKX_VALUE_INT";
+ SIMPLIFICATION_KEY: constant String := "SIMPLIFICATION";
+ SUBSTANCE_KEY: constant String := "SUBSTANCE";
+ --
+ ANSWER_PH_KEY: constant String := "ANSWER_PH";
+ ANSWER_SIMPLIFICATION_KEY: constant String := "ANSWER_SIMPLIFICATION";
+ --
+ PARAMETER_NO_BOTH_SIMPLIFICATIONS_KEY: constant String := "PARAMETER_NO_BOTH_SIMPLIFICATIONS";
+ end Acidobazic_Suite;
+
+end Problem_Generator_Syswides;
--- /dev/null
+with Ada.Text_IO;
+with Face_Generator;
+
+package body Problem_Manager is
+
+ function Display_Checked_Answer(UID: in Unique_ID; Answer: in Problem_Generator_Syswides.Answer_Info.Map; HTML: out HTML_Code) return Boolean is
+ Answer_Message: UB_Text;
+ ARC: Problem_Generator_Syswides.Answer_RetCode;
+ Assignment: Problem_Generator_Syswides.Assignment_Info.Map;
+ Parameters: Problem_Generator_Syswides.Parameters_Info.Map;
+ Success: Boolean;
+ USD: User_Session_Data_Access;
+ begin
+ USD := Active_Sessions.Get_Session_Data(UID, Success);
+ if Success = False then
+ return False;
+ end if;
+
+ Success := USD.Problem.Get_Parameters(Parameters);
+ if Success = False then
+ -- TODO: Handle error in a better way
+ return False;
+ end if;
+ Success := USD.Problem.Get_Assignment(Assignment);
+ if Success = False then
+ -- TODO: Handle error in a better way
+ return False;
+ end if;
+
+ ARC := USD.Problem.Check_Answer(Answer, Answer_Message);
+ return Face_Generator.Generate_Face_With_Answer(Assignment => Assignment, Answer_Message => Answer_Message,
+ Answer_Code => ARC, HTML => HTML,
+ Parameters => Parameters);
+ end Display_Checked_Answer;
+
+ function Display_New_Assignment(UID: in Unique_ID; HTML: out HTML_Code) return Boolean is
+ Assignment: Problem_Generator_Syswides.Assignment_Info.Map;
+ Parameters: Problem_Generator_Syswides.Parameters_Info.Map;
+ USD: User_Session_Data_Access;
+ Success: Boolean;
+ begin
+ USD := Active_Sessions.Get_Session_Data(UID, Success);
+ if Success = False then
+ return False;
+ end if;
+
+ -- Get default problem parameters
+ Success := USD.Problem.Get_Parameters(Parameters);
+ if Success = False then
+ return False;
+ end if;
+ -- Get assignment
+ Success := USD.Problem.Get_Assignment(Assignment);
+ if Success = False then
+ return False;
+ end if;
+
+ return Face_Generator.Generate_Face(Assignment => Assignment, HTML => HTML, Parameters => Parameters);
+ end Display_New_Assignment;
+
+ function Display_Next_Assignment(UID: in Unique_ID; Problem_Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
+ HTML: out HTML_Code) return Boolean is
+ Assignment: Problem_Generator_Syswides.Assignment_Info.Map;
+ Success: Boolean;
+ USD: User_Session_Data_Access;
+ begin
+ USD := Active_Sessions.Get_Session_Data(UID, Success);
+ if Success = False then
+ return False;
+ end if;
+
+ -- Set parameters before creating a new problem
+ Success := USD.Problem.Set_Parameters(Problem_Parameters);
+ if Success = False then
+ -- TODO: Handle error
+ return False;
+ end if;
+
+ -- Create new problem and display it
+ USD.Problem.New_Problem;
+ Success := USD.Problem.Get_Assignment(Assignment);
+ if Success = False then
+ -- TODO: Handle error in a better way
+ return False;
+ end if;
+
+ return Face_Generator.Generate_Face(Assignment => Assignment, HTML => HTML, Parameters => Problem_Parameters);
+ end Display_Next_Assignment;
+
+ function Get_UID(Raw_UID: in String; UID: out Unique_ID) return Boolean is
+ begin
+ begin
+ UID := Unique_ID'Value(Raw_UID);
+ return Active_Sessions.Contains(UID);
+ exception
+ when Constraint_Error =>
+ return False;
+ end;
+ end Get_UID;
+
+ function Prepare_Problem(UID: in Unique_ID; P_Cat: in Problem_Category) return Boolean is
+ New_USD: User_Session_Data_Access;
+ Success: Boolean;
+ begin
+ New_USD := new User_Session_Data;
+ New_USD.P_Cat := P_Cat;
+
+ case P_Cat is
+ when Acidobazic =>
+ New_USD.Problem := Problem_Generator.Get_Problem(Problem_Generator_Syswides.Acidobazic);
+ when others =>
+ return False;
+ end case;
+
+ New_USD.Problem.New_Problem;
+
+ Active_Sessions.Set_Session_Data(UID, New_USD, Success);
+ return Success;
+ end Prepare_Problem;
+
+ function Register_UID(UID: out Unique_ID) return Boolean is
+ Success: Boolean;
+ begin
+ -- Raw UID is empty, assuming a new session
+ Active_Sessions.Register_UID(UID, Success);
+ return Success;
+ end Register_UID;
+
+ procedure Session_Expired(SID: in AWS.Session.ID) is
+ Raw_UID: constant String := AWS.Session.Get(SID, "UID");
+ UID: Unique_ID;
+ Success: Boolean;
+ begin
+ Success := Get_UID(Raw_UID, UID);
+ if Success = False then
+ Ada.Text_IO.Put_Line("Session expired: No such session");
+ return;
+ end if;
+
+ Active_Sessions.Remove_Session(UID);
+ Ada.Text_IO.Put_Line("Session expired: Session " & Raw_UID & " data deleted");
+ end Session_Expired;
+
+ -- BEGIN: Private functions
+ protected body Active_Sessions is
+
+ function Contains(UID: in Unique_ID) return Boolean is
+ use Session_Keeping;
+ begin
+ if Sessions.Find(UID) = Session_Keeping.No_Element then
+ return False;
+ else
+ return True;
+ end if;
+ end Contains;
+
+ procedure Check_Free_And_Register(UID: in Unique_ID; Success: out Boolean; Stop: out Boolean) is
+ use Session_Keeping;
+
+ C: Session_Keeping.Cursor;
+ begin
+ Success := False;
+ if Sessions.Find(UID) = Session_Keeping.No_Element then
+ -- We have a free slot
+ Sessions.Insert(UID, null, C, Success);
+ if Success then
+ Last_UID := UID;
+ -- Registration successful
+ Stop := True;
+ return;
+ else
+ -- Registration failed
+ Stop := True;
+ return;
+ end if;
+ end if;
+ -- Slot occupied, keep looking
+ Stop := False;
+ end Check_Free_And_Register;
+
+ function Get_Session_Data(UID: in Unique_ID; Success: out Boolean) return User_Session_Data_Access is
+ use Session_Keeping;
+ begin
+ if Sessions.Find(UID) = Session_Keeping.No_Element then
+ Success := False;
+ return null;
+ else
+ Success := True;
+ return Sessions.Element(UID);
+ end if;
+ end Get_Session_Data;
+
+ procedure Set_Session_Data(UID: in Unique_ID; USD: in User_Session_Data_Access; Success: out Boolean) is
+ use Session_Keeping;
+ begin
+ if Sessions.Find(UID) = Session_Keeping.No_Element then
+ Success := False;
+ else
+ Sessions.Replace(UID, USD);
+ Success := True;
+ end if;
+ end Set_Session_Data;
+
+ procedure Register_UID(UID: out Unique_ID; Success: out Boolean) is
+ Stop: Boolean;
+ begin
+ -- Look for an available UID slot
+ for Idx in Last_UID .. Unique_ID'Last loop
+ Check_Free_And_Register(Idx, Success, Stop);
+ if Stop then
+ UID := Idx;
+ return;
+ end if;
+ end loop;
+
+ -- We found no free slot above, search the area below Last_UID
+ for Idx in Unique_ID'First .. Last_UID loop
+ Check_Free_And_Register(Idx, Success, Stop);
+ if Stop then
+ UID := Idx;
+ return;
+ end if;
+ end loop;
+
+ -- There are no free slots available
+ Success := False;
+ end Register_UID;
+
+ procedure Remove_Session(UID: in Unique_ID) is
+ use Session_Keeping;
+ begin
+ Sessions.Delete(UID);
+ end Remove_Session;
+
+ end Active_Sessions;
+
+end Problem_Manager;
--- /dev/null
+with Global_Types;
+with Problem_Generator;
+with Problem_Generator_Syswides;
+with Ada.Containers.Ordered_Maps;
+with AWS.Session;
+
+use Global_Types;
+package Problem_Manager is
+
+ type Problem_Category is (Invalid, Acidobazic);
+
+ 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;
+ function Display_Next_Assignment(UID: in Unique_ID;
+ Problem_Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
+ HTML: out HTML_Code) return Boolean;
+ function Get_UID(Raw_UID: in String; UID: out Unique_ID) return Boolean;
+ function Prepare_Problem(UID: in Unique_ID; P_Cat: in Problem_Category) return Boolean;
+ function Register_UID(UID: out Unique_ID) return Boolean;
+ procedure Session_Expired(SID: AWS.Session.ID);
+
+private
+ type User_Session_Data is
+ record
+ P_Cat: Problem_Category;
+ Problem: access Problem_Generator.Chem_Problem'Class;
+ end record;
+ type User_Session_Data_Access is access all User_Session_Data;
+
+ package Session_Keeping is new Ada.Containers.Ordered_Maps(Key_Type => Unique_ID, Element_Type => User_Session_Data_Access);
+
+ protected Active_Sessions is
+ function Contains(UID: in Unique_ID) return Boolean;
+ function Get_Session_Data(UID: in Unique_ID; Success: out Boolean) return User_Session_Data_Access;
+ procedure Set_Session_Data(UID: in Unique_ID; USD: in User_Session_Data_Access; Success: out Boolean);
+ procedure Register_UID(UID: out Unique_ID; Success: out Boolean);
+ procedure Remove_Session(UID: in Unique_ID);
+
+ private
+ procedure Check_Free_And_Register(UID: in Unique_ID; Success: out Boolean; Stop: out Boolean);
+
+ Sessions: Session_Keeping.Map;
+ Last_UID: Unique_ID := Unique_ID'First;
+ end Active_Sessions;
+
+end Problem_Manager;