Tests passing, all formulas validated
This commit is contained in:
parent
eb38cfcb0e
commit
75fad84cac
5 changed files with 103 additions and 102 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -11,3 +11,5 @@
|
||||||
.local/
|
.local/
|
||||||
.zcompdump
|
.zcompdump
|
||||||
.zshrc
|
.zshrc
|
||||||
|
.idea
|
||||||
|
/d4rt-formulas.iml
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ A comprehensive command-line application for managing and computing mathematical
|
||||||
This project uses dart language, and flutter framework. It leverages d4rt library to execute formulas.
|
This project uses dart language, and flutter framework. It leverages d4rt library to execute formulas.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Development guidelines
|
# Development guidelines
|
||||||
If you are a contributor or an agent, please follow [CLAUDE.md](./CLAUDE.md) for development guidelines.
|
If you are a contributor or an agent, please follow [CLAUDE.md](./CLAUDE.md) for development guidelines.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class FormulaEvaluator {
|
||||||
|
|
||||||
/// Evaluates a formula with the given input values.
|
/// Evaluates a formula with the given input values.
|
||||||
///
|
///
|
||||||
/// The [formula] must have exactly one output variable.
|
/// The [formula] must have exactly one output variable (validated during construction).
|
||||||
/// The [inputValues] map must contain values for all input variables defined
|
/// The [inputValues] map must contain values for all input variables defined
|
||||||
/// in the formula.
|
/// in the formula.
|
||||||
///
|
///
|
||||||
|
|
@ -48,11 +48,9 @@ class FormulaEvaluator {
|
||||||
/// Returns the computed value of the single output variable.
|
/// Returns the computed value of the single output variable.
|
||||||
///
|
///
|
||||||
/// Throws [FormulaEvaluationException] if:
|
/// Throws [FormulaEvaluationException] if:
|
||||||
/// - The formula has zero or more than one output variable
|
|
||||||
/// - Required input variables are missing
|
/// - Required input variables are missing
|
||||||
/// - The d4rt code execution fails
|
/// - The d4rt code execution fails
|
||||||
dynamic evaluate(Formula formula, Map<String, dynamic> inputValues) {
|
dynamic evaluate(Formula formula, Map<String, dynamic> inputValues) {
|
||||||
_validateFormula(formula);
|
|
||||||
_validateInputValues(formula, inputValues);
|
_validateInputValues(formula, inputValues);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -71,16 +69,6 @@ class FormulaEvaluator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates that the formula has exactly one output variable
|
|
||||||
void _validateFormula(Formula formula) {
|
|
||||||
if (formula.output.length != 1) {
|
|
||||||
throw FormulaEvaluationException(
|
|
||||||
'Formula "${formula.name}" must have exactly one output variable, '
|
|
||||||
'but has ${formula.output.length}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates that all required input variables are provided
|
/// Validates that all required input variables are provided
|
||||||
void _validateInputValues(Formula formula, Map<String, dynamic> inputValues) {
|
void _validateInputValues(Formula formula, Map<String, dynamic> inputValues) {
|
||||||
final missingVars = <String>[];
|
final missingVars = <String>[];
|
||||||
|
|
@ -101,13 +89,13 @@ class FormulaEvaluator {
|
||||||
|
|
||||||
/// Gets the name of the single output variable from the formula
|
/// Gets the name of the single output variable from the formula
|
||||||
String getOutputVariableName(Formula formula) {
|
String getOutputVariableName(Formula formula) {
|
||||||
_validateFormula(formula);
|
// Formula construction already ensures exactly one output variable
|
||||||
return formula.output.keys.first;
|
return formula.output.keys.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the magnitude of the single output variable from the formula
|
/// Gets the magnitude of the single output variable from the formula
|
||||||
String getOutputVariableMagnitude(Formula formula) {
|
String getOutputVariableMagnitude(Formula formula) {
|
||||||
_validateFormula(formula);
|
// Formula construction already ensures exactly one output variable
|
||||||
return formula.output.values.first.magnitude;
|
return formula.output.values.first.magnitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,26 @@ class Formula {
|
||||||
final Map<String, VariableSpec> output; // Supports multiple output variables
|
final Map<String, VariableSpec> output; // Supports multiple output variables
|
||||||
final String d4rtCode;
|
final String d4rtCode;
|
||||||
|
|
||||||
const Formula({
|
Formula({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.input,
|
required this.input,
|
||||||
required this.output,
|
required this.output,
|
||||||
required this.d4rtCode,
|
required this.d4rtCode,
|
||||||
});
|
}){
|
||||||
|
validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
validate() {
|
||||||
|
if (name.trim().isEmpty) {
|
||||||
|
throw ArgumentError('Formula name cannot be empty');
|
||||||
|
}
|
||||||
|
if (output.length != 1) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Formula "$name" must have exactly one output variable, '
|
||||||
|
'but has ${output.length}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
factory Formula.fromJson(Map<String, dynamic> json) {
|
factory Formula.fromJson(Map<String, dynamic> json) {
|
||||||
final name = json['name'];
|
final name = json['name'];
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,7 @@ void main() {
|
||||||
'm': VariableSpec(magnitude: 'mass'),
|
'm': VariableSpec(magnitude: 'mass'),
|
||||||
'a': VariableSpec(magnitude: 'acceleration'),
|
'a': VariableSpec(magnitude: 'acceleration'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {'F': VariableSpec(magnitude: 'force')},
|
||||||
'F': VariableSpec(magnitude: 'force'),
|
|
||||||
},
|
|
||||||
d4rtCode: '''
|
d4rtCode: '''
|
||||||
main() {
|
main() {
|
||||||
return a * m;
|
return a * m;
|
||||||
|
|
@ -43,9 +41,7 @@ void main() {
|
||||||
'x': VariableSpec(magnitude: 'scalar'),
|
'x': VariableSpec(magnitude: 'scalar'),
|
||||||
'y': VariableSpec(magnitude: 'scalar'),
|
'y': VariableSpec(magnitude: 'scalar'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {'result': VariableSpec(magnitude: 'scalar')},
|
||||||
'result': VariableSpec(magnitude: 'scalar'),
|
|
||||||
},
|
|
||||||
d4rtCode: '''
|
d4rtCode: '''
|
||||||
main() {
|
main() {
|
||||||
return x + y;
|
return x + y;
|
||||||
|
|
@ -53,10 +49,7 @@ void main() {
|
||||||
''',
|
''',
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = evaluator.evaluate(formula, {
|
final result = evaluator.evaluate(formula, {'x': 5, 'y': 3});
|
||||||
'x': 5,
|
|
||||||
'y': 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result, 8);
|
expect(result, 8);
|
||||||
});
|
});
|
||||||
|
|
@ -64,12 +57,8 @@ void main() {
|
||||||
test('handles single input variable', () {
|
test('handles single input variable', () {
|
||||||
final formula = Formula(
|
final formula = Formula(
|
||||||
name: 'Square function',
|
name: 'Square function',
|
||||||
input: {
|
input: {'n': VariableSpec(magnitude: 'scalar')},
|
||||||
'n': VariableSpec(magnitude: 'scalar'),
|
output: {'result': VariableSpec(magnitude: 'scalar')},
|
||||||
},
|
|
||||||
output: {
|
|
||||||
'result': VariableSpec(magnitude: 'scalar'),
|
|
||||||
},
|
|
||||||
d4rtCode: '''
|
d4rtCode: '''
|
||||||
main() {
|
main() {
|
||||||
return n * n;
|
return n * n;
|
||||||
|
|
@ -89,9 +78,7 @@ void main() {
|
||||||
'b': VariableSpec(magnitude: 'scalar'),
|
'b': VariableSpec(magnitude: 'scalar'),
|
||||||
'c': VariableSpec(magnitude: 'scalar'),
|
'c': VariableSpec(magnitude: 'scalar'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {'discriminant': VariableSpec(magnitude: 'scalar')},
|
||||||
'discriminant': VariableSpec(magnitude: 'scalar'),
|
|
||||||
},
|
|
||||||
d4rtCode: '''
|
d4rtCode: '''
|
||||||
main() {
|
main() {
|
||||||
return b * b - 4 * a * c;
|
return b * b - 4 * a * c;
|
||||||
|
|
@ -99,11 +86,7 @@ void main() {
|
||||||
''',
|
''',
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = evaluator.evaluate(formula, {
|
final result = evaluator.evaluate(formula, {'a': 1, 'b': 5, 'c': 6});
|
||||||
'a': 1,
|
|
||||||
'b': 5,
|
|
||||||
'c': 6,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result, 1); // b² - 4ac = 25 - 24 = 1
|
expect(result, 1); // b² - 4ac = 25 - 24 = 1
|
||||||
});
|
});
|
||||||
|
|
@ -118,9 +101,7 @@ void main() {
|
||||||
'a': VariableSpec(magnitude: 'scalar'),
|
'a': VariableSpec(magnitude: 'scalar'),
|
||||||
'b': VariableSpec(magnitude: 'scalar'),
|
'b': VariableSpec(magnitude: 'scalar'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {'result': VariableSpec(magnitude: 'scalar')},
|
||||||
'result': VariableSpec(magnitude: 'scalar'),
|
|
||||||
},
|
|
||||||
d4rtCode: 'main() { return a + b + z; }',
|
d4rtCode: 'main() { return a + b + z; }',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -136,9 +117,7 @@ void main() {
|
||||||
'a': VariableSpec(magnitude: 'scalar'),
|
'a': VariableSpec(magnitude: 'scalar'),
|
||||||
'y': VariableSpec(magnitude: 'scalar'),
|
'y': VariableSpec(magnitude: 'scalar'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {'result': VariableSpec(magnitude: 'scalar')},
|
||||||
'result': VariableSpec(magnitude: 'scalar'),
|
|
||||||
},
|
|
||||||
d4rtCode: '''
|
d4rtCode: '''
|
||||||
main() {
|
main() {
|
||||||
// Variables: a=1, y=2, z=3
|
// Variables: a=1, y=2, z=3
|
||||||
|
|
@ -147,47 +126,44 @@ void main() {
|
||||||
''',
|
''',
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = evaluator.evaluate(formula, {
|
final result = evaluator.evaluate(formula, {'z': 3, 'a': 1, 'y': 2});
|
||||||
'z': 3,
|
|
||||||
'a': 1,
|
|
||||||
'y': 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result, 123); // 1*100 + 2*10 + 3 = 123
|
expect(result, 123); // 1*100 + 2*10 + 3 = 123
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('Error handling', () {
|
group('Error handling', () {
|
||||||
test('throws exception for formula with no output variables', () {
|
test(
|
||||||
final formula = Formula(
|
'throws exception for formula with no output variables during construction',
|
||||||
|
() {
|
||||||
|
expect(() {
|
||||||
|
return Formula(
|
||||||
name: 'Invalid formula',
|
name: 'Invalid formula',
|
||||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||||
output: {}, // No output variables
|
output: {}, // No output variables
|
||||||
d4rtCode: 'main() { return x; }',
|
d4rtCode: 'main() { return x; }',
|
||||||
);
|
);
|
||||||
|
}, throwsA(isA<ArgumentError>()));
|
||||||
expect(
|
},
|
||||||
() => evaluator.evaluate(formula, {'x': 1}),
|
|
||||||
throwsA(isA<FormulaEvaluationException>()),
|
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
test('throws exception for formula with multiple output variables', () {
|
test(
|
||||||
final formula = Formula(
|
'throws exception for formula with multiple output variables during construction',
|
||||||
|
() {
|
||||||
|
expect(
|
||||||
|
() => Formula(
|
||||||
name: 'Invalid formula',
|
name: 'Invalid formula',
|
||||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||||
output: {
|
output: {
|
||||||
'y': VariableSpec(magnitude: 'scalar'),
|
'y': VariableSpec(magnitude: 'scalar'),
|
||||||
'z': VariableSpec(magnitude: 'scalar'),
|
'z': VariableSpec(magnitude: 'scalar'),
|
||||||
},
|
},
|
||||||
d4rtCode: 'main(x) { return x; }',
|
d4rtCode: 'main() { return x; }',
|
||||||
|
),
|
||||||
|
throwsA(isA<ArgumentError>()),
|
||||||
);
|
);
|
||||||
|
},
|
||||||
expect(
|
|
||||||
() => evaluator.evaluate(formula, {'x': 1}),
|
|
||||||
throwsA(isA<FormulaEvaluationException>()),
|
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
test('throws exception for missing input variables', () {
|
test('throws exception for missing input variables', () {
|
||||||
final formula = Formula(
|
final formula = Formula(
|
||||||
|
|
@ -233,7 +209,9 @@ void main() {
|
||||||
expect(evaluator.getOutputVariableName(formula), 'force');
|
expect(evaluator.getOutputVariableName(formula), 'force');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getOutputVariableMagnitude returns the output variable magnitude', () {
|
test(
|
||||||
|
'getOutputVariableMagnitude returns the output variable magnitude',
|
||||||
|
() {
|
||||||
final formula = Formula(
|
final formula = Formula(
|
||||||
name: 'Test',
|
name: 'Test',
|
||||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||||
|
|
@ -242,24 +220,44 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(evaluator.getOutputVariableMagnitude(formula), 'Newton');
|
expect(evaluator.getOutputVariableMagnitude(formula), 'Newton');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test('utility methods work correctly with valid formulas', () {
|
||||||
|
final validFormula = Formula(
|
||||||
|
name: 'Valid Formula',
|
||||||
|
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||||
|
output: {'result': VariableSpec(magnitude: 'Newton')},
|
||||||
|
d4rtCode: 'main() { return x; }',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(evaluator.getOutputVariableName(validFormula), 'result');
|
||||||
|
expect(evaluator.getOutputVariableMagnitude(validFormula), 'Newton');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('utility methods throw exception for invalid formulas', () {
|
test('validates formula construction with factory method', () {
|
||||||
final invalidFormula = Formula(
|
// Test the factory method validation
|
||||||
|
expect(
|
||||||
|
() => Formula(
|
||||||
name: 'Invalid',
|
name: 'Invalid',
|
||||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||||
output: {}, // No output variables
|
output: {}, // No output variables
|
||||||
d4rtCode: 'main() { return x; }',
|
d4rtCode: 'main() { return x; }',
|
||||||
|
),
|
||||||
|
throwsA(isA<ArgumentError>()),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() => evaluator.getOutputVariableName(invalidFormula),
|
() => Formula(
|
||||||
throwsA(isA<FormulaEvaluationException>()),
|
name: 'Invalid',
|
||||||
);
|
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||||
|
output: {
|
||||||
expect(
|
'y': VariableSpec(magnitude: 'scalar'),
|
||||||
() => evaluator.getOutputVariableMagnitude(invalidFormula),
|
'z': VariableSpec(magnitude: 'scalar'),
|
||||||
throwsA(isA<FormulaEvaluationException>()),
|
},
|
||||||
|
d4rtCode: 'main() { return x; }',
|
||||||
|
),
|
||||||
|
throwsA(isA<ArgumentError>()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue