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-10-05 14:53:46 +00:00
|
|
|
if( !_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>;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-21 14:35:54 +00:00
|
|
|
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-09-14 14:40:27 +00:00
|
|
|
List<String> unitsOfSameMagnitude(String unit){
|
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)) {
|
2025-09-21 14:35:54 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 08:10:29 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 08:10:29 +00:00
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 08:10:29 +00:00
|
|
|
final ret = _convertUsingCode(x, unit.codeFromUnitToBase as String);
|
2025-09-07 11:59:03 +00:00
|
|
|
return ret as Number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 08:10:29 +00:00
|
|
|
final ret = _convertUsingCode(x, unit.codeFromBaseToUnit as String);
|
2025-09-07 11:59:03 +00:00
|
|
|
return ret as Number;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 08:10:29 +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;
|
|
|
|
|
}
|
|
|
|
|
catch(e1,stack){
|
|
|
|
|
try{
|
|
|
|
|
completeSourceStatement = _converterFromCodeStringAsStatement(x, code);
|
|
|
|
|
final ret = _evaluate(completeSourceStatement);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
catch( e2, stack ){
|
|
|
|
|
print(completeSourceExpression);
|
|
|
|
|
print(e1);
|
|
|
|
|
print(completeSourceStatement);
|
|
|
|
|
print(e2);
|
|
|
|
|
throw FormulaEvaluationException( "Evaluation as statement and expression failed" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 17:28:17 +00:00
|
|
|
static Number _evaluate(String code, [D4rt? interpreter]) {
|
2025-10-15 08:10:29 +00:00
|
|
|
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
|
|
|
}
|