///|
priv enum PdfCalculatorValue {
PdfCalculatorInt(Int)
PdfCalculatorFloat(Double)
PdfCalculatorBoolean(Bool)
}
///|
fn pdf_fun_calculator_pop_value(
stack : Array[PdfCalculatorValue],
) -> PdfCalculatorValue raise @core.PdfError {
match stack.pop() {
Some(value) => value
None => raise BadFunctionEvaluation("calculator stack underflow")
}
}
///|
fn pdf_fun_calculator_pop_number(
stack : Array[PdfCalculatorValue],
) -> Double raise @core.PdfError {
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) => value.to_double()
PdfCalculatorFloat(value) => value
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator number expected")
}
}
///|
fn pdf_fun_calculator_pop_int(
stack : Array[PdfCalculatorValue],
) -> Int raise @core.PdfError {
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) => value
_ => raise BadFunctionEvaluation("calculator integer operand expected")
}
}
///|
fn pdf_fun_calculator_pop_bool(
stack : Array[PdfCalculatorValue],
) -> Bool raise @core.PdfError {
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorBoolean(value) => value
PdfCalculatorInt(_) | PdfCalculatorFloat(_) =>
raise BadFunctionEvaluation("calculator boolean expected")
}
}
///|
fn pdf_fun_calculator_eval_operations(
operations : ArrayView[PdfCalculatorOp],
stack : Array[PdfCalculatorValue],
) -> Unit raise @core.PdfError {
for operation in operations {
match operation {
PdfCalculatorInt(value) => stack.push(PdfCalculatorInt(value))
PdfCalculatorFloat(value) => stack.push(PdfCalculatorFloat(value))
PdfCalculatorAbs =>
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) =>
if value == pdf_fun_calculator_int_min() {
stack.push(
PdfCalculatorFloat(pdf_fun_calculator_int_max().to_double()),
)
} else {
stack.push(PdfCalculatorInt(value.abs()))
}
PdfCalculatorFloat(value) =>
stack.push(PdfCalculatorFloat(value.abs()))
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator number expected")
}
PdfCalculatorAdd =>
pdf_fun_calculator_numeric_binary(
stack,
fn(left, right) { left + right },
fn(left, right) { left + right },
)
PdfCalculatorAnd =>
pdf_fun_calculator_boolean_or_integer_binary(
stack,
fn(left, right) { left && right },
fn(left, right) { left & right },
)
PdfCalculatorBool(value) => stack.push(PdfCalculatorBoolean(value))
PdfCalculatorBitshift => {
let shift = pdf_fun_calculator_pop_int(stack)
let value = pdf_fun_calculator_pop_int(stack)
stack.push(PdfCalculatorInt(pdf_fun_calculator_bitshift(value, shift)))
}
PdfCalculatorAtan => {
let denominator = pdf_fun_calculator_pop_number(stack)
let numerator = pdf_fun_calculator_pop_number(stack)
stack.push(
PdfCalculatorFloat(
pdf_fun_radians_to_degrees(@math.atan2(numerator, denominator)),
),
)
}
PdfCalculatorCeiling =>
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) => stack.push(PdfCalculatorInt(value))
PdfCalculatorFloat(value) =>
stack.push(PdfCalculatorFloat(value.ceil()))
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator number expected")
}
PdfCalculatorCos =>
stack.push(
PdfCalculatorFloat(
@math.cos(
pdf_fun_degrees_to_radians(pdf_fun_calculator_pop_number(stack)),
),
),
)
PdfCalculatorCopy => pdf_fun_calculator_copy(stack)
PdfCalculatorCvi =>
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) => stack.push(PdfCalculatorInt(value))
PdfCalculatorFloat(value) =>
stack.push(PdfCalculatorInt(value.floor().to_int()))
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator number expected")
}
PdfCalculatorCvr =>
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) =>
stack.push(PdfCalculatorFloat(value.to_double()))
PdfCalculatorFloat(value) => stack.push(PdfCalculatorFloat(value))
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator number expected")
}
PdfCalculatorSub =>
pdf_fun_calculator_numeric_binary(
stack,
fn(left, right) { left - right },
fn(left, right) { left - right },
)
PdfCalculatorMul =>
pdf_fun_calculator_numeric_binary(
stack,
fn(left, right) { left * right },
fn(left, right) { left * right },
)
PdfCalculatorDiv => {
let right = pdf_fun_calculator_pop_number(stack)
let left = pdf_fun_calculator_pop_number(stack)
stack.push(PdfCalculatorFloat(left / right))
}
PdfCalculatorDup => {
if stack.length() == 0 {
raise BadFunctionEvaluation("calculator stack underflow")
}
stack.push(stack[stack.length() - 1])
}
PdfCalculatorExch => {
if stack.length() < 2 {
raise BadFunctionEvaluation("calculator stack underflow")
}
let last = stack.length() - 1
let value = stack[last]
stack[last] = stack[last - 1]
stack[last - 1] = value
}
PdfCalculatorExp => {
let exponent = pdf_fun_calculator_pop_number(stack)
let base = pdf_fun_calculator_pop_number(stack)
stack.push(PdfCalculatorFloat(@math.pow(base, exponent)))
}
PdfCalculatorFloor =>
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) => stack.push(PdfCalculatorInt(value))
PdfCalculatorFloat(value) =>
stack.push(PdfCalculatorInt(value.floor().to_int()))
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator number expected")
}
PdfCalculatorIdiv => {
let right = pdf_fun_calculator_pop_int(stack)
let left = pdf_fun_calculator_pop_int(stack)
stack.push(PdfCalculatorInt(left / right))
}
PdfCalculatorIf(then_branch) =>
if pdf_fun_calculator_pop_bool(stack) {
pdf_fun_calculator_eval_operations(then_branch, stack)
}
PdfCalculatorIfElse(then_branch, else_branch) =>
if pdf_fun_calculator_pop_bool(stack) {
pdf_fun_calculator_eval_operations(then_branch, stack)
} else {
pdf_fun_calculator_eval_operations(else_branch, stack)
}
PdfCalculatorIndex => pdf_fun_calculator_index(stack)
PdfCalculatorLn =>
stack.push(
PdfCalculatorFloat(@math.ln(pdf_fun_calculator_pop_number(stack))),
)
PdfCalculatorLog =>
stack.push(
PdfCalculatorFloat(@math.log10(pdf_fun_calculator_pop_number(stack))),
)
PdfCalculatorEq => {
let right = pdf_fun_calculator_pop_value(stack)
let left = pdf_fun_calculator_pop_value(stack)
stack.push(
PdfCalculatorBoolean(pdf_fun_calculator_values_equal(left, right)),
)
}
PdfCalculatorGe => {
let right = pdf_fun_calculator_pop_number(stack)
let left = pdf_fun_calculator_pop_number(stack)
stack.push(PdfCalculatorBoolean(left >= right))
}
PdfCalculatorGt => {
let right = pdf_fun_calculator_pop_number(stack)
let left = pdf_fun_calculator_pop_number(stack)
stack.push(PdfCalculatorBoolean(left > right))
}
PdfCalculatorLe => {
let right = pdf_fun_calculator_pop_number(stack)
let left = pdf_fun_calculator_pop_number(stack)
stack.push(PdfCalculatorBoolean(left <= right))
}
PdfCalculatorLt => {
let right = pdf_fun_calculator_pop_number(stack)
let left = pdf_fun_calculator_pop_number(stack)
stack.push(PdfCalculatorBoolean(left < right))
}
PdfCalculatorMod => {
let right = pdf_fun_calculator_pop_int(stack)
let left = pdf_fun_calculator_pop_int(stack)
stack.push(PdfCalculatorInt(left % right))
}
PdfCalculatorNe => {
let right = pdf_fun_calculator_pop_value(stack)
let left = pdf_fun_calculator_pop_value(stack)
stack.push(
PdfCalculatorBoolean(!pdf_fun_calculator_values_equal(left, right)),
)
}
PdfCalculatorNeg =>
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) =>
if value == pdf_fun_calculator_int_min() {
stack.push(
PdfCalculatorFloat(pdf_fun_calculator_int_max().to_double()),
)
} else {
stack.push(PdfCalculatorInt(-value))
}
PdfCalculatorFloat(value) => stack.push(PdfCalculatorFloat(-value))
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator number expected")
}
PdfCalculatorNot => pdf_fun_calculator_not(stack)
PdfCalculatorOr =>
pdf_fun_calculator_boolean_or_integer_binary(
stack,
fn(left, right) { left || right },
fn(left, right) { left | right },
)
PdfCalculatorPop => {
let _ = pdf_fun_calculator_pop_value(stack)
}
PdfCalculatorRoll => pdf_fun_calculator_roll(stack)
PdfCalculatorRound =>
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) => stack.push(PdfCalculatorInt(value))
PdfCalculatorFloat(value) =>
stack.push(PdfCalculatorInt(value.round().to_int()))
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator number expected")
}
PdfCalculatorSin =>
stack.push(
PdfCalculatorFloat(
@math.sin(
pdf_fun_degrees_to_radians(pdf_fun_calculator_pop_number(stack)),
),
),
)
PdfCalculatorSqrt =>
stack.push(
PdfCalculatorFloat(pdf_fun_calculator_pop_number(stack).sqrt()),
)
PdfCalculatorTruncate =>
match pdf_fun_calculator_pop_value(stack) {
PdfCalculatorInt(value) => stack.push(PdfCalculatorInt(value))
PdfCalculatorFloat(value) =>
stack.push(PdfCalculatorInt(value.trunc().to_int()))
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator number expected")
}
PdfCalculatorXor =>
pdf_fun_calculator_boolean_or_integer_binary(
stack,
fn(left, right) { left != right },
fn(left, right) { left ^ right },
)
}
}
}
///|
fn PdfCalculatorFunction::eval_calculator(
self : PdfCalculatorFunction,
clamped_inputs : ArrayView[Double],
) -> Array[Double] raise @core.PdfError {
let stack : Array[PdfCalculatorValue] = []
for input in clamped_inputs {
stack.push(PdfCalculatorFloat(input))
}
pdf_fun_calculator_eval_operations(self.operations, stack)
let output : Array[Double] = []
for value in stack {
match value {
PdfCalculatorInt(value) => output.push(value.to_double())
PdfCalculatorFloat(value) => output.push(value)
PdfCalculatorBoolean(_) =>
raise BadFunctionEvaluation("calculator output must be numeric")
}
}
output
}