These are a running webassembly demos (using cheerpj).
Note
|
This section is work in progress |
Calculator
java
This is the source code for the above.
@Getter
public enum FieldInformation {
rational(RationalNumbers.INSTANCE, "1 + 2", "1 + 3/5"),
real(RealField.INSTANCE, "1 + 2", "1 + 3/5", "sin(𝜋/2)", "sqr(𝜑) - 𝜑"),
bigdecimal(BigDecimalField.INSTANCE, "1 + 2", "1 + 3/5", "sin(𝜋/2)"),
gaussian(GaussianRationals.INSTANCE, "1 + 2", "1 + 3/5", "\"1 + 2i\" ⋅ 8i"),
complex(ComplexNumbers.INSTANCE, "1 + 2", "1 + 3/5", "sin(𝜋/2)", "exp(-i ⋅ 𝜋)", "\"2 + 3i\" ⋅ i"),
bigcomplex(BigComplexNumbers.INSTANCE, "1 + 2", "1 + 3/5", "\"1 + 2i\" ⋅ 8i"),
quaternions(Quaternions.of(RationalNumbers.INSTANCE),
"1 + 2", "1 + 3/5", "\"1 + 2i + 3j + 4k\" ⋅ 8i"),
quaternions_bigdecimal(Quaternions.of(BigDecimalField.INSTANCE),
"1 + 2", "1 + 3/5", "\"1 + 2i + 3j + 4k\" ⋅ 8i"),
integers(Integers.INSTANCE, "4 ⋅ 7", "9 - 3"),
modulo10(ModuloRing.of(10), "4 ⋅ 7", "9 - 3"),
modulo13(ModuloField.of(13), "10 ⋅ 7", "10 - 3", "12 ⋅ 6 / 4"),
natural(NaturalNumbers.INSTANCE, "10 ⋅ 7", "10 - 3", "12 ⋅ 6 / 4"),
even(EvenIntegers.INSTANCE, "10 ⋅ 8", "10 - 4"),
squares(Squares.INSTANCE, "2 ⋅ 9"),
klein(KleinGroup.INSTANCE,
"a * b * c * e",
"a * b"
),
quaterniongroup(QuaternionGroup.INSTANCE, "i", "e" ),
dihedral3(DihedralGroup.D3,
"r1 * r2",
"s0 * r1 * s0"
),
dihedral4(DihedralGroup.of(4),
"r1 * r2",
"s0 * r1 * s0 * s3"
)
;
private final Magma<?> field;
private final String[] examples;
private final String[] elements;
private final String[] binaryOperators;
private final String[] unaryOperators;
private final boolean finite;
FieldInformation(Magma<?> field, String... examples) {
this.field = field;
this.finite = field.isFinite();
this.examples = examples;
this.elements = elements(field);
this.binaryOperators = field.getSupportedOperators()
.stream()
.map(AlgebraicBinaryOperator::getSymbol)
.toArray(String[]::new);
this.unaryOperators = field.getSupportedUnaryOperators()
.stream()
.map(AlgebraicUnaryOperator::getSymbol)
.toArray(String[]::new);
log.fine("Created %s, operators: %s, unary: %s examples: %s, elements: %s".formatted(field,
List.of(binaryOperators),
List.of(unaryOperators),
List.of(examples), List.of(elements)));
}
public static String[] elements(Magma<?> field) {
Set<String> elements = new LinkedHashSet<>(field.getConstants().keySet());
if (field.getCardinality().isCountable() && field instanceof Streamable<?> streamable) {
streamable.stream().limit(100).map(Object::toString).forEach(elements::add);
}
return elements.toArray(new String[0]);
}
public String getDescription() {
return field.getClass().getSimpleName() + " " + field;
}
public String getHelp() {
return field.getDescription().orElse(null);
}
}
public static String eval(final String expression, final String field) {
try (var r = ConfigurationService.setConfiguration(cb -> cb
.configure(UncertaintyConfiguration.class,
(ub) -> ub.withNotation(ROUND_VALUE))
.configure(MathContextConfiguration.class,
(mc) -> mc.withContext(new MathContext(Utils.PI.length())))
)) {
var f = FieldInformation.valueOf(field).getField();
log.fine(() -> "Evaluating expression in %s: %s. Binary: %s, Unary: %s".formatted(f, expression, f.getSupportedOperators(), f.getSupportedUnaryOperators()));
if (f.getSupportedOperators().isEmpty()) {
log.log(Level.SEVERE, "Supported operators is empty for " + f);
}
var parsedExpression = AST.parse(expression, f);
log.fine(() -> "Parsed expression: %s".formatted( parsedExpression));
var result = parsedExpression.eval();
var resultAsString = result.toString();
log.info(() -> "Result: %s = %s".formatted(expression, resultAsString));
return resultAsString;
} catch (Throwable ex) {
log.log(Level.SEVERE, ex.getClass() + " " + ex.getMessage(), ex);
throw ex;
} finally {
log.finer("Ready evaluation");
}
}
javascript
This is the source code for the above.
constructor() {
super('#calculator', 'org.meeuw.math.demo.Calculator');
this.input = this.form.querySelector('input');
this.field = this.form.querySelector('select');
this.inputDataList= this.form.querySelector('datalist');
this.information = null;
}
insert(string) {
const input = this.input;
const start = input.selectionStart;
const end = input.selectionEnd;
const value = input.value;
input.value = value.slice(0, start) + string + value.slice(end);
input.setSelectionRange(
start + string.length,
start + string.length
);
input.focus();
}
insertOperator(string) {
const needsBrackets = string.length > 1;
if (! needsBrackets) {
return this.insert(string);
}
const input = this.input;
const start = input.selectionStart;
const end = input.selectionEnd;
const value = input.value;
if (start === end) {
input.value = string + "(" + value + ")";
input.setSelectionRange(
start + string.length + 1,
start + string.length + 1
);
} else {
input.value = value.slice(0, start) + string + "(" + value.slice(start, end) + ")" + value.slice(end);
input.setSelectionRange(
start,
end + string.length + 2
);
}
input.focus();
}
async setupForm() {
await super.setupForm();
this.form.addEventListener('beforeinput', async (e) => {
this.form.querySelector("span.help").innerHTML = '';
if (e.data === '=') {
console.log(this.input.value);
e.preventDefault();
e.stopImmediatePropagation();
await this.handleSubmit();
}
if (e.data === '*') {
this.form.querySelector("span.help").innerHTML = "to type * use ;";
e.preventDefault();
e.stopImmediatePropagation();
this.insert('⋅')
}
if (e.data === ';') {
e.preventDefault();
e.stopImmediatePropagation();
this.insert('*')
}
});
}
async onInView(Calculator){
await super.onInView(Calculator);
// using the field information to update the example per field
if (this.information === null) {
this.information = {};
const fi = await (await BaseClass.cj)['org.meeuw.math.demo.Calculator$FieldInformation'];
const values = await fi.values();
for (let i = 0; i < values.length; i++) {
const value = await values[i];
const elements = await BaseClass.awaitedArray(value.getElements());
let elementSpans = null;
if (elements) {
elementSpans = [];
for (let j = 0; j < elements.length; j++) {
const span = document.createElement('span');
span.classList.add('element');
span.textContent = elements[j];
span.onclick = async e => {
this.insert(e.target.textContent);
};
elementSpans[j] = span;
}
}
this.information[await values[i].name()] = {
examples: await BaseClass.awaitedArray(value.getExamples()),
elements: elements,
elementsSpans: elementSpans,
binaryOperators: await BaseClass.awaitedArray(value.getBinaryOperators()),
unaryOperators: await BaseClass.awaitedArray(value.getUnaryOperators()),
finite: await value.isFinite(),
description: await value.getDescription(),
help: await value.getHelp()
};
}
console.log(JSON.stringify(this.information));
}
await this.updateFieldList();
this.field.addEventListener('change', () => {
this.updateDataList();
this.updateHelp();
this.updateOperators();
this.updateDigits();
});
await this.updateDataList();
await this.updateHelp();
await this.updateOperators();
await this.updateDigits();
}
updateFieldList() {
for (const [key, value] of Object.entries(this.information)) {
const option = document.createElement('option');
option.value = key;
option.text = value.description;
this.field.appendChild(option);
}
}
async updateDataList() {
const selectedField = this.field.value;
const information = this.information[selectedField];
if (information) {
this.inputDataList.innerHTML = '';
for (const example of information.examples) {
const option = document.createElement('option');
option.value = example;
this.inputDataList.appendChild(option);
}
console.log("Updated data list for", selectedField, information.examples);
}
}
async updateHelp() {
const fieldInformation = this.information[this.field.value];
const div = this.field.parentNode.querySelector("div.help");
div.innerHTML = '';
let help = fieldInformation.help;
if (help) {
div.appendChild(document.createTextNode(help));
}
const elements = this.information[this.field.value].elementSpans;
if (elements) {
div.appendChild(document.createElement("br"));
div.appendChild(document.createTextNode("elements: "));
elements.forEach(element => {
div.appendChild(element);
})
if (!fieldInformation.finite) {
div.appendChild(document.createTextNode("... infinitely many more"));
}
}
}
operatorDts(dl, operators) {
this.dts(dl, operators, async e => {
this.insertOperator(e.target.textContent);
});
}
elementsDts(dl, operators) {
this.dts(dl, operators, async e => {
this.insert(e.target.textContent);
});
}
dts(dl, operators, onclick) {
const list = dl.querySelectorAll("dt");
const symbolsInList = Array.from(list).map(e => e.textContent.trim());
const unmatchedOperators = operators.filter(op => !symbolsInList.includes(op));
unmatchedOperators.forEach(op => {
const dt = document.createElement("dt");
dt.classList.add('hdlist1');
dt.textContent = op;
dl.appendChild(dt);
const dd = document.createElement("dd");
dl.appendChild(dd);
});
for (const e of dl.querySelectorAll("dt")) {
const symbol = e.textContent.trim();
const title = e.nextElementSibling.textContent;
if (!e.hasAttribute("original-display")) {
e.setAttribute("original-display", window.getComputedStyle(e).display);
e.onclick =onclick;
}
if (!operators.includes(symbol)) {
e.style.display = 'none';
e.nextElementSibling.hidden = true;
} else {
e.title = title;
e.style.display = e.getAttribute("original-display");
e.nextElementSibling.hidden = false;
}
}
}
async updateOperators() {
const fieldInformation = this.information[this.field.value];
const operators = fieldInformation.binaryOperators;
this.operatorDts(document.querySelector("#calculator_operators dl"), operators);
const unaryOperators = fieldInformation.unaryOperators;
this.operatorDts(document.querySelector("#calculator_unary_operator dl"), unaryOperators);
}
async updateDigits() {
const fieldInformation = this.information[this.field.value];
const elements = fieldInformation.elements;
this.elementsDts(document.querySelector("#calculator_digits dl"), elements);
}
async onSubmit(Calculator) {
this.output.value = '';
this.textContent = "executing..";
//console.log("evaluating", this.input.value, "for", this.field.value);
this.output.value = await Calculator.eval(
this.input.value, this.field.value
);
}
}
Solver
The same idea as my very first applet. Solving the '24 flippo game'.
Give the desired outcome number, and a few input numbers, and it will find the possible ways to get it using those input numbers.
Tip
|
This combines several aspects of this project:
Cheerpj still sometimes behaves a bit erraticly, I think something may be wrong with default methods? |
java source code
This is the source code for the above.
package org.meeuw.math.demo;
import lombok.Getter;
import lombok.extern.java.Log;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import org.meeuw.math.abstractalgebra.Ring;
import org.meeuw.math.abstractalgebra.RingElement;
import org.meeuw.math.abstractalgebra.complex.GaussianRationals;
import org.meeuw.math.abstractalgebra.permutations.PermutationGroup;
import org.meeuw.math.abstractalgebra.quaternions.Quaternions;
import org.meeuw.math.abstractalgebra.rationalnumbers.RationalNumbers;
import org.meeuw.math.arithmetic.ast.*;
import org.meeuw.math.exceptions.MathException;
import org.meeuw.math.exceptions.NotParsable;
import org.meeuw.math.operators.AlgebraicBinaryOperator;
import static org.meeuw.math.CollectionUtils.navigableSet;
import static org.meeuw.math.operators.BasicAlgebraicBinaryOperator.*;
/**
* A tool to evaluate all possible expressions (of a certain number of rational numbers) (and check if it equals a certain value)
*/
@Log
public class Solver<E extends RingElement<E>> {
private static final NavigableSet<AlgebraicBinaryOperator> OPERATORS = navigableSet(
ADDITION, SUBTRACTION, MULTIPLICATION, DIVISION
);
private final AtomicLong tries = new AtomicLong();
@Getter
private final Ring<E> structure;
public Solver(Ring<E> structure) {
this.structure = structure;
}
@SafeVarargs
public final Stream<Expression<E>> stream(E... set) {
PermutationGroup permutations = PermutationGroup.ofDegree(set.length);
return permutations.stream()
.map(permutation -> permutation.permute(set))
.map(List::of)
.distinct()
.flatMap(permuted ->
AST.stream(
permuted,
OPERATORS
)
)
.map( e -> e.canonize(structure))
.distinct()
.peek(e -> tries.getAndIncrement());
}
public Stream<EvaluatedExpression<E>> evaledStream(E... set) {
return stream(set)
.map(e -> {
try {
E evaled = e.eval();
return new EvaluatedExpression<>(e, evaled);
} catch (MathException ex) {
return null;
}
})
.filter(Objects::nonNull);
}
/**
*
*/
public static <E extends RingElement<E>> SolverResult solve(Ring<E> structure, String outcomeString, String inputStrings) {
ParseResult<E> outcome = parseOutcome(structure, outcomeString);
ParseResult<E[]> input = parseInput(structure, inputStrings);
if (outcome.success() && input.success()) {
return solve(structure, outcome.result(), input.result());
} else {
throw new NotParsable(outcome.error() + "/" + input.error());
}
}
public static <E extends RingElement<E>> SolverResult solve(Ring<E> structure, E outcome, E[] input) {
Solver<E> solver = new Solver<>(structure);
AtomicLong matches = new AtomicLong();
log.info(() -> "Solving input " + List.of(input) + " for " + outcome + " ( in field " + structure + ")");
return new SolverResult(solver.evaledStream(input)
.filter(e ->
e.result().eq(outcome)
).peek(e -> matches.getAndIncrement())
.map(EvaluatedExpression::toString),
solver.tries, matches, structure);
}
public static <F extends RingElement<F>> ParseResult<F> parseOutcome(Ring<F> field, String outcomeString) {
log.info(() -> "Parsing input " + outcomeString + " in field " + field);
String resultError = null;
F result;
try {
result = field.fromString(outcomeString);
} catch (NotParsable pe) {
result = null;
resultError = pe.getMessage();
}
return new ParseResult<F>(outcomeString, result, resultError);
}
public static <F extends RingElement<F>> ParseResult<F[]> parseInput(Ring<F> field, String inputStrings) {
log.info(() -> "Parsing input " + inputStrings + " in field " + field);
String inputError = null;
String[] input = inputStrings.split("\\s+");
F[] set = field.newArray(input.length);
try {
for (int i = 0; i < set.length; i++) {
set[i] = field.fromString(input[i]);
}
} catch (NotParsable pe) {
inputError = pe.getMessage();
}
return new ParseResult<>(inputStrings, set, inputError);
}
public static Ring<?> algebraicStructureFor(String outcomeString, String input) {
log.info(() -> "Determining algebraic structure for outcome " + outcomeString + " and input " + input);
if (outcomeString.matches(".*[jk].*") || input.matches(".*[jk].*")) {
return Quaternions.of(RationalNumbers.INSTANCE);
} else if (outcomeString.contains("i") || input.contains("i")) {
return GaussianRationals.INSTANCE;
} else {
return RationalNumbers.INSTANCE;
}
}
public record SolverResult(Stream<String> stream, AtomicLong tries, AtomicLong matches, Ring<?> field) {
}
public static void main(String[] integers) {
if (integers.length < 3) {
System.out.println();
System.exit(1);
}
String resultString = integers[0];
String inputStrings = String.join(" ", Arrays.copyOfRange(integers, 1, integers.length));
Ring<?> field = algebraicStructureFor(resultString, inputStrings);
SolverResult solverResult = Solver.solve(field, resultString, inputStrings);
solverResult.stream().forEach(System.out::println);
System.out.println("ready, found " + solverResult.matches().get() + ", tried " + solverResult.tries.get() + ", field " + solverResult.field().toString());
}
}
javascript
This is the source code for the above.
async onSubmit(Solver) {
this.output.value += "using: " + await (this.model.field).toString();
const solverResult = await Solver.solve(
this.model.field, this.outcome.value, this.input.value
);
const stream = await solverResult.stream();
const lines = await stream.toArray();
for (let i = 0; i < lines.length; i++) {
this.output.value += "\n" + await lines[i].toString();
}
const tries = await (await solverResult.tries()).get();
const matches = await (await solverResult.matches()).get();
this.output.value += `\nFound: ${matches}`;
this.output.value += `\nTried: ${tries}`;
}
}
Dynamic date parsing
The mihxil-time
module contain a 'dynamic date parser. I dusted this of from old mmbase code.
javascript
This is the source code for the above.
async onSubmit(DynamicDateTime){
try {
const parser = await new DynamicDateTime();
const parseResult = await parser.applyWithException(this.form.querySelector("#dynamicdate_toparse").value);
this.output.value = await parseResult.toString();
} catch (error) {
console.log(error);
}
}