2024-05-10 18:05:03 +00:00
package main
import (
"bufio"
2024-07-12 16:11:25 +00:00
"flag"
2024-05-10 18:05:03 +00:00
"fmt"
2024-06-19 12:30:21 +00:00
"git.nunosempere.com/NunoSempere/fermi/sample"
2024-05-10 18:25:23 +00:00
"math"
2024-05-10 18:05:03 +00:00
"os"
"strings"
)
2024-06-19 02:02:42 +00:00
/* Types and interfaces */
type Stack struct {
old_dist Dist
vars map [ string ] Dist
}
2024-06-09 20:27:49 +00:00
2024-06-09 21:35:36 +00:00
type Dist interface {
2024-12-24 15:32:17 +00:00
Sampler ( int , sample . State ) float64
2024-06-09 22:24:06 +00:00
}
2024-06-19 02:02:42 +00:00
2024-06-10 01:08:10 +00:00
type Scalar float64
2024-06-19 02:02:42 +00:00
2024-06-10 01:08:10 +00:00
type Lognormal struct {
low float64
high float64
}
2024-06-19 02:02:42 +00:00
2024-06-10 01:08:10 +00:00
type Beta struct {
a float64
b float64
}
2024-06-19 02:02:42 +00:00
2024-06-10 01:08:10 +00:00
type FilledSamples struct {
xs [ ] float64
2024-06-09 22:24:06 +00:00
}
2024-06-19 02:44:24 +00:00
/* Dist interface functions */
// https://go.dev/tour/methods/9
2024-12-24 15:32:17 +00:00
func ( p Scalar ) Sampler ( i int , r sample . State ) float64 {
2024-12-24 15:11:37 +00:00
return float64 ( p )
}
2024-06-19 02:44:24 +00:00
2024-12-24 15:32:17 +00:00
func ( ln Lognormal ) Sampler ( i int , r sample . State ) float64 {
2024-12-28 22:25:35 +00:00
return sample . To ( ln . low , ln . high , r )
2024-12-24 15:11:37 +00:00
}
2024-06-19 02:44:24 +00:00
2024-12-24 15:32:17 +00:00
func ( beta Beta ) Sampler ( i int , r sample . State ) float64 {
2024-12-28 22:25:35 +00:00
return sample . Beta ( beta . a , beta . b , r )
2024-12-24 15:11:37 +00:00
}
2024-06-19 02:44:24 +00:00
2024-12-24 15:32:17 +00:00
func ( fs FilledSamples ) Sampler ( i int , r sample . State ) float64 {
// This is a bit subtle, because sampling from FilledSamples randomly iteratively converges
2024-12-24 15:11:37 +00:00
// to something different than the initial distribution
2024-12-24 15:32:17 +00:00
// So instead we have an i parameter.
2024-12-24 15:11:37 +00:00
return fs . xs [ i ]
}
2024-06-19 02:44:24 +00:00
2024-06-19 02:02:42 +00:00
/* Constants */
2024-10-01 07:56:31 +00:00
const HELP_MSG = "1. Grammar:\n" +
" Operation | Variable assignment | Special\n" +
2024-06-19 02:02:42 +00:00
" 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" +
" Variable assignment and clear stack: =. variable_name\n" +
2024-09-15 20:33:56 +00:00
" Special commands: \n" +
2024-07-07 14:30:35 +00:00
" Comment: # this is a comment\n" +
2024-10-01 07:56:31 +00:00
" Summary stats: stats\n" +
2024-07-07 14:30:35 +00:00
" Clear stack: clear | c | .\n" +
" Print debug info: debug | d\n" +
" Print help message: help | h\n" +
" Start additional stack: operator (\n" +
" Return from additional stack )\n" +
" Exit: exit | e\n" +
2024-06-19 02:02:42 +00:00
" 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" +
2024-12-28 23:36:09 +00:00
" =: x\n" +
2024-06-19 02:02:42 +00:00
" .\n" +
" 1 100\n" +
" + x\n" +
2024-07-07 14:30:35 +00:00
" # this is a comment\n" +
" * 1 12 # this is an operation followed by a comment\n" +
" * (\n" +
" 1 10\n" +
" + beta 1 100\n" +
" )\n" +
2024-12-28 23:36:09 +00:00
" =. y\n" +
" mx x 1 y 2.33\n" +
" + mx x 30% y 70%\n" +
2024-10-01 07:56:31 +00:00
" exit\n" +
"\n" +
"2. Command flags:\n" +
" -echo\n" +
" Specifies whether inputs should be echoed back. Useful if reading from a file\n." +
" -f string\n" +
" Specifies a file with a model to run\n" +
" -n int\n" +
" Specifies the number of samples to draw when using samples (default 100000)\n" +
2024-12-28 23:53:42 +00:00
" -h Shows help message\n"
2024-07-07 14:30:35 +00:00
2024-06-19 02:02:42 +00:00
const NORMAL90CONFIDENCE = 1.6448536269514727
const INIT_DIST Scalar = Scalar ( 1 )
2024-07-12 16:11:25 +00:00
2024-07-12 22:39:13 +00:00
var N_SAMPLES = 100_000
2024-06-19 02:02:42 +00:00
2024-06-19 13:46:08 +00:00
/* Operations */
// Generic operations with samples
2024-06-19 02:02:42 +00:00
func operateDistsAsSamples ( dist1 Dist , dist2 Dist , op string ) ( Dist , error ) {
2024-06-10 01:08:10 +00:00
2024-12-28 22:25:35 +00:00
xs := sample . Serially ( dist1 . Sampler , N_SAMPLES )
ys := sample . Serially ( dist2 . Sampler , N_SAMPLES )
2024-06-09 22:51:05 +00:00
zs := make ( [ ] float64 , N_SAMPLES )
for i := 0 ; i < N_SAMPLES ; i ++ {
2024-06-10 01:08:10 +00:00
switch op {
case "*" :
zs [ i ] = xs [ i ] * ys [ i ]
case "/" :
if ys [ 0 ] != 0 {
zs [ i ] = xs [ i ] / ys [ i ]
} else {
2024-12-28 23:36:09 +00:00
// fmt.Println("Error: When dividing as samples, division by zero")
return nil , FermiError { Code : "Division by zero" , Msg : "When operating on samples, division by zero" }
2024-06-10 01:08:10 +00:00
}
case "+" :
zs [ i ] = xs [ i ] + ys [ i ]
case "-" :
zs [ i ] = xs [ i ] - ys [ i ]
2024-09-15 20:36:51 +00:00
default :
2024-12-28 23:36:09 +00:00
// fmt.Println("Error: Operation not recognized")
return nil , FermiError { Code : "Unknown operation" , Msg : "When operating on samples, operation not recognized" }
2024-06-10 01:08:10 +00:00
}
2024-06-09 13:15:53 +00:00
}
2024-06-09 21:35:36 +00:00
2024-06-10 01:08:10 +00:00
return FilledSamples { xs : zs } , nil
2024-06-09 22:24:06 +00:00
}
2024-06-19 13:46:08 +00:00
// Multiplication
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 ) }
}
2024-11-20 11:54:31 +00:00
func multiplyLogDistAndScalar ( l Lognormal , s Scalar ) ( Dist , error ) {
if s == 0.0 {
return Scalar ( 0.0 ) , nil
} else if s < 0.0 {
2024-12-27 22:21:14 +00:00
return operateDistsAsSamples ( s , l , "*" )
2024-11-20 11:54:31 +00:00
} else {
return multiplyLogDists ( l , Lognormal { low : float64 ( s ) , high : float64 ( s ) } ) , nil
}
}
2024-06-09 22:24:06 +00:00
func multiplyDists ( old_dist Dist , new_dist Dist ) ( Dist , error ) {
switch o := old_dist . ( type ) {
case Lognormal :
{
switch n := new_dist . ( type ) {
case Lognormal :
return multiplyLogDists ( o , n ) , nil
case Scalar :
2024-11-20 11:54:31 +00:00
return multiplyLogDistAndScalar ( o , n )
2024-06-09 22:24:06 +00:00
}
}
case Scalar :
{
2024-11-20 11:54:31 +00:00
switch o {
case 1.0 :
2024-06-09 23:12:02 +00:00
return new_dist , nil
2024-11-20 11:54:31 +00:00
case 0.0 :
return Scalar ( 0.0 ) , nil
2024-06-09 23:12:02 +00:00
}
2024-06-09 22:24:06 +00:00
switch n := new_dist . ( type ) {
case Lognormal :
2024-11-20 11:54:31 +00:00
return multiplyLogDistAndScalar ( n , o )
2024-06-09 22:24:06 +00:00
case Scalar :
2024-06-10 01:08:10 +00:00
return Scalar ( float64 ( o ) * float64 ( n ) ) , nil
2024-06-09 22:24:06 +00:00
}
}
2024-06-10 01:08:10 +00:00
}
2024-09-15 20:33:56 +00:00
return operateDistsAsSamples ( old_dist , new_dist , "*" )
2024-06-10 01:08:10 +00:00
}
func divideDists ( old_dist Dist , new_dist Dist ) ( Dist , error ) {
switch o := old_dist . ( type ) {
2024-11-20 11:59:27 +00:00
// I miss you, OCaml switches
2024-06-10 01:08:10 +00:00
case Lognormal :
{
switch n := new_dist . ( type ) {
case Lognormal :
2024-06-19 13:46:08 +00:00
if n . high == 0 || n . low == 0 {
2024-12-28 23:36:09 +00:00
return nil , FermiError { Code : "Division by zero" , Msg : "When operating two lognormals, one of the parameters is zero, which would result in division by zero" }
2024-06-19 13:46:08 +00:00
}
2024-06-10 01:08:10 +00:00
return multiplyLogDists ( o , Lognormal { low : 1.0 / n . high , high : 1.0 / n . low } ) , nil
case Scalar :
2024-06-19 13:46:08 +00:00
if n == 0.0 {
2024-12-28 23:36:09 +00:00
return nil , FermiError { Code : "Division by zero" , Msg : "When operating a lognormal with a scalar, trying to divide but the scalar is zero" }
2024-06-19 13:46:08 +00:00
}
2024-11-20 11:54:31 +00:00
return multiplyLogDistAndScalar ( o , Scalar ( 1.0 / n ) )
2024-06-10 01:08:10 +00:00
}
}
case Scalar :
{
switch n := new_dist . ( type ) {
case Lognormal :
2024-11-20 11:54:31 +00:00
return multiplyLogDistAndScalar ( Lognormal { low : 1.0 / n . high , high : 1.0 / n . low } , o )
2024-06-10 01:08:10 +00:00
case Scalar :
2024-06-19 13:46:08 +00:00
if n == 0.0 {
2024-12-28 23:36:09 +00:00
return nil , FermiError { Code : "Division by zero" , Msg : "When operating two scalars, trying to divide but the divisor is is zero" }
2024-06-19 13:46:08 +00:00
}
2024-06-10 01:08:10 +00:00
return Scalar ( float64 ( o ) / float64 ( n ) ) , nil
}
}
2024-06-09 22:24:06 +00:00
}
2024-09-15 20:34:30 +00:00
return operateDistsAsSamples ( old_dist , new_dist , "/" )
2024-06-09 22:24:06 +00:00
}
2024-06-19 13:46:08 +00:00
// Generic distribution operations
func operateDists ( old_dist Dist , new_dist Dist , op string ) ( Dist , error ) {
2024-06-19 02:02:42 +00:00
switch op {
case "*" :
2024-06-19 13:46:08 +00:00
return multiplyDists ( old_dist , new_dist )
2024-06-19 02:02:42 +00:00
case "/" :
2024-06-19 13:46:08 +00:00
return divideDists ( old_dist , new_dist )
2024-06-19 02:02:42 +00:00
case "+" :
2024-06-19 13:46:08 +00:00
return operateDistsAsSamples ( old_dist , new_dist , "+" )
2024-06-19 02:02:42 +00:00
case "-" :
2024-06-19 13:46:08 +00:00
return operateDistsAsSamples ( old_dist , new_dist , "-" )
default :
2024-12-28 23:36:09 +00:00
return nil , FermiError { Code : "Unknown operation" , Msg : "When operating distributions, operation not recognized" }
2024-06-19 13:46:08 +00:00
}
}
2024-12-24 14:31:21 +00:00
/* Mixtures */
func parseMixture ( words [ ] string , vars map [ string ] Dist ) ( Dist , error ) {
// mx, mix, var weight var weight var weight ...
// Check syntax
2024-12-28 23:36:09 +00:00
switch {
case len ( words ) < 1 :
return nil , FermiError { Code : "Not a mixture" , Msg : "Input can't be a mixture, since it doesn't have enough words" }
case words [ 0 ] != "mx" :
return nil , FermiError { Code : "Not a mixture" , Msg : "Input can't be a mixture, since it is not preceded by the mx keyword" }
case len ( words ) % 2 != 1 :
return nil , FermiError { Code : "Not a mixture" , Msg : "When parsing a mixture, input doesn't have equal number of variables and weights. \nMixture syntax: \nmx x 20% y 70% z 10%\ni.e.: mx var weight var2 weight2 ... var_n weight_n" }
case len ( words ) < 5 :
return nil , FermiError { Code : "Not a mixture" , Msg : "When parsing a mixture, not enough words. \nMixture syntax: \nmx x 20% y 70% z 10%\ni.e.: mx var weight var2 weight2 ... var_n weight_n" }
2024-12-24 14:31:21 +00:00
}
2024-12-28 23:36:09 +00:00
words = words [ 1 : ] // crop "mx" at the beginning
2024-12-24 14:31:21 +00:00
2024-12-24 15:32:17 +00:00
var fs [ ] func ( int , sample . State ) float64
2024-12-24 14:31:21 +00:00
var weights [ ] float64
2024-12-24 14:39:45 +00:00
for i , word := range words {
2024-12-24 14:31:21 +00:00
if i % 2 == 0 {
dist , exists := vars [ word ]
if ! exists {
2024-12-28 23:36:09 +00:00
return nil , FermiError { Code : "Not a mixture variable" , Msg : "When parsing a mixture, expected mixture variable but didn't get a variable. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n" }
2024-12-24 14:31:21 +00:00
}
2024-12-24 15:32:17 +00:00
f := dist . Sampler
fs = append ( fs , f )
2024-12-24 14:31:21 +00:00
} else {
2024-12-28 22:25:35 +00:00
weight , err := ParseFloat ( word )
2024-12-24 14:31:21 +00:00
if err != nil {
2024-12-28 23:36:09 +00:00
return nil , FermiError { Code : "Not a mixture weight" , Msg : "When parsing a mixture, expected mixture weight but didn't get a variable. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n" }
2024-12-24 14:31:21 +00:00
}
weights = append ( weights , weight )
}
}
// Sample from mixture
2024-12-28 22:25:35 +00:00
xs , err := sample . Mixture_serially_from_samplers ( fs , weights , N_SAMPLES )
2024-12-24 14:31:21 +00:00
if err != nil {
2024-12-28 22:25:35 +00:00
return nil , PrintAndReturnErr ( err . Error ( ) )
2024-12-24 14:31:21 +00:00
}
return FilledSamples { xs : xs } , nil
}
2024-06-19 13:46:08 +00:00
/* Parser and repl */
2024-06-19 14:41:47 +00:00
func parseWordsIntoOpAndDist ( words [ ] string , vars map [ string ] Dist ) ( string , Dist , error ) {
2024-12-28 23:36:09 +00:00
parseWordsErr := func ( msg string ) ( string , Dist , error ) {
return "" , nil , FermiError { Code : "Malformed input" , Msg : "When parsing words into operator and distribution: " + msg }
}
2024-06-19 13:46:08 +00:00
op := ""
var dist Dist
switch words [ 0 ] {
2024-12-28 23:36:09 +00:00
case "*" , "/" , "+" , "-" :
2024-06-19 13:46:08 +00:00
op = words [ 0 ]
words = words [ 1 : ]
default :
2024-12-24 14:31:21 +00:00
op = "*"
2024-06-19 13:46:08 +00:00
}
2024-12-28 23:36:09 +00:00
switch {
case len ( words ) == 0 :
2024-06-19 14:41:47 +00:00
return parseWordsErr ( "Operator must have operand; can't operate on nothing" )
2024-12-28 23:36:09 +00:00
case len ( words ) == 1 :
2024-06-19 13:46:08 +00:00
var_word , var_word_exists := vars [ words [ 0 ] ]
2024-12-28 22:25:35 +00:00
single_float , err1 := ParseFloat ( words [ 0 ] ) // abstract this away to search for K/M/B/T/etc.
2024-06-19 13:46:08 +00:00
switch {
case var_word_exists :
dist = var_word
case err1 == nil :
dist = Scalar ( single_float )
case err1 != nil && ! var_word_exists :
2024-06-19 14:41:47 +00:00
return parseWordsErr ( "Trying to operate on a scalar, but scalar is neither a float nor an assigned variable" )
2024-06-19 13:46:08 +00:00
}
2024-12-28 23:36:09 +00:00
case len ( words ) == 2 :
2024-12-28 22:25:35 +00:00
new_low , err1 := ParseFloat ( words [ 0 ] )
new_high , err2 := ParseFloat ( words [ 1 ] )
2024-11-20 11:54:31 +00:00
switch {
case err1 != nil || err2 != nil :
2024-06-19 14:41:47 +00:00
return parseWordsErr ( "Trying to operate by a distribution, but distribution is not specified as two floats" )
2024-11-20 11:54:31 +00:00
case new_low <= 0.0 || new_high <= 0.0 :
return parseWordsErr ( "Trying to parse two floats as a lognormal, but the two floats must be greater than 0" )
case new_low == new_high :
return parseWordsErr ( "Trying to parse two floats as a lognormal, but the two floats must be different. Try a single scalar instead?" )
case new_low > new_high :
return parseWordsErr ( "Trying to parse two floats as a lognormal, but the first number is larger than the second number" )
2024-06-19 13:46:08 +00:00
}
dist = Lognormal { low : new_low , high : new_high }
2024-12-28 23:36:09 +00:00
case len ( words ) == 3 :
2024-12-24 14:31:21 +00:00
switch {
case words [ 0 ] == "beta" || words [ 0 ] == "b" :
2024-12-28 22:25:35 +00:00
a , err1 := ParseFloat ( words [ 1 ] )
b , err2 := ParseFloat ( words [ 2 ] )
2024-06-19 13:46:08 +00:00
if err1 != nil || err2 != nil {
2024-06-19 14:41:47 +00:00
return parseWordsErr ( "Trying to specify a beta distribution? Try beta 1 2" )
2024-06-19 13:46:08 +00:00
}
dist = Beta { a : a , b : b }
2024-12-24 14:31:21 +00:00
default :
2024-06-19 14:41:47 +00:00
return parseWordsErr ( "Input not understood or not implemented yet" )
2024-06-19 02:02:42 +00:00
}
2024-12-28 23:36:09 +00:00
case len ( words ) >= 4 : // four or more words
if words [ 0 ] == "mx" {
dist , err := parseMixture ( words , vars )
return op , dist , err
2024-12-24 14:31:21 +00:00
}
2024-12-28 23:36:09 +00:00
return parseWordsErr ( "Input not understood or not implemented yet" )
2024-06-19 02:02:42 +00:00
}
2024-06-19 13:46:08 +00:00
return op , dist , nil
2024-06-19 00:29:55 +00:00
}
2024-06-19 13:46:08 +00:00
/* Combine old dist and new line */
// We want this as a function (rather than just be in main)
// to be able to have parenthesis/recusion, possibly functions
2024-08-09 15:39:38 +00:00
func runRepl ( stack Stack , reader * bufio . Reader , echo_flag * bool ) Stack {
2024-06-19 12:30:21 +00:00
replForLoop :
2024-05-10 18:05:03 +00:00
for {
2024-06-19 00:37:25 +00:00
new_line , _ := reader . ReadString ( '\n' )
2024-08-09 15:39:38 +00:00
if * echo_flag {
2024-11-19 19:43:45 +00:00
fmt . Print ( new_line )
2024-08-09 15:39:38 +00:00
}
2024-07-07 14:06:15 +00:00
new_line_before_comments , _ , _ := strings . Cut ( new_line , "#" )
new_line_trimmed := strings . TrimSpace ( new_line_before_comments )
words := strings . Split ( new_line_trimmed , " " )
2024-06-19 02:28:19 +00:00
switch {
2024-12-28 23:36:09 +00:00
/* Empty line */
case strings . TrimSpace ( new_line_trimmed ) == "" :
2024-07-08 21:21:49 +00:00
continue replForLoop
2024-12-28 23:36:09 +00:00
2024-06-19 02:28:19 +00:00
/* Special operations */
case words [ 0 ] == "exit" || words [ 0 ] == "e" :
os . Exit ( 0 )
case words [ 0 ] == "help" || words [ 0 ] == "h" :
2024-06-19 13:46:08 +00:00
fmt . Println ( HELP_MSG )
2024-06-19 02:28:19 +00:00
case words [ 0 ] == "debug" || words [ 0 ] == "d" :
2024-06-19 13:46:08 +00:00
fmt . Printf ( "%v" , stack )
2024-06-19 02:28:19 +00:00
case words [ 0 ] == "clear" || words [ 0 ] == "c" || words [ 0 ] == "." :
stack . old_dist = INIT_DIST
fmt . Println ( )
2024-09-15 21:24:40 +00:00
case words [ 0 ] == "stats" || words [ 0 ] == "s" :
2024-12-28 22:25:35 +00:00
PrettyPrintStats ( stack . old_dist )
2024-12-28 23:36:09 +00:00
2024-06-19 12:30:21 +00:00
/* Variable assignment */
2024-06-19 02:28:19 +00:00
case words [ 0 ] == "=:" && len ( words ) == 2 :
stack . vars [ words [ 1 ] ] = stack . old_dist
fmt . Printf ( "%s " , words [ 1 ] )
case words [ 0 ] == "=." && len ( words ) == 2 :
stack . vars [ words [ 1 ] ] = stack . old_dist
fmt . Printf ( "%s " , words [ 1 ] )
2024-12-28 22:25:35 +00:00
PrettyPrintDist ( stack . old_dist )
2024-06-19 02:28:19 +00:00
stack . old_dist = INIT_DIST
2024-12-28 23:36:09 +00:00
/* 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 , echo_flag )
combined_dist , err := operateDists ( stack . old_dist , new_stack . old_dist , words [ 0 ] )
if err != nil {
PrintError ( err )
} else {
stack . old_dist = combined_dist
}
case len ( words ) == 1 && words [ 0 ] == ")" :
return stack
/* Bread and butter distribution operations */
2024-06-19 02:28:19 +00:00
default :
2024-06-19 14:41:47 +00:00
op , new_dist , err := parseWordsIntoOpAndDist ( words , stack . vars )
2024-06-19 02:28:19 +00:00
if err != nil {
2024-12-28 23:36:09 +00:00
PrintError ( err )
PrettyPrintDist ( stack . old_dist )
2024-06-19 02:28:19 +00:00
continue replForLoop
}
2024-06-19 13:46:08 +00:00
combined_dist , err := operateDists ( stack . old_dist , new_dist , op )
2024-12-28 23:36:09 +00:00
if err != nil {
PrintError ( err )
PrettyPrintDist ( stack . old_dist )
continue replForLoop
2024-06-19 13:46:08 +00:00
}
2024-12-28 23:36:09 +00:00
stack . old_dist = combined_dist
2024-06-19 02:28:19 +00:00
}
2024-12-28 23:36:09 +00:00
2024-12-28 22:25:35 +00:00
PrettyPrintDist ( stack . old_dist )
2024-05-10 18:05:03 +00:00
}
}
2024-06-19 02:02:42 +00:00
func main ( ) {
2024-07-12 16:11:25 +00:00
num_samples_flag := flag . Int ( "n" , N_SAMPLES , "Specifies the number of samples to draw when using samples" )
2024-11-10 17:07:19 +00:00
filename := flag . String ( "f" , "" , "Specifies a file with a model to run. Sets the echo flag to true" )
2024-08-09 15:39:38 +00:00
echo_flag := flag . Bool ( "echo" , false , "Specifies whether inputs should be echoed back. Useful if reading from a file." )
2024-10-01 07:56:31 +00:00
help_flag := flag . Bool ( "h" , false , "Shows help message" )
2024-07-12 22:29:55 +00:00
flag . Parse ( )
2024-08-09 15:33:14 +00:00
N_SAMPLES = * num_samples_flag
2024-10-01 07:56:31 +00:00
if * help_flag {
fmt . Println ( HELP_MSG )
}
2024-07-12 16:11:25 +00:00
2024-07-12 22:29:55 +00:00
var reader * bufio . Reader = nil
if * filename != "" {
file , err := os . Open ( * filename )
if err == nil {
2024-11-10 17:07:19 +00:00
* echo_flag = true
2024-07-12 22:29:55 +00:00
reader = bufio . NewReader ( file )
} else {
fmt . Printf ( "Error opening filename; reading from stdin instead\n" )
}
}
if reader == nil {
reader = bufio . NewReader ( os . Stdin )
}
2024-06-19 02:02:42 +00:00
stack := Stack { old_dist : INIT_DIST , vars : make ( map [ string ] Dist ) }
2024-08-09 15:39:38 +00:00
runRepl ( stack , reader , echo_flag )
2024-07-12 22:29:55 +00:00
2024-06-19 02:02:42 +00:00
}