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/
|
||||
.zcompdump
|
||||
.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.
|
||||
|
||||
|
||||
|
||||
# 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.
|
||||
///
|
||||
/// 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
|
||||
/// in the formula.
|
||||
///
|
||||
|
|
@ -48,11 +48,9 @@ class FormulaEvaluator {
|
|||
/// Returns the computed value of the single output variable.
|
||||
///
|
||||
/// Throws [FormulaEvaluationException] if:
|
||||
/// - The formula has zero or more than one output variable
|
||||
/// - Required input variables are missing
|
||||
/// - The d4rt code execution fails
|
||||
dynamic evaluate(Formula formula, Map<String, dynamic> inputValues) {
|
||||
_validateFormula(formula);
|
||||
_validateInputValues(formula, inputValues);
|
||||
|
||||
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
|
||||
void _validateInputValues(Formula formula, Map<String, dynamic> inputValues) {
|
||||
final missingVars = <String>[];
|
||||
|
|
@ -101,13 +89,13 @@ class FormulaEvaluator {
|
|||
|
||||
/// Gets the name of the single output variable from the formula
|
||||
String getOutputVariableName(Formula formula) {
|
||||
_validateFormula(formula);
|
||||
// Formula construction already ensures exactly one output variable
|
||||
return formula.output.keys.first;
|
||||
}
|
||||
|
||||
/// Gets the magnitude of the single output variable from the formula
|
||||
String getOutputVariableMagnitude(Formula formula) {
|
||||
_validateFormula(formula);
|
||||
// Formula construction already ensures exactly one output variable
|
||||
return formula.output.values.first.magnitude;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,12 +46,26 @@ class Formula {
|
|||
final Map<String, VariableSpec> output; // Supports multiple output variables
|
||||
final String d4rtCode;
|
||||
|
||||
const Formula({
|
||||
Formula({
|
||||
required this.name,
|
||||
required this.input,
|
||||
required this.output,
|
||||
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) {
|
||||
final name = json['name'];
|
||||
|
|
|
|||
|
|
@ -18,9 +18,7 @@ void main() {
|
|||
'm': VariableSpec(magnitude: 'mass'),
|
||||
'a': VariableSpec(magnitude: 'acceleration'),
|
||||
},
|
||||
output: {
|
||||
'F': VariableSpec(magnitude: 'force'),
|
||||
},
|
||||
output: {'F': VariableSpec(magnitude: 'force')},
|
||||
d4rtCode: '''
|
||||
main() {
|
||||
return a * m;
|
||||
|
|
@ -29,8 +27,8 @@ void main() {
|
|||
);
|
||||
|
||||
final result = evaluator.evaluate(formula, {
|
||||
'm': 10.0, // 10 kg
|
||||
'a': 9.8, // 9.8 m/s²
|
||||
'm': 10.0, // 10 kg
|
||||
'a': 9.8, // 9.8 m/s²
|
||||
});
|
||||
|
||||
expect(result, 98.0); // F = m * a = 10 * 9.8 = 98 N
|
||||
|
|
@ -43,9 +41,7 @@ void main() {
|
|||
'x': VariableSpec(magnitude: 'scalar'),
|
||||
'y': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
output: {
|
||||
'result': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
output: {'result': VariableSpec(magnitude: 'scalar')},
|
||||
d4rtCode: '''
|
||||
main() {
|
||||
return x + y;
|
||||
|
|
@ -53,10 +49,7 @@ void main() {
|
|||
''',
|
||||
);
|
||||
|
||||
final result = evaluator.evaluate(formula, {
|
||||
'x': 5,
|
||||
'y': 3,
|
||||
});
|
||||
final result = evaluator.evaluate(formula, {'x': 5, 'y': 3});
|
||||
|
||||
expect(result, 8);
|
||||
});
|
||||
|
|
@ -64,12 +57,8 @@ void main() {
|
|||
test('handles single input variable', () {
|
||||
final formula = Formula(
|
||||
name: 'Square function',
|
||||
input: {
|
||||
'n': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
output: {
|
||||
'result': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
input: {'n': VariableSpec(magnitude: 'scalar')},
|
||||
output: {'result': VariableSpec(magnitude: 'scalar')},
|
||||
d4rtCode: '''
|
||||
main() {
|
||||
return n * n;
|
||||
|
|
@ -89,9 +78,7 @@ void main() {
|
|||
'b': VariableSpec(magnitude: 'scalar'),
|
||||
'c': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
output: {
|
||||
'discriminant': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
output: {'discriminant': VariableSpec(magnitude: 'scalar')},
|
||||
d4rtCode: '''
|
||||
main() {
|
||||
return b * b - 4 * a * c;
|
||||
|
|
@ -99,11 +86,7 @@ void main() {
|
|||
''',
|
||||
);
|
||||
|
||||
final result = evaluator.evaluate(formula, {
|
||||
'a': 1,
|
||||
'b': 5,
|
||||
'c': 6,
|
||||
});
|
||||
final result = evaluator.evaluate(formula, {'a': 1, 'b': 5, 'c': 6});
|
||||
|
||||
expect(result, 1); // b² - 4ac = 25 - 24 = 1
|
||||
});
|
||||
|
|
@ -118,9 +101,7 @@ void main() {
|
|||
'a': VariableSpec(magnitude: 'scalar'),
|
||||
'b': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
output: {
|
||||
'result': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
output: {'result': VariableSpec(magnitude: 'scalar')},
|
||||
d4rtCode: 'main() { return a + b + z; }',
|
||||
);
|
||||
|
||||
|
|
@ -136,9 +117,7 @@ void main() {
|
|||
'a': VariableSpec(magnitude: 'scalar'),
|
||||
'y': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
output: {
|
||||
'result': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
output: {'result': VariableSpec(magnitude: 'scalar')},
|
||||
d4rtCode: '''
|
||||
main() {
|
||||
// Variables: a=1, y=2, z=3
|
||||
|
|
@ -147,47 +126,44 @@ void main() {
|
|||
''',
|
||||
);
|
||||
|
||||
final result = evaluator.evaluate(formula, {
|
||||
'z': 3,
|
||||
'a': 1,
|
||||
'y': 2,
|
||||
});
|
||||
final result = evaluator.evaluate(formula, {'z': 3, 'a': 1, 'y': 2});
|
||||
|
||||
expect(result, 123); // 1*100 + 2*10 + 3 = 123
|
||||
});
|
||||
});
|
||||
|
||||
group('Error handling', () {
|
||||
test('throws exception for formula with no output variables', () {
|
||||
final formula = Formula(
|
||||
name: 'Invalid formula',
|
||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||
output: {}, // No output variables
|
||||
d4rtCode: 'main() { return x; }',
|
||||
);
|
||||
test(
|
||||
'throws exception for formula with no output variables during construction',
|
||||
() {
|
||||
expect(() {
|
||||
return Formula(
|
||||
name: 'Invalid formula',
|
||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||
output: {}, // No output variables
|
||||
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', () {
|
||||
final formula = Formula(
|
||||
name: 'Invalid formula',
|
||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||
output: {
|
||||
'y': VariableSpec(magnitude: 'scalar'),
|
||||
'z': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
d4rtCode: 'main(x) { return x; }',
|
||||
);
|
||||
|
||||
expect(
|
||||
() => evaluator.evaluate(formula, {'x': 1}),
|
||||
throwsA(isA<FormulaEvaluationException>()),
|
||||
);
|
||||
});
|
||||
test(
|
||||
'throws exception for formula with multiple output variables during construction',
|
||||
() {
|
||||
expect(
|
||||
() => Formula(
|
||||
name: 'Invalid formula',
|
||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||
output: {
|
||||
'y': VariableSpec(magnitude: 'scalar'),
|
||||
'z': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
d4rtCode: 'main() { return x; }',
|
||||
),
|
||||
throwsA(isA<ArgumentError>()),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('throws exception for missing input variables', () {
|
||||
final formula = Formula(
|
||||
|
|
@ -233,33 +209,55 @@ void main() {
|
|||
expect(evaluator.getOutputVariableName(formula), 'force');
|
||||
});
|
||||
|
||||
test('getOutputVariableMagnitude returns the output variable magnitude', () {
|
||||
final formula = Formula(
|
||||
name: 'Test',
|
||||
test(
|
||||
'getOutputVariableMagnitude returns the output variable magnitude',
|
||||
() {
|
||||
final formula = Formula(
|
||||
name: 'Test',
|
||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||
output: {'force': VariableSpec(magnitude: 'Newton')},
|
||||
d4rtCode: 'main() { return x; }',
|
||||
);
|
||||
|
||||
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: {'force': VariableSpec(magnitude: 'Newton')},
|
||||
output: {'result': VariableSpec(magnitude: 'Newton')},
|
||||
d4rtCode: 'main() { return x; }',
|
||||
);
|
||||
|
||||
expect(evaluator.getOutputVariableMagnitude(formula), 'Newton');
|
||||
expect(evaluator.getOutputVariableName(validFormula), 'result');
|
||||
expect(evaluator.getOutputVariableMagnitude(validFormula), 'Newton');
|
||||
});
|
||||
|
||||
test('utility methods throw exception for invalid formulas', () {
|
||||
final invalidFormula = Formula(
|
||||
name: 'Invalid',
|
||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||
output: {}, // No output variables
|
||||
d4rtCode: 'main() { return x; }',
|
||||
test('validates formula construction with factory method', () {
|
||||
// Test the factory method validation
|
||||
expect(
|
||||
() => Formula(
|
||||
name: 'Invalid',
|
||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||
output: {}, // No output variables
|
||||
d4rtCode: 'main() { return x; }',
|
||||
),
|
||||
throwsA(isA<ArgumentError>()),
|
||||
);
|
||||
|
||||
expect(
|
||||
() => evaluator.getOutputVariableName(invalidFormula),
|
||||
throwsA(isA<FormulaEvaluationException>()),
|
||||
);
|
||||
|
||||
expect(
|
||||
() => evaluator.getOutputVariableMagnitude(invalidFormula),
|
||||
throwsA(isA<FormulaEvaluationException>()),
|
||||
() => Formula(
|
||||
name: 'Invalid',
|
||||
input: {'x': VariableSpec(magnitude: 'scalar')},
|
||||
output: {
|
||||
'y': VariableSpec(magnitude: 'scalar'),
|
||||
'z': VariableSpec(magnitude: 'scalar'),
|
||||
},
|
||||
d4rtCode: 'main() { return x; }',
|
||||
),
|
||||
throwsA(isA<ArgumentError>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue