commit 110d6b6dc66d0b2127f3a4d59e585369d1804aff
parent ae6ad02b566b0147af8bc33505574df571b7bad1
Author: NunoSempere <nuno.semperelh@protonmail.com>
Date: Sun, 9 Jun 2024 15:15:53 +0200
f2 to f, f to f0
Diffstat:
| M | f.go | | | 301 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
| C | f.go -> f0.go | | | 0 | |
| D | f2.go | | | 225 | ------------------------------------------------------------------------------- |
3 files changed, 148 insertions(+), 378 deletions(-)
diff --git a/f.go b/f.go
@@ -2,6 +2,7 @@ package main
import (
"bufio"
+ "errors"
"fmt"
"math"
"os"
@@ -10,37 +11,110 @@ import (
)
const NORMAL90CONFIDENCE = 1.6448536269514727
+const general_err_msg = "Valid inputs: 2 || * 2 || / 2 || 2 20 || * 2 20 || / 2 20 || clean || =: var || op var || clean || help || debug || exit"
-func boundsToLogParams(low float64, high float64) (float64, float64) {
- loglow := math.Log(low)
- loghigh := math.Log(high)
- logmean := (loghigh + loglow) / 2.0
- logstd := (loghigh - loglow) / (2.0 * NORMAL90CONFIDENCE)
- return logmean, logstd
+// Actually, I should look up how do do a) enums in go, b) union types
+type Lognormal struct {
+ low float64
+ high float64
+}
+type Dist struct {
+ Type string
+ Lognormal Lognormal
+ Samples []float64
}
-func multiplyLognormals(logmean1 float64, logstd1 float64, logmean2 float64, logstd2 float64) (float64, float64) {
- return logmean1 + logmean2, math.Sqrt(logstd1*logstd1 + logstd2*logstd2)
+// Parse line into Distribution
+func parseLineErr(err_msg string) (string, Dist, error) {
+ fmt.Println(general_err_msg)
+ fmt.Println(err_msg)
+ return "", Dist{}, errors.New(err_msg)
}
+func parseLine(line string, vars map[string]Dist) (string, Dist, error) {
+
+ words := strings.Split(strings.TrimSpace(line), " ")
+ op := ""
+ var dist Dist
+
+ switch words[0] {
+ case "*":
+ op = "*"
+ words = words[1:]
+ case "/":
+ op = "/"
+ words = words[1:]
+ case "+":
+ return parseLineErr("+ operation not implemented yet")
+ case "-":
+ return parseLineErr("- operation not implemented yet")
+ default:
+ op = "*" // later, change the below to
+ }
+
+ switch len(words) {
+ case 0:
+ return parseLineErr("Operator must have operand; can't operate on nothing")
+ case 1:
+ var_word, var_word_exists := vars[words[0]]
+ single_float, err1 := strconv.ParseFloat(words[0], 64)
+ switch {
+ case var_word_exists:
+ dist = var_word
+ case err1 == nil:
+ dist = Dist{Type: "Lognormal", Lognormal: Lognormal{low: single_float, high: single_float}, Samples: nil}
+ case err1 != nil && !var_word_exists:
+ return parseLineErr("Trying to operate on a scalar, but scalar is neither a float nor an assigned variable")
+ }
+ case 2:
+ new_low, err1 := strconv.ParseFloat(words[0], 64)
+ new_high, err2 := strconv.ParseFloat(words[1], 64)
+ if err1 != nil || err2 != nil {
+ return parseLineErr("Trying to operate by a distribution, but distribution is not specified as two floats")
+ }
+ dist = Dist{Type: "Lognormal", Lognormal: Lognormal{low: new_low, high: new_high}, Samples: nil}
+ default:
+ return parseLineErr("Other input methods not implemented yet")
+ }
+ return op, dist, nil
-func logParamsToBounds(logmean float64, logstd float64) (float64, float64) {
- h := logstd * NORMAL90CONFIDENCE
- loglow := logmean - h
- loghigh := logmean + h
- return math.Exp(loglow), math.Exp(loghigh)
}
-func combineBounds(old_low, old_high, new_low, new_high float64) (float64, float64) {
- logmean_old, logstd_old := boundsToLogParams(old_low, old_high)
- logmean_new, logstd_new := boundsToLogParams(new_low, new_high)
+// Join distributions
+// Multiply lognormals
+
+func multiplyLogDists(l1 Lognormal, l2 Lognormal) Lognormal {
+ logmean1 := (math.Log(l1.high) + math.Log(l1.low)) / 2.0
+ logstd1 := (math.Log(l1.high) - math.Log(l1.low)) / (2.0 * NORMAL90CONFIDENCE)
- logmean_product, logstd_product := multiplyLognormals(logmean_old, logstd_old, logmean_new, logstd_new)
+ logmean2 := (math.Log(l2.high) + math.Log(l2.low)) / 2.0
+ logstd2 := (math.Log(l2.high) - math.Log(l2.low)) / (2.0 * NORMAL90CONFIDENCE)
+
+ logmean_product := logmean1 + logmean2
+ logstd_product := math.Sqrt(logstd1*logstd1 + logstd2*logstd2)
+
+ h := logstd_product * NORMAL90CONFIDENCE
+ loglow := logmean_product - h
+ loghigh := logmean_product + h
+ return Lognormal{low: math.Exp(loglow), high: math.Exp(loghigh)}
- return logParamsToBounds(logmean_product, logstd_product)
}
-func prettyPrintDist(low float64, high float64) {
+func joinDists(old_dist Dist, new_dist Dist, op string) (Dist, error) {
+ switch {
+ case old_dist.Type == "Lognormal" && new_dist.Type == "Lognormal" && op == "*":
+ return Dist{Type: "Lognormal", Lognormal: multiplyLogDists(old_dist.Lognormal, new_dist.Lognormal), Samples: nil}, nil
+ case old_dist.Type == "Lognormal" && new_dist.Type == "Lognormal" && op == "/":
+ tmp_dist := Lognormal{low: 1.0 / new_dist.Lognormal.high, high: 1.0 / new_dist.Lognormal.low}
+ return Dist{Type: "Lognormal", Lognormal: multiplyLogDists(old_dist.Lognormal, tmp_dist), Samples: nil}, nil
+ default:
+ fmt.Printf("For now, can't do anything besides multiplying lognormals\n")
+ }
+ return old_dist, errors.New("Can't combine distributions in this way")
+}
+
+/* Pretty print distributions */
+func prettyPrintLognormal(low float64, high float64) {
// fmt.Printf("=> %.1f %.1f\n", low, high)
fmt.Printf("=> ")
switch {
@@ -76,155 +150,76 @@ func prettyPrintDist(low float64, high float64) {
// fmt.Printf("=> %.1f %.1f\n", low, high)
}
-func main() {
- reader := bufio.NewReader(os.Stdin)
-
- var old_low, old_high float64
- var input string
- var err1, err2 error
-
-InitialForLoop:
- for {
- input, _ = reader.ReadString('\n')
- input = strings.TrimSpace(input)
- words := strings.Split(input, " ")
-
- switch len(words) {
- case 1:
- single_float, err1 := strconv.ParseFloat(words[0], 64)
- if err1 != nil {
- fmt.Println("Trying to initialize with a scalar, but scalar is not a float")
- continue InitialForLoop
- }
- old_low = single_float
- old_high = single_float
- case 2:
- old_low, err1 = strconv.ParseFloat(words[0], 64)
- old_high, err2 = strconv.ParseFloat(words[1], 64)
- if err1 != nil || err2 != nil {
- fmt.Println("Trying to initialize with a distribution, but distribution is not specified as two floats")
- continue InitialForLoop
- }
- default:
- fmt.Println("Please enter two floats separated by a space, like: 1 10")
- continue InitialForLoop
- }
- if err1 != nil || err2 != nil {
- fmt.Println("Please enter two floats separated by a space, like: 1 10")
- continue
- }
- break
+func prettyPrintDist(dist Dist) {
+ if dist.Type == "Lognormal" {
+ prettyPrintLognormal(dist.Lognormal.low, dist.Lognormal.high)
+ } else {
+ fmt.Printf("%v", dist)
}
- prettyPrintDist(old_low, old_high)
+}
- error_msg_cont := "Valid inputs: 2 || * 2 || / 2 || 2 20 || * 2 20 || / 2 20 || i || e"
+/* Main event loop */
+func main() {
+ reader := bufio.NewReader(os.Stdin)
+ init_dist := Dist{Type: "Lognormal", Lognormal: Lognormal{low: 1, high: 1}, Samples: nil} // Could also just be a scalar
+ old_dist := init_dist
+ vars := make(map[string]Dist)
+ // Could eventually be a more complex struct with:
+ // { Dist, VariableMaps, ConfigParams } or smth
EventForLoop:
for {
- input, _ = reader.ReadString('\n')
+ input, _ := reader.ReadString('\n')
if strings.TrimSpace(input) == "" {
continue EventForLoop
}
- words := strings.Split(strings.TrimSpace(input), " ")
-
- var new_low, new_high float64
- switch words[0] {
- case "*":
- switch len(words) {
- case 1:
- fmt.Println("Can't multiply by nothing")
- fmt.Println(error_msg_cont)
+ {
+ words := strings.Split(strings.TrimSpace(input), " ")
+ switch {
+ case words[0] == "exit" || words[0] == "e":
+ break EventForLoop
+ case words[0] == "help" || words[0] == "h":
+ fmt.Println(general_err_msg)
continue EventForLoop
- case 2:
- single_float, err1 := strconv.ParseFloat(words[1], 64)
- if err1 != nil {
- fmt.Println("Trying to multiply by a scalar, but scalar is not a float")
- fmt.Println(error_msg_cont)
- continue EventForLoop
- }
- new_low = single_float
- new_high = single_float
- case 3:
- new_low, err1 = strconv.ParseFloat(words[1], 64)
- new_high, err2 = strconv.ParseFloat(words[2], 64)
- if err1 != nil || err2 != nil {
- fmt.Println(error_msg_cont)
- fmt.Println("Trying to multiply by a distribution, but distribution is not specified as two floats")
- continue EventForLoop
- }
- default:
- fmt.Println("Trying to multiply by something, but this something is neither a scalar nor a distribution")
- fmt.Println(error_msg_cont)
+ case words[0] == "debug" || words[0] == "d":
+ fmt.Printf("Old dist: %v\n", old_dist)
+ fmt.Printf("Vars: %v\n", vars)
continue EventForLoop
- }
- case "/":
- switch len(words) {
- case 1:
- fmt.Println("Can't divide by nothing")
- fmt.Println(error_msg_cont)
+ case words[0] == "=:" && len(words) == 2:
+ vars[words[1]] = old_dist
+ fmt.Printf("%s ", words[1])
+ prettyPrintDist(old_dist)
continue EventForLoop
- case 2:
- single_float, err1 := strconv.ParseFloat(words[1], 64)
- if err1 != nil {
- fmt.Println("Trying to divide by a scalar, but scalar is not a float")
- fmt.Println(error_msg_cont)
- continue EventForLoop
- }
- new_low = 1.0 / single_float
- new_high = 1.0 / single_float
- case 3:
- new_low, err1 = strconv.ParseFloat(words[1], 64)
- new_high, err2 = strconv.ParseFloat(words[2], 64)
- if err1 != nil || err2 != nil {
- fmt.Println("Trying to divide by a distribution, but distribution is not specified as two floats")
- fmt.Println(error_msg_cont)
- continue EventForLoop
- }
- tmp := new_low
- new_low = 1.0 / new_high
- new_high = 1.0 / tmp
- default:
- fmt.Println("Trying to divide by something, but this something is neither a scalar nor a distribution")
- }
- default:
- switch len(words) {
- case 0:
+ case words[0] == "." || words[0] == "clean" || words[0] == "c":
+ old_dist = init_dist
+ fmt.Println()
continue EventForLoop
- case 1:
- switch words[0] {
- case "i":
- fmt.Printf("=> %.1f %.1f\n", old_low, old_high)
- logmean_old, logstd_old := boundsToLogParams(old_low, old_high)
- fmt.Printf("=> Lognormal, with logmean: %.1f, logstd: %.1f\n", logmean_old, logstd_old)
- continue EventForLoop
- case "e":
- break EventForLoop
- default:
- single_float, err1 := strconv.ParseFloat(words[0], 64)
- if err1 != nil {
- fmt.Println("Unrecognized command")
- fmt.Println(error_msg_cont)
- continue EventForLoop
- }
- new_low = single_float
- new_high = single_float
- }
- case 2:
- new_low, err1 = strconv.ParseFloat(words[0], 64)
- new_high, err2 = strconv.ParseFloat(words[1], 64)
- if err1 != nil || err2 != nil {
- fmt.Println("Trying to multiply by a distribution, but distribution is not specified as two floats")
- fmt.Println(error_msg_cont)
- continue EventForLoop
- }
- default:
- fmt.Println("No operation takes more than 3 words")
- fmt.Println(error_msg_cont)
+ case words[0] == "=." && len(words) == 2:
+ vars[words[1]] = old_dist
+ fmt.Printf("%s ", words[1])
+ prettyPrintDist(old_dist)
+ old_dist = init_dist
+ fmt.Println()
continue EventForLoop
+ // Other possible cases:
+ // Save to file
+ // Sample n samples
+ // Save stack to a variable?
+ // clean stack
+ // Define a function? No, too much of a nerdsnipea
}
}
- old_low, old_high = combineBounds(old_low, old_high, new_low, new_high)
- prettyPrintDist(old_low, old_high)
+ op, new_dist, err := parseLine(input, vars)
+ if err != nil {
+ continue EventForLoop
+ }
+
+ joint_dist, err := joinDists(old_dist, new_dist, op)
+ if err != nil {
+ continue EventForLoop
+ }
+ old_dist = joint_dist
+ prettyPrintDist(old_dist)
}
}
diff --git a/f.go b/f0.go
diff --git a/f2.go b/f2.go
@@ -1,225 +0,0 @@
-package main
-
-import (
- "bufio"
- "errors"
- "fmt"
- "math"
- "os"
- "strconv"
- "strings"
-)
-
-const NORMAL90CONFIDENCE = 1.6448536269514727
-const general_err_msg = "Valid inputs: 2 || * 2 || / 2 || 2 20 || * 2 20 || / 2 20 || clean || =: var || op var || clean || help || debug || exit"
-
-// Actually, I should look up how do do a) enums in go, b) union types
-type Lognormal struct {
- low float64
- high float64
-}
-
-type Dist struct {
- Type string
- Lognormal Lognormal
- Samples []float64
-}
-
-// Parse line into Distribution
-func parseLineErr(err_msg string) (string, Dist, error) {
- fmt.Println(general_err_msg)
- fmt.Println(err_msg)
- return "", Dist{}, errors.New(err_msg)
-}
-func parseLine(line string, vars map[string]Dist) (string, Dist, error) {
-
- words := strings.Split(strings.TrimSpace(line), " ")
- op := ""
- var dist Dist
-
- switch words[0] {
- case "*":
- op = "*"
- words = words[1:]
- case "/":
- op = "/"
- words = words[1:]
- case "+":
- return parseLineErr("+ operation not implemented yet")
- case "-":
- return parseLineErr("- operation not implemented yet")
- default:
- op = "*" // later, change the below to
- }
-
- switch len(words) {
- case 0:
- return parseLineErr("Operator must have operand; can't operate on nothing")
- case 1:
- var_word, var_word_exists := vars[words[0]]
- single_float, err1 := strconv.ParseFloat(words[0], 64)
- switch {
- case var_word_exists:
- dist = var_word
- case err1 == nil:
- dist = Dist{Type: "Lognormal", Lognormal: Lognormal{low: single_float, high: single_float}, Samples: nil}
- case err1 != nil && !var_word_exists:
- return parseLineErr("Trying to operate on a scalar, but scalar is neither a float nor an assigned variable")
- }
- case 2:
- new_low, err1 := strconv.ParseFloat(words[0], 64)
- new_high, err2 := strconv.ParseFloat(words[1], 64)
- if err1 != nil || err2 != nil {
- return parseLineErr("Trying to operate by a distribution, but distribution is not specified as two floats")
- }
- dist = Dist{Type: "Lognormal", Lognormal: Lognormal{low: new_low, high: new_high}, Samples: nil}
- default:
- return parseLineErr("Other input methods not implemented yet")
- }
- return op, dist, nil
-
-}
-
-// Join distributions
-// Multiply lognormals
-
-func multiplyLogDists(l1 Lognormal, l2 Lognormal) Lognormal {
- logmean1 := (math.Log(l1.high) + math.Log(l1.low)) / 2.0
- logstd1 := (math.Log(l1.high) - math.Log(l1.low)) / (2.0 * NORMAL90CONFIDENCE)
-
- logmean2 := (math.Log(l2.high) + math.Log(l2.low)) / 2.0
- logstd2 := (math.Log(l2.high) - math.Log(l2.low)) / (2.0 * NORMAL90CONFIDENCE)
-
- logmean_product := logmean1 + logmean2
- logstd_product := math.Sqrt(logstd1*logstd1 + logstd2*logstd2)
-
- h := logstd_product * NORMAL90CONFIDENCE
- loglow := logmean_product - h
- loghigh := logmean_product + h
- return Lognormal{low: math.Exp(loglow), high: math.Exp(loghigh)}
-
-}
-
-func joinDists(old_dist Dist, new_dist Dist, op string) (Dist, error) {
- switch {
- case old_dist.Type == "Lognormal" && new_dist.Type == "Lognormal" && op == "*":
- return Dist{Type: "Lognormal", Lognormal: multiplyLogDists(old_dist.Lognormal, new_dist.Lognormal), Samples: nil}, nil
- case old_dist.Type == "Lognormal" && new_dist.Type == "Lognormal" && op == "/":
- tmp_dist := Lognormal{low: 1.0 / new_dist.Lognormal.high, high: 1.0 / new_dist.Lognormal.low}
- return Dist{Type: "Lognormal", Lognormal: multiplyLogDists(old_dist.Lognormal, tmp_dist), Samples: nil}, nil
- default:
- fmt.Printf("For now, can't do anything besides multiplying lognormals\n")
- }
- return old_dist, errors.New("Can't combine distributions in this way")
-}
-
-/* Pretty print distributions */
-func prettyPrintLognormal(low float64, high float64) {
- // fmt.Printf("=> %.1f %.1f\n", low, high)
- fmt.Printf("=> ")
- switch {
- case math.Abs(low) >= 1_000_000_000_000:
- fmt.Printf("%.1fT", low/1_000_000_000_000)
- case math.Abs(low) >= 1_000_000_000:
- fmt.Printf("%.1fB", low/1_000_000_000)
- case math.Abs(low) >= 1_000_000:
- fmt.Printf("%.1fM", low/1_000_000)
- case math.Abs(low) >= 1_000:
- fmt.Printf("%.1fK", low/1_000)
- case math.Abs(low) >= 1_000:
- fmt.Printf("%.1fK", low/1_000)
- default:
- fmt.Printf("%.1f", low)
- }
- fmt.Printf(" ")
- switch {
- case math.Abs(high) >= 1_000_000_000_000:
- fmt.Printf("%.1fT", high/1_000_000_000_000)
- case math.Abs(high) >= 1_000_000_000:
- fmt.Printf("%.1fB", high/1_000_000_000)
- case math.Abs(high) >= 1_000_000:
- fmt.Printf("%.1fM", high/1_000_000)
- case math.Abs(high) >= 1_000:
- fmt.Printf("%.1fK", high/1_000)
- case math.Abs(high) >= 1_000:
- fmt.Printf("%.1fK", high/1_000)
- default:
- fmt.Printf("%.1f", high)
- }
- fmt.Printf("\n")
- // fmt.Printf("=> %.1f %.1f\n", low, high)
-}
-
-func prettyPrintDist(dist Dist) {
- if dist.Type == "Lognormal" {
- prettyPrintLognormal(dist.Lognormal.low, dist.Lognormal.high)
- } else {
- fmt.Printf("%v", dist)
- }
-}
-
-/* Main event loop */
-func main() {
- reader := bufio.NewReader(os.Stdin)
- init_dist := Dist{Type: "Lognormal", Lognormal: Lognormal{low: 1, high: 1}, Samples: nil} // Could also just be a scalar
- old_dist := init_dist
- vars := make(map[string]Dist)
- // Could eventually be a more complex struct with:
- // { Dist, VariableMaps, ConfigParams } or smth
-EventForLoop:
- for {
- input, _ := reader.ReadString('\n')
- if strings.TrimSpace(input) == "" {
- continue EventForLoop
- }
-
- {
- words := strings.Split(strings.TrimSpace(input), " ")
- switch {
- case words[0] == "exit" || words[0] == "e":
- break EventForLoop
- case words[0] == "help" || words[0] == "h":
- fmt.Println(general_err_msg)
- continue EventForLoop
- case words[0] == "debug" || words[0] == "d":
- fmt.Printf("Old dist: %v\n", old_dist)
- fmt.Printf("Vars: %v\n", vars)
- continue EventForLoop
- case words[0] == "=:" && len(words) == 2:
- vars[words[1]] = old_dist
- fmt.Printf("%s ", words[1])
- prettyPrintDist(old_dist)
- continue EventForLoop
- case words[0] == "." || words[0] == "clean" || words[0] == "c":
- old_dist = init_dist
- fmt.Println()
- continue EventForLoop
- case words[0] == "=." && len(words) == 2:
- vars[words[1]] = old_dist
- fmt.Printf("%s ", words[1])
- prettyPrintDist(old_dist)
- old_dist = init_dist
- fmt.Println()
- continue EventForLoop
- // Other possible cases:
- // Save to file
- // Sample n samples
- // Save stack to a variable?
- // clean stack
- // Define a function? No, too much of a nerdsnipea
- }
- }
-
- op, new_dist, err := parseLine(input, vars)
- if err != nil {
- continue EventForLoop
- }
-
- joint_dist, err := joinDists(old_dist, new_dist, op)
- if err != nil {
- continue EventForLoop
- }
- old_dist = joint_dist
- prettyPrintDist(old_dist)
- }
-}