Files
tablizer/vendor/github.com/glycerine/zygomys/zygo/numerictower.go
2024-05-14 12:10:58 +02:00

247 lines
5.7 KiB
Go

package zygo
import (
"errors"
"math"
)
type IntegerOp int
const (
ShiftLeft IntegerOp = iota
ShiftRightArith
ShiftRightLog
Modulo
BitAnd
BitOr
BitXor
)
var WrongType error = errors.New("operands have invalid type")
func IntegerDo(op IntegerOp, a, b Sexp) (Sexp, error) {
var ia *SexpInt
var ib *SexpInt
switch i := a.(type) {
case *SexpInt:
ia = i
case *SexpUint64:
return UintegerDo(op, i, b)
case *SexpChar:
ia = &SexpInt{Val: int64(i.Val)}
default:
return SexpNull, WrongType
}
switch i := b.(type) {
case *SexpInt:
ib = i
case *SexpUint64:
return UintegerDo(op, &SexpUint64{Val: uint64(ia.Val)}, b)
case *SexpChar:
ib = &SexpInt{Val: int64(i.Val)}
default:
return SexpNull, WrongType
}
switch op {
case ShiftLeft:
return &SexpInt{Val: ia.Val << uint(ib.Val)}, nil
case ShiftRightArith:
return &SexpInt{Val: ia.Val >> uint(ib.Val)}, nil
case ShiftRightLog:
return &SexpInt{Val: int64(uint(ia.Val) >> uint(ib.Val))}, nil
case Modulo:
return &SexpInt{Val: ia.Val % ib.Val}, nil
case BitAnd:
return &SexpInt{Val: ia.Val & ib.Val}, nil
case BitOr:
return &SexpInt{Val: ia.Val | ib.Val}, nil
case BitXor:
return &SexpInt{Val: ia.Val ^ ib.Val}, nil
}
return SexpNull, errors.New("unrecognized shift operation")
}
func UintegerDo(op IntegerOp, ia *SexpUint64, b Sexp) (Sexp, error) {
var ib *SexpUint64
switch i := b.(type) {
case *SexpUint64:
ib = i
case *SexpInt:
ib = &SexpUint64{Val: uint64(i.Val)}
case *SexpChar:
ib = &SexpUint64{Val: uint64(i.Val)}
default:
return SexpNull, WrongType
}
switch op {
case ShiftLeft:
return &SexpUint64{Val: ia.Val << ib.Val}, nil
case ShiftRightArith:
return &SexpUint64{Val: ia.Val >> ib.Val}, nil
case ShiftRightLog:
return &SexpUint64{Val: ia.Val >> ib.Val}, nil
case Modulo:
return &SexpUint64{Val: ia.Val % ib.Val}, nil
case BitAnd:
return &SexpUint64{Val: ia.Val & ib.Val}, nil
case BitOr:
return &SexpUint64{Val: ia.Val | ib.Val}, nil
case BitXor:
return &SexpUint64{Val: ia.Val ^ ib.Val}, nil
}
return SexpNull, errors.New("unrecognized shift operation")
}
type NumericOp int
const (
Add NumericOp = iota
Sub
Mult
Div
Pow
)
func NumericFloatDo(op NumericOp, a, b *SexpFloat) Sexp {
switch op {
case Add:
return &SexpFloat{Val: a.Val + b.Val}
case Sub:
return &SexpFloat{Val: a.Val - b.Val}
case Mult:
return &SexpFloat{Val: a.Val * b.Val}
case Div:
return &SexpFloat{Val: a.Val / b.Val}
case Pow:
return &SexpFloat{Val: math.Pow(float64(a.Val), float64(b.Val))}
}
return SexpNull
}
func NumericIntDo(op NumericOp, a, b *SexpInt) Sexp {
switch op {
case Add:
return &SexpInt{Val: a.Val + b.Val}
case Sub:
return &SexpInt{Val: a.Val - b.Val}
case Mult:
return &SexpInt{Val: a.Val * b.Val}
case Div:
if a.Val%b.Val == 0 {
return &SexpInt{Val: a.Val / b.Val}
} else {
return &SexpFloat{Val: float64(a.Val) / float64(b.Val)}
}
case Pow:
return &SexpInt{Val: int64(math.Pow(float64(a.Val), float64(b.Val)))}
}
return SexpNull
}
func NumericUint64Do(op NumericOp, a, b *SexpUint64) Sexp {
switch op {
case Add:
return &SexpUint64{Val: a.Val + b.Val}
case Sub:
return &SexpUint64{Val: a.Val - b.Val}
case Mult:
return &SexpUint64{Val: a.Val * b.Val}
case Div:
if a.Val%b.Val == 0 {
return &SexpUint64{Val: a.Val / b.Val}
} else {
return &SexpFloat{Val: float64(a.Val) / float64(b.Val)}
}
case Pow:
return &SexpUint64{Val: uint64(math.Pow(float64(a.Val), float64(b.Val)))}
}
return SexpNull
}
func NumericMatchFloat(op NumericOp, a *SexpFloat, b Sexp) (Sexp, error) {
var fb *SexpFloat
switch tb := b.(type) {
case *SexpFloat:
fb = tb
case *SexpInt:
fb = &SexpFloat{Val: float64(tb.Val)}
case *SexpUint64:
fb = &SexpFloat{Val: float64(tb.Val)}
case *SexpChar:
fb = &SexpFloat{Val: float64(tb.Val)}
default:
return SexpNull, WrongType
}
return NumericFloatDo(op, a, fb), nil
}
func NumericMatchInt(op NumericOp, a *SexpInt, b Sexp) (Sexp, error) {
switch tb := b.(type) {
case *SexpFloat:
return NumericFloatDo(op, &SexpFloat{Val: float64(a.Val)}, tb), nil
case *SexpInt:
return NumericIntDo(op, a, tb), nil
case *SexpUint64:
return NumericUint64Do(op, &SexpUint64{Val: uint64(a.Val)}, tb), nil
case *SexpChar:
return NumericIntDo(op, a, &SexpInt{Val: int64(tb.Val)}), nil
}
return SexpNull, WrongType
}
func NumericMatchUint64(op NumericOp, a *SexpUint64, b Sexp) (Sexp, error) {
switch tb := b.(type) {
case *SexpFloat:
return NumericFloatDo(op, &SexpFloat{Val: float64(a.Val)}, tb), nil
case *SexpInt:
return NumericUint64Do(op, a, &SexpUint64{Val: uint64(tb.Val)}), nil
case *SexpUint64:
return NumericUint64Do(op, a, tb), nil
case *SexpChar:
return NumericUint64Do(op, a, &SexpUint64{Val: uint64(tb.Val)}), nil
}
return SexpNull, WrongType
}
func NumericMatchChar(op NumericOp, a *SexpChar, b Sexp) (Sexp, error) {
var res Sexp
switch tb := b.(type) {
case *SexpFloat:
res = NumericFloatDo(op, &SexpFloat{Val: float64(a.Val)}, tb)
case *SexpInt:
res = NumericIntDo(op, &SexpInt{Val: int64(a.Val)}, tb)
case *SexpUint64:
return NumericUint64Do(op, &SexpUint64{Val: uint64(a.Val)}, tb), nil
case *SexpChar:
res = NumericIntDo(op, &SexpInt{Val: int64(a.Val)}, &SexpInt{Val: int64(tb.Val)})
default:
return SexpNull, WrongType
}
switch tres := res.(type) {
case *SexpFloat:
return tres, nil
case *SexpInt:
return &SexpChar{Val: rune(tres.Val)}, nil
}
return SexpNull, errors.New("unexpected result")
}
func NumericDo(op NumericOp, a, b Sexp) (Sexp, error) {
switch ta := a.(type) {
case *SexpFloat:
return NumericMatchFloat(op, ta, b)
case *SexpInt:
return NumericMatchInt(op, ta, b)
case *SexpUint64:
return NumericMatchUint64(op, ta, b)
case *SexpChar:
return NumericMatchChar(op, ta, b)
}
return SexpNull, WrongType
}