diff --git a/fx/src/main/kotlin/org/nwapw/abacus/fx/graphing/Graph.kt b/fx/src/main/kotlin/org/nwapw/abacus/fx/graphing/Graph.kt index 34b72b9..9486da1 100644 --- a/fx/src/main/kotlin/org/nwapw/abacus/fx/graphing/Graph.kt +++ b/fx/src/main/kotlin/org/nwapw/abacus/fx/graphing/Graph.kt @@ -12,30 +12,66 @@ import org.nwapw.abacus.number.NumberInterface 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, - expression: String, pointExpression: String, var domain: ClosedRange, var range: ClosedRange, 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 - var expression: String = "" + /** + * The expression being graphed. + */ + var expression: String? = null set(value) { - expressionTree = abacus.parseString(value) field = value + expressionTree = abacus.parseString(value ?: return) } - var pointExpression: String = "" + /** + * The expression being used to generate points. + */ + var pointExpression: String? = null set(value) { - pointExpressionTree = abacus.parseString(value) field = value + pointExpressionTree = abacus.parseString(value ?: return) } - init { - this.expression = expression - this.pointExpression = pointExpression - } - + /** + * 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 evaluateWith(tree: TreeNode, values: List, contextModifier: MutableEvaluationContext.(T) -> Unit) = runBlocking { values.map { @@ -45,10 +81,23 @@ class Graph(val abacus: Abacus, }.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 List.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 = generateSequence(1) { it + 1 @@ -59,8 +108,13 @@ class Graph(val abacus: Abacus, abacus.evaluateTreeWithContext(pointExpressionTree!!, context).value }.takeWhile { it in domain }.toList() - fun generateOutputs(inputs: List): List = + /** + * 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): List> = inputs.evaluateWith(expressionTree!!) { setVariable(inputVariable, it) - }.map { it.value } + }.mapIndexed { index, (value) -> inputs[index] to value } }