From 4bfa03b9a08dc7a22f60bd3999200f1e9ec5e0d3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Mal=C3=BD?= Date: Tue, 23 Dec 2014 03:33:53 +0100 Subject: [PATCH] - Print difference between calculated and answered pH instead of trying to rank the answer s correct or wrong - Tweaks to the rounding and int/dec/exp splitting - Fix simplification calculations in acidobazic suite. Generate pKx first and then decide which simplification can be taken into account. --- src/formatting_helpers.adb | 24 +-- src/formatting_helpers.ads | 8 +- src/global_types.adb | 9 + src/global_types.ads | 3 + .../problem_generator-acidobazic_suite.adb | 164 +++++++----------- .../problem_generator-solubility_suite.adb | 4 +- src/problem_generators/problem_generator.ads | 12 +- 7 files changed, 103 insertions(+), 121 deletions(-) diff --git a/src/formatting_helpers.adb b/src/formatting_helpers.adb index 182619d..b59049d 100644 --- a/src/formatting_helpers.adb +++ b/src/formatting_helpers.adb @@ -22,40 +22,43 @@ package body Formatting_Helpers is end; end String_To_Float; - function Round_To_Valid_Nums(Num: in FH_Float; Decimals: in FH_Float) return FH_Float is + function Round_To_Valid_Nums(Num: in FH_Float; Decimals: in Natural) return FH_Float is package FHEF is new Ada.Numerics.Generic_Elementary_Functions(FH_Float); use FHEF; + ExpDecimals: FH_Float := 10.0 ** Decimals; Log_Arg_Floor: FH_Float; Temp: FH_Float; begin Log_Arg_Floor := FH_Float'Floor(Log(Base => 10.0, X => Num)); Temp := Num / (10.0 ** Log_Arg_Floor); - Temp := FH_Float'Rounding(Temp * (Decimals / 10.0)); - return (Temp / (Decimals / 10.0)) * (10.0 ** Log_Arg_Floor); + Temp := FH_Float'Rounding(Temp * ExpDecimals); + return (Temp / ExpDecimals) * (10.0 ** Log_Arg_Floor); end Round_To_Valid_Nums; - procedure Split_Integer_Decimal_Unscaled_Strs(Num: in FH_Float; Decimals: in FH_Float; Integer_Part: out UB_Text; Decimal_Part: out UB_Text) is + procedure Split_Integer_Decimal_Unscaled_Strs(Num: in FH_Float; Decimals: in Natural; Integer_Part: out UB_Text; Decimal_Part: out UB_Text) is Integer_Part_F: FH_Float; Decimal_Part_F: FH_Float; Decimal_Part_I: Integer; + ExpDecimals: constant FH_Float := 10.0 ** Decimals; begin Integer_Part_F := FH_Float'Floor(Num); - Decimal_Part_F := (Num - Integer_Part_F) * Decimals; + Decimal_Part_F := (Num - Integer_Part_F) * ExpDecimals; Decimal_Part_I := Integer(Decimal_Part_F); Integer_Part := To_UB_Text(Ada.Strings.Fixed.Trim(Source => Integer'Image(Integer(Integer_Part_F)), Side => Ada.Strings.Left)); Decimal_Part := To_UB_Text(Ada.Strings.Fixed.Trim(Source => Integer'Image(Decimal_Part_I), Side => Ada.Strings.Left)); - Prepend_Zeros_To_Text(Decimal_Part_F, Decimals, Decimal_Part); + Prepend_Zeros_To_Text(Decimal_Part_F, ExpDecimals, Decimal_Part); end Split_Integer_Decimal_Unscaled_Strs; - procedure Split_Integer_Decimal_Exponent_Nums(Num: in FH_Float; Decimals: in FH_Float; Integer_Part: out Integer; Decimal_Part: out FH_Float; Exponent_Part: out Integer) is + procedure Split_Integer_Decimal_Exponent_Nums(Num: in FH_Float; Decimals: in Natural; Integer_Part: out Integer; Decimal_Part: out FH_Float; Exponent_Part: out Integer) is package FHEF is new Ada.Numerics.Generic_Elementary_Functions(FH_Float); use FHEF; Expanded: FH_Float; Integer_Part_F: FH_Float; Log_Arg_Floored: FH_Float; + ExpDecimals: constant FH_Float := 10.0 ** Decimals; begin if Num = 0.0 then Integer_Part := 0; @@ -68,7 +71,7 @@ package body Formatting_Helpers is Expanded := 10.0 ** Log_Arg_Floored; Integer_Part_F := FH_Float'Floor(Num / Expanded); - Decimal_Part := ((Num - (Integer_Part_F * Expanded)) / Expanded) * Decimals; + Decimal_Part := ((Num - (Integer_Part_F * Expanded)) / Expanded) * ExpDecimals; Exponent_Part := Integer(Log_Arg_Floored); Integer_Part := Integer(Integer_Part_F); @@ -76,7 +79,7 @@ package body Formatting_Helpers is --Ada.Text_IO.Put_Line(FH_Float'Image(Num) & " --- " & Integer'Image(Integer_Part) & "," & FH_Float'Image(Decimal_Part) & "e" & Integer'Image(Exponent_Part)); end Split_Integer_Decimal_Exponent_Nums; - procedure Split_Integer_Decimal_Exponent_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 Natural; 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; @@ -85,13 +88,14 @@ package body Formatting_Helpers is Decimal_Part_F: FH_Float; Decimal_Part_I: Integer; Exponent_Part_I: Integer; + ExpDecimals: constant FH_Float := 10.0 ** Decimals; begin Split_Integer_Decimal_Exponent_Nums(Num, Decimals, Integer_Part_I, Decimal_Part_F, Exponent_Part_I); Decimal_Part_I := Integer(Decimal_Part_F); Integer_Part := To_UB_Text(Ada.Strings.Fixed.Trim(Source => Integer'Image(Integer_Part_I), Side => Ada.Strings.Left)); Decimal_Part := To_UB_Text(Ada.Strings.Fixed.Trim(Source => Integer'Image(Decimal_Part_I), Side => Ada.Strings.Left)); - Prepend_Zeros_To_Text(Decimal_Part_F, Decimals, Decimal_Part); + Prepend_Zeros_To_Text(Decimal_Part_F, ExpDecimals, Decimal_Part); Exponent_Part := To_UB_Text(Ada.Strings.Fixed.Trim(Source => Integer'Image(Exponent_Part_I), Side => Ada.Strings.Left)); end Split_Integer_Decimal_Exponent_Strs; diff --git a/src/formatting_helpers.ads b/src/formatting_helpers.ads index ad365cb..0f6bb26 100644 --- a/src/formatting_helpers.ads +++ b/src/formatting_helpers.ads @@ -6,11 +6,11 @@ type FH_Float is digits <>; package Formatting_Helpers is 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_Unscaled_Strs(Num: in FH_Float; Decimals: in FH_Float; Integer_Part: out UB_Text; Decimal_Part: out UB_Text); - procedure Split_Integer_Decimal_Exponent_Nums(Num: in FH_Float; Decimals: in FH_Float; Integer_Part: out Integer; Decimal_Part: out FH_Float; + function Round_To_Valid_Nums(Num: in FH_Float; Decimals: Natural) return FH_Float; + procedure Split_Integer_Decimal_Unscaled_Strs(Num: in FH_Float; Decimals: in Natural; Integer_Part: out UB_Text; Decimal_Part: out UB_Text); + procedure Split_Integer_Decimal_Exponent_Nums(Num: in FH_Float; Decimals: in Natural; Integer_Part: out Integer; Decimal_Part: out FH_Float; Exponent_Part: out Integer); - procedure Split_Integer_Decimal_Exponent_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 Natural; Integer_Part: out UB_Text; Decimal_Part: out UB_Text; Exponent_Part: out UB_Text); private diff --git a/src/global_types.adb b/src/global_types.adb index e4c067c..1397790 100644 --- a/src/global_types.adb +++ b/src/global_types.adb @@ -42,6 +42,15 @@ package body Global_Types is return To_String(Text); end UB_Text_To_Fixed_String; + -- Overloaded operators + function "&" (Left: in UB_Text; Right: in UB_Text) return UB_Text is + use Ada.Strings.Unbounded; + + T: UB_Text := Left; + begin + Append_UB_Text(Source => T, New_Item => Right); + return T; + end "&"; protected body Simple_Mutex is entry Lock when Locked = False is diff --git a/src/global_types.ads b/src/global_types.ads index 67e93e8..6d6d4b2 100644 --- a/src/global_types.ads +++ b/src/global_types.ads @@ -17,6 +17,9 @@ package Global_Types is function To_UB_Text(S: in String) return UB_Text; function UB_Text_To_Fixed_String(Text: in UB_Text) return String; + -- Overloaded operators + function "&" (Left: in UB_Text; Right: in UB_Text) return UB_Text; + protected type Simple_Mutex is entry Lock; entry Unlock; diff --git a/src/problem_generators/problem_generator-acidobazic_suite.adb b/src/problem_generators/problem_generator-acidobazic_suite.adb index 7374365..4aa8a87 100644 --- a/src/problem_generators/problem_generator-acidobazic_suite.adb +++ b/src/problem_generators/problem_generator-acidobazic_suite.adb @@ -59,28 +59,26 @@ package body Acidobazic_Suite is end; -- Check correctness of the result - pH := FH.Round_To_Valid_Nums(pH, Decimals); - pH_Answered := FH.Round_To_Valid_Nums(pH_Answered, Decimals); + --pH := FH.Round_To_Valid_Nums(pH, Decimals); + --pH_Answered := FH.Round_To_Valid_Nums(pH_Answered, Decimals); + + declare + pH_Diff: pH_Float := Abs(pH - pH_Answered); + pH_Str_Int: UB_Text; + pH_Str_Dec: UB_Text; + pH_A_Str_Int: UB_Text; + pH_A_Str_Dec: UB_Text; + pH_Diff_Str_Int: UB_Text; + pH_Diff_Str_Dec: UB_Text; + begin + FH.Split_Integer_Decimal_Unscaled_Strs(pH, DECIMALS, pH_Str_Int, pH_Str_Dec); + FH.Split_Integer_Decimal_Unscaled_Strs(pH_Answered, DECIMALS, pH_A_Str_Int, pH_A_Str_Dec); + FH.Split_Integer_Decimal_Unscaled_Strs(pH_Diff, DECIMALS, pH_Diff_Str_Int, pH_Diff_Str_Dec); + + Message := To_UB_Text("pH vypočtené programem = ") & pH_Str_Int & To_UB_Text(",") & pH_Str_Dec & To_UB_Text(" - Vaše odpověď = ") & pH_A_Str_Int & To_UB_Text(",") & pH_A_Str_Dec & To_UB_Text(" - (rozdíl = ") & pH_Diff_Str_Int & To_UB_Text(",") & pH_Diff_Str_Dec & To_UB_Text(")"); - if pH_Answered - (Precision * 5.0) < pH and pH_Answered + (Precision * 5.0) > pH then - Message := To_UB_Text("Správná odpověď"); 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_Unscaled_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(")")); - return Wrong_Answer; - end; - end if; + end; end Check_Answer; function Get_Assignment(Problem: in out Acidobazic_Problem; Assignment: in out Assignment_Info.Map; Resource_Prefix: in String) return RetCode is @@ -152,46 +150,19 @@ package body Acidobazic_Suite is 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 - -- 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); + -- Get random dissociation constant + Problem.Kx := Random_Kx; -- 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; + Problem.Simpl := Random_Simplification(Problem.Kx, Problem.Parameters.No_Both_Simplifications); Calculate_Concentration_Limits(cX_Min, cX_Max, Problem.Kx, Problem.Simpl); Problem.cX := Random_cX(cX_Min, cX_Max); @@ -265,13 +236,13 @@ package body Acidobazic_Suite is -- We are ignoring everything when Both => declare - pH: constant pH_Float := X_To_pX(Sqrt(Problem.Kx * Problem.cX)); + pX: constant pH_Float := X_To_pX((Problem.Kx * Problem.cX) ** 0.5); begin case Problem.Subst_Type is when Acid => - return pH; + return pX; when Base => - return 14.0 - pH; + return 14.0 - pX; end case; end; -- We are ignoring autoprotolysis and taking dissociation into account @@ -280,33 +251,33 @@ package body Acidobazic_Suite is D: pH_Float; X_1: pH_Float; X_2: pH_Float; - pH: pH_Float; + pX: pH_Float; begin -- 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); + D := D ** 0.5; 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)); + pX := X_To_pX(pH_Float'Max(X_1, X_2)); if Problem.Subst_Type = Base then - return 14.0 - pH; + return 14.0 - pX; else - return pH; + return pX; 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)); + pX: constant pH_Float := X_To_pX((Problem.Kx * Problem.cX + K_W) ** 0.5); begin case Problem.Subst_Type is when Acid => - return pH; + return pX; when Base => - return 14.0 - pH; + return 14.0 - pX; end case; end; end case; @@ -336,7 +307,7 @@ package body Acidobazic_Suite is 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)); + F_Log_Dec: constant pH_Float := pH_Float'Floor(Log(Base => 10.0, X => pH_Float(DECIMALS))); begin return F_Log_Num - F_Log_Dec; end Correction_Exponent; @@ -409,43 +380,40 @@ package body Acidobazic_Suite is 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; + function Random_Simplification(Kx: in pH_Float; No_Both_Simplifications: in Boolean) return Simplification is + package Random_Simplification_Gen is new Ada.Numerics.Discrete_Random(Result_Subtype => Boolean); + SIM_G: Random_Simplification_Gen.Generator; + B: Boolean; + pKx: constant pH_Float := X_To_pX(Kx); 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; + if pKx <= Acidic_Max_pH then + if No_Both_Simplifications then + return Autoprotolysis; -- Ignore autoprotolysis, take dissociation into account + else + Random_Simplification_Gen.Reset(Gen => SIM_G); + B := Random_Simplification_Gen.Random(Gen => SIM_G); + if B then + return Autoprotolysis; -- Ignore autoprotolysis, take dissociation into account + else + return Both; -- Ignore everything + end if; + end if; + elsif pKx >= Basic_Min_pH then + if No_Both_Simplifications then + return Dissociation; -- Ignore dissociation, take autoprotolysis into account + else + Random_Simplification_Gen.Reset(Gen => SIM_G); + B := Random_Simplification_Gen.Random(Gen => SIM_G); + if B then + return Dissociation; -- Ignore dissociation, take autoprotolysis into account + else + return Both; -- Ignore everything + end if; + end if; + else + raise Constraint_Error; + end if; + end Random_Simplification; function X_To_pX(X: in pH_Float) return pH_Float is package MEF is new Ada.Numerics.Generic_Elementary_Functions(pH_Float); diff --git a/src/problem_generators/problem_generator-solubility_suite.adb b/src/problem_generators/problem_generator-solubility_suite.adb index db05b0f..336fe2f 100644 --- a/src/problem_generators/problem_generator-solubility_suite.adb +++ b/src/problem_generators/problem_generator-solubility_suite.adb @@ -229,7 +229,7 @@ package body Solubility_Suite is G := Round_To_Valid_Nums(G, DECIMALS); Ks := Round_To_Valid_Nums(Generate_Solubility_Product, DECIMALS); MW := (SS_Float(Random(Gen => Float_RGen)) * MOLAR_WEIGHT_RANGE) + MOLAR_WEIGHT_MIN; - MW := SS_Float'Rounding(MW * DECIMALS_MW) / DECIMALS_MW; + MW := SS_Float'Rounding(MW * SS_Float(DECIMALS_MW)) / SS_Float(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); @@ -246,7 +246,7 @@ package body Solubility_Suite is 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)) * MOLAR_WEIGHT_RANGE) + MOLAR_WEIGHT_MIN; - MW := SS_Float'Rounding(MW * DECIMALS_MW) / DECIMALS_MW; + MW := SS_Float'Rounding(MW * SS_Float(DECIMALS_MW)) / SS_Float(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), diff --git a/src/problem_generators/problem_generator.ads b/src/problem_generators/problem_generator.ads index 78a4655..9f15b76 100644 --- a/src/problem_generators/problem_generator.ads +++ b/src/problem_generators/problem_generator.ads @@ -58,14 +58,13 @@ private 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 Random_Simplification(Kx: in pH_Float; No_Both_Simplifications: in Boolean) return Simplification; 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; @@ -80,8 +79,7 @@ private -- 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; + DECIMALS: constant Natural := 3; end Acidobazic_Suite; @@ -112,8 +110,8 @@ private 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; + DECIMALS: constant Natural := 3; + DECIMALS_MW: constant Natural := 2; PRECISION: constant SS_Float := 1.0E2; type Ion is @@ -228,7 +226,7 @@ private TITR_BASE_KB: constant T_Float := 0.0001; -- Corresponds to NaOH KW: constant T_Float := 1.0E-14; -- - DECIMALS: constant T_Float := 1.0E3; + DECIMALS: constant Natural := 3; MAX_DIFFERENCE: constant := 3.0E-14; -- IMAGE_WIDTH: constant Glib.GInt := 800; -- 2.43.5