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;
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);
--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;
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;
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
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
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;
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
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);
-- 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
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;
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;
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);
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);
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),
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;
-- 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;
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
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;