diff --git a/.fermi.go.swp b/.fermi.go.swp new file mode 100644 index 0000000..c7848d5 Binary files /dev/null and b/.fermi.go.swp differ diff --git a/fermi.go b/fermi.go index 7b05951..ca596cf 100644 --- a/fermi.go +++ b/fermi.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "git.nunosempere.com/NunoSempere/fermi/sample" + "git.nunosempere.com/NunoSempere/fermi/pretty" "math" "os" "sort" @@ -39,33 +40,6 @@ type FilledSamples struct { xs []float64 } -/* Constants */ -const GENERAL_ERR_MSG = " Operation | Variable assignment | Special\n" + - " Operation: operator operand\n" + - " operator: (empty) | * | / | + | -\n" + - " operand: scalar | lognormal | beta | variable\n" + - " lognormal: low high\n" + - " beta: beta alpha beta\n" + - " Variable assignment: =: variable_name\n" + - " Clear stack: . | c | clear\n" + - " Variable assignment and clear stack: =. variable_name\n" + - " Other special operations: help | debug | exit\n" + - " Examples: \n" + - " + 2\n" + - " / 2.5\n" + - " * 1 10 (interpreted as lognormal)\n" + - " + 1 10\n" + - " * beta 1 10\n" + - " 1 10 (multiplication taken as default operation)\n" + - " =: x\n" + - " .\n" + - " 1 100\n" + - " + x\n" + - " exit\n" -const NORMAL90CONFIDENCE = 1.6448536269514727 -const INIT_DIST Scalar = Scalar(1) -const N_SAMPLES = 100_000 - /* Dist interface functions */ // https://go.dev/tour/methods/9 func (p Scalar) Samples() []float64 { @@ -93,8 +67,71 @@ func (fs FilledSamples) Samples() []float64 { return fs.xs } -// Parse line into Distribution +/* Constants */ +const GENERAL_ERR_MSG = " Operation | Variable assignment | Special\n" + + " Operation: operator operand\n" + + " operator: (empty) | * | / | + | -\n" + + " operand: scalar | lognormal | beta | variable\n" + + " lognormal: low high\n" + + " beta: beta alpha beta\n" + + " Variable assignment: =: variable_name\n" + + " Clear stack: . | c | clear\n" + + " Variable assignment and clear stack: =. variable_name\n" + + " Other special operations: help | debug | exit\n" + + " Examples: \n" + + " + 2\n" + + " / 2.5\n" + + " * 1 10 (interpreted as lognormal)\n" + + " + 1 10\n" + + " * beta 1 10\n" + + " 1 10 (multiplication taken as default operation)\n" + + " =: x\n" + + " .\n" + + " 1 100\n" + + " + x\n" + + " exit\n" +const NORMAL90CONFIDENCE = 1.6448536269514727 +const INIT_DIST Scalar = Scalar(1) +const N_SAMPLES = 100_000 +/* Pretty print for distributions */ +// Needs types +func prettyPrintDist(dist Dist) { + switch v := dist.(type) { + case Lognormal: + fmt.Printf("=> ") + pretty.PrettyPrint2Floats(v.low, v.high) + case FilledSamples: + tmp_xs := make([]float64, N_SAMPLES) + copy(tmp_xs, v.xs) + sort.Slice(tmp_xs, func(i, j int) bool { + return tmp_xs[i] < tmp_xs[j] + }) + low_int := N_SAMPLES / 20 + low := tmp_xs[low_int] + high_int := N_SAMPLES * 19 / 20 + high := tmp_xs[high_int] + fmt.Printf("=> ") + pretty.PrettyPrintFloat(low) + fmt.Printf(" ") + pretty.PrettyPrintFloat(high) + fmt.Printf(" (") + pretty.PrettyPrintInt(N_SAMPLES) + fmt.Printf(" samples)\n") + case Beta: + fmt.Printf("=> beta ") + pretty.PrettyPrint2Floats(v.a, v.b) + case Scalar: + fmt.Printf("=> scalar ") + w := float64(v) + pretty.PrettyPrintFloat(w) + fmt.Println() + default: + fmt.Printf("%v", v) + } +} + +// Parse line into Distribution func parseLineErr(err_msg string) (string, Dist, error) { fmt.Println(GENERAL_ERR_MSG) fmt.Println(err_msg) @@ -253,8 +290,10 @@ func divideDists(old_dist Dist, new_dist Dist) (Dist, error) { { switch n := new_dist.(type) { case Lognormal: + // to do: check division by zero return multiplyLogDists(o, Lognormal{low: 1.0 / n.high, high: 1.0 / n.low}), nil case Scalar: + // to do: check division by zero return multiplyLogDists(o, Lognormal{low: 1.0 / float64(n), high: 1.0 / float64(n)}), nil default: return operateDistsAsSamples(old_dist, new_dist, "/") @@ -266,6 +305,7 @@ func divideDists(old_dist Dist, new_dist Dist) (Dist, error) { case Lognormal: return multiplyLogDists(Lognormal{low: float64(o), high: float64(o)}, Lognormal{low: 1.0 / n.high, high: 1.0 / n.low}), nil case Scalar: + // to do: check division by zero return Scalar(float64(o) / float64(n)), nil default: return operateDistsAsSamples(old_dist, new_dist, "/") @@ -276,92 +316,9 @@ func divideDists(old_dist Dist, new_dist Dist) (Dist, error) { } } -/* Pretty print distributions - */ -func prettyPrintInt(n int) { - switch { - case math.Abs(float64(n)) >= 1_000_000_000_000: - fmt.Printf("%dT", n/1_000_000_000_000) - case math.Abs(float64(n)) >= 1_000_000_000: - fmt.Printf("%dB", n/1_000_000_000) - case math.Abs(float64(n)) >= 1_000_000: - fmt.Printf("%dM", n/1_000_000) - case math.Abs(float64(n)) >= 1_000: - fmt.Printf("%dK", n/1_000) - default: - fmt.Printf("%df", n) - } -} - -func prettyPrintFloat(f float64) { - switch { - case math.Abs(f) >= 1_000_000_000_000: - fmt.Printf("%.1fT", f/1_000_000_000_000) - case math.Abs(f) >= 1_000_000_000: - fmt.Printf("%.1fB", f/1_000_000_000) - case math.Abs(f) >= 1_000_000: - fmt.Printf("%.1fM", f/1_000_000) - case math.Abs(f) >= 1_000: - fmt.Printf("%.1fK", f/1_000) - - case math.Abs(f) <= 0.0001: - fmt.Printf("%.5f", f) - case math.Abs(f) <= 0.001: - fmt.Printf("%.4f", f) - case math.Abs(f) <= 0.01: - fmt.Printf("%.3f", f) - case math.Abs(f) <= 0.1: - fmt.Printf("%.2f", f) - default: - fmt.Printf("%.1f", f) - } - -} -func prettyPrint2Floats(low float64, high float64) { - prettyPrintFloat(low) - fmt.Printf(" ") - prettyPrintFloat(high) - fmt.Printf("\n") -} - -func prettyPrintDist(dist Dist) { - switch v := dist.(type) { - case Lognormal: - fmt.Printf("=> ") - prettyPrint2Floats(v.low, v.high) - case FilledSamples: - tmp_xs := make([]float64, N_SAMPLES) - copy(tmp_xs, v.xs) - sort.Slice(tmp_xs, func(i, j int) bool { - return tmp_xs[i] < tmp_xs[j] - }) - low_int := N_SAMPLES / 20 - low := tmp_xs[low_int] - high_int := N_SAMPLES * 19 / 20 - high := tmp_xs[high_int] - fmt.Printf("=> ") - prettyPrintFloat(low) - fmt.Printf(" ") - prettyPrintFloat(high) - fmt.Printf(" (") - prettyPrintInt(N_SAMPLES) - fmt.Printf(" samples)\n") - case Beta: - fmt.Printf("=> beta ") - prettyPrint2Floats(v.a, v.b) - case Scalar: - fmt.Printf("=> scalar ") - w := float64(v) - prettyPrintFloat(w) - fmt.Println() - default: - fmt.Printf("%v", v) - } -} - /* Combine old dist and new line */ // We want this as a function to be able to have parenthesis/recusion, possibly functions -func combineStackAndDist(stack Stack, new_dist Dist, op string) Stack { +func operateStackWithDist(stack Stack, new_dist Dist, op string) Stack { var combined_dist Dist var err error @@ -403,7 +360,7 @@ func runRepl(stack Stack, reader *bufio.Reader) Stack { /* Parenthesis */ case len(words) == 2 && (words[0] == "*" || words[0] == "+" || words[0] == "-" || words[0] == "/") && words[1] == "(": new_stack := runRepl(Stack{old_dist: INIT_DIST, vars: stack.vars}, reader) - stack = combineStackAndDist(stack, new_stack.old_dist, words[0]) + stack = operateStackWithDist(stack, new_stack.old_dist, words[0]) prettyPrintDist(stack.old_dist) case len(words) == 1 && words[0] == ")": return stack @@ -439,7 +396,7 @@ func runRepl(stack Stack, reader *bufio.Reader) Stack { if err != nil { continue replForLoop } - stack = combineStackAndDist(stack, new_dist, op) + stack = operateStackWithDist(stack, new_dist, op) prettyPrintDist(stack.old_dist) } } diff --git a/pretty/pretty.go b/pretty/pretty.go new file mode 100644 index 0000000..371f9b4 --- /dev/null +++ b/pretty/pretty.go @@ -0,0 +1,52 @@ +package pretty + +import ( + "fmt" + "math" +) + +func PrettyPrintInt(n int) { + switch { + case math.Abs(float64(n)) >= 1_000_000_000_000: + fmt.Printf("%dT", n/1_000_000_000_000) + case math.Abs(float64(n)) >= 1_000_000_000: + fmt.Printf("%dB", n/1_000_000_000) + case math.Abs(float64(n)) >= 1_000_000: + fmt.Printf("%dM", n/1_000_000) + case math.Abs(float64(n)) >= 1_000: + fmt.Printf("%dK", n/1_000) + default: + fmt.Printf("%df", n) + } +} + +func PrettyPrintFloat(f float64) { + switch { + case math.Abs(f) >= 1_000_000_000_000: + fmt.Printf("%.1fT", f/1_000_000_000_000) + case math.Abs(f) >= 1_000_000_000: + fmt.Printf("%.1fB", f/1_000_000_000) + case math.Abs(f) >= 1_000_000: + fmt.Printf("%.1fM", f/1_000_000) + case math.Abs(f) >= 1_000: + fmt.Printf("%.1fK", f/1_000) + + case math.Abs(f) <= 0.0001: + fmt.Printf("%.5f", f) + case math.Abs(f) <= 0.001: + fmt.Printf("%.4f", f) + case math.Abs(f) <= 0.01: + fmt.Printf("%.3f", f) + case math.Abs(f) <= 0.1: + fmt.Printf("%.2f", f) + default: + fmt.Printf("%.1f", f) + } + +} +func PrettyPrint2Floats(low float64, high float64) { + PrettyPrintFloat(low) + fmt.Printf(" ") + PrettyPrintFloat(high) + fmt.Printf("\n") +}