mathpan/tree.go

499 lines
11 KiB
Go
Raw Normal View History

2022-11-20 22:51:09 -07:00
package main
import "math"
import "git.tebibyte.media/sashakoshka/stone"
var selectedExpression Expression
type Opcode int
const (
OpcodeUnknown Opcode = iota
OpcodeAdd
OpcodeSubtract
OpcodeMultiply
OpcodeDivide
OpcodePower
OpcodeRoot
OpcodeModulo
OpcodeOr
OpcodeNot
OpcodeAnd
OpcodeXor
2022-11-20 23:07:46 -07:00
OpcodeLeftShift
OpcodeRightShift
2022-11-20 22:51:09 -07:00
OpcodeMean
)
func (opcode Opcode) String () (output string) {
switch opcode {
2022-11-20 23:07:46 -07:00
case OpcodeUnknown: output = "?"
case OpcodeAdd: output = "+"
case OpcodeSubtract: output = "-"
case OpcodeMultiply: output = "*"
case OpcodeDivide: output = "/"
case OpcodePower: output = "p"
case OpcodeRoot: output = "r"
case OpcodeModulo: output = "%"
case OpcodeOr: output = "|"
case OpcodeNot: output = "~"
case OpcodeAnd: output = "&"
case OpcodeXor: output = "^"
case OpcodeLeftShift: output = "<<"
case OpcodeRightShift: output = ">>"
case OpcodeMean: output = "mean"
2022-11-20 22:51:09 -07:00
}
return
}
type Expression interface {
Parent () (parent *Operation)
Next () (next Expression)
Previous () (previous Expression)
Render (target stone.Buffer, offset int) (moved int)
Solution () (solution int64, err error)
// InexactSolution () (solution float64, err error)
}
type Operation struct {
parent *Operation
opcode Opcode
operands []Expression
}
func (operation *Operation) Parent () (parent *Operation) {
parent = operation.parent
return
}
func (operation *Operation) Adopt (child Expression) {
operation.operands = append(operation.operands, child)
operation.adopt(child)
}
func (operation *Operation) adopt (child Expression) {
switch child.(type) {
case *Operation:
child.(*Operation).parent = operation
case *IntegerLiteral:
child.(*IntegerLiteral).parent = operation
}
}
func (operation *Operation) Insert (after Expression, child Expression) {
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == after { found = true; break }
}
if !found { return }
index ++
operation.operands = append(operation.operands, nil)
copy(operation.operands[index + 1:], operation.operands[index:])
operation.operands[index] = child
operation.adopt(child)
}
func (operation *Operation) Disown (child Expression) {
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == child { found = true; break }
}
if !found { return }
operation.operands = append (
operation.operands[:index],
operation.operands[index + 1:]...)
}
func (operation *Operation) Swap (child Expression, with Expression) {
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == child { found = true; break }
}
if !found { return }
operation.operands[index] = with
operation.adopt(with)
}
func (operation *Operation) Next () (next Expression) {
if operation == selectedExpression && len(operation.operands) > 0 {
// selection descends into this operation
next = operation.operands[0]
return
}
next = operation.next(selectedExpression)
return
}
func (operation *Operation) next (after Expression) (next Expression) {
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == after { found = true; break }
}
if !found { return }
index ++
if index >= len(operation.operands) {
// selection goes out of bounds to the right, ascending
// out of this operation
if operation.parent != nil {
next = operation.parent.next(operation)
}
} else {
// selection moves to the right within this operation
next = operation.operands[index]
}
return
}
func (operation *Operation) Previous () (previous Expression) {
if operation == selectedExpression {
if operation.parent != nil {
previous = operation.parent.Previous()
}
return
}
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == selectedExpression { found = true; break }
}
if !found {
if operation.parent != nil {
previous = operation.parent.Next()
}
} else {
index --
if index < 0 {
previous = operation
} else {
previous = operation.operands[index]
subOperation, isOperation := previous.(*Operation)
if isOperation {
previous = subOperation.Last()
if previous == nil {
previous = subOperation
}
}
}
}
return
}
func (operation *Operation) Last () (last Expression) {
if len(operation.operands) < 1 { return }
last = operation.operands[len(operation.operands) - 1]
return
}
func (operation *Operation) Render (target stone.Buffer, offset int) (moved int) {
target.SetRune(offset, 0, '[')
if selectedExpression == operation {
target.SetColor(offset, 0, stone.ColorBlue)
} else {
target.SetColor(offset, 0, stone.ColorDim)
}
moved ++
opcodeSymbol := operation.opcode.String()
for _, character := range opcodeSymbol {
target.SetRune(offset + moved, 0, character)
if selectedExpression == operation {
target.SetColor(offset + moved, 0, stone.ColorBlue)
} else {
target.SetColor(offset + moved, 0, stone.ColorYellow)
}
moved ++
}
for _, operand := range operation.operands {
target.SetRune(offset + moved, 0, ' ')
moved ++
moved += operand.Render(target, offset + moved)
}
target.SetRune(offset + moved, 0, ']')
if selectedExpression == operation {
target.SetColor(offset + moved, 0, stone.ColorBlue)
} else {
target.SetColor(offset + moved, 0, stone.ColorDim)
}
moved ++
return
}
func (operation *Operation) Solution () (solution int64, err error) {
var subSolution int64
switch operation.opcode {
case OpcodeAdd:
for _, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { return }
solution += subSolution
}
case OpcodeSubtract:
2022-11-21 11:49:36 -07:00
if len(operation.operands) == 1 {
solution, err = operation.operands[0].Solution()
solution *= -1
if err != nil { return }
break
}
for index, operand := range operation.operands {
2022-11-20 22:51:09 -07:00
subSolution, err = operand.Solution()
if err != nil { return }
2022-11-21 11:49:36 -07:00
if index == 0 {
solution = subSolution
} else {
solution -= subSolution
}
2022-11-20 22:51:09 -07:00
}
case OpcodeMultiply:
solution = 1
for _, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { return }
solution *= subSolution
}
case OpcodeDivide:
for index, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { return }
if index == 0 {
solution = subSolution
} else if subSolution == 0 {
err = ErrorDivideByZero
} else {
solution /= subSolution
}
}
case OpcodePower:
for index, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { return }
if index == 0 {
solution = subSolution
} else {
solution = integerPower(solution, subSolution)
}
}
case OpcodeRoot:
for index, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { return }
if index == 0 {
solution = subSolution
} else {
solution = integerPower (
solution, subSolution * -1)
}
}
case OpcodeModulo:
for index, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { return }
if index == 0 {
solution = subSolution
} else {
solution %= subSolution
}
}
case OpcodeOr:
for _, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { return }
solution |= subSolution
}
case OpcodeNot:
if len(operation.operands) > 1 {
err = ErrorWrongOperandCount
return
}
case OpcodeAnd:
for index, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { return }
if index == 0 {
solution = subSolution
} else {
solution &= subSolution
}
}
case OpcodeXor:
for index, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { return }
if index == 0 {
solution = subSolution
} else {
solution ^= subSolution
}
}
2022-11-20 23:07:46 -07:00
case OpcodeLeftShift:
if len(operation.operands) != 2 {
err = ErrorWrongOperandCount
return
}
var left, right int64
left, err = operation.operands[0].Solution()
if err != nil { return }
right, err = operation.operands[1].Solution()
if err != nil { return }
if right < 0 {
err = ErrorNegativeShiftAmount
return
}
solution = left << right
case OpcodeRightShift:
if len(operation.operands) != 2 {
err = ErrorWrongOperandCount
return
}
var left, right int64
left, err = operation.operands[0].Solution()
if err != nil { return }
right, err = operation.operands[1].Solution()
if err != nil { return }
if right < 0 {
err = ErrorNegativeShiftAmount
return
}
solution = left >> right
2022-11-20 22:51:09 -07:00
case OpcodeMean:
if len(operation.operands) == 0 { break }
for _, operand := range operation.operands {
subSolution, err = operand.Solution()
if err != nil { break }
solution += subSolution
}
solution /= int64(len(operation.operands))
default:
err = ErrorUnknownOpcode
}
return
}
func integerPower (x, y int64) (result int64) {
if y == 0 {
result = 1
return
} else if y < 0 {
// FIXME: find some algorithm for the nth root of an integer
result = int64(math.Pow(float64(x), float64(y)))
}
result = x
for index := int64(2); index <= y; index ++ {
result *= x
}
return
}
type IntegerLiteral struct {
parent *Operation
displayRadix int
value int64
}
func (literal *IntegerLiteral) Parent () (parent *Operation) {
parent = literal.parent
return
}
func (literal *IntegerLiteral) Next () (next Expression) {
if literal.parent != nil {
next = literal.parent.Next()
}
return
}
func (literal *IntegerLiteral) Previous () (previous Expression) {
if literal.parent != nil {
previous = literal.parent.Previous()
}
return
}
func (literal *IntegerLiteral) Render (target stone.Buffer, offset int) (moved int) {
var output []rune
value := literal.value
for value > 0 {
digit := rune(value % int64(literal.displayRadix))
value /= int64(literal.displayRadix)
if digit < 10 {
digit += '0'
} else {
digit += 'A' - 10
}
output = append([]rune { digit }, output...)
}
if len(output) == 0 {
output = []rune { '0' }
}
switch literal.displayRadix {
case 2:
output = append([]rune("0b"), output...)
case 8:
output = append([]rune("0"), output...)
case 10:
case 16:
output = append([]rune("0x"), output...)
default:
output = append([]rune("0?"), output...)
}
for _, character := range output {
target.SetRune(offset + moved, 0, character)
if selectedExpression == literal {
target.SetColor(offset + moved, 0, stone.ColorBlue)
} else {
target.SetColor(offset + moved, 0, stone.ColorPurple)
}
moved ++
}
return
}
func (literal *IntegerLiteral) Solution () (solution int64, err error) {
solution = literal.value
return
}