d4t_formulas/lib/corpus.dart

185 lines
5.2 KiB
Dart
Raw Normal View History

2025-09-07 11:59:03 +00:00
import 'package:d4rt/d4rt.dart';
import 'package:collection/collection.dart';
import 'package:d4rt_formulas/d4rt_formulas.dart';
class Multimap<K, V> extends DelegatingMap<K, List<V>> {
final Map<K, List<V>> _map;
Multimap(super.map) : _map = map;
factory Multimap.create() {
return Multimap({});
}
@override
List<V>? operator [](Object? key) {
if (_map.containsKey(key)) {
return super[key];
}
final List<V> newList = [];
super[key as K] = newList;
return super[key];
}
}
2025-09-20 14:46:21 +00:00
class Corpus{
2025-09-16 16:22:29 +00:00
final Multimap<String, Formula> _tags = Multimap.create();
final Map<String, Formula> _allFormulas = {};
2025-09-20 14:46:21 +00:00
void loadFormulas(List<Formula> formulas, {bool replaceOnDuplicates = false, bool checkUnits = true}) {
for (final formula in formulas) {
if (!replaceOnDuplicates && _allFormulas.containsKey(formula.name)) {
throw ArgumentError("Duplicate formula:$formula");
}
if( checkUnits ){
for( final inputVar in formula.input + [formula.output] ){
2025-11-09 19:29:58 +00:00
if( inputVar.unit != null && !_allUnits.containsKey(inputVar.unit) ){
2025-09-20 14:46:21 +00:00
throw ArgumentError( "Unit not found: ${inputVar.unit}");
}
}
}
_allFormulas[formula.name] = formula;
for( final tag in formula.tags ){
_tags[tag]?.add(formula);
}
}
}
List<Formula> getTagFormulas(String tag){
if( _tags[tag] == null ){
return [];
}
return _tags[tag]?.toList(growable:false) as List<Formula>;
}
List<Formula> getFormulas(){
return _allFormulas.values.toList(growable:false);
}
2025-09-07 11:59:03 +00:00
final Multimap<String, String> _baseToUnits = Multimap.create();
final Map<String, UnitSpec> _allUnits = {};
void loadUnits(List<UnitSpec> units, [bool replaceOnDuplicates = false]) {
for (final unit in units) {
if (!replaceOnDuplicates && _allUnits.containsKey(unit.name)) {
2025-09-07 12:04:42 +00:00
throw ArgumentError("Duplicate unit:$unit");
2025-09-07 11:59:03 +00:00
}
_allUnits[unit.name] = unit;
_baseToUnits[unit.baseUnit]?.add(unit.name);
}
}
2025-11-09 19:29:58 +00:00
List<String> unitsOfSameMagnitude(String? unit){
if( unit == null ){
return ["unitless"];
}
2025-09-20 14:46:21 +00:00
final base = getUnit(unit).baseUnit;
2025-09-14 14:40:27 +00:00
return _baseToUnits[base] as List<String>;
}
2025-09-20 14:46:21 +00:00
UnitSpec getUnit(String unit) {
2025-09-07 11:59:03 +00:00
if (!_allUnits.containsKey(unit)) {
print( _allUnits.keys.join(",") );
2025-09-07 12:04:42 +00:00
throw ArgumentError("Unit not found:$unit");
2025-09-07 11:59:03 +00:00
}
return _allUnits.get(unit);
}
String _converterFromCodeStringAsExpression(Number x, String codeString) {
2025-09-07 11:59:03 +00:00
final buffer = StringBuffer();
buffer.writeln("final x = ${x};");
buffer.writeln("main(){return $codeString;}");
final code = buffer.toString();
return code;
}
String _converterFromCodeStringAsStatement(Number x, String codeString) {
final buffer = StringBuffer();
buffer.writeln("final x = ${x};");
buffer.writeln("main(){ $codeString; return x; }");
final code = buffer.toString();
return code;
}
2025-09-07 11:59:03 +00:00
Number _convertToBase(Number x, String fromUnit) {
2025-09-20 14:46:21 +00:00
final unit = getUnit(fromUnit);
2025-09-07 11:59:03 +00:00
if (unit.factorFromUnitToBase != null) {
return x * (unit.factorFromUnitToBase as Number);
}
if (unit.codeFromUnitToBase == null) {
throw ArgumentError("Unit has no codeFromUnitToBase: $unit");
}
final ret = _convertUsingCode(x, unit.codeFromUnitToBase as String);
2025-11-09 19:29:58 +00:00
return ret;
2025-09-07 11:59:03 +00:00
}
Number _convertFromBase(Number x, String toUnit) {
2025-09-20 14:46:21 +00:00
final unit = getUnit(toUnit);
2025-09-07 11:59:03 +00:00
if (unit.factorFromUnitToBase != null) {
return x / (unit.factorFromUnitToBase as Number);
}
if (unit.codeFromBaseToUnit == null) {
throw ArgumentError("Unit has no codeFromBaseToUnit: $unit");
}
final ret = _convertUsingCode(x, unit.codeFromBaseToUnit as String);
2025-11-09 19:29:58 +00:00
return ret;
2025-09-07 11:59:03 +00:00
}
Number _convertUsingCode(Number x, String code ){
late String completeSourceExpression;
late String completeSourceStatement;
try {
completeSourceExpression = _converterFromCodeStringAsExpression(x, code);
final ret = _evaluate(completeSourceExpression);
return ret;
}
2025-11-09 19:29:58 +00:00
catch(e1, stack1){
try{
completeSourceStatement = _converterFromCodeStringAsStatement(x, code);
final ret = _evaluate(completeSourceStatement);
return ret;
}
2025-11-09 19:29:58 +00:00
catch( e2, stack2 ){
print(completeSourceExpression);
print(e1);
2025-11-09 19:29:58 +00:00
print(stack1);
print(completeSourceStatement);
print(e2);
2025-11-09 19:29:58 +00:00
print(stack2);
throw FormulaEvaluationException( "Evaluation as statement and expression failed" );
}
}
}
static Number _evaluate(String code, [D4rt? interpreter]) {
final d4rtInterpreter = interpreter ?? FormulaEvaluator.createDefaultInterpreter();
FormulaEvaluator.prepareInterpreter(d4rtInterpreter);
final completeCode = "${FormulaEvaluator.d4rtImports}\n$code";
final result = d4rtInterpreter.execute(source: completeCode);
return result.toDouble();
}
2025-09-07 11:59:03 +00:00
Number convert(Number x, String fromUnit, String toUnit) {
final xBase = _convertToBase(x, fromUnit);
final xTo = _convertFromBase(xBase, toUnit);
2025-10-14 17:21:35 +00:00
//print( "convert: x:${x}${fromUnit} xTo:${xTo}${toUnit}");
2025-09-07 11:59:03 +00:00
return xTo;
}
2025-09-10 15:17:28 +00:00
Iterable<UnitSpec> allUnits() => _allUnits.values;
2025-09-20 14:46:21 +00:00
2025-09-07 11:59:03 +00:00
}