1
0
mirror of https://github.com/DanilaFe/abacus synced 2026-01-26 08:35:20 +00:00

Compare commits

..

7 Commits

27 changed files with 690 additions and 655 deletions

View File

@@ -1,5 +1,5 @@
buildscript { buildscript {
ext.kotlin_version = '1.1.3' ext.kotlin_version = '1.1.4-3'
ext.dokka_version = '0.9.15' ext.dokka_version = '0.9.15'
repositories { repositories {
@@ -19,7 +19,7 @@ subprojects {
apply plugin: 'org.jetbrains.dokka' apply plugin: 'org.jetbrains.dokka'
repositories { repositories {
mavenCentral() jcenter()
} }
dependencies { dependencies {

View File

@@ -1,41 +1,73 @@
package org.nwapw.abacus.number package org.nwapw.abacus.number;
import org.nwapw.abacus.exception.ComputationInterruptedException import org.nwapw.abacus.exception.ComputationInterruptedException;
abstract class NumberInterface: Comparable<NumberInterface> { /**
* An interface used to represent a number.
*/
public abstract class NumberInterface implements Comparable<NumberInterface> {
/** /**
* Check if the thread was interrupted and * Check if the thread was interrupted and
* throw an exception to end the computation. * throw an exception to end the computation.
*/ */
private fun checkInterrupted(){ private static void checkInterrupted() {
if(Thread.currentThread().isInterrupted) if (Thread.currentThread().isInterrupted())
throw ComputationInterruptedException() throw new ComputationInterruptedException();
} }
/** /**
* Returns the integer representation of this number, discarding any fractional part, * The maximum precision to which this number operates.
* if int can hold the value.
* *
* @return the integer value of this number. * @return the precision.
*/ */
abstract fun intValue(): Int public abstract int getMaxPrecision();
/**
* Same as Math.signum().
*
* @return 1 if this number is positive, -1 if this number is negative, 0 if this number is 0.
*/
abstract fun signum(): Int
/** /**
* The maximum precision to which this number operates. * Multiplies this number by another, returning
* a new number instance.
*
* @param multiplier the multiplier
* @return the result of the multiplication.
*/ */
abstract val maxPrecision: Int protected abstract NumberInterface multiplyInternal(NumberInterface multiplier);
/** /**
* Returns the smallest error this instance can tolerate depending * Multiplies this number by another, returning
* on its precision and value. * a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param multiplier the multiplier
* @return the result of the multiplication.
*/ */
abstract val maxError: NumberInterface public final NumberInterface multiply(NumberInterface multiplier) {
checkInterrupted();
return multiplyInternal(multiplier);
}
/**
* Divides this number by another, returning
* a new number instance.
*
* @param divisor the divisor
* @return the result of the division.
*/
protected abstract NumberInterface divideInternal(NumberInterface divisor);
/**
* Divides this number by another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param divisor the divisor
* @return the result of the division.
*/
public final NumberInterface divide(NumberInterface divisor) {
checkInterrupted();
return divideInternal(divisor);
}
/** /**
* Adds this number to another, returning * Adds this number to another, returning
@@ -44,7 +76,22 @@ abstract class NumberInterface: Comparable<NumberInterface> {
* @param summand the summand * @param summand the summand
* @return the result of the summation. * @return the result of the summation.
*/ */
abstract fun addInternal(summand: NumberInterface): NumberInterface protected abstract NumberInterface addInternal(NumberInterface summand);
/**
* Adds this number to another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param summand the summand
* @return the result of the summation.
*/
public final NumberInterface add(NumberInterface summand) {
checkInterrupted();
return addInternal(summand);
}
/** /**
* Subtracts another number from this number, * Subtracts another number from this number,
* a new number instance. * a new number instance.
@@ -52,111 +99,30 @@ abstract class NumberInterface: Comparable<NumberInterface> {
* @param subtrahend the subtrahend. * @param subtrahend the subtrahend.
* @return the result of the subtraction. * @return the result of the subtraction.
*/ */
abstract fun subtractInternal(subtrahend: NumberInterface): NumberInterface protected abstract NumberInterface subtractInternal(NumberInterface subtrahend);
/** /**
* Multiplies this number by another, returning * Subtracts another number from this number,
* a new number instance. * a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
* *
* @param multiplier the multiplier * @param subtrahend the subtrahend.
* @return the result of the multiplication. * @return the result of the subtraction.
*/ */
abstract fun multiplyInternal(multiplier: NumberInterface): NumberInterface public final NumberInterface subtract(NumberInterface subtrahend) {
/** checkInterrupted();
* Divides this number by another, returning return subtractInternal(subtrahend);
* a new number instance. }
*
* @param divisor the divisor
* @return the result of the division.
*/
abstract fun divideInternal(divisor: NumberInterface): NumberInterface
/** /**
* Returns a new instance of this number with * Returns a new instance of this number with
* the sign flipped. * the sign flipped.
* *
* @return the new instance. * @return the new instance.
*/ */
abstract fun negateInternal(): NumberInterface protected abstract NumberInterface negateInternal();
/**
* Raises this number to an integer power.
*
* @param exponent the exponent to which to take the number.
* @return the resulting value.
*/
abstract fun intPowInternal(pow: Int): NumberInterface
/**
* Returns the least integer greater than or equal to the number.
*
* @return the least integer greater or equal to the number, if int can hold the value.
*/
abstract fun ceilingInternal(): NumberInterface
/**
* Return the greatest integer less than or equal to the number.
*
* @return the greatest integer smaller or equal the number.
*/
abstract fun floorInternal(): NumberInterface
/**
* Returns the fractional part of the number.
*
* @return the fractional part of the number.
*/
abstract fun fractionalPartInternal(): NumberInterface
/**
* Adds this number to another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param summand the summand
* @return the result of the summation.
*/
fun add(summand: NumberInterface): NumberInterface {
checkInterrupted()
return addInternal(summand)
}
/**
* Subtracts another number from this number,
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param subtrahend the subtrahend.
* @return the result of the subtraction.
*/
fun subtract(subtrahend: NumberInterface): NumberInterface {
checkInterrupted()
return subtractInternal(subtrahend)
}
/**
* Multiplies this number by another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param multiplier the multiplier
* @return the result of the multiplication.
*/
fun multiply(multiplier: NumberInterface): NumberInterface {
checkInterrupted()
return multiplyInternal(multiplier)
}
/**
* Divides this number by another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param divisor the divisor
* @return the result of the division.
*/
fun divide(divisor: NumberInterface): NumberInterface {
checkInterrupted()
return divideInternal(divisor)
}
/** /**
* Returns a new instance of this number with * Returns a new instance of this number with
@@ -166,11 +132,19 @@ abstract class NumberInterface: Comparable<NumberInterface> {
* *
* @return the new instance. * @return the new instance.
*/ */
fun negate(): NumberInterface { public final NumberInterface negate() {
checkInterrupted() checkInterrupted();
return negateInternal() return negateInternal();
} }
/**
* Raises this number to an integer power.
*
* @param exponent the exponent to which to take the number.
* @return the resulting value.
*/
protected abstract NumberInterface intPowInternal(int exponent);
/** /**
* Raises this number to an integer power. Also, checks if the * Raises this number to an integer power. Also, checks if the
* thread has been interrupted, and if so, throws * thread has been interrupted, and if so, throws
@@ -179,11 +153,25 @@ abstract class NumberInterface: Comparable<NumberInterface> {
* @param exponent the exponent to which to take the number. * @param exponent the exponent to which to take the number.
* @return the resulting value. * @return the resulting value.
*/ */
fun intPow(exponent: Int): NumberInterface { public final NumberInterface intPow(int exponent) {
checkInterrupted() checkInterrupted();
return intPowInternal(exponent) return intPowInternal(exponent);
} }
/**
* Same as Math.signum().
*
* @return 1 if this number is positive, -1 if this number is negative, 0 if this number is 0.
*/
public abstract int signum();
/**
* Returns the least integer greater than or equal to the number.
*
* @return the least integer greater or equal to the number, if int can hold the value.
*/
protected abstract NumberInterface ceilingInternal();
/** /**
* Returns the least integer greater than or equal to the number. * Returns the least integer greater than or equal to the number.
* Also, checks if the thread has been interrupted, and if so, throws * Also, checks if the thread has been interrupted, and if so, throws
@@ -191,11 +179,18 @@ abstract class NumberInterface: Comparable<NumberInterface> {
* *
* @return the least integer bigger or equal to the number. * @return the least integer bigger or equal to the number.
*/ */
fun ceiling(): NumberInterface { public final NumberInterface ceiling() {
checkInterrupted() checkInterrupted();
return ceilingInternal() return ceilingInternal();
} }
/**
* Return the greatest integer less than or equal to the number.
*
* @return the greatest integer smaller or equal the number.
*/
protected abstract NumberInterface floorInternal();
/** /**
* Return the greatest integer less than or equal to the number. * Return the greatest integer less than or equal to the number.
* Also, checks if the thread has been interrupted, and if so, throws * Also, checks if the thread has been interrupted, and if so, throws
@@ -203,11 +198,18 @@ abstract class NumberInterface: Comparable<NumberInterface> {
* *
* @return the greatest int smaller than or equal to the number. * @return the greatest int smaller than or equal to the number.
*/ */
fun floor(): NumberInterface { public final NumberInterface floor() {
checkInterrupted() checkInterrupted();
return floorInternal() return floorInternal();
} }
/**
* Returns the fractional part of the number.
*
* @return the fractional part of the number.
*/
protected abstract NumberInterface fractionalPartInternal();
/** /**
* Returns the fractional part of the number, specifically x - floor(x). * Returns the fractional part of the number, specifically x - floor(x).
* Also, checks if the thread has been interrupted, * Also, checks if the thread has been interrupted,
@@ -215,11 +217,27 @@ abstract class NumberInterface: Comparable<NumberInterface> {
* *
* @return the fractional part of the number. * @return the fractional part of the number.
*/ */
fun fractionalPart(): NumberInterface { public final NumberInterface fractionalPart() {
checkInterrupted() checkInterrupted();
return fractionalPartInternal() return fractionalPartInternal();
} }
/**
* Returns the integer representation of this number, discarding any fractional part,
* if int can hold the value.
*
* @return the integer value of this number.
*/
public abstract int intValue();
/**
* Returns the smallest error this instance can tolerate depending
* on its precision and value.
*
* @return the smallest error that should be permitted in calculations.
*/
public abstract NumberInterface getMaxError();
/** /**
* Returns a NumberRangeBuilder object, which is used to create a range. * Returns a NumberRangeBuilder object, which is used to create a range.
* The reason that this returns a builder and not an actual range is that * The reason that this returns a builder and not an actual range is that
@@ -228,41 +246,8 @@ abstract class NumberInterface: Comparable<NumberInterface> {
* @param other the value at the bottom of the range. * @param other the value at the bottom of the range.
* @return the resulting range builder. * @return the resulting range builder.
*/ */
operator fun rangeTo(other: NumberInterface) = NumberRangeBuilder(this, other) public NumberRangeBuilder rangeTo(NumberInterface other){
return new NumberRangeBuilder(this, other);
/** }
* Plus operator overloaded to allow "nice" looking math.
* @param other the value to add to this number.
* @return the result of the addition.
*/
operator fun plus(other: NumberInterface) = add(other)
/**
* Minus operator overloaded to allow "nice" looking math.
* @param other the value to subtract to this number.
* @return the result of the subtraction.
*/
operator fun minus(other: NumberInterface) = subtract(other)
/**
* Times operator overloaded to allow "nice" looking math.
* @param other the value to multiply this number by.
* @return the result of the multiplication.
*/
operator fun times(other: NumberInterface) = multiply(other)
/**
* Divide operator overloaded to allow "nice" looking math.
* @param other the value to divide this number by.
* @return the result of the division.
*/
operator fun div(other: NumberInterface) = divide(other)
/**
* The plus operator.
* @return this number.
*/
operator fun unaryPlus() = this
/**
* The minus operator.
* @return the negative of this number.
*/
operator fun unaryMinus() = negate()
} }

View File

@@ -1,17 +1,16 @@
package org.nwapw.abacus.parsing package org.nwapw.abacus.parsing;
import org.nwapw.abacus.tree.TreeNode import org.nwapw.abacus.tree.TreeNode;
import java.util.List;
/** /**
* Converter from tokens into a parse tree. * An itnerface that provides the ability to convert a list of tokens
*
* An interface that provides the ability to convert a list of tokens
* into a parse tree. * into a parse tree.
* *
* @param <T> the type of tokens accepted by this parser. * @param <T> the type of tokens accepted by this parser.
*/ */
public interface Parser<T> {
interface Parser<in T> {
/** /**
* Constructs a tree out of the given tokens. * Constructs a tree out of the given tokens.
@@ -19,6 +18,5 @@ interface Parser<in T> {
* @param tokens the tokens to construct a tree from. * @param tokens the tokens to construct a tree from.
* @return the constructed tree, or null on error. * @return the constructed tree, or null on error.
*/ */
fun constructTree(tokens: List<T>): TreeNode public TreeNode constructTree(List<T> tokens);
} }

View File

@@ -124,7 +124,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
* @param matches the list of tokens from the source string. * @param matches the list of tokens from the source string.
* @return the construct tree expression. * @return the construct tree expression.
*/ */
public TreeNode constructRecursive(List<? extends Match<TokenType>> matches) { public TreeNode constructRecursive(List<Match<TokenType>> matches) {
if (matches.size() == 0) throw new ParseException("no tokens left in input"); if (matches.size() == 0) throw new ParseException("no tokens left in input");
Match<TokenType> match = matches.remove(0); Match<TokenType> match = matches.remove(0);
TokenType matchType = match.getType(); TokenType matchType = match.getType();
@@ -172,7 +172,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
} }
@Override @Override
public TreeNode constructTree(List<? extends Match<TokenType>> tokens) { public TreeNode constructTree(List<Match<TokenType>> tokens) {
if (tokens.isEmpty()) throw new ParseException("no input tokens"); if (tokens.isEmpty()) throw new ParseException("no input tokens");
tokens = intoPostfix(new ArrayList<>(tokens)); tokens = intoPostfix(new ArrayList<>(tokens));
Collections.reverse(tokens); Collections.reverse(tokens);

View File

@@ -0,0 +1,20 @@
package org.nwapw.abacus.parsing;
import java.util.List;
/**
* Interface that provides the ability to convert a string into a list of tokens.
*
* @param <T> the type of the tokens produced.
*/
public interface Tokenizer<T> {
/**
* Converts a string into tokens.
*
* @param string the string to convert.
* @return the list of tokens, or null on error.
*/
public List<T> tokenizeString(String string);
}

View File

@@ -0,0 +1,48 @@
package org.nwapw.abacus.parsing;
import org.nwapw.abacus.tree.TreeNode;
import java.util.List;
/**
* TreeBuilder class used to piece together a Tokenizer and
* Parser of the same kind. This is used to essentially avoid
* working with any parameters at all, and the generics
* in this class are used only to ensure the tokenizer and parser
* are of the same type.
*
* @param <T> the type of tokens created by the tokenizer and used by the parser.
*/
public class TreeBuilder<T> {
/**
* The tokenizer used to convert a string into tokens.
*/
private Tokenizer<T> tokenizer;
/**
* The parser used to parse a list of tokens into a tree.
*/
private Parser<T> parser;
/**
* Create a new Tree Builder with the given tokenizer and parser
*
* @param tokenizer the tokenizer to turn strings into tokens
* @param parser the parser to turn tokens into a tree
*/
public TreeBuilder(Tokenizer<T> tokenizer, Parser<T> parser) {
this.tokenizer = tokenizer;
this.parser = parser;
}
/**
* Parse the given string into a tree.
*
* @param input the string to parse into a tree.
* @return the resulting tree.
*/
public TreeNode fromString(String input) {
return parser.constructTree(tokenizer.tokenizeString(input));
}
}

View File

@@ -1,14 +1,12 @@
package org.nwapw.abacus.plugin.standard; package org.nwapw.abacus.plugin;
import org.nwapw.abacus.context.MutableEvaluationContext; import org.nwapw.abacus.context.MutableEvaluationContext;
import org.nwapw.abacus.function.*; import org.nwapw.abacus.function.*;
import org.nwapw.abacus.number.NaiveNumber; import org.nwapw.abacus.number.NaiveNumber;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.number.PreciseNumber; import org.nwapw.abacus.number.PreciseNumber;
import org.nwapw.abacus.plugin.NumberImplementation; import org.nwapw.abacus.tree.TreeNode;
import org.nwapw.abacus.plugin.Plugin; import org.nwapw.abacus.tree.VariableNode;
import org.nwapw.abacus.plugin.PluginManager;
import org.nwapw.abacus.plugin.standard.operator.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -22,27 +20,93 @@ public class StandardPlugin extends Plugin {
/** /**
* The set operator. * The set operator.
*/ */
public static final TreeValueOperator OP_SET = new OperatorSet(); public final TreeValueOperator opSet = new TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
@Override
public boolean matchesParams(MutableEvaluationContext context, TreeNode[] params) {
return params.length == 2 && params[0] instanceof VariableNode;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, TreeNode[] params) {
String assignTo = ((VariableNode) params[0]).getVariable();
NumberInterface value = params[1].reduce(context.getInheritedReducer());
context.setVariable(assignTo, value);
return value;
}
};
/** /**
* The define operator. * The define operator.
*/ */
public final TreeValueOperator OP_DEFINE = new OperatorDefine(); public final TreeValueOperator opDefine = new TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
@Override
public boolean matchesParams(MutableEvaluationContext context, TreeNode[] params) {
return params.length == 2 && params[0] instanceof VariableNode;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, TreeNode[] params) {
String assignTo = ((VariableNode) params[0]).getVariable();
context.setDefinition(assignTo, params[1]);
return params[1].reduce(context.getInheritedReducer());
}
};
/** /**
* The addition operator, + * The addition operator, +
*/ */
public static final NumberOperator OP_ADD = new OperatorAdd(); public static final NumberOperator OP_ADD = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].add(params[1]);
}
};
/** /**
* The subtraction operator, - * The subtraction operator, -
*/ */
public static final NumberOperator OP_SUBTRACT = new OperatorSubtract(); public static final NumberOperator OP_SUBTRACT = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].subtract(params[1]);
}
};
/** /**
* The negation operator, - * The negation operator, -
*/ */
public static final NumberOperator OP_NEGATE = new OperatorNegate(); public static final NumberOperator OP_NEGATE = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.UNARY_PREFIX, 0) {
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 1;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].negate();
}
};
/** /**
* The multiplication operator, * * The multiplication operator, *
*/ */
public static final NumberOperator OP_MULTIPLY = new OperatorMultiply(); public static final NumberOperator OP_MULTIPLY = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) {
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].multiply(params[1]);
}
};
/** /**
* The implementation for double-based naive numbers. * The implementation for double-based naive numbers.
*/ */
@@ -97,19 +161,96 @@ public class StandardPlugin extends Plugin {
/** /**
* The division operator, / * The division operator, /
*/ */
public static final NumberOperator OP_DIVIDE = new OperatorDivide(); public static final NumberOperator OP_DIVIDE = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) {
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2 && params[1].compareTo(context.getInheritedNumberImplementation().instanceForString(Integer.toString(0))) != 0;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].divide(params[1]);
}
};
/** /**
* The factorial operator, ! * The factorial operator, !
*/ */
public static final NumberOperator OP_FACTORIAL = new OperatorFactorial(); public static final NumberOperator OP_FACTORIAL = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.UNARY_POSTFIX, 0) {
//private HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> storedList = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 1
&& params[0].fractionalPart().compareTo(context.getInheritedNumberImplementation().instanceForString("0")) == 0
&& params[0].signum() >= 0;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation();
if (params[0].signum() == 0) {
return implementation.instanceForString("1");
}
NumberInterface one = implementation.instanceForString("1");
NumberInterface factorial = params[0];
NumberInterface multiplier = params[0];
//It is necessary to later prevent calls of factorial on anything but non-negative integers.
while ((multiplier = multiplier.subtract(one)).signum() == 1) {
factorial = factorial.multiply(multiplier);
}
return factorial;
/*if(!storedList.containsKey(params[0].getClass())){
storedList.put(params[0].getClass(), new ArrayList<NumberInterface>());
storedList.get(params[0].getClass()).add(NaiveNumber.ONE.promoteTo(params[0].getClass()));
storedList.get(params[0].getClass()).add(NaiveNumber.ONE.promoteTo(params[0].getClass()));
}*/
}
};
/** /**
* The permutation operator. * The permutation operator.
*/ */
public static final NumberOperator OP_NPR = new OperatorNpr(); public static final NumberOperator OP_NPR = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) {
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2 && params[0].fractionalPart().signum() == 0
&& params[1].fractionalPart().signum() == 0;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation();
if (params[0].compareTo(params[1]) < 0 ||
params[0].signum() < 0 ||
(params[0].signum() == 0 && params[1].signum() != 0)) return implementation.instanceForString("0");
NumberInterface total = implementation.instanceForString("1");
NumberInterface multiplyBy = params[0];
NumberInterface remainingMultiplications = params[1];
NumberInterface halfway = params[0].divide(implementation.instanceForString("2"));
if (remainingMultiplications.compareTo(halfway) > 0) {
remainingMultiplications = params[0].subtract(remainingMultiplications);
}
while (remainingMultiplications.signum() > 0) {
total = total.multiply(multiplyBy);
remainingMultiplications = remainingMultiplications.subtract(implementation.instanceForString("1"));
multiplyBy = multiplyBy.subtract(implementation.instanceForString("1"));
}
return total;
}
};
/** /**
* The combination operator. * The combination operator.
*/ */
public static final NumberOperator OP_NCR = new OperatorNcr(); public static final NumberOperator OP_NCR = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) {
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2 && params[0].fractionalPart().signum() == 0
&& params[1].fractionalPart().signum() == 0;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return OP_NPR.apply(context, params).divide(OP_FACTORIAL.apply(context, params[1]));
}
};
/** /**
* The absolute value function, abs(-3) = 3 * The absolute value function, abs(-3) = 3
*/ */
@@ -222,7 +363,34 @@ public class StandardPlugin extends Plugin {
/** /**
* The caret / pow operator, ^ * The caret / pow operator, ^
*/ */
public static final NumberOperator OP_CARET = new OperatorCaret(); public static final NumberOperator OP_CARET = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2) {
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
NumberInterface zero = context.getInheritedNumberImplementation().instanceForString("0");
return params.length == 2
&& !(params[0].compareTo(zero) == 0
&& params[1].compareTo(zero) == 0)
&& !(params[0].signum() == -1 && params[1].fractionalPart().compareTo(zero) != 0);
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation();
NumberInterface zero = implementation.instanceForString("0");
if (params[0].compareTo(zero) == 0)
return zero;
else if (params[1].compareTo(zero) == 0)
return implementation.instanceForString("1");
//Detect integer bases:
if (params[0].fractionalPart().compareTo(implementation.instanceForString("0")) == 0
&& FUNCTION_ABS.apply(context, params[1]).compareTo(implementation.instanceForString(Integer.toString(Integer.MAX_VALUE))) < 0
&& FUNCTION_ABS.apply(context, params[1]).compareTo(implementation.instanceForString("1")) >= 0) {
NumberInterface[] newParams = {params[0], params[1].fractionalPart()};
return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(context, newParams));
}
return FUNCTION_EXP.apply(context, FUNCTION_LN.apply(context, FUNCTION_ABS.apply(context, params[0])).multiply(params[1]));
}
};
/** /**
* The square root function. * The square root function.
*/ */
@@ -588,8 +756,8 @@ public class StandardPlugin extends Plugin {
registerOperator("^", OP_CARET); registerOperator("^", OP_CARET);
registerOperator("!", OP_FACTORIAL); registerOperator("!", OP_FACTORIAL);
registerTreeValueOperator("=", OP_SET); registerTreeValueOperator("=", opSet);
registerTreeValueOperator(":=", OP_DEFINE); registerTreeValueOperator(":=", opDefine);
registerOperator("nPr", OP_NPR); registerOperator("nPr", OP_NPR);
registerOperator("nCr", OP_NCR); registerOperator("nCr", OP_NCR);

View File

@@ -8,7 +8,7 @@ import org.nwapw.abacus.parsing.LexerTokenizer
import org.nwapw.abacus.parsing.ShuntingYardParser import org.nwapw.abacus.parsing.ShuntingYardParser
import org.nwapw.abacus.parsing.TreeBuilder import org.nwapw.abacus.parsing.TreeBuilder
import org.nwapw.abacus.plugin.PluginManager import org.nwapw.abacus.plugin.PluginManager
import org.nwapw.abacus.plugin.standard.StandardPlugin import org.nwapw.abacus.plugin.StandardPlugin
import org.nwapw.abacus.tree.EvaluationResult import org.nwapw.abacus.tree.EvaluationResult
import org.nwapw.abacus.tree.NumberReducer import org.nwapw.abacus.tree.NumberReducer
import org.nwapw.abacus.tree.TreeNode import org.nwapw.abacus.tree.TreeNode

View File

@@ -1,21 +0,0 @@
package org.nwapw.abacus.parsing
/**
* Converter from string to tokens.
*
* Interface that converts a string into a list
* of tokens of a certain type.
*
* @param <T> the type of the tokens produced.
*/
interface Tokenizer<out T> {
/**
* Converts a string into tokens.
*
* @param string the string to convert.
* @return the list of tokens, or null on error.
*/
fun tokenizeString(string: String): List<T>
}

View File

@@ -1,26 +0,0 @@
package org.nwapw.abacus.parsing
import org.nwapw.abacus.tree.TreeNode
/**
* Class to combine a [Tokenizer] and a [Parser]
*
* TreeBuilder class used to piece together a Tokenizer and
* Parser of the same kind. This is used to essentially avoid
* working with any parameters at all, and the generics
* in this class are used only to ensure the tokenizer and parser
* are of the same type.
*
* @param <T> the type of tokens created by the tokenizer and used by the parser.
*/
class TreeBuilder<T>(private val tokenizer: Tokenizer<T>, private val parser: Parser<T>) {
/**
* Parses the given [string] into a tree.
*
* @param string the string to parse into a tree.
* @return the resulting tree.
*/
fun fromString(string: String): TreeNode = parser.constructTree(tokenizer.tokenizeString(string))
}

View File

@@ -1,21 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.NumberOperator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface
/**
* The addition operator.
*
* This is a standard operator that simply performs addition.
*/
class OperatorAdd: NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
override fun applyInternal(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params[0] + params[1]
}

View File

@@ -1,38 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.NumberOperator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.plugin.standard.StandardPlugin.*
/**
* The power operator.
*
* This is a standard operator that brings one number to the power of the other.
*/
class OperatorCaret: NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
&& !(params[0].signum() == 0 && params[1].signum() == 0)
&& !(params[0].signum() == -1 && params[1].fractionalPart().signum() != 0)
override fun applyInternal(context: MutableEvaluationContext, params: Array<out NumberInterface>): NumberInterface {
val implementation = context.inheritedNumberImplementation
if (params[0].signum() == 0)
return implementation.instanceForString("0")
else if (params[1].signum() == 0)
return implementation.instanceForString("1")
//Detect integer bases:
if (params[0].fractionalPart().signum() == 0
&& FUNCTION_ABS.apply(context, params[1]) < implementation.instanceForString(Integer.toString(Integer.MAX_VALUE))
&& FUNCTION_ABS.apply(context, params[1]) >= implementation.instanceForString("1")) {
val newParams = arrayOf(params[0], params[1].fractionalPart())
return params[0].intPow(params[1].floor().intValue()) * applyInternal(context, newParams)
}
return FUNCTION_EXP.apply(context, FUNCTION_LN.apply(context, FUNCTION_ABS.apply(context, params[0])) * params[1])
}
}

View File

@@ -1,28 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.TreeValueOperator
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.tree.TreeNode
import org.nwapw.abacus.tree.VariableNode
/**
* The definition operator.
*
* This is a standard operator that creates a definition - something that doesn't capture variable values
* when it's created, but rather the variables themselves, and changes when the variables it uses change.
*/
class OperatorDefine: TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out TreeNode>) =
params.size == 2 && params[0] is VariableNode
override fun applyInternal(context: MutableEvaluationContext, params: Array<out TreeNode>): NumberInterface {
val assignTo = (params[0] as VariableNode).variable
context.setDefinition(assignTo, params[1])
return params[1].reduce(context.inheritedReducer)
}
}

View File

@@ -1,21 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.NumberOperator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface
/**
* The division operator.
*
* This is a standard operator that simply performs division.
*/
class OperatorDivide: NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
override fun applyInternal(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params[0] / params[1]
}

View File

@@ -1,37 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.NumberOperator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface
/**
* The factorial operator.
*
* This is a standard operator that simply evaluates the factorial of a number.
*/
class OperatorFactorial: NumberOperator(OperatorAssociativity.LEFT, OperatorType.UNARY_POSTFIX, 0) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params.size == 1
&& params[0].fractionalPart().signum() == 0
&& params[0].signum() >= 0
override fun applyInternal(context: MutableEvaluationContext, params: Array<out NumberInterface>): NumberInterface {
val implementation = context.inheritedNumberImplementation
val one = implementation.instanceForString("1")
if (params[0].signum() == 0) {
return one
}
var factorial = params[0]
var multiplier = params[0] - one
//It is necessary to later prevent calls of factorial on anything but non-negative integers.
while (multiplier.signum() == 1) {
factorial *= multiplier
multiplier -= one
}
return factorial
}
}

View File

@@ -1,21 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.NumberOperator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface
/**
* The multiplication operator.
*
* This is a standard operator that simply performs multiplication.
*/
class OperatorMultiply: NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
override fun applyInternal(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params[0] * params[1]
}

View File

@@ -1,25 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.NumberOperator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.plugin.standard.StandardPlugin.*
/**
* The "N choose R" operator.
*
* This is a standard operator that returns the number of possible combinations, regardless of order,
* of a certain size can be taken out of a pool of a bigger size.
*/
class OperatorNcr: NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2 && params[0].fractionalPart().signum() == 0
&& params[1].fractionalPart().signum() == 0
override fun applyInternal(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
OP_NPR.apply(context, *params) / OP_FACTORIAL.apply(context, params[1])
}

View File

@@ -1,22 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.NumberOperator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface
/**
* The negation operator.
*
* This is a standard operator that negates a number.
*/
class OperatorNegate: NumberOperator(OperatorAssociativity.LEFT, OperatorType.UNARY_PREFIX, 0) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params.size == 1
override fun applyInternal(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
-params[0]
}

View File

@@ -1,42 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.NumberOperator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface
/**
* The "N pick R" operator.
*
* his is a standard operator that returns the number of possible combinations
* of a certain size can be taken out of a pool of a bigger size.
*/
class OperatorNpr: NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2 && params[0].fractionalPart().signum() == 0
&& params[1].fractionalPart().signum() == 0
override fun applyInternal(context: MutableEvaluationContext, params: Array<out NumberInterface>): NumberInterface {
val implementation = context.inheritedNumberImplementation
if (params[0] < params[1] ||
params[0].signum() < 0 ||
params[0].signum() == 0 && params[1].signum() != 0)
return implementation.instanceForString("0")
var total = implementation.instanceForString("1")
var multiplyBy = params[0]
var remainingMultiplications = params[1]
val halfway = params[0] / implementation.instanceForString("2")
if (remainingMultiplications > halfway) {
remainingMultiplications = params[0] - remainingMultiplications
}
while (remainingMultiplications.signum() > 0) {
total *= multiplyBy
remainingMultiplications -= implementation.instanceForString("1")
multiplyBy -= implementation.instanceForString("1")
}
return total
}
}

View File

@@ -1,28 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.TreeValueOperator
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.tree.TreeNode
import org.nwapw.abacus.tree.VariableNode
/**
* The set operator.
*
* This is a standard operator that assigns a value to a variable name.
*/
class OperatorSet: TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out TreeNode>) =
params.size == 2 && params[0] is VariableNode
override fun applyInternal(context: MutableEvaluationContext, params: Array<out TreeNode>): NumberInterface {
val assignTo = (params[0] as VariableNode).variable
val value = params[1].reduce(context.inheritedReducer)
context.setVariable(assignTo, value)
return value
}
}

View File

@@ -1,21 +0,0 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.function.NumberOperator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface
/**
* The subtraction operator.
*
* This is a standard operator that performs subtraction.
*/
class OperatorSubtract: NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
override fun matchesParams(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
override fun applyInternal(context: MutableEvaluationContext, params: Array<out NumberInterface>) =
params[0] - params[1]
}

View File

@@ -7,7 +7,7 @@ import org.nwapw.abacus.Abacus;
import org.nwapw.abacus.config.Configuration; import org.nwapw.abacus.config.Configuration;
import org.nwapw.abacus.exception.DomainException; import org.nwapw.abacus.exception.DomainException;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.plugin.standard.StandardPlugin; import org.nwapw.abacus.plugin.StandardPlugin;
import org.nwapw.abacus.tree.TreeNode; import org.nwapw.abacus.tree.TreeNode;
public class CalculationTests { public class CalculationTests {

View File

@@ -9,7 +9,7 @@ import org.nwapw.abacus.number.NaiveNumber;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.number.NumberRange; import org.nwapw.abacus.number.NumberRange;
import org.nwapw.abacus.number.PreciseNumber; import org.nwapw.abacus.number.PreciseNumber;
import org.nwapw.abacus.plugin.standard.StandardPlugin; import org.nwapw.abacus.plugin.StandardPlugin;
import java.util.function.Function; import java.util.function.Function;

View File

@@ -2,7 +2,14 @@ apply plugin: 'application'
dependencies { dependencies {
compile 'com.moandjiezana.toml:toml4j:0.7.1' compile 'com.moandjiezana.toml:toml4j:0.7.1'
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.18'
compile project(':core') compile project(':core')
} }
kotlin {
experimental {
coroutines "enable"
}
}
mainClassName = 'org.nwapw.abacus.fx.AbacusApplication' mainClassName = 'org.nwapw.abacus.fx.AbacusApplication'

View File

@@ -19,7 +19,7 @@ import org.nwapw.abacus.number.*;
import org.nwapw.abacus.plugin.ClassFinder; import org.nwapw.abacus.plugin.ClassFinder;
import org.nwapw.abacus.plugin.PluginListener; import org.nwapw.abacus.plugin.PluginListener;
import org.nwapw.abacus.plugin.PluginManager; import org.nwapw.abacus.plugin.PluginManager;
import org.nwapw.abacus.plugin.standard.StandardPlugin; import org.nwapw.abacus.plugin.StandardPlugin;
import org.nwapw.abacus.tree.EvaluationResult; import org.nwapw.abacus.tree.EvaluationResult;
import org.nwapw.abacus.tree.TreeNode; import org.nwapw.abacus.tree.TreeNode;

View File

@@ -0,0 +1,121 @@
package org.nwapw.abacus.fx.graphing
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.runBlocking
import org.nwapw.abacus.Abacus
import org.nwapw.abacus.config.Configuration
import org.nwapw.abacus.context.EvaluationContext
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.number.NaiveNumber
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.number.NumberRange
import org.nwapw.abacus.plugin.StandardPlugin
import org.nwapw.abacus.tree.TreeNode
/**
* A class that holds information about a graph.
*
* Graph that uses an instance of [Abacus] to generate graphs of an
* [expression]. Continuous graphing on large expressions is expensive,
* and therefore the method of graphing is discrete. The graph class
* uses the [pointExpression] to generate inputs until an input is outside
* the function's domain. In both the expressions, the [inputVariable] and
* [pointInputVariable] are used, respectively, in order to either pass in
* the x-coordinate or the number of the input value being generated.
*
* @property abacus the abacus instance to use to evaluate expressions.
* @property domain the domain used in computation.
* @property range the range used for displaying the output.
* @property inputVariable the variable which is substituted for the input x-coordinate.
* @property pointInputVariable the variable which is substituted for the number of the input point being generated.
*/
class Graph(val abacus: Abacus,
var domain: NumberRange, var range: NumberRange,
var inputVariable: String = "x", var pointInputVariable: String = "n") {
/**
* Property used for storing the parsed version of the [expression]
*/
private var expressionTree: TreeNode? = null
/**
* Property used for storing the parsed version of the [pointExpression]
*/
private var pointExpressionTree: TreeNode? = null
/**
* The expression being graphed.
*/
var expression: String? = null
set(value) {
field = value
expressionTree = abacus.parseString(value ?: return)
}
/**
* The expression being used to generate points.
*/
var pointExpression: String? = null
set(value) {
field = value
pointExpressionTree = abacus.parseString(value ?: return)
}
/**
* Evaluates a parsed expression [tree] with the given input [values] of indeterminate type.
* This is is an asynchronous operation using coroutines, and for every input
* value a new sub-context is created. The [contextModifier] function is executed
* on each new sub-context, and is expected to use the input value to modify the context
* state so that the evaluation of the expression produces a correct result.
*
* @param T the type of input values.
* @param tree the tree to evaluate.
* @param values the values to plug into the expression.
* @param contextModifier the function that plugs values into the context.
* @return the list of outputs.
*/
fun <T> evaluateWith(tree: TreeNode, values: List<T>,
contextModifier: MutableEvaluationContext.(T) -> Unit) = runBlocking {
values.map {
val context = abacus.context.mutableSubInstance()
context.contextModifier(it)
async(CommonPool) { abacus.evaluateTreeWithContext(tree, context) }
}.map { it.await() }
}
/**
* Extension function that calls [evaluateWith] with the current list.
* @param T the type of the values in the list.
* @param tree the tree node to evaluate.
* @param contextModifier the function that plugs values into the context.
* @return the list of outputs.
*/
fun <T> List<T>.evaluateWith(tree: TreeNode,
contextModifier: MutableEvaluationContext.(T) -> Unit ) =
evaluateWith(tree, this, contextModifier)
/**
* Uses the [pointExpression] and [pointInputVariable] to generate
* a set of points as inputs for the actual expression. These points are generated
* as long as they are within the [domain].
* @return the list of generated input values.
*/
fun generateInputs(): List<NumberInterface> =
generateSequence(1) {
it + 1
}.map {
val context = abacus.context.mutableSubInstance()
context.setVariable(pointInputVariable,
context.inheritedNumberImplementation!!.instanceForString(it.toString()))
abacus.evaluateTreeWithContext(pointExpressionTree!!, context).value
}.takeWhile { it in domain }.toList()
/**
* Uses the [expression] and [inputVariable] to generate
* a set of outputs from the given set of [inputs].
* @return the list of generated points.
*/
fun generateOutputs(inputs: List<NumberInterface>): List<Pair<NumberInterface, NumberInterface>> =
inputs.evaluateWith(expressionTree!!) {
setVariable(inputVariable, it)
}.mapIndexed { index, (value) -> inputs[index] to value }
}

View File

@@ -0,0 +1,39 @@
package org.nwapw.abacus.fx.graphing
import javafx.beans.value.ChangeListener
import javafx.scene.canvas.Canvas
/**
* A canvas that renders a graph.
*
* The GraphCanvas uses the provided [Graph] instance in order to draw the outputs on itself.
* @param graph the graph used to render.
*/
class GraphCanvas(graph: Graph): Canvas() {
/**
* The graph that is currently being used to generate inputs / outputs.
* The redraw is triggered if this graph is reset.
*/
var graph: Graph = graph
set(value) {
field = value
redraw()
}
init {
val redrawListener = ChangeListener<Number> { _, _, _ -> redraw() }
widthProperty().addListener(redrawListener)
heightProperty().addListener(redrawListener)
redraw()
}
/**
* Redraws the graph onto the canvas.
*/
fun redraw() {
val graphicsContext = graphicsContext2D
val outputs = graph.generateOutputs(graph.generateInputs())
}
}