Upgrade bc to 2.7.2 am: ed6d588661

Change-Id: Id554854b52a3384a6bdc1de8f0949889d310d33f
diff --git a/METADATA b/METADATA
index 29af332..cf9a24c 100644
--- a/METADATA
+++ b/METADATA
@@ -5,11 +5,11 @@
     type: GIT
     value: "https://github.com/gavinhoward/bc"
   }
-  version: "2.6.1"
+  version: "2.7.2"
   license_type: NOTICE
   last_upgrade_date {
     year: 2020
-    month: 4
-    day: 13
+    month: 5
+    day: 6
   }
 }
diff --git a/Makefile.in b/Makefile.in
index ef31068..3a6bfe9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -29,7 +29,7 @@
 #
 .POSIX:
 
-VERSION = 2.6.1
+VERSION = 2.7.2
 
 SRC = %%SRC%%
 OBJ = %%OBJ%%
@@ -51,6 +51,11 @@
 HISTORY_GCDA = %%HISTORY_GCDA%%
 HISTORY_GCNO = %%HISTORY_GCNO%%
 
+RAND_SRC = %%RAND_SRC%%
+RAND_OBJ = %%RAND_OBJ%%
+RAND_GCDA = %%RAND_GCDA%%
+RAND_GCNO = %%RAND_GCNO%%
+
 BC_ENABLED_NAME = BC_ENABLED
 BC_ENABLED = %%BC_ENABLED%%
 DC_ENABLED_NAME = DC_ENABLED
@@ -120,7 +125,6 @@
 BC_ENABLE_EXTRA_MATH = %%EXTRA_MATH%%
 BC_ENABLE_NLS = %%NLS%%
 BC_ENABLE_PROMPT = %%PROMPT%%
-BC_ENABLE_LONG_OPTIONS = %%LONG_OPTIONS%%
 BC_LONG_BIT = %%LONG_BIT%%
 
 RM = rm
@@ -146,7 +150,7 @@
 CPPFLAGS6 = $(CPPFLAGS5) -DBC_ENABLE_NLS=$(BC_ENABLE_NLS) -DBC_ENABLE_PROMPT=$(BC_ENABLE_PROMPT)
 CPPFLAGS7 = $(CPPFLAGS6) -D$(BC_ENABLE_EXTRA_MATH_NAME)=$(BC_ENABLE_EXTRA_MATH)
 CPPFLAGS = $(CPPFLAGS7) -DBC_ENABLE_SIGNALS=$(BC_ENABLE_SIGNALS) -DBC_ENABLE_HISTORY=$(BC_ENABLE_HISTORY)
-CFLAGS = $(CPPFLAGS) -DBC_ENABLE_LONG_OPTIONS=$(BC_ENABLE_LONG_OPTIONS) %%CPPFLAGS%% %%CFLAGS%%
+CFLAGS = $(CPPFLAGS) %%CPPFLAGS%% %%CFLAGS%%
 LDFLAGS = %%LDFLAGS%%
 
 HOSTCFLAGS = %%HOSTCFLAGS%%
@@ -157,8 +161,8 @@
 .c.o:
 	$(CC) $(CFLAGS) -o $@ -c $<
 
-all: make_bin $(DC_HELP_O) $(BC_HELP_O) $(BC_LIB_O) $(BC_LIB2_O) $(BC_LIB3_O) $(BC_OBJ) $(DC_OBJ) $(HISTORY_OBJ) $(OBJ)
-	$(CC) $(CFLAGS) $(OBJ) $(DC_OBJ) $(BC_OBJ) $(HISTORY_OBJ) $(BC_HELP_O) $(DC_HELP_O) \
+all: make_bin $(DC_HELP_O) $(BC_HELP_O) $(BC_LIB_O) $(BC_LIB2_O) $(BC_LIB3_O) $(BC_OBJ) $(DC_OBJ) $(HISTORY_OBJ) $(RAND_OBJ) $(OBJ)
+	$(CC) $(CFLAGS) $(OBJ) $(DC_OBJ) $(BC_OBJ) $(HISTORY_OBJ) $(RAND_OBJ) $(BC_HELP_O) $(DC_HELP_O) \
 	$(BC_LIB_O) $(BC_LIB2_O) $(BC_LIB3_O) $(LDFLAGS) -o $(EXEC)
 	%%LINK%%
 
@@ -271,6 +275,7 @@
 	@$(RM) -f $(BC_OBJ)
 	@$(RM) -f $(DC_OBJ)
 	@$(RM) -f $(HISTORY_OBJ)
+	@$(RM) -f $(RAND_OBJ)
 	@$(RM) -f $(BC_EXEC)
 	@$(RM) -f $(DC_EXEC)
 	@$(RM) -fr $(BIN)
@@ -308,6 +313,7 @@
 	@$(RM) -f $(BC_GCDA) $(BC_GCNO)
 	@$(RM) -f $(DC_GCDA) $(DC_GCNO)
 	@$(RM) -f $(HISTORY_GCDA) $(HISTORY_GCNO)
+	@$(RM) -f $(RAND_GCDA) $(RAND_GCNO)
 	@$(RM) -f $(BC_LIB_GCDA) $(BC_LIB_GCNO)
 	@$(RM) -f $(BC_LIB2_GCDA) $(BC_LIB2_GCNO)
 	@$(RM) -f $(BC_HELP_GCDA) $(BC_HELP_GCNO)
diff --git a/NEWS.md b/NEWS.md
index c24bb8d..f2acc9b 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,76 @@
 # News
 
+## 2.7.2
+
+This is a production release with one major bug fix.
+
+The `length()` built-in function can take either a number or an array. If it
+takes an array, it returns the length of the array. Arrays can be passed by
+reference. The bug is that the `length()` function would not properly
+dereference arrays that were references. This is a bug that affects all users.
+
+**ALL USERS SHOULD UPDATE `bc`**.
+
+## 2.7.1
+
+This is a production release with fixes for new locales and fixes for compiler
+warnings on FreeBSD.
+
+## 2.7.0
+
+This is a production release with a bug fix for Linux, new translations, and new
+features.
+
+Bug fixes:
+
+* Option parsing in `BC_ENV_ARGS` was broken on Linux in 2.6.1 because `glibc`'s
+  `getopt_long()` is broken. To get around that, and to support long options on
+  every platform, an adapted version of [`optparse`][17] was added. Now, `bc`
+  does not even use `getopt()`.
+* Parsing `BC_ENV_ARGS` with quotes now works. It isn't the smartest, but it
+  does the job if there are spaces in file names.
+
+The following new languages are supported:
+
+* Dutch
+* Polish
+* Russian
+* Japanes
+* Simplified Chinese
+
+All of these translations were generated using [DeepL][18], so improvements are
+welcome.
+
+There is only one new feature: **`bc` now has a built-in pseudo-random number
+generator** (PRNG).
+
+The PRNG is seeded, making it useful for applications where
+`/dev/urandom` does not work because output needs to be reproducible. However,
+it also uses `/dev/urandom` to seed itself by default, so it will start with a
+good seed by default.
+
+It also outputs 32 bits on 32-bit platforms and 64 bits on 64-bit platforms, far
+better than the 15 bits of C's `rand()` and `bash`'s `$RANDOM`.
+
+In addition, the PRNG can take a bound, and when it gets a bound, it
+automatically adjusts to remove bias. It can also generate numbers of arbitrary
+size. (As of the time of release, the largest pseudo-random number generated by
+this `bc` was generated with a bound of `2^(2^20)`.)
+
+***IMPORTANT: read the [`bc` manual][9] and the [`dc` manual][10] to find out
+exactly what guarantees the PRNG provides. The underlying implementation is not
+guaranteed to stay the same, but the guarantees that it provides are guaranteed
+to stay the same regardless of the implementation.***
+
+On top of that, four functions were added to `bc`'s [extended math library][16]
+to make using the PRNG easier:
+
+* `frand(p)`: Generates a number between `[0,1)` to `p` decimal places.
+* `ifrand(i, p)`: Generates an integer with bound `i` and adds it to `frand(p)`.
+* `srand(x)`: Randomizes the sign of `x`. In other words, it flips the sign of
+  `x` with probability `0.5`.
+* `brand()`: Returns a random boolean value (either `0` or `1`).
+
 ## 2.6.1
 
 This is a production release with a bug fix for FreeBSD.
@@ -560,3 +631,5 @@
 [14]: https://github.com/stesser
 [15]: https://github.com/bugcrazy
 [16]: ./manuals/bc.1.ronn#extended-library
+[17]: https://github.com/skeeto/optparse
+[18]: https://www.deepl.com/translator
diff --git a/README.md b/README.md
index dd5f10f..39146b6 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 [![Coverity Scan Build Status][17]][18]
 
 ***WARNING: This project has moved to [https://git.yzena.com/][20] for [these
-reasons][21].***
+reasons][21], though GitHub will remain a mirror.***
 
 This is an implementation of the [POSIX `bc` calculator][12] that implements
 [GNU `bc`][1] extensions, as well as the period (`.`) extension for the BSD
@@ -40,7 +40,7 @@
 * NetBSD
 * Mac OSX
 * Solaris
-* AIX (without long options)
+* AIX
 
 Please submit bug reports if this `bc` does not build out of the box on any
 system besides Windows. If Windows binaries are needed, they can be found at
@@ -167,7 +167,8 @@
 If a package maintainer wishes to add both a prefix and a suffix, that is
 allowed.
 
-**Note**: The suggested name (and package name) is `bc-gh`.
+**Note**: The suggested name (and package name) when `bc` is not available is
+`bc-gh`.
 
 #### Karatsuba Number
 
@@ -232,9 +233,14 @@
 
 ## Locales
 
-Currently, this `bc` only has support for English (and US English), French and
-German locales. Patches are welcome for translations; use the existing `*.msg`
-files in `locales/` as a starting point.
+Currently, this `bc` only has support for English (and US English), French,
+German, Portuguese, Dutch, Polish, Russian, Japanese, and Chinese locales.
+Patches are welcome for translations; use the existing `*.msg` files in
+`locales/` as a starting point.
+
+In addition, patches for improvements are welcome; the last two messages in
+Portuguese were made with Google Translate, and the Dutch, Polish, Russian,
+Japanese, and Chinese locales were all generated with [DeepL][22].
 
 The message files provided assume that locales apply to all regions where a
 language is used, but this might not be true for, e.g., `fr_CA` and `fr_CH`.
@@ -253,7 +259,7 @@
 
 ## Language
 
-This `bc` is written in pure ISO C99, using POSIX 2008 APIs.
+This `bc` is written in pure ISO C99, using POSIX 2001 APIs.
 
 ## Commit Messages
 
@@ -319,3 +325,4 @@
 [19]: ./manuals/benchmarks.md
 [20]: https://git.yzena.com/gavin/bc
 [21]: https://gavinhoward.com/2020/04/i-am-moving-away-from-github/
+[22]: https://www.deepl.com/translator
diff --git a/configure.sh b/configure.sh
index ea0f93e..b69f330 100755
--- a/configure.sh
+++ b/configure.sh
@@ -47,20 +47,20 @@
 
 	printf 'usage: %s -h\n' "$script"
 	printf '       %s --help\n' "$script"
-	printf '       %s [-bD|-dB|-c] [-EfgGHLMNPST] [-O OPT_LEVEL] [-k KARATSUBA_LEN]\n' "$script"
+	printf '       %s [-bD|-dB|-c] [-EfgGHMNPST] [-O OPT_LEVEL] [-k KARATSUBA_LEN]\n' "$script"
 	printf '       %s \\\n' "$script"
 	printf '           [--bc-only --disable-dc|--dc-only --disable-bc|--coverage]      \\\n'
 	printf '           [--debug --disable-extra-math --disable-generated-tests]        \\\n'
-	printf '           [--disable-history --disable-long-options --disable-man-pages]  \\\n'
-	printf '           [--disable-nls --disable-prompt --disable-signal-handling]      \\\n'
-	printf '           [--disable-strip] [--opt=OPT_LEVEL]                             \\\n'
-	printf '           [--karatsuba-len=KARATSUBA_LEN] [--prefix=PREFIX]               \\\n'
-	printf '           [--bindir=BINDIR] [--datarootdir=DATAROOTDIR]                   \\\n'
+	printf '           [--disable-history --disable-man-pages --disable-nls]           \\\n'
+	printf '           [--disable-prompt --disable-signal-handling --disable-strip]    \\\n'
+	printf '           [--opt=OPT_LEVEL] [--karatsuba-len=KARATSUBA_LEN]               \\\n'
+	printf '           [--prefix=PREFIX] [--bindir=BINDIR] [--datarootdir=DATAROOTDIR] \\\n'
 	printf '           [--datadir=DATADIR] [--mandir=MANDIR] [--man1dir=MAN1DIR]       \\\n'
 	printf '           [--force]                                                       \\\n'
 	printf '\n'
 	printf '    -b, --bc-only\n'
-	printf '        Build bc only. It is an error if "-d" or "-B" are specified too.\n'
+	printf '        Build bc only. It is an error if "-d", "--dc-only", "-B", or "--disable-bc"\n'
+	printf '        are specified too.\n'
 	printf '    -B, --disable-bc\n'
 	printf '        Disable bc. It is an error if "-b", "--bc-only", "-D", or "--disable-dc"\n'
 	printf '        are specified too.\n'
@@ -69,7 +69,8 @@
 	printf '        It is an error if either "-b" ("-D") or "-d" ("-B") is specified.\n'
 	printf '        Requires a compiler that use gcc-compatible coverage options\n'
 	printf '    -d, --dc-only\n'
-	printf '        Build dc only. It is an error if "-b" is specified too.\n'
+	printf '        Build dc only. It is an error if "-b", "--bc-only", "-D", or "--disable-dc"\n'
+	printf '        are specified too.\n'
 	printf '    -D, --disable-dc\n'
 	printf '        Disable dc. It is an error if "-d", "--dc-only" "-B", or "--disable-bc"\n'
 	printf '        are specified too.\n'
@@ -97,10 +98,6 @@
 	printf '    -k KARATSUBA_LEN, --karatsuba-len KARATSUBA_LEN\n'
 	printf '        Set the karatsuba length to KARATSUBA_LEN (default is 64).\n'
 	printf '        It is an error if KARATSUBA_LEN is not a number or is less than 16.\n'
-	printf '    -L, --disable-long-options\n'
-	printf '        Disable use of getopt_long() and use getopt() instead. This is for\n'
-	printf '        platforms that do not have getopt_long() since it is not POSIX\n'
-	printf '        standard. This means that long options will be disabled.\n'
 	printf '    -M, --disable-man-pages\n'
 	printf '        Disable installing manpages.\n'
 	printf '    -N, --disable-nls\n'
@@ -325,9 +322,8 @@
 prompt=1
 force=0
 strip_bin=1
-loptions=1
 
-while getopts "bBcdDEfgGhHk:LMNO:PST-" opt; do
+while getopts "bBcdDEfgGhHk:MNO:PST-" opt; do
 
 	case "$opt" in
 		b) bc_only=1 ;;
@@ -342,7 +338,6 @@
 		h) usage ;;
 		H) hist=0 ;;
 		k) karatsuba_len="$OPTARG" ;;
-		L) loptions=0 ;;
 		M) install_manpages=0 ;;
 		N) nls=0 ;;
 		O) optimization="$OPTARG" ;;
@@ -428,7 +423,6 @@
 				disable-extra-math) extra_math=0 ;;
 				disable-generated-tests) generate_tests=0 ;;
 				disable-history) hist=0 ;;
-				disable-long-options) loptions=0 ;;
 				disable-man-pages) install_manpages=0 ;;
 				disable-nls) nls=0 ;;
 				disable-prompt) prompt=0 ;;
@@ -529,57 +523,22 @@
 	HOSTCFLAGS="$HOST_CFLAGS"
 fi
 
-if [ "$loptions" -eq 1 ]; then
-
-	set +e
-
-	printf 'Testing long options...\n'
-
-	flags="-DBC_ENABLE_LONG_OPTIONS=1 -DBC_ENABLED=1 -DDC_ENABLED=1 -DBC_ENABLE_SIGNALS=$signals"
-	flags="$flags -DBC_ENABLE_NLS=0 -DBC_ENABLE_HISTORY=0"
-	flags="$flags -DBC_ENABLE_EXTRA_MATH=$extra_math -I./include/"
-	flags="$flags -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600"
-
-	"$HOSTCC" $HOSTCFLAGS $flags -c "src/args.c" -o "$scriptdir/args.o" > /dev/null 2>&1
-
-	err="$?"
-
-	rm -rf "$scriptdir/args.o"
-
-	# If this errors, it's probably because the platform does not have
-	# getopt_long(), so disable it.
-	if [ "$err" -ne 0 ]; then
-		printf 'Long options do not work.\n'
-		if [ $force -eq 0 ]; then
-			printf 'Disabling long options...\n\n'
-			loptions=0
-		else
-			printf 'Forcing long options...\n\n'
-		fi
-	else
-		printf 'Long options work.\n\n'
-	fi
-
-	set -e
-
-fi
-
 link="@printf 'No link necessary\\\\n'"
 main_exec="BC"
 executable="BC_EXEC"
 
-bc_test="@tests/all.sh bc $extra_math 1 $loptions $generate_tests 0 \$(BC_EXEC)"
-bc_time_test="@tests/all.sh bc $extra_math 1 $loptions $generate_tests 1 \$(BC_EXEC)"
-dc_test="@tests/all.sh dc $extra_math 1 $loptions $generate_tests 0 \$(DC_EXEC)"
-dc_time_test="@tests/all.sh dc $extra_math 1 $loptions $generate_tests 1 \$(DC_EXEC)"
+bc_test="@tests/all.sh bc $extra_math 1 $generate_tests 0 \$(BC_EXEC)"
+bc_time_test="@tests/all.sh bc $extra_math 1 $generate_tests 1 \$(BC_EXEC)"
+dc_test="@tests/all.sh dc $extra_math 1 $generate_tests 0 \$(DC_EXEC)"
+dc_time_test="@tests/all.sh dc $extra_math 1 $generate_tests 1 \$(DC_EXEC)"
 
 timeconst="@tests/bc/timeconst.sh tests/bc/scripts/timeconst.bc \$(BC_EXEC)"
 
 # In order to have cleanup at exit, we need to be in
 # debug mode, so don't run valgrind without that.
 if [ "$debug" -ne 0 ]; then
-	vg_bc_test="@tests/all.sh bc $extra_math 1 $loptions $generate_tests 0 valgrind \$(VALGRIND_ARGS) \$(BC_EXEC)"
-	vg_dc_test="@tests/all.sh dc $extra_math 1 $loptions $generate_tests 0 valgrind \$(VALGRIND_ARGS) \$(DC_EXEC)"
+	vg_bc_test="@tests/all.sh bc $extra_math 1 $generate_tests 0 valgrind \$(VALGRIND_ARGS) \$(BC_EXEC)"
+	vg_dc_test="@tests/all.sh dc $extra_math 1 $generate_tests 0 valgrind \$(VALGRIND_ARGS) \$(DC_EXEC)"
 else
 	vg_bc_test="@printf 'Cannot run valgrind without debug flags\\\\n'"
 	vg_dc_test="@printf 'Cannot run valgrind without debug flags\\\\n'"
@@ -828,7 +787,7 @@
 			printf 'Forcing history...\n\n'
 		fi
 	else
-		printf 'History works.\n'
+		printf 'History works.\n\n'
 	fi
 
 	set -e
@@ -872,7 +831,6 @@
 printf 'BC_ENABLE_EXTRA_MATH=%s\n' "$extra_math"
 printf 'BC_ENABLE_NLS=%s\n' "$nls"
 printf 'BC_ENABLE_PROMPT=%s\n' "$prompt"
-printf 'BC_ENABLE_LONG_OPTIONS=%s\n' "$loptions"
 printf '\n'
 printf 'BC_NUM_KARATSUBA_LEN=%s\n' "$karatsuba_len"
 printf '\n'
@@ -907,6 +865,7 @@
 contents=$(gen_file_lists "$contents" "$scriptdir/src/bc" "BC_" "$bc")
 contents=$(gen_file_lists "$contents" "$scriptdir/src/dc" "DC_" "$dc")
 contents=$(gen_file_lists "$contents" "$scriptdir/src/history" "HISTORY_" "$hist")
+contents=$(gen_file_lists "$contents" "$scriptdir/src/rand" "RAND_" "$extra_math")
 
 contents=$(replace "$contents" "BC_ENABLED" "$bc")
 contents=$(replace "$contents" "DC_ENABLED" "$dc")
@@ -917,7 +876,6 @@
 contents=$(replace "$contents" "EXTRA_MATH" "$extra_math")
 contents=$(replace "$contents" "NLS" "$nls")
 contents=$(replace "$contents" "PROMPT" "$prompt")
-contents=$(replace "$contents" "LONG_OPTIONS" "$loptions")
 contents=$(replace "$contents" "BC_LIB_O" "$bc_lib")
 contents=$(replace "$contents" "BC_HELP_O" "$bc_help")
 contents=$(replace "$contents" "DC_HELP_O" "$dc_help")
diff --git a/gen/lib2.bc b/gen/lib2.bc
index 524a090..bfbbfca 100644
--- a/gen/lib2.bc
+++ b/gen/lib2.bc
@@ -190,6 +190,16 @@
 	scale=s
 	return r@s
 }
+define frand(p){
+	p=abs(p)$
+	return irand(10^p)>>p
+}
+define ifrand(i,p){return irand(abs(i)$)+frand(p)}
+define srand(x){
+	if(irand(2))return -x
+	return x
+}
+define brand(){return irand(2)}
 define void output(x,b){
 	auto c
 	c=obase
diff --git a/include/args.h b/include/args.h
index 30d0aac..aa6dede 100644
--- a/include/args.h
+++ b/include/args.h
@@ -41,11 +41,6 @@
 
 BcStatus bc_args(int argc, char *argv[]);
 
-#if !BC_ENABLE_LONG_OPTIONS
-#define getopt_long(argc, argv, opts, longopts, longidx) \
-	getopt(argc, argv, opts)
-#endif // BC_ENABLE_LONG_OPTIONS
-
 extern const char* const bc_args_env_name;
 
 #endif // BC_ARGS_H
diff --git a/include/bc.h b/include/bc.h
index 51114e7..939fc01 100644
--- a/include/bc.h
+++ b/include/bc.h
@@ -50,6 +50,7 @@
 extern const char bc_help[];
 extern const char bc_lib[];
 extern const char* bc_lib_name;
+
 #if BC_ENABLE_EXTRA_MATH
 extern const char bc_lib2[];
 extern const char* bc_lib2_name;
@@ -128,8 +129,14 @@
 #define BC_PARSE_TOP_OP(p) (*((BcLexType*) bc_vec_top(&(p)->ops)))
 #define BC_PARSE_LEAF(prev, bin_last, rparen) \
 	(!(bin_last) && ((rparen) || bc_parse_inst_isLeaf(prev)))
+
+#if BC_ENABLE_EXTRA_MATH
+#define BC_PARSE_INST_VAR(t) \
+	((t) >= BC_INST_VAR && (t) <= BC_INST_SEED && (t) != BC_INST_ARRAY)
+#else // BC_ENABLE_EXTRA_MATH
 #define BC_PARSE_INST_VAR(t) \
 	((t) >= BC_INST_VAR && (t) <= BC_INST_SCALE && (t) != BC_INST_ARRAY)
+#endif // BC_ENABLE_EXTRA_MATH
 
 #define BC_PARSE_PREV_PREFIX(p) \
 	((p) >= BC_INST_INC_PRE && (p) <= BC_INST_BOOL_NOT)
diff --git a/include/lang.h b/include/lang.h
index 39252dc..57515d8 100644
--- a/include/lang.h
+++ b/include/lang.h
@@ -136,14 +136,26 @@
 	BC_INST_IBASE,
 	BC_INST_OBASE,
 	BC_INST_SCALE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_INST_SEED,
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_INST_LENGTH,
 	BC_INST_SCALE_FUNC,
 	BC_INST_SQRT,
 	BC_INST_ABS,
+#if BC_ENABLE_EXTRA_MATH
+	BC_INST_IRAND,
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_INST_READ,
+#if BC_ENABLE_EXTRA_MATH
+	BC_INST_RAND,
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_INST_MAXIBASE,
 	BC_INST_MAXOBASE,
 	BC_INST_MAXSCALE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_INST_MAXRAND,
+#endif // BC_ENABLE_EXTRA_MATH
 
 	BC_INST_PRINT,
 	BC_INST_PRINT_POP,
@@ -251,6 +263,9 @@
 	BC_RESULT_IBASE,
 	BC_RESULT_OBASE,
 	BC_RESULT_SCALE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_RESULT_SEED,
+#endif // BC_ENABLE_EXTRA_MATH
 
 } BcResultType;
 
diff --git a/include/lex.h b/include/lex.h
index 43e6a45..4151a3c 100644
--- a/include/lex.h
+++ b/include/lex.h
@@ -142,15 +142,27 @@
 	BC_LEX_KW_IBASE,
 	BC_LEX_KW_OBASE,
 	BC_LEX_KW_SCALE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_SEED,
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_KW_LENGTH,
 	BC_LEX_KW_PRINT,
 	BC_LEX_KW_SQRT,
 	BC_LEX_KW_ABS,
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_IRAND,
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_KW_QUIT,
 	BC_LEX_KW_READ,
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_RAND,
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_KW_MAXIBASE,
 	BC_LEX_KW_MAXOBASE,
 	BC_LEX_KW_MAXSCALE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_MAXRAND,
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_KW_ELSE,
 
 #if DC_ENABLED
@@ -173,6 +185,9 @@
 	BC_LEX_STORE_IBASE,
 	BC_LEX_STORE_OBASE,
 	BC_LEX_STORE_SCALE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_STORE_SEED,
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_LOAD,
 	BC_LEX_LOAD_POP,
 	BC_LEX_STORE_PUSH,
diff --git a/include/num.h b/include/num.h
index ed232ba..2e49bc0 100644
--- a/include/num.h
+++ b/include/num.h
@@ -45,6 +45,7 @@
 #include <sys/types.h>
 
 #include <status.h>
+#include <vector.h>
 
 #ifndef BC_ENABLE_EXTRA_MATH
 #define BC_ENABLE_EXTRA_MATH (1)
@@ -71,9 +72,9 @@
 #if BC_LONG_BIT >= 64
 
 typedef int_least32_t BcDig;
-typedef uint_fast64_t BcBigDig;
+typedef uint64_t BcBigDig;
 
-#define BC_NUM_BIGDIG_MAX (UINT_FAST64_MAX)
+#define BC_NUM_BIGDIG_MAX ((BcBigDig) UINT64_MAX)
 
 #define BC_BASE_DIGS (9)
 #define BC_BASE_POW (1000000000)
@@ -84,9 +85,9 @@
 #elif BC_LONG_BIT >= 32
 
 typedef int_least16_t BcDig;
-typedef uint_fast32_t BcBigDig;
+typedef uint32_t BcBigDig;
 
-#define BC_NUM_BIGDIG_MAX (UINT_FAST32_MAX)
+#define BC_NUM_BIGDIG_MAX ((BcBigDig) UINT32_MAX)
 
 #define BC_BASE_DIGS (4)
 #define BC_BASE_POW (10000)
@@ -109,6 +110,11 @@
 	bool neg;
 } BcNum;
 
+#if BC_ENABLE_EXTRA_MATH
+// Forward declaration
+struct BcRNG;
+#endif // BC_ENABLE_EXTRA_MATH
+
 #define BC_NUM_MIN_BASE (BC_NUM_BIGDIG_C(2))
 #define BC_NUM_MAX_POSIX_IBASE (BC_NUM_BIGDIG_C(16))
 #define BC_NUM_MAX_IBASE (BC_NUM_BIGDIG_C(36))
@@ -167,8 +173,16 @@
 size_t bc_num_len(const BcNum *restrict n);
 
 BcStatus bc_num_bigdig(const BcNum *restrict n, BcBigDig *result);
+void bc_num_bigdig2(const BcNum *restrict n, BcBigDig *result);
 void bc_num_bigdig2num(BcNum *restrict n, BcBigDig val);
 
+#if BC_ENABLE_EXTRA_MATH
+BcStatus bc_num_irand(const BcNum *restrict a, BcNum *restrict b,
+                      struct BcRNG *restrict rng);
+BcStatus bc_num_rng(const BcNum *restrict n, struct BcRNG *rng);
+BcStatus bc_num_createFromRNG(BcNum *restrict n, struct BcRNG *rng);
+#endif // BC_ENABLE_EXTRA_MATH
+
 BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale);
 BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale);
 BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale);
@@ -218,4 +232,7 @@
 extern const char bc_num_hex_digits[];
 extern const BcBigDig bc_num_pow10[BC_BASE_DIGS + 1];
 
+extern const BcDig bc_num_bigdigMax[];
+extern const size_t bc_num_bigdigMax_size;
+
 #endif // BC_NUM_H
diff --git a/include/opt.h b/include/opt.h
new file mode 100644
index 0000000..f572385
--- /dev/null
+++ b/include/opt.h
@@ -0,0 +1,79 @@
+/*
+ * *****************************************************************************
+ *
+ * Copyright (c) 2018-2020 Gavin D. Howard and contributors.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * *****************************************************************************
+ *
+ * Adapted from https://github.com/skeeto/optparse
+ *
+ * *****************************************************************************
+ *
+ * Definitions for getopt_long() replacement.
+ *
+ */
+
+#ifndef BC_OPT_H
+#define BC_OPT_H
+
+#include <stdbool.h>
+
+typedef struct BcOpt {
+	char **argv;
+	size_t optind;
+	int optopt;
+	int subopt;
+	char *optarg;
+} BcOpt;
+
+typedef enum BcOptType {
+	BC_OPT_NONE,
+	BC_OPT_REQUIRED,
+	BC_OPT_BC_ONLY,
+	BC_OPT_DC_ONLY,
+} BcOptType;
+
+typedef struct BcOptLong {
+	const char *name;
+	BcOptType type;
+	int val;
+} BcOptLong;
+
+void bc_opt_init(BcOpt *o, char **argv);
+
+int bc_opt_parse(BcOpt *o, const BcOptLong *longopts);
+
+#define BC_OPT_ISDASHDASH(a) \
+	((a) != NULL && (a)[0] == '-' && (a)[1] == '-' && (a)[2] == '\0')
+
+#define BC_OPT_ISSHORTOPT(a) \
+	((a) != NULL && (a)[0] == '-' && (a)[1] != '-' && (a)[1] != '\0')
+
+#define BC_OPT_ISLONGOPT(a) \
+	((a) != NULL && (a)[0] == '-' && (a)[1] == '-' && (a)[2] != '\0')
+
+#endif // BC_OPT_H
diff --git a/include/program.h b/include/program.h
index f070ea6..5f08fdc 100644
--- a/include/program.h
+++ b/include/program.h
@@ -42,11 +42,17 @@
 #include <parse.h>
 #include <lang.h>
 #include <num.h>
+#include <rand.h>
 
 #define BC_PROG_GLOBALS_IBASE (0)
 #define BC_PROG_GLOBALS_OBASE (1)
 #define BC_PROG_GLOBALS_SCALE (2)
-#define BC_PROG_GLOBALS_LEN (3)
+
+#if BC_ENABLE_EXTRA_MATH
+#define BC_PROG_MAX_RAND (3)
+#endif // BC_ENABLE_EXTRA_MATH
+
+#define BC_PROG_GLOBALS_LEN (3 + BC_ENABLE_EXTRA_MATH)
 
 #define BC_PROG_ONE_CAP (1)
 
@@ -55,6 +61,10 @@
 	BcBigDig globals[BC_PROG_GLOBALS_LEN];
 	BcVec globals_v[BC_PROG_GLOBALS_LEN];
 
+#if BC_ENABLE_EXTRA_MATH
+	BcRNG rng;
+#endif // BC_ENABLE_EXTRA_MATH
+
 	BcVec results;
 	BcVec stack;
 
diff --git a/include/rand.h b/include/rand.h
new file mode 100644
index 0000000..8cc1ce4
--- /dev/null
+++ b/include/rand.h
@@ -0,0 +1,225 @@
+/*
+ * *****************************************************************************
+ *
+ * Copyright (c) 2018-2019 Gavin D. Howard and contributors.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * *****************************************************************************
+ *
+ * Parts of this code are adapted from the following:
+ *
+ * PCG, A Family of Better Random Number Generators.
+ *
+ * You can find the original source code at:
+ *   https://github.com/imneme/pcg-c
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Parts of this code are also under the following license:
+ *
+ * Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * *****************************************************************************
+ *
+ * Definitions for the RNG.
+ *
+ */
+
+#ifndef BC_RAND_H
+#define BC_RAND_H
+
+#if BC_ENABLE_EXTRA_MATH
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <vector.h>
+#include <num.h>
+
+typedef uchar (*BcRandChar)(void*);
+
+#if BC_LONG_BIT >= 64
+
+#ifdef BC_RAND_BUILTIN
+#if BC_RAND_BUILTIN
+#ifndef __SIZEOF_INT128__
+#undef BC_RAND_BUILTIN
+#define BC_RAND_BUILTIN (0)
+#endif // __SIZEOF_INT128__
+#endif // BC_RAND_BUILTIN
+#endif // BC_RAND_BUILTIN
+
+#ifndef BC_RAND_BUILTIN
+#ifdef __SIZEOF_INT128__
+#define BC_RAND_BUILTIN (1)
+#else // __SIZEOF_INT128__
+#define BC_RAND_BUILTIN (0)
+#endif // __SIZEOF_INT128__
+#endif // BC_RAND_BUILTIN
+
+typedef uint64_t BcRand;
+
+#define BC_RAND_ROTC (63)
+
+#if BC_RAND_BUILTIN
+
+typedef __uint128_t BcRandState;
+
+#define bc_rand_mul(a, b) (((BcRandState) (a)) * ((BcRandState) (b)))
+#define bc_rand_add(a, b) (((BcRandState) (a)) + ((BcRandState) (b)))
+
+#define bc_rand_mul2(a, b) (((BcRandState) (a)) * ((BcRandState) (b)))
+#define bc_rand_add2(a, b) (((BcRandState) (a)) + ((BcRandState) (b)))
+
+#define BC_RAND_NOTMODIFIED(r) (((r)->inc & 1UL) == 0)
+#define BC_RAND_ZERO(r) (!(r)->state)
+
+#define BC_RAND_CONSTANT(h, l) ((((BcRandState) (h)) << 64) + (BcRandState) (l))
+
+#define BC_RAND_TRUNC(s) ((uint64_t) (s))
+#define BC_RAND_CHOP(s) ((uint64_t) ((s) >> 64UL))
+#define BC_RAND_ROTAMT(s) ((unsigned int) ((s) >> 122UL))
+
+#else // BC_RAND_BUILTIN
+
+typedef struct BcRandState {
+
+	uint_fast64_t lo;
+	uint_fast64_t hi;
+
+} BcRandState;
+
+#define bc_rand_mul(a, b) (bc_rand_multiply((a), (b)))
+#define bc_rand_add(a, b) (bc_rand_addition((a), (b)))
+
+#define bc_rand_mul2(a, b) (bc_rand_multiply2((a), (b)))
+#define bc_rand_add2(a, b) (bc_rand_addition2((a), (b)))
+
+#define BC_RAND_NOTMODIFIED(r) (((r)->inc.lo & 1) == 0)
+#define BC_RAND_ZERO(r) (!(r)->state.lo && !(r)->state.hi)
+
+#define BC_RAND_CONSTANT(h, l) { .lo = (l), .hi = (h) }
+
+#define BC_RAND_TRUNC(s) ((s).lo)
+#define BC_RAND_CHOP(s) ((s).hi)
+#define BC_RAND_ROTAMT(s) ((unsigned int) ((s).hi >> 58UL))
+
+#define BC_RAND_BOTTOM32 (((uint_fast64_t) 0xffffffffULL))
+#define BC_RAND_TRUNC32(n) ((n) & BC_RAND_BOTTOM32)
+#define BC_RAND_CHOP32(n) ((n) >> 32)
+
+#endif // BC_RAND_BUILTIN
+
+#define BC_RAND_MULTIPLIER \
+	BC_RAND_CONSTANT(2549297995355413924ULL, 4865540595714422341ULL)
+
+#define BC_RAND_FOLD(s) ((BcRand) (BC_RAND_CHOP(s) ^ BC_RAND_TRUNC(s)))
+
+#else // BC_LONG_BIT >= 64
+
+#undef BC_RAND_BUILTIN
+#define BC_RAND_BUILTIN (1)
+
+typedef uint32_t BcRand;
+
+#define BC_RAND_ROTC (31)
+
+typedef uint_fast64_t BcRandState;
+
+#define bc_rand_mul(a, b) (((BcRandState) (a)) * ((BcRandState) (b)))
+#define bc_rand_add(a, b) (((BcRandState) (a)) + ((BcRandState) (b)))
+
+#define bc_rand_mul2(a, b) (((BcRandState) (a)) * ((BcRandState) (b)))
+#define bc_rand_add2(a, b) (((BcRandState) (a)) + ((BcRandState) (b)))
+
+#define BC_RAND_NOTMODIFIED(r) (((r)->inc & 1UL) == 0)
+#define BC_RAND_ZERO(r) (!(r)->state)
+
+#define BC_RAND_CONSTANT UINT64_C
+#define BC_RAND_MULTIPLIER BC_RAND_CONSTANT(6364136223846793005)
+
+#define BC_RAND_TRUNC(s) ((uint32_t) (s))
+#define BC_RAND_CHOP(s) ((uint32_t) ((s) >> 32UL))
+#define BC_RAND_ROTAMT(s) ((unsigned int) ((s) >> 59UL))
+
+#define BC_RAND_FOLD(s) ((BcRand) ((((s) >> 18U) ^ (s)) >> 27U))
+
+#endif // BC_LONG_BIT >= 64
+
+#define BC_RAND_ROT(v, r) \
+	((BcRand) (((v) >> (r)) | ((v) << ((0 - (r)) & BC_RAND_ROTC))))
+
+#define BC_RAND_BITS (sizeof(BcRand) * CHAR_BIT)
+#define BC_RAND_STATE_BITS (sizeof(BcRandState) * CHAR_BIT)
+
+#define BC_RAND_NUM_SIZE (BC_NUM_BIGDIG_LOG10 * 2 + 2)
+
+typedef struct BcRNGData {
+
+	BcRandState state;
+	BcRandState inc;
+
+} BcRNGData;
+
+typedef struct BcRNG {
+
+	BcVec v;
+
+} BcRNG;
+
+void bc_rand_init(BcRNG *r);
+void bc_rand_free(BcRNG *r);
+
+BcRand bc_rand_int(BcRNG *r);
+BcRand bc_rand_bounded(BcRNG *r, BcRand bound);
+void bc_rand_seed(BcRNG *r, ulong state1, ulong state2, ulong inc1, ulong inc2);
+void bc_rand_push(BcRNG *r);
+void bc_rand_pop(BcRNG *r);
+void bc_rand_getRands(BcRNG *r, BcRand *s1, BcRand *s2, BcRand *i1, BcRand *i2);
+
+extern const BcRandState bc_rand_multiplier;
+
+#endif // BC_ENABLE_EXTRA_MATH
+
+#endif // BC_RAND_H
diff --git a/include/status.h b/include/status.h
index b449551..4b9d6c8 100644
--- a/include/status.h
+++ b/include/status.h
@@ -73,6 +73,8 @@
 	BC_ERROR_FATAL_BIN_FILE,
 	BC_ERROR_FATAL_PATH_DIR,
 	BC_ERROR_FATAL_OPTION,
+	BC_ERROR_FATAL_OPTION_NO_ARG,
+	BC_ERROR_FATAL_OPTION_ARG,
 
 	BC_ERROR_EXEC_IBASE,
 	BC_ERROR_EXEC_OBASE,
diff --git a/include/vm.h b/include/vm.h
index 77c2954..02b57dc 100644
--- a/include/vm.h
+++ b/include/vm.h
@@ -119,12 +119,17 @@
 #define BC_MAX(a, b) ((a) > (b) ? (a) : (b))
 #define BC_MIN(a, b) ((a) < (b) ? (a) : (b))
 
-#define BC_MAX_OBASE ((ulong) (BC_BASE_POW))
-#define BC_MAX_DIM ((ulong) (SIZE_MAX - 1))
-#define BC_MAX_SCALE ((ulong) (BC_NUM_BIGDIG_MAX - 1))
-#define BC_MAX_STRING ((ulong) (BC_NUM_BIGDIG_MAX - 1))
+#define BC_MAX_OBASE ((BcBigDig) (BC_BASE_POW))
+#define BC_MAX_DIM ((BcBigDig) (SIZE_MAX - 1))
+#define BC_MAX_SCALE ((BcBigDig) (BC_NUM_BIGDIG_MAX - 1))
+#define BC_MAX_STRING ((BcBigDig) (BC_NUM_BIGDIG_MAX - 1))
 #define BC_MAX_NAME BC_MAX_STRING
 #define BC_MAX_NUM BC_MAX_SCALE
+
+#if BC_ENABLE_EXTRA_MATH
+#define BC_MAX_RAND ((BcBigDig) (((BcRand) 0) - 1))
+#endif // BC_ENABLE_EXTRA_MATH
+
 #define BC_MAX_EXP ((ulong) (BC_NUM_BIGDIG_MAX))
 #define BC_MAX_VARS ((ulong) (SIZE_MAX - 1))
 
@@ -177,7 +182,7 @@
 
 	uint16_t line_len;
 
-	BcBigDig maxes[BC_PROG_GLOBALS_LEN];
+	BcBigDig maxes[BC_PROG_GLOBALS_LEN + BC_ENABLE_EXTRA_MATH];
 
 	BcVec files;
 	BcVec exprs;
@@ -205,6 +210,12 @@
 	BcBigDig last_exp;
 	BcBigDig last_rem;
 
+	char *env_args_buffer;
+	BcVec env_args;
+
+	BcNum max;
+	BcDig max_num[BC_NUM_BIGDIG_LOG10];
+
 #if BC_ENABLE_NLS
 	nl_catd catalog;
 #endif // BC_ENABLE_NLS
diff --git a/locales/de_DE.ISO8859-1.msg b/locales/de_DE.ISO8859-1.msg
index bdbee7f..d058b56 100644
--- a/locales/de_DE.ISO8859-1.msg
+++ b/locales/de_DE.ISO8859-1.msg
@@ -34,7 +34,7 @@
 7 "leerer Ausdruck"
 8 "Ungültige Druckanweisung"
 9 "Ungültige Funktionsdefinition"
-10 "Ungültige Zuweisung: Die linke Seite muss \"scale\", \"ibase\", \"obase\", \"last\", \"var\" oder \"array element\" sein"
+10 "Ungültige Zuweisung: Die linke Seite muss \"scale\", \"ibase\", \"obase\", \"seed\", \"last\", \"var\" oder \"array element\" sein"
 11 "keine automatische Variable gefunden"
 12 "Funktionsparameter oder Variable \"%s%s\" existiert bereits"
 13 "Blockende konnte nicht gefunden werden"
@@ -75,4 +75,6 @@
 3 "konnte die Datei nicht öffnen: %s"
 4 "Datei ist nicht ASCII: %s"
 5 "Pfad ist ein Verzeichnis: %s"
-6 "ungültige Befehlszeilenoption: '%c' (\"%s\")"
+6 "ungültige Befehlszeilenoption: \"%s\""
+7 "Option erfordert ein Argument: '%c' (\"%s\")"
+8 "Option benutzt keine Argumente: '%c' (\"%s\")"
diff --git a/locales/de_DE.UTF-8.msg b/locales/de_DE.UTF-8.msg
index e3ad236..4177c1c 100644
--- a/locales/de_DE.UTF-8.msg
+++ b/locales/de_DE.UTF-8.msg
@@ -34,7 +34,7 @@
 7 "leerer Ausdruck"
 8 "Ungültige Druckanweisung"
 9 "Ungültige Funktionsdefinition"
-10 "Ungültige Zuweisung: Die linke Seite muss \"scale\", \"ibase\", \"obase\", \"last\", \"var\" oder \"array element\" sein"
+10 "Ungültige Zuweisung: Die linke Seite muss \"scale\", \"ibase\", \"obase\", \"seed\", \"last\", \"var\" oder \"array element\" sein"
 11 "keine automatische Variable gefunden"
 12 "Funktionsparameter oder Variable \"%s%s\" existiert bereits"
 13 "Blockende konnte nicht gefunden werden"
@@ -75,4 +75,6 @@
 3 "konnte die Datei nicht öffnen: %s"
 4 "Datei ist nicht ASCII: %s"
 5 "Pfad ist ein Verzeichnis: %s"
-6 "ungültige Befehlszeilenoption: '%c' (\"%s\")"
+6 "ungültige Befehlszeilenoption: \"%s\""
+7 "Option erfordert ein Argument: '%c' (\"%s\")"
+8 "Option benutzt keine Argumente: '%c' (\"%s\")"
diff --git a/locales/en_US.msg b/locales/en_US.msg
index 3f6840b..e7bc257 100644
--- a/locales/en_US.msg
+++ b/locales/en_US.msg
@@ -55,17 +55,17 @@
 
 1 "end of file"
 2 "invalid character '%c'"
-3 "string end could not be found"
-4 "comment end could not be found"
+3 "string end cannot be found"
+4 "comment end cannot be found"
 5 "invalid token"
 6 "invalid expression"
 7 "empty expression"
 8 "invalid print statement"
 9 "invalid function definition"
-10 "invalid assignment: left side must be scale, ibase, obase, last, var, or array element"
+10 "invalid assignment: left side must be scale, ibase, obase, seed, last, var, or array element"
 11 "no auto variable found"
 12 "function parameter or auto \"%s%s\" already exists"
-13 "block end could not be found"
+13 "block end cannot be found"
 14 "cannot return a value from void function: %s()"
 15 "var cannot be a reference: %s"
 16 "POSIX does not allow names longer than 1 character: %s"
@@ -100,7 +100,9 @@
 
 1 "memory allocation failed"
 2 "I/O error"
-3 "could not open file: %s"
+3 "cannot open file: %s"
 4 "file is not ASCII: %s"
 5 "path is a directory: %s"
-6 "invalid command-line option: '%c' (\"%s\")"
+6 "invalid command-line option: \"%s\""
+7 "option requires an argument: '%c' (\"%s\")"
+8 "option takes no arguments: '%c' (\"%s\")"
diff --git a/locales/fr_FR.ISO8859-1.msg b/locales/fr_FR.ISO8859-1.msg
index 9456c38..c272a05 100644
--- a/locales/fr_FR.ISO8859-1.msg
+++ b/locales/fr_FR.ISO8859-1.msg
@@ -62,7 +62,7 @@
 7 "expression vide"
 8 "instruction d'écriture invalide"
 9 "définition de fonction invalide"
-10 "affectation invalide : la partie gauche doit être 'scale', 'ibase', 'obase', 'last', une variable ou une case de tableau"
+10 "affectation invalide : la partie gauche doit être 'scale', 'ibase', 'obase', 'seed', 'last', une variable ou une case de tableau"
 11 "aucune variable auto trouvée"
 12 "Le paramètre de fonction ou variable auto \"%s%s\" existe déjà"
 13 "fin de bloc non trouvée"
@@ -103,4 +103,6 @@
 3 "impossible d'ouvrir le fichier : %s"
 4 "fichier non ASCII : %s"
 5 "le chemin est un répertoire : %s"
-6 "option de ligne de commande invalide : '%c' (\"%s\")"
+6 "option de ligne de commande invalide : \"%s\""
+7 "l'option '%c' (\"%s\") requiert un argument"
+8 "l'option '%c' (\"%s\") ne prend pas d'argument"
diff --git a/locales/fr_FR.UTF-8.msg b/locales/fr_FR.UTF-8.msg
index 9456c38..c272a05 100644
--- a/locales/fr_FR.UTF-8.msg
+++ b/locales/fr_FR.UTF-8.msg
@@ -62,7 +62,7 @@
 7 "expression vide"
 8 "instruction d'écriture invalide"
 9 "définition de fonction invalide"
-10 "affectation invalide : la partie gauche doit être 'scale', 'ibase', 'obase', 'last', une variable ou une case de tableau"
+10 "affectation invalide : la partie gauche doit être 'scale', 'ibase', 'obase', 'seed', 'last', une variable ou une case de tableau"
 11 "aucune variable auto trouvée"
 12 "Le paramètre de fonction ou variable auto \"%s%s\" existe déjà"
 13 "fin de bloc non trouvée"
@@ -103,4 +103,6 @@
 3 "impossible d'ouvrir le fichier : %s"
 4 "fichier non ASCII : %s"
 5 "le chemin est un répertoire : %s"
-6 "option de ligne de commande invalide : '%c' (\"%s\")"
+6 "option de ligne de commande invalide : \"%s\""
+7 "l'option '%c' (\"%s\") requiert un argument"
+8 "l'option '%c' (\"%s\") ne prend pas d'argument"
diff --git a/locales/ja_JP.UTF-8.msg b/locales/ja_JP.UTF-8.msg
new file mode 100644
index 0000000..766fcf4
--- /dev/null
+++ b/locales/ja_JP.UTF-8.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ その他のメッセージ。
+$set 1
+
+1 "関数:"
+
+$ エラーの種類。
+$set 2
+
+1 "数学のエラー:"
+2 "パースエラー:"
+3 "ランタイムエラー:"
+4 "致命的なエラー:"
+5 "警告:"
+
+$ 数学のエラーです。
+$set 3
+
+1 "負の数"
+2 "非整数"
+3 "オーバーフロー:数字がハードウェア番号に収まらない"
+4 "0で割る"
+
+$ 構文解析のエラー。
+$set 4
+
+1 "ファイルの終了"
+2 "無効な文字 '%c'"
+3 "文字列の終端が見つかりませんでした"
+4 "コメントエンドが見つかりませんでした"
+5 "無効なトークン"
+6 "無効な式"
+7 "空の式"
+8 "無効な印刷文"
+9 "無効な関数定義"
+10 "無効な代入:左側は scale, ibase, obase, last, var, または配列要素でなければなりません"
+11 "自動変数が見つかりませんでした"
+12 "関数パラメータまたは自動\"%s%s\"はすでに存在します"
+13 "ブロックエンドが見つかりませんでした"
+14 "void 関数から値を返すことはできません:%s()"
+15 "varは参照にできません:%s"
+16 "POSIX は 1 文字より長い名前を許可しません:%s"
+17 "POSIX は '#' スクリプトのコメントを許可しません。"
+18 "POSIX は以下のキーワードを許可しません:%s"
+19 "POSIX は最後の結果のショートカットとしてピリオド ('.') を許可しません。"
+20 "POSIX は戻り値式の周りに括弧を必要とします。"
+21 "POSIX は次の演算子を許可しません:%s"
+22 "POSIX は if 文やループの外の比較演算子を許可しません。"
+23 "POSIXは条件ごとに0または1の比較演算子を必要とします。"
+24 "POSIXはforループの3つの部分がすべて空でないことを要求します。"
+25 "POSIXは指数表記を許可しません。"
+26 "POSIX は関数パラメータとして配列参照を許可しません。"
+27 "POSIXでは、関数ヘッダと同じ行に左中括弧があることが必要です。"
+
+$ ランタイムエラー。
+$set 5
+
+1 "無効なibase:は[%lu、%lu]でなければなりません"
+2 "無効なobase:は[%lu、%lu]でなければなりません"
+3 "無効なscale:は[%lu、%lu]でなければなりません"
+4 "式が無効read()"
+5 "再帰的読み込み()呼び出し"
+6 "変数または配列要素の型が間違っている"
+7 "スタックの要素が少なすぎる"
+8 "パラメータの数が間違っています。"
+9 "定義されていない関数:%s()"
+10 "式では void 値を使用できません"
+
+$ 致命的なエラーが発生しました。
+$set 6
+
+1 "メモリの割り当てに失敗しました"
+2 "I/Oエラー"
+3 "ファイルを開けませんでした。%s"
+4 "ファイルがASCIIではありません:%s"
+5 "パスはディレクトリです:%s"
+6 "無効なコマンドラインオプション:'%c'(\"%s\")"
+
+$set 7
+
+1 "オプションには引数が必要です:'%c' (\"%s\")"
+2 "オプションは引数を取りません:'%c' (\"%s\")"
diff --git a/locales/ja_JP.eucJP.msg b/locales/ja_JP.eucJP.msg
new file mode 100644
index 0000000..926840e
--- /dev/null
+++ b/locales/ja_JP.eucJP.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ ¤½¤Î¾¤Î¥á¥Ã¥»¡¼¥¸¡£
+$set 1
+
+1 "´Ø¿ô¡§"
+
+$ ¥¨¥é¡¼¤Î¼ïÎà¡£
+$set 2
+
+1 "¿ô³Ø¤Î¥¨¥é¡¼¡§"
+2 "¥Ñ¡¼¥¹¥¨¥é¡¼¡§"
+3 "¥é¥ó¥¿¥¤¥à¥¨¥é¡¼¡§"
+4 "Ã×̿Ū¤Ê¥¨¥é¡¼¡§"
+5 "·Ù¹ð¡§"
+
+$ ¿ô³Ø¤Î¥¨¥é¡¼¤Ç¤¹¡£
+$set 3
+
+1 "Éé¤Î¿ô"
+2 "ÈóÀ°¿ô"
+3 "¥ª¡¼¥Ð¡¼¥Õ¥í¡¼¡§¿ô»ú¤¬¥Ï¡¼¥É¥¦¥§¥¢ÈÖ¹æ¤Ë¼ý¤Þ¤é¤Ê¤¤"
+4 "0¤Ç³ä¤ë"
+
+$ ¹½Ê¸²òÀϤΥ¨¥é¡¼¡£
+$set 4
+
+1 "¥Õ¥¡¥¤¥ë¤Î½ªÎ»"
+2 "̵¸ú¤Êʸ»ú '%c'"
+3 "ʸ»úÎó¤Î½ªÃ¼¤¬¸«¤Ä¤«¤ê¤Þ¤»¤ó¤Ç¤·¤¿"
+4 "¥³¥á¥ó¥È¥¨¥ó¥É¤¬¸«¤Ä¤«¤ê¤Þ¤»¤ó¤Ç¤·¤¿"
+5 "̵¸ú¤Ê¥È¡¼¥¯¥ó"
+6 "̵¸ú¤Ê¼°"
+7 "¶õ¤Î¼°"
+8 "̵¸ú¤Ê°õºþʸ"
+9 "̵¸ú¤Ê´Ø¿ôÄêµÁ"
+10 "̵¸ú¤ÊÂåÆþ¡§º¸Â¦¤Ï scale, ibase, obase, last, var, ¤Þ¤¿¤ÏÇÛÎóÍ×ÁǤǤʤ±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó"
+11 "¼«Æ°ÊÑ¿ô¤¬¸«¤Ä¤«¤ê¤Þ¤»¤ó¤Ç¤·¤¿"
+12 "´Ø¿ô¥Ñ¥é¥á¡¼¥¿¤Þ¤¿¤Ï¼«Æ°\"¡ós¡ós\"¤Ï¤¹¤Ç¤Ë¸ºß¤·¤Þ¤¹"
+13 "¥Ö¥í¥Ã¥¯¥¨¥ó¥É¤¬¸«¤Ä¤«¤ê¤Þ¤»¤ó¤Ç¤·¤¿"
+14 "void ´Ø¿ô¤«¤éÃͤòÊÖ¤¹¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡§%s()"
+15 "var¤Ï»²¾È¤Ë¤Ç¤­¤Þ¤»¤ó¡§¡ós"
+16 "POSIX ¤Ï 1 ʸ»ú¤è¤êŤ¤Ì¾Á°¤òµö²Ä¤·¤Þ¤»¤ó¡§%s"
+17 "POSIX ¤Ï '#' ¥¹¥¯¥ê¥×¥È¤Î¥³¥á¥ó¥È¤òµö²Ä¤·¤Þ¤»¤ó¡£"
+18 "POSIX ¤Ï°Ê²¼¤Î¥­¡¼¥ï¡¼¥É¤òµö²Ä¤·¤Þ¤»¤ó¡§%s"
+19 "POSIX ¤ÏºÇ¸å¤Î·ë²Ì¤Î¥·¥ç¡¼¥È¥«¥Ã¥È¤È¤·¤Æ¥Ô¥ê¥ª¥É ('.') ¤òµö²Ä¤·¤Þ¤»¤ó¡£"
+20 "POSIX ¤ÏÌá¤êÃͼ°¤Î¼þ¤ê¤Ë³ç¸Ì¤òɬÍפȤ·¤Þ¤¹¡£"
+21 "POSIX ¤Ï¼¡¤Î±é»»»Ò¤òµö²Ä¤·¤Þ¤»¤ó¡§%s"
+22 "POSIX ¤Ï if ʸ¤ä¥ë¡¼¥×¤Î³°¤ÎÈæ³Ó±é»»»Ò¤òµö²Ä¤·¤Þ¤»¤ó¡£"
+23 "POSIX¤Ï¾ò·ï¤´¤È¤Ë0¤Þ¤¿¤Ï1¤ÎÈæ³Ó±é»»»Ò¤òɬÍפȤ·¤Þ¤¹¡£"
+24 "POSIX¤Ïfor¥ë¡¼¥×¤Î3¤Ä¤ÎÉôʬ¤¬¤¹¤Ù¤Æ¶õ¤Ç¤Ê¤¤¤³¤È¤òÍ׵ᤷ¤Þ¤¹¡£"
+25 "POSIX¤Ï»Ø¿ôɽµ­¤òµö²Ä¤·¤Þ¤»¤ó¡£"
+26 "POSIX ¤Ï´Ø¿ô¥Ñ¥é¥á¡¼¥¿¤È¤·¤ÆÇÛÎ󻲾Ȥòµö²Ä¤·¤Þ¤»¤ó¡£"
+27 "POSIX¤Ç¤Ï¡¢´Ø¿ô¥Ø¥Ã¥À¤ÈƱ¤¸¹Ô¤Ëº¸Ãæ³ç¸Ì¤¬¤¢¤ë¤³¤È¤¬É¬ÍפǤ¹¡£"
+
+$ ¥é¥ó¥¿¥¤¥à¥¨¥é¡¼¡£
+$set 5
+
+1 "̵¸ú¤Êibase¡§¤Ï[¡ólu¡¢¡ólu]¤Ç¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó"
+2 "̵¸ú¤Êobase¡§¤Ï[¡ólu¡¢¡ólu]¤Ç¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó"
+3 "̵¸ú¤Êscale¡§¤Ï[¡ólu¡¢¡ólu]¤Ç¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó"
+4 "¼°¤¬Ìµ¸úread()"
+5 "ºÆµ¢ÅªÆɤ߹þ¤ß()¸Æ¤Ó½Ð¤·"
+6 "ÊÑ¿ô¤Þ¤¿¤ÏÇÛÎóÍ×ÁǤη¿¤¬´Ö°ã¤Ã¤Æ¤¤¤ë"
+7 "¥¹¥¿¥Ã¥¯¤ÎÍ×ÁǤ¬¾¯¤Ê¤¹¤®¤ë"
+8 "¥Ñ¥é¥á¡¼¥¿¤Î¿ô¤¬´Ö°ã¤Ã¤Æ¤¤¤Þ¤¹¡£"
+9 "ÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤¤´Ø¿ô¡§%s()"
+10 "¼°¤Ç¤Ï void Ãͤò»ÈÍѤǤ­¤Þ¤»¤ó"
+
+$ Ã×̿Ū¤Ê¥¨¥é¡¼¤¬È¯À¸¤·¤Þ¤·¤¿¡£
+$set 6
+
+1 "¥á¥â¥ê¤Î³ä¤êÅö¤Æ¤Ë¼ºÇÔ¤·¤Þ¤·¤¿"
+2 "I/O¥¨¥é¡¼"
+3 "¥Õ¥¡¥¤¥ë¤ò³«¤±¤Þ¤»¤ó¤Ç¤·¤¿¡£%s"
+4 "¥Õ¥¡¥¤¥ë¤¬ASCII¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡§%s"
+5 "¥Ñ¥¹¤Ï¥Ç¥£¥ì¥¯¥È¥ê¤Ç¤¹¡§%s"
+6 "̵¸ú¤Ê¥³¥Þ¥ó¥É¥é¥¤¥ó¥ª¥×¥·¥ç¥ó¡§'¡óc'¡Ê\"¡ós\")"
+
+$set 7
+
+1 "¥ª¥×¥·¥ç¥ó¤Ë¤Ï°ú¿ô¤¬É¬ÍפǤ¹¡§'%c' (\"%s\")"
+2 "¥ª¥×¥·¥ç¥ó¤Ï°ú¿ô¤ò¼è¤ê¤Þ¤»¤ó¡§'%c' (\"%s\")"
diff --git a/locales/nl_BE.ISO8859-1.msg b/locales/nl_BE.ISO8859-1.msg
new file mode 120000
index 0000000..cb19bde
--- /dev/null
+++ b/locales/nl_BE.ISO8859-1.msg
@@ -0,0 +1 @@
+nl_NL.ISO8859-1.msg
\ No newline at end of file
diff --git a/locales/nl_BE.ISO8859-15.msg b/locales/nl_BE.ISO8859-15.msg
new file mode 120000
index 0000000..33f4a25
--- /dev/null
+++ b/locales/nl_BE.ISO8859-15.msg
@@ -0,0 +1 @@
+nl_BE.ISO8859-1.msg
\ No newline at end of file
diff --git a/locales/nl_NL.ISO8859-1.msg b/locales/nl_NL.ISO8859-1.msg
new file mode 100644
index 0000000..f7bf7f3
--- /dev/null
+++ b/locales/nl_NL.ISO8859-1.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ Diversen berichten.
+$set 1
+
+1 "Functie:"
+
+$ Fouttypes.
+$set 2
+
+1 "Rekenfout:"
+2 "Parse error:"
+3 "Runtime error:"
+4 "Fatale fout:"
+5 "Waarschuwing:"
+
+$ Math error.
+$set 3
+
+1 "negatief getal"
+2 "niet-integraal getal"
+3 "overloop: nummer past niet in een hardware-nummer"
+4 "delen door 0"
+
+$ Parsefouten.
+$set 4
+
+1 "einde van het file"
+2 "ongeldig teken '%c'"
+3 "string einde kon niet worden gevonden"
+4 "commentaar einde kon niet worden gevonden"
+5 "ongeldige token"
+6 "ongeldige uitdrukking"
+7 "lege uitdrukking"
+8 "ongeldige afdruk"
+9 "ongeldige functiedefinitie"
+10 "ongeldige toewijzing: linkerzijde moet scale, ibase, obase, last, var of array element zijn"
+11 "geen autovariabele gevonden"
+12 "Functieparameter of automatisch bestaat al"
+13 "blokuiteinde kon niet worden gevonden"
+14 "kan geen waarde uit de nietige functie teruggeven: %s()"
+15 "var kan geen referentie zijn: %s"
+16 "POSIX staat geen namen toe die langer zijn dan 1 teken: %s"
+17 "POSIX staat geen '#'-scriptcommentaar toe"
+18 "POSIX laat het volgende sleutelwoord niet toe: %s"
+19 "POSIX staat geen periode ('.') toe als een kortere weg voor het laatste resultaat"
+20 "POSIX vereist haakjes rond de terugkeeruitdrukkingen"
+21 "POSIX laat de volgende operator niet toe: %s"
+22 "POSIX laat geen vergelijking toe tussen operatoren buiten als verklaringen of lussen"
+23 "POSIX vereist 0 of 1 vergelijkingsoperator per conditie"
+24 "POSIX vereist dat alle 3 de delen van een lus niet leeg zijn"
+25 "POSIX laat geen exponentiële notatie toe"
+26 "POSIX staat geen arrayreferenties toe als functieparameters"
+27 "POSIX vereist dat de linkse beugel op dezelfde regel staat als de functiehoofding"
+
+$ Runtime fouten.
+$set 5
+
+1 "ongeldige ibase: moet [%lu, %lu] zijn"
+2 "ongeldige obase: moet [%lu, %lu] zijn"
+3 "ongeldige schaal: moet [%lu, %lu] zijn"
+4 "ongeldige read() expressie"
+5 "recursieve read() call"
+6 "Variabele of matrix-element is het verkeerde type"
+7 "Stapel heeft te weinig elementen"
+8 "Verkeerd aantal parameters; hebben %zu nodig, hebben %zu"
+9 "ongedefinieerde functie: %s()"
+10 "kan geen nietige waarde in een uitdrukking gebruiken"
+
+$ Fatale fouten.
+$set 6
+
+1 "geheugentoewijzing mislukt"
+2 "I/O-fout"
+3 "kon geen file openen: %s"
+4 "bestand is niet ASCII: %s"
+5 "pad is een directory: %s"
+6 "ongeldige opdrachtregeloptie: '%c' (%s)"
+
+$set 7
+
+1 "optie vereist een argument: '%c' (\"%s\")"
+2 "optie neemt geen argumenten aan: '%c' (\"%s\")"
diff --git a/locales/nl_NL.ISO8859-15.msg b/locales/nl_NL.ISO8859-15.msg
new file mode 120000
index 0000000..cb19bde
--- /dev/null
+++ b/locales/nl_NL.ISO8859-15.msg
@@ -0,0 +1 @@
+nl_NL.ISO8859-1.msg
\ No newline at end of file
diff --git a/locales/nl_NL.UTF-8.msg b/locales/nl_NL.UTF-8.msg
new file mode 100644
index 0000000..4ab8bcb
--- /dev/null
+++ b/locales/nl_NL.UTF-8.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ Diversen berichten.
+$set 1
+
+1 "Functie:"
+
+$ Fouttypes.
+$set 2
+
+1 "Rekenfout:"
+2 "Parse error:"
+3 "Runtime error:"
+4 "Fatale fout:"
+5 "Waarschuwing:"
+
+$ Math error.
+$set 3
+
+1 "negatief getal"
+2 "niet-integraal getal"
+3 "overloop: nummer past niet in een hardware-nummer"
+4 "delen door 0"
+
+$ Parsefouten.
+$set 4
+
+1 "einde van het file"
+2 "ongeldig teken '%c'"
+3 "string einde kon niet worden gevonden"
+4 "commentaar einde kon niet worden gevonden"
+5 "ongeldige token"
+6 "ongeldige uitdrukking"
+7 "lege uitdrukking"
+8 "ongeldige afdruk"
+9 "ongeldige functiedefinitie"
+10 "ongeldige toewijzing: linkerzijde moet scale, ibase, obase, last, var of array element zijn"
+11 "geen autovariabele gevonden"
+12 "Functieparameter of automatisch bestaat al"
+13 "blokuiteinde kon niet worden gevonden"
+14 "kan geen waarde uit de nietige functie teruggeven: %s()"
+15 "var kan geen referentie zijn: %s"
+16 "POSIX staat geen namen toe die langer zijn dan 1 teken: %s"
+17 "POSIX staat geen '#'-scriptcommentaar toe"
+18 "POSIX laat het volgende sleutelwoord niet toe: %s"
+19 "POSIX staat geen periode ('.') toe als een kortere weg voor het laatste resultaat"
+20 "POSIX vereist haakjes rond de terugkeeruitdrukkingen"
+21 "POSIX laat de volgende operator niet toe: %s"
+22 "POSIX laat geen vergelijking toe tussen operatoren buiten als verklaringen of lussen"
+23 "POSIX vereist 0 of 1 vergelijkingsoperator per conditie"
+24 "POSIX vereist dat alle 3 de delen van een lus niet leeg zijn"
+25 "POSIX laat geen exponentiële notatie toe"
+26 "POSIX staat geen arrayreferenties toe als functieparameters"
+27 "POSIX vereist dat de linkse beugel op dezelfde regel staat als de functiehoofding"
+
+$ Runtime fouten.
+$set 5
+
+1 "ongeldige ibase: moet [%lu, %lu] zijn"
+2 "ongeldige obase: moet [%lu, %lu] zijn"
+3 "ongeldige schaal: moet [%lu, %lu] zijn"
+4 "ongeldige read() expressie"
+5 "recursieve read() call"
+6 "Variabele of matrix-element is het verkeerde type"
+7 "Stapel heeft te weinig elementen"
+8 "Verkeerd aantal parameters; hebben %zu nodig, hebben %zu"
+9 "ongedefinieerde functie: %s()"
+10 "kan geen nietige waarde in een uitdrukking gebruiken"
+
+$ Fatale fouten.
+$set 6
+
+1 "geheugentoewijzing mislukt"
+2 "I/O-fout"
+3 "kon geen file openen: %s"
+4 "bestand is niet ASCII: %s"
+5 "pad is een directory: %s"
+6 "ongeldige opdrachtregeloptie: '%c' (%s)"
+
+$set 7
+
+1 "optie vereist een argument: '%c' (\"%s\")"
+2 "optie neemt geen argumenten aan: '%c' (\"%s\")"
diff --git a/locales/pl_PL.ISO8859-2.msg b/locales/pl_PL.ISO8859-2.msg
new file mode 100644
index 0000000..bd92815
--- /dev/null
+++ b/locales/pl_PL.ISO8859-2.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ Ró¿ne wiadomo¶ci.
+$set 1
+
+1 "Funkcja:"
+
+$ Typy b³êdów.
+$set 2
+
+1 "B³±d matematyczny:"
+2 "B³±d parse'a:"
+3 "B³±d biegu:"
+4 "B³±d ¶miertelny:"
+5 "Ostrze¿enie:"
+
+$ B³êdy matematyczne.
+$set 3
+
+1 "liczba ujemna"
+2 "numer nieintegracyjny"
+3 "przelewanie: liczba nie mie¶ci siê w numerze sprzêtowym"
+4 "dzielenie przez 0"
+
+$ B³êdy Parse'a.
+$set 4
+
+1 "koniec akt"
+2 "niewa¿ny znak '%c'"
+3 "koniec sznurka nie móg³ byæ znaleziony"
+4 "koniec komentarza nie móg³ byæ znaleziony"
+5 "niewa¿ny token"
+6 "niewa¿ne wyra¿enie"
+7 "puste wyra¿enie"
+8 "niewa¿ny wyci±g z wydruku"
+9 "nieprawid³owa definicja funkcji"
+10 "nieprawid³owe przyporz±dkowanie: lewa strona musi byæ elementem scale, ibase, obase, last, var lub element array"
+11 "nie znaleziono zmiennej automatycznej"
+12 "parametr funkcji lub auto \"%s%s\" ju¿ istnieje"
+13 "koñca bloku nie mo¿na by³o znale¼æ"
+14 "nie mo¿e zwróciæ warto¶ci z funkcji void: %s()"
+15 "var nie mo¿e byæ odniesieniem: %s"
+16 "POSIX nie zezwala na nazwy d³u¿sze ni¿ 1 znak: %s"
+17 "POSIX nie pozwala na komentarze skryptu '#'"
+18 "POSIX nie pozwala na u¿ycie nastêpuj±cego s³owa kluczowego: %s"
+19 "POSIX nie dopuszcza kropki ('.') jako skrótu do ostatniego wyniku"
+20 "POSIX wymaga nawiasów wokó³ wyra¿eñ zwrotnych"
+21 "POSIX nie pozwala nastêpuj±cemu operatorowi: %s"
+22 "POSIX nie pozwala na porównywanie operatorów na zewn±trz, je¶li deklaracje lub pêtle"
+23 "POSIX wymaga 0 lub 1 operatora porównawczego na jeden warunek"
+24 "POSIX wymaga, aby wszystkie 3 czê¶ci pêtli nie by³y puste"
+25 "POSIX nie pozwala na notacjê wyk³adnicz±"
+26 "POSIX nie zezwala na odniesienia do tablicy jako parametrów funkcji"
+27 "POSIX wymaga, aby lewe usztywnienie znajdowa³o siê na tej samej linii co nag³ówek funkcji"
+
+$ B³êdy Runtime'u.
+$set 5
+
+1 "nieprawid³owa ibase: musi byæ [%lu, %lu]"
+2 "nieprawid³owa obase: musi byæ [%lu, %lu]"
+3 "nieprawid³owa scale: musi byæ [%lu, %lu]"
+4 "nieprawid³owe wyra¿enie read()"
+5 "powtarzalne wywo³anie read()"
+6 "element zmienny lub tablicowy jest niew³a¶ciwym typem"
+7 "stos ma zbyt ma³o elementów"
+8 "niew³a¶ciwa liczba parametrów; potrzeba %zu, maj± %zu"
+9 "niezdefiniowana funkcja: %s()"
+10 "nie mo¿e u¿yæ warto¶ci pustej w wyra¿eniu"
+
+$ Fatalne b³êdy.
+$set 6
+
+1 "Alokacja pamiêci nie powiod³a siê"
+2 "B³±d we/wy"
+3 "nie móg³ otworzyæ pliku: %s"
+4 "plik nie jest ASCII: %s"
+5 "¶cie¿ka to katalog: %s"
+6 "nieprawid³owa opcja wiersza poleceñ: '%c' (\"%s\")"
+
+$set 7
+
+1 "opcja wymaga argumentu: '%c' (\"%s\")"
+2 "opcja nie wymaga ¿adnych argumentów: '%c' (\"%s\")"
diff --git a/locales/pl_PL.UTF-8.msg b/locales/pl_PL.UTF-8.msg
new file mode 100644
index 0000000..608f7a6
--- /dev/null
+++ b/locales/pl_PL.UTF-8.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ Różne wiadomości.
+$set 1
+
+1 "Funkcja:"
+
+$ Typy błędów.
+$set 2
+
+1 "Błąd matematyczny:"
+2 "Błąd parse'a:"
+3 "Błąd biegu:"
+4 "Błąd śmiertelny:"
+5 "Ostrzeżenie:"
+
+$ Błędy matematyczne.
+$set 3
+
+1 "liczba ujemna"
+2 "numer nieintegracyjny"
+3 "przelewanie: liczba nie mieści się w numerze sprzętowym"
+4 "dzielenie przez 0"
+
+$ Błędy Parse'a.
+$set 4
+
+1 "koniec akt"
+2 "nieważny znak '%c'"
+3 "koniec sznurka nie mógł być znaleziony"
+4 "koniec komentarza nie mógł być znaleziony"
+5 "nieważny token"
+6 "nieważne wyrażenie"
+7 "puste wyrażenie"
+8 "nieważny wyciąg z wydruku"
+9 "nieprawidłowa definicja funkcji"
+10 "nieprawidłowe przyporządkowanie: lewa strona musi być elementem scale, ibase, obase, last, var lub element array"
+11 "nie znaleziono zmiennej automatycznej"
+12 "parametr funkcji lub auto \"%s%s\" już istnieje"
+13 "końca bloku nie można było znaleźć"
+14 "nie może zwrócić wartości z funkcji void: %s()"
+15 "var nie może być odniesieniem: %s"
+16 "POSIX nie zezwala na nazwy dłuższe niż 1 znak: %s"
+17 "POSIX nie pozwala na komentarze skryptu '#'"
+18 "POSIX nie pozwala na użycie następującego słowa kluczowego: %s"
+19 "POSIX nie dopuszcza kropki ('.') jako skrótu do ostatniego wyniku"
+20 "POSIX wymaga nawiasów wokół wyrażeń zwrotnych"
+21 "POSIX nie pozwala następującemu operatorowi: %s"
+22 "POSIX nie pozwala na porównywanie operatorów na zewnątrz, jeśli deklaracje lub pętle"
+23 "POSIX wymaga 0 lub 1 operatora porównawczego na jeden warunek"
+24 "POSIX wymaga, aby wszystkie 3 części pętli nie były puste"
+25 "POSIX nie pozwala na notację wykładniczą"
+26 "POSIX nie zezwala na odniesienia do tablicy jako parametrów funkcji"
+27 "POSIX wymaga, aby lewe usztywnienie znajdowało się na tej samej linii co nagłówek funkcji"
+
+$ Błędy Runtime'u.
+$set 5
+
+1 "nieprawidłowa ibase: musi być [%lu, %lu]"
+2 "nieprawidłowa obase: musi być [%lu, %lu]"
+3 "nieprawidłowa scale: musi być [%lu, %lu]"
+4 "nieprawidłowe wyrażenie read()"
+5 "powtarzalne wywołanie read()"
+6 "element zmienny lub tablicowy jest niewłaściwym typem"
+7 "stos ma zbyt mało elementów"
+8 "niewłaściwa liczba parametrów; potrzeba %zu, mają %zu"
+9 "niezdefiniowana funkcja: %s()"
+10 "nie może użyć wartości pustej w wyrażeniu"
+
+$ Fatalne błędy.
+$set 6
+
+1 "Alokacja pamięci nie powiodła się"
+2 "Błąd we/wy"
+3 "nie mógł otworzyć pliku: %s"
+4 "plik nie jest ASCII: %s"
+5 "ścieżka to katalog: %s"
+6 "nieprawidłowa opcja wiersza poleceń: '%c' (\"%s\")"
+
+$set 7
+
+1 "opcja wymaga argumentu: '%c' (\"%s\")"
+2 "opcja nie wymaga żadnych argumentów: '%c' (\"%s\")"
diff --git a/locales/pt_PT.ISO8859-1.msg b/locales/pt_PT.ISO8859-1.msg
index d856480..c278adc 100644
--- a/locales/pt_PT.ISO8859-1.msg
+++ b/locales/pt_PT.ISO8859-1.msg
@@ -31,76 +31,78 @@
 $ Miscellaneous messages.
 $set 1
 
-1 "Função:"
+1 "Função:"
 
 $ Error types.
 $set 2
 
-1 "Erro de cálculo:"
-2 "Erro de análise de sintaxe:"
-3 "Erro de execução:"
+1 "Erro de cálculo:"
+2 "Erro de análise de sintaxe:"
+3 "Erro de execução:"
 4 "Erro fatal:"
 5 "Aviso:"
 
 $ Math errors.
 $set 3
 
-1 "número negativo"
-2 "número não inteiro"
-3 "Estouro: número não cabe no registro"
+1 "número negativo"
+2 "número não inteiro"
+3 "Estouro: número não cabe no registro"
 4 "dividir por 0"
 
 $ Parse errors.
 $set 4
 
 1 "fim do arquivo"
-2 "caractere inválido '%c'"
-3 "Não foi possível encontrar o final da string"
-4 "Não foi possível encontrar o final do comentário"
-5 "token inválido"
-6 "expressão inválida"
-7 "expressão vazia"
-8 "instrução de gravação inválida"
-9 "definição de função inválida"
-10 "atribuição inválida: a parte esquerda deve ser 'scale', 'ibase', 'obase', 'last', uma variável ou um elemento da matriz"
-11 "nenhuma variável automática encontrada"
-12 "parâmetro de função ou variável automática \"%s%s\" já existe"
-13 "fim do bloco não encontrado"
-14 "uma função 'void' não pode retornar um valor: %s()"
-15 "Uma variável não pode ser uma referência: %s"
-16 "POSIX não permite nomes com mais de 1 caractere: %s"
-17 "POSIX não permite comentários de script '#'"
-18 "POSIX não permite a seguinte palavra-chave: %s"
-19 "POSIX não permite um ponto ('.') como um atalho para o último resultado"
-20 "POSIX requer parênteses em torno de expressões de retorno"
-21 "POSIX não permite o seguinte operador: %s"
-22 "POSIX não permite operadores de comparação fora das expressões 'if' ou loops"
-23 "POSIX requer operadores 0 ou 1 de comparação por condição"
-24 "POSIX não permite uma expressão vazia em um loop 'for'"
-25 "POSIX não permite notação exponencial"
-26 "POSIX não permite referências de matriz como parâmetros de função"
-27 "POSIX requer que o cabeçalho da função '{' estejam na mesma linha"
+2 "caractere inválido '%c'"
+3 "Não foi possível encontrar o final da string"
+4 "Não foi possível encontrar o final do comentário"
+5 "token inválido"
+6 "expressão inválida"
+7 "expressão vazia"
+8 "instrução de gravação inválida"
+9 "definição de função inválida"
+10 "atribuição inválida: a parte esquerda deve ser 'scale', 'ibase', 'obase', 'last', uma variável ou um elemento da matriz"
+11 "nenhuma variável automática encontrada"
+12 "parâmetro de função ou variável automática \"%s%s\" já existe"
+13 "fim do bloco não encontrado"
+14 "uma função 'void' não pode retornar um valor: %s()"
+15 "Uma variável não pode ser uma referência: %s"
+16 "POSIX não permite nomes com mais de 1 caractere: %s"
+17 "POSIX não permite comentários de script '#'"
+18 "POSIX não permite a seguinte palavra-chave: %s"
+19 "POSIX não permite um ponto ('.') como um atalho para o último resultado"
+20 "POSIX requer parênteses em torno de expressões de retorno"
+21 "POSIX não permite o seguinte operador: %s"
+22 "POSIX não permite operadores de comparação fora das expressões 'if' ou loops"
+23 "POSIX requer operadores 0 ou 1 de comparação por condição"
+24 "POSIX não permite uma expressão vazia em um loop 'for'"
+25 "POSIX não permite notação exponencial"
+26 "POSIX não permite referências de matriz como parâmetros de função"
+27 "POSIX requer que o cabeçalho da função '{' estejam na mesma linha"
 
 $ Runtime errors.
 $set 5
 
-1 "ibase inválido: deve ser [%lu, %lu]"
-2 "obase inválido: deve ser [%lu, %lu]"
-3 "scale inválida: deve ser [%lu, %lu]"
-4 "expressão read() inválida"
+1 "ibase inválido: deve ser [%lu, %lu]"
+2 "obase inválido: deve ser [%lu, %lu]"
+3 "scale inválida: deve ser [%lu, %lu]"
+4 "expressão read() inválida"
 5 "chamada read() recursiva"
-6 "tipo errado de variável ou elemento de matriz"
+6 "tipo errado de variável ou elemento de matriz"
 7 "pilha tem poucos elementos"
-8 "número incorreto de parâmetros - esperado: %zu, obtido: %zu"
-9 "função indefinida: %s()"
-10 "um valor 'void' não pode ser usado em uma expressão"
+8 "número incorreto de parâmetros - esperado: %zu, obtido: %zu"
+9 "função indefinida: %s()"
+10 "um valor 'void' não pode ser usado em uma expressão"
 
 $ Fatal errors.
 $set 6
 
-1 "falha na alocação de memória"
-2 "erro de entrada-saída"
-3 "impossível abrir o arquivo: %s"
-4 "arquivo não é ASCII: %s"
-5 "caminho é um diretório: %s"
-6 "opção de linha de comando inválida: '%c' (\"%s\")"
+1 "falha na alocação de memória"
+2 "erro de entrada-saída"
+3 "impossível abrir o arquivo: %s"
+4 "arquivo não é ASCII: %s"
+5 "caminho é um diretório: %s"
+6 "opção de linha de comando inválida: \"%s\""
+7 "opção requer um argumento: '%c' (\"%s\")"
+8 "a opção não aceita argumentos: '%c' (\"%s\")"
diff --git a/locales/pt_PT.UTF-8.msg b/locales/pt_PT.UTF-8.msg
index d856480..573de6a 100644
--- a/locales/pt_PT.UTF-8.msg
+++ b/locales/pt_PT.UTF-8.msg
@@ -103,4 +103,6 @@
 3 "impossível abrir o arquivo: %s"
 4 "arquivo não é ASCII: %s"
 5 "caminho é um diretório: %s"
-6 "opção de linha de comando inválida: '%c' (\"%s\")"
+6 "opção de linha de comando inválida: \"%s\""
+7 "opção requer um argumento: '%c' (\"%s\")"
+8 "a opção não aceita argumentos: '%c' (\"%s\")"
diff --git a/locales/ru_RU.CP1251.msg b/locales/ru_RU.CP1251.msg
new file mode 100644
index 0000000..16ff7b3
--- /dev/null
+++ b/locales/ru_RU.CP1251.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ Ðàçíûå ñîîáùåíèÿ.
+$set 1
+
+1 "Ôóíêöèÿ:"
+
+$ Òèïû îøèáîê.
+$set 2
+
+1 "Ìàòåìàòè÷åñêàÿ îøèáêà:"
+2 "Îøèáêà ïðè ðàçáîðå:"
+3 "Îøèáêà âûïîëíåíèÿ:"
+4 "Ôàòàëüíàÿ îøèáêà:"
+5 "Ïðåäóïðåæäåíèå:"
+
+$ Ìàòåìàòè÷åñêèå îøèáêè.
+$set 3
+
+1 "îòðèöàòåëüíîå ÷èñëî"
+2 "íåèíòåãðèðîâàííîå ÷èñëî"
+3 "ïåðåïîëíåíèå: íîìåð íå ïîìåùàåòñÿ â àïïàðàòíûé íîìåð"
+4 "äåëèòü íà 0"
+
+$ Îøèáêè ïðè ðàçáîðå.
+$set 4
+
+1 "êîíåö ôàéëà"
+2 "íåäîïóñòèìûé ñèìâîë '%c'"
+3 "êîíåö ñòðîêè íå íàéäåí"
+4 "êîíåö êîììåíòàðèÿ íå íàéäåí"
+5 "íåäåéñòâèòåëüíûé æåòîí"
+6 "íåïðàâèëüíîå âûðàæåíèå"
+7 "ïóñòîå âûðàæåíèå"
+8 "çàÿâëåíèå î íåäåéñòâèòåëüíîñòè ïå÷àòè"
+9 "îïðåäåëåíèå íåäåéñòâèòåëüíîé ôóíêöèè"
+10 "íåâåðíîå ïðèñâîåíèå: ëåâàÿ ñòîðîíà äîëæíà áûòü scale, ibase, obase, last, âàðîì èëè ýëåìåíòîì ìàññèâà"
+11 "àâòîìàòè÷åñêàÿ ïåðåìåííàÿ íå íàéäåíà"
+12 "ïàðàìåòð ôóíêöèè èëè auto \"%s%s\" óæå ñóùåñòâóåò"
+13 "êîíåö áëîêà íå íàéäåí"
+14 "íå ìîæåò âåðíóòü çíà÷åíèå èç ôóíêöèè void: %s()"
+15 "var íå ìîæåò áûòü ññûëêîé: %s"
+16 "POSIX íå äîïóñêàåò èìåí äëèííåå 1 ñèìâîëà: %s"
+17 "POSIX íå äîïóñêàåò êîììåíòàðèåâ ê ñöåíàðèþ '#'"
+18 "POSIX íå äîïóñêàåò ñëåäóþùåå êëþ÷åâîå ñëîâî: %s"
+19 "POSIX íå äîïóñêàåò òî÷êó ('.') â êà÷åñòâå ÿðëûêà äëÿ ïîñëåäíåãî ðåçóëüòàòà"
+20 "POSIX òðåáóåò ñêîáîê âîêðóã âûðàæåíèé âîçâðàòà"
+21 "POSIX íå ðàçðåøàåò èñïîëüçîâàòü ñëåäóþùèé îïåðàòîð: %s"
+22 "POSIX íå ðàçðåøàåò îïåðàòîðàì ñðàâíåíèÿ âûõîäèòü çà ïðåäåëû, åñëè óòâåðæäåíèÿ èëè öèêëû"
+23 "POSIX òðåáóåò 0 èëè 1 îïåðàòîðà ñðàâíåíèÿ íà óñëîâèå"
+24 "POSIX òðåáóåò, ÷òîáû âñå 3 ÷àñòè ïåòëè áûëè íåïóñòûìè"
+25 "POSIX íå äîïóñêàåò ýêñïîíåíöèàëüíîé íîòàöèè"
+26 "POSIX íå äîïóñêàåò ññûëêè íà ìàññèâ â êà÷åñòâå ïàðàìåòðîâ ôóíêöèè"
+27 "POSIX òðåáóåò, ÷òîáû ëåâàÿ ñêîáêà áûëà íà òîé æå ëèíèè, ÷òî è çàãîëîâîê ôóíêöèè"
+
+$ Îøèáêè âûïîëíåíèÿ.
+$set 5
+
+1 "Íåäåéñòâèòåëüíûé ibase: äîëæåí áûòü [%lu, %lu]"
+2 "Íåäåéñòâèòåëüíûé obase: äîëæåí áûòü [%lu, %lu]"
+3 "íåäåéñòâèòåëüíàÿ scale: äîëæíà áûòü [%lu, %lu]"
+4 "íåäåéñòâèòåëüíîå âûðàæåíèå read()"
+5 "ðåêóðñèâíûé âûçîâ read()"
+6 "ïåðåìåííàÿ èëè ýëåìåíò ìàññèâà ÿâëÿåòñÿ íåïðàâèëüíûì òèïîì"
+7 "ñòîïêà èìååò ñëèøêîì ìàëî ýëåìåíòîâ"
+8 "íåïðàâèëüíîå êîëè÷åñòâî ïàðàìåòðîâ; íóæíî %zu, íóæíî %zu"
+9 "íåîïðåäåëåííàÿ ôóíêöèÿ: %s()"
+10 "íå ìîæåò èñïîëüçîâàòü ïóñòîå çíà÷åíèå â âûðàæåíèè"
+
+$ Ôàòàëüíûå îøèáêè.
+$set 6
+
+1 "Íå óäàëîñü âûäåëèòü ïàìÿòü"
+2 "Îøèáêà ââîäà/âûâîäà"
+3 "íå ñìîã îòêðûòü ôàéë: %s"
+4 "ôàéë íå ASCII: %s"
+5 "ïóòü - ýòî êàòàëîã: %s"
+6 "íåâåðíàÿ îïöèÿ êîìàíäíîé ñòðîêè: '%c' (\"%s\")"
+
+$set 7
+
+1 "îïöèÿ òðåáóåò àðãóìåíòà: '%c' (\"%s\")"
+2 "îïöèÿ íå ïðèíèìàåò àðãóìåíòîâ: '%c' (\"%s\")"
diff --git a/locales/ru_RU.CP866.msg b/locales/ru_RU.CP866.msg
new file mode 100644
index 0000000..dcb9d02
--- /dev/null
+++ b/locales/ru_RU.CP866.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$  §­ë¥ á®®¡é¥­¨ï.
+$set 1
+
+1 "”ã­ªæ¨ï:"
+
+$ ’¨¯ë ®è¨¡®ª.
+$set 2
+
+1 "Œ â¥¬ â¨ç¥áª ï ®è¨¡ª :"
+2 "Žè¨¡ª  ¯à¨ à §¡®à¥:"
+3 "Žè¨¡ª  ¢ë¯®«­¥­¨ï:"
+4 "” â «ì­ ï ®è¨¡ª :"
+5 "à¥¤ã¯à¥¦¤¥­¨¥:"
+
+$ Œ â¥¬ â¨ç¥áª¨¥ ®è¨¡ª¨.
+$set 3
+
+1 "®âà¨æ â¥«ì­®¥ ç¨á«®"
+2 "­¥¨­â¥£à¨à®¢ ­­®¥ ç¨á«®"
+3 "¯¥à¥¯®«­¥­¨¥: ­®¬¥à ­¥ ¯®¬¥é ¥âáï ¢  ¯¯ à â­ë© ­®¬¥à"
+4 "¤¥«¨âì ­  0"
+
+$ Žè¨¡ª¨ ¯à¨ à §¡®à¥.
+$set 4
+
+1 "ª®­¥æ ä ©« "
+2 "­¥¤®¯ãáâ¨¬ë© á¨¬¢®« '%c'"
+3 "ª®­¥æ áâப¨ ­¥ ­ ©¤¥­"
+4 "ª®­¥æ ª®¬¬¥­â à¨ï ­¥ ­ ©¤¥­"
+5 "­¥¤¥©á⢨⥫ì­ë© ¦¥â®­"
+6 "­¥¯à ¢¨«ì­®¥ ¢ëà ¦¥­¨¥"
+7 "¯ãá⮥ ¢ëà ¦¥­¨¥"
+8 "§ ï¢«¥­¨¥ ® ­¥¤¥©á⢨⥫쭮á⨠¯¥ç â¨"
+9 "®¯à¥¤¥«¥­¨¥ ­¥¤¥©á⢨⥫쭮© ä㭪樨"
+10 "­¥¢¥à­®¥ ¯à¨á¢®¥­¨¥: «¥¢ ï áâ®à®­  ¤®«¦­  ¡ëâì scale, ibase, obase, last, ¢ à®¬ ¨«¨ í«¥¬¥­â®¬ ¬ áᨢ "
+11 " ¢â®¬ â¨ç¥áª ï ¯¥à¥¬¥­­ ï ­¥ ­ ©¤¥­ "
+12 "¯ à ¬¥âà ä㭪樨 ¨«¨ auto \"%s%s\" 㦥 áãé¥áâ¢ã¥â"
+13 "ª®­¥æ ¡«®ª  ­¥ ­ ©¤¥­"
+14 "­¥ ¬®¦¥â ¢¥à­ãâì §­ ç¥­¨¥ ¨§ ä㭪樨 void: %s()"
+15 "var ­¥ ¬®¦¥â ¡ëâì áá뫪®©: %s"
+16 "POSIX ­¥ ¤®¯ã᪠¥â ¨¬¥­ ¤«¨­­¥¥ 1 ᨬ¢®« : %s"
+17 "POSIX ­¥ ¤®¯ã᪠¥â ª®¬¬¥­â à¨¥¢ ª á業 à¨î '#'"
+18 "POSIX ­¥ ¤®¯ã᪠¥â á«¥¤ãî饥 ª«î祢®¥ á«®¢®: %s"
+19 "POSIX ­¥ ¤®¯ã᪠¥â â®çªã ('.') ¢ ª ç¥á⢥ ïà«ëª  ¤«ï ¯®á«¥¤­¥£® १ã«ìâ â "
+20 "POSIX âॡã¥â ᪮¡®ª ¢®ªà㣠¢ëà ¦¥­¨© ¢®§¢à â "
+21 "POSIX ­¥ à §à¥è ¥â ¨á¯®«ì§®¢ âì á«¥¤ãî騩 ®¯¥à â®à: %s"
+22 "POSIX ­¥ à §à¥è ¥â ®¯¥à â®à ¬ áà ¢­¥­¨ï ¢ë室¨âì §  ¯à¥¤¥«ë, ¥á«¨ ã⢥ত¥­¨ï ¨«¨ 横«ë"
+23 "POSIX âॡã¥â 0 ¨«¨ 1 ®¯¥à â®à  áà ¢­¥­¨ï ­  ãá«®¢¨¥"
+24 "POSIX âॡã¥â, çâ®¡ë ¢á¥ 3 ç á⨠¯¥â«¨ ¡ë«¨ ­¥¯ãáâ묨"
+25 "POSIX ­¥ ¤®¯ã᪠¥â íªá¯®­¥­æ¨ «ì­®© ­®â æ¨¨"
+26 "POSIX ­¥ ¤®¯ã᪠¥â áá뫪¨ ­  ¬ áᨢ ¢ ª ç¥á⢥ ¯ à ¬¥â஢ ä㭪樨"
+27 "POSIX âॡã¥â, çâ®¡ë «¥¢ ï ᪮¡ª  ¡ë«  ­  ⮩ ¦¥ «¨­¨¨, çâ® ¨ § £®«®¢®ª ä㭪樨"
+
+$ Žè¨¡ª¨ ¢ë¯®«­¥­¨ï.
+$set 5
+
+1 "¥¤¥©á⢨⥫ì­ë© ibase: ¤®«¦¥­ ¡ëâì [%lu, %lu]"
+2 "¥¤¥©á⢨⥫ì­ë© obase: ¤®«¦¥­ ¡ëâì [%lu, %lu]"
+3 "­¥¤¥©á⢨⥫쭠ï scale: ¤®«¦­  ¡ëâì [%lu, %lu]"
+4 "­¥¤¥©á⢨⥫쭮¥ ¢ëà ¦¥­¨¥ read()"
+5 "४ãàᨢ­ë© ¢ë§®¢ read()"
+6 "¯¥à¥¬¥­­ ï ¨«¨ í«¥¬¥­â ¬ áᨢ  ï¥âáï ­¥¯à ¢¨«ì­ë¬ ⨯®¬"
+7 "á⮯ª  ¨¬¥¥â ᫨誮¬ ¬ «® í«¥¬¥­â®¢"
+8 "­¥¯à ¢¨«ì­®¥ ª®«¨ç¥á⢮ ¯ à ¬¥â஢; ­ã¦­® %zu, ­ã¦­® %zu"
+9 "­¥®¯à¥¤¥«¥­­ ï äã­ªæ¨ï: %s()"
+10 "­¥ ¬®¦¥â ¨á¯®«ì§®¢ âì ¯ãá⮥ §­ ç¥­¨¥ ¢ ¢ëà ¦¥­¨¨"
+
+$ ” â «ì­ë¥ ®è¨¡ª¨.
+$set 6
+
+1 "¥ 㤠«®áì ¢ë¤¥«¨âì ¯ ¬ïâì"
+2 "Žè¨¡ª  ¢¢®¤ /¢ë¢®¤ "
+3 "­¥ ᬮ£ ®âªàëâì ä ©«: %s"
+4 "ä ©« ­¥ ASCII: %s"
+5 "¯ãâì - íâ® ª â «®£: %s"
+6 "­¥¢¥à­ ï ®¯æ¨ï ª®¬ ­¤­®© áâப¨: '%c' (\"%s\")"
+
+$set 7
+
+1 "®¯æ¨ï âॡã¥â  à£ã¬¥­â : '%c' (\"%s\")"
+2 "®¯æ¨ï ­¥ ¯à¨­¨¬ ¥â  à£ã¬¥­â®¢: '%c' (\"%s\")"
diff --git a/locales/ru_RU.ISO8859-5.msg b/locales/ru_RU.ISO8859-5.msg
new file mode 100644
index 0000000..a9e7ad0
--- /dev/null
+++ b/locales/ru_RU.ISO8859-5.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ ÀÐ×ÝëÕ áÞÞÑéÕÝØï.
+$set 1
+
+1 "ÄãÝÚæØï:"
+
+$ ÂØßë ÞèØÑÞÚ.
+$set 2
+
+1 "¼ÐâÕÜÐâØçÕáÚÐï ÞèØÑÚÐ:"
+2 "¾èØÑÚÐ ßàØ àÐ×ÑÞàÕ:"
+3 "¾èØÑÚÐ ÒëßÞÛÝÕÝØï:"
+4 "ÄÐâÐÛìÝÐï ÞèØÑÚÐ:"
+5 "¿àÕÔãßàÕÖÔÕÝØÕ:"
+
+$ ¼ÐâÕÜÐâØçÕáÚØÕ ÞèØÑÚØ.
+$set 3
+
+1 "ÞâàØæÐâÕÛìÝÞÕ çØáÛÞ"
+2 "ÝÕØÝâÕÓàØàÞÒÐÝÝÞÕ çØáÛÞ"
+3 "ßÕàÕßÞÛÝÕÝØÕ: ÝÞÜÕà ÝÕ ßÞÜÕéÐÕâáï Ò ÐßßÐàÐâÝëÙ ÝÞÜÕà"
+4 "ÔÕÛØâì ÝÐ 0"
+
+$ ¾èØÑÚØ ßàØ àÐ×ÑÞàÕ.
+$set 4
+
+1 "ÚÞÝÕæ äÐÙÛÐ"
+2 "ÝÕÔÞßãáâØÜëÙ áØÜÒÞÛ '%c'"
+3 "ÚÞÝÕæ áâàÞÚØ ÝÕ ÝÐÙÔÕÝ"
+4 "ÚÞÝÕæ ÚÞÜÜÕÝâÐàØï ÝÕ ÝÐÙÔÕÝ"
+5 "ÝÕÔÕÙáâÒØâÕÛìÝëÙ ÖÕâÞÝ"
+6 "ÝÕßàÐÒØÛìÝÞÕ ÒëàÐÖÕÝØÕ"
+7 "ßãáâÞÕ ÒëàÐÖÕÝØÕ"
+8 "×ÐïÒÛÕÝØÕ Þ ÝÕÔÕÙáâÒØâÕÛìÝÞáâØ ßÕçÐâØ"
+9 "ÞßàÕÔÕÛÕÝØÕ ÝÕÔÕÙáâÒØâÕÛìÝÞÙ äãÝÚæØØ"
+10 "ÝÕÒÕàÝÞÕ ßàØáÒÞÕÝØÕ: ÛÕÒÐï áâÞàÞÝÐ ÔÞÛÖÝÐ Ñëâì scale, ibase, obase, last, ÒÐàÞÜ ØÛØ íÛÕÜÕÝâÞÜ ÜÐááØÒÐ"
+11 "ÐÒâÞÜÐâØçÕáÚÐï ßÕàÕÜÕÝÝÐï ÝÕ ÝÐÙÔÕÝÐ"
+12 "ßÐàÐÜÕâà äãÝÚæØØ ØÛØ auto \"%s%s\" ãÖÕ áãéÕáâÒãÕâ"
+13 "ÚÞÝÕæ ÑÛÞÚÐ ÝÕ ÝÐÙÔÕÝ"
+14 "ÝÕ ÜÞÖÕâ ÒÕàÝãâì ×ÝÐçÕÝØÕ Ø× äãÝÚæØØ void: %s()"
+15 "var ÝÕ ÜÞÖÕâ Ñëâì ááëÛÚÞÙ: %s"
+16 "POSIX ÝÕ ÔÞßãáÚÐÕâ ØÜÕÝ ÔÛØÝÝÕÕ 1 áØÜÒÞÛÐ: %s"
+17 "POSIX ÝÕ ÔÞßãáÚÐÕâ ÚÞÜÜÕÝâÐàØÕÒ Ú áæÕÝÐàØî '#'"
+18 "POSIX ÝÕ ÔÞßãáÚÐÕâ áÛÕÔãîéÕÕ ÚÛîçÕÒÞÕ áÛÞÒÞ: %s"
+19 "POSIX ÝÕ ÔÞßãáÚÐÕâ âÞçÚã ('.') Ò ÚÐçÕáâÒÕ ïàÛëÚÐ ÔÛï ßÞáÛÕÔÝÕÓÞ àÕ×ãÛìâÐâÐ"
+20 "POSIX âàÕÑãÕâ áÚÞÑÞÚ ÒÞÚàãÓ ÒëàÐÖÕÝØÙ ÒÞ×ÒàÐâÐ"
+21 "POSIX ÝÕ àÐ×àÕèÐÕâ ØáßÞÛì×ÞÒÐâì áÛÕÔãîéØÙ ÞßÕàÐâÞà: %s"
+22 "POSIX ÝÕ àÐ×àÕèÐÕâ ÞßÕàÐâÞàÐÜ áàÐÒÝÕÝØï ÒëåÞÔØâì ×Ð ßàÕÔÕÛë, ÕáÛØ ãâÒÕàÖÔÕÝØï ØÛØ æØÚÛë"
+23 "POSIX âàÕÑãÕâ 0 ØÛØ 1 ÞßÕàÐâÞàÐ áàÐÒÝÕÝØï ÝÐ ãáÛÞÒØÕ"
+24 "POSIX âàÕÑãÕâ, çâÞÑë ÒáÕ 3 çÐáâØ ßÕâÛØ ÑëÛØ ÝÕßãáâëÜØ"
+25 "POSIX ÝÕ ÔÞßãáÚÐÕâ íÚáßÞÝÕÝæØÐÛìÝÞÙ ÝÞâÐæØØ"
+26 "POSIX ÝÕ ÔÞßãáÚÐÕâ ááëÛÚØ ÝÐ ÜÐááØÒ Ò ÚÐçÕáâÒÕ ßÐàÐÜÕâàÞÒ äãÝÚæØØ"
+27 "POSIX âàÕÑãÕâ, çâÞÑë ÛÕÒÐï áÚÞÑÚÐ ÑëÛÐ ÝÐ âÞÙ ÖÕ ÛØÝØØ, çâÞ Ø ×ÐÓÞÛÞÒÞÚ äãÝÚæØØ"
+
+$ ¾èØÑÚØ ÒëßÞÛÝÕÝØï.
+$set 5
+
+1 "½ÕÔÕÙáâÒØâÕÛìÝëÙ ibase: ÔÞÛÖÕÝ Ñëâì [%lu, %lu]"
+2 "½ÕÔÕÙáâÒØâÕÛìÝëÙ obase: ÔÞÛÖÕÝ Ñëâì [%lu, %lu]"
+3 "ÝÕÔÕÙáâÒØâÕÛìÝÐï scale: ÔÞÛÖÝÐ Ñëâì [%lu, %lu]"
+4 "ÝÕÔÕÙáâÒØâÕÛìÝÞÕ ÒëàÐÖÕÝØÕ read()"
+5 "àÕÚãàáØÒÝëÙ Òë×ÞÒ read()"
+6 "ßÕàÕÜÕÝÝÐï ØÛØ íÛÕÜÕÝâ ÜÐááØÒÐ ïÒÛïÕâáï ÝÕßàÐÒØÛìÝëÜ âØßÞÜ"
+7 "áâÞßÚÐ ØÜÕÕâ áÛØèÚÞÜ ÜÐÛÞ íÛÕÜÕÝâÞÒ"
+8 "ÝÕßàÐÒØÛìÝÞÕ ÚÞÛØçÕáâÒÞ ßÐàÐÜÕâàÞÒ; ÝãÖÝÞ %zu, ÝãÖÝÞ %zu"
+9 "ÝÕÞßàÕÔÕÛÕÝÝÐï äãÝÚæØï: %s()"
+10 "ÝÕ ÜÞÖÕâ ØáßÞÛì×ÞÒÐâì ßãáâÞÕ ×ÝÐçÕÝØÕ Ò ÒëàÐÖÕÝØØ"
+
+$ ÄÐâÐÛìÝëÕ ÞèØÑÚØ.
+$set 6
+
+1 "½Õ ãÔÐÛÞáì ÒëÔÕÛØâì ßÐÜïâì"
+2 "¾èØÑÚÐ ÒÒÞÔÐ/ÒëÒÞÔÐ"
+3 "ÝÕ áÜÞÓ ÞâÚàëâì äÐÙÛ: %s"
+4 "äÐÙÛ ÝÕ ASCII: %s"
+5 "ßãâì - íâÞ ÚÐâÐÛÞÓ: %s"
+6 "ÝÕÒÕàÝÐï ÞßæØï ÚÞÜÐÝÔÝÞÙ áâàÞÚØ: '%c' (\"%s\")"
+
+$set 7
+
+1 "ÞßæØï âàÕÑãÕâ ÐàÓãÜÕÝâÐ: '%c' (\"%s\")"
+2 "ÞßæØï ÝÕ ßàØÝØÜÐÕâ ÐàÓãÜÕÝâÞÒ: '%c' (\"%s\")"
diff --git a/locales/ru_RU.KOI8-R.msg b/locales/ru_RU.KOI8-R.msg
new file mode 100644
index 0000000..e0a5f5b
--- /dev/null
+++ b/locales/ru_RU.KOI8-R.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ òÁÚÎÙÅ ÓÏÏÂÝÅÎÉÑ.
+$set 1
+
+1 "æÕÎËÃÉÑ:"
+
+$ ôÉÐÙ ÏÛÉÂÏË.
+$set 2
+
+1 "íÁÔÅÍÁÔÉÞÅÓËÁÑ ÏÛÉÂËÁ:"
+2 "ïÛÉÂËÁ ÐÒÉ ÒÁÚÂÏÒÅ:"
+3 "ïÛÉÂËÁ ×ÙÐÏÌÎÅÎÉÑ:"
+4 "æÁÔÁÌØÎÁÑ ÏÛÉÂËÁ:"
+5 "ðÒÅÄÕÐÒÅÖÄÅÎÉÅ:"
+
+$ íÁÔÅÍÁÔÉÞÅÓËÉÅ ÏÛÉÂËÉ.
+$set 3
+
+1 "ÏÔÒÉÃÁÔÅÌØÎÏÅ ÞÉÓÌÏ"
+2 "ÎÅÉÎÔÅÇÒÉÒÏ×ÁÎÎÏÅ ÞÉÓÌÏ"
+3 "ÐÅÒÅÐÏÌÎÅÎÉÅ: ÎÏÍÅÒ ÎÅ ÐÏÍÅÝÁÅÔÓÑ × ÁÐÐÁÒÁÔÎÙÊ ÎÏÍÅÒ"
+4 "ÄÅÌÉÔØ ÎÁ 0"
+
+$ ïÛÉÂËÉ ÐÒÉ ÒÁÚÂÏÒÅ.
+$set 4
+
+1 "ËÏÎÅÃ ÆÁÊÌÁ"
+2 "ÎÅÄÏÐÕÓÔÉÍÙÊ ÓÉÍ×ÏÌ '%c'"
+3 "ËÏÎÅÃ ÓÔÒÏËÉ ÎÅ ÎÁÊÄÅÎ"
+4 "ËÏÎÅÃ ËÏÍÍÅÎÔÁÒÉÑ ÎÅ ÎÁÊÄÅÎ"
+5 "ÎÅÄÅÊÓÔ×ÉÔÅÌØÎÙÊ ÖÅÔÏÎ"
+6 "ÎÅÐÒÁ×ÉÌØÎÏÅ ×ÙÒÁÖÅÎÉÅ"
+7 "ÐÕÓÔÏÅ ×ÙÒÁÖÅÎÉÅ"
+8 "ÚÁÑ×ÌÅÎÉÅ Ï ÎÅÄÅÊÓÔ×ÉÔÅÌØÎÏÓÔÉ ÐÅÞÁÔÉ"
+9 "ÏÐÒÅÄÅÌÅÎÉÅ ÎÅÄÅÊÓÔ×ÉÔÅÌØÎÏÊ ÆÕÎËÃÉÉ"
+10 "ÎÅ×ÅÒÎÏÅ ÐÒÉÓ×ÏÅÎÉÅ: ÌÅ×ÁÑ ÓÔÏÒÏÎÁ ÄÏÌÖÎÁ ÂÙÔØ scale, ibase, obase, last, ×ÁÒÏÍ ÉÌÉ ÜÌÅÍÅÎÔÏÍ ÍÁÓÓÉ×Á"
+11 "Á×ÔÏÍÁÔÉÞÅÓËÁÑ ÐÅÒÅÍÅÎÎÁÑ ÎÅ ÎÁÊÄÅÎÁ"
+12 "ÐÁÒÁÍÅÔÒ ÆÕÎËÃÉÉ ÉÌÉ auto \"%s%s\" ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
+13 "ËÏÎÅÃ ÂÌÏËÁ ÎÅ ÎÁÊÄÅÎ"
+14 "ÎÅ ÍÏÖÅÔ ×ÅÒÎÕÔØ ÚÎÁÞÅÎÉÅ ÉÚ ÆÕÎËÃÉÉ void: %s()"
+15 "var ÎÅ ÍÏÖÅÔ ÂÙÔØ ÓÓÙÌËÏÊ: %s"
+16 "POSIX ÎÅ ÄÏÐÕÓËÁÅÔ ÉÍÅÎ ÄÌÉÎÎÅÅ 1 ÓÉÍ×ÏÌÁ: %s"
+17 "POSIX ÎÅ ÄÏÐÕÓËÁÅÔ ËÏÍÍÅÎÔÁÒÉÅ× Ë ÓÃÅÎÁÒÉÀ '#'"
+18 "POSIX ÎÅ ÄÏÐÕÓËÁÅÔ ÓÌÅÄÕÀÝÅÅ ËÌÀÞÅ×ÏÅ ÓÌÏ×Ï: %s"
+19 "POSIX ÎÅ ÄÏÐÕÓËÁÅÔ ÔÏÞËÕ ('.') × ËÁÞÅÓÔ×Å ÑÒÌÙËÁ ÄÌÑ ÐÏÓÌÅÄÎÅÇÏ ÒÅÚÕÌØÔÁÔÁ"
+20 "POSIX ÔÒÅÂÕÅÔ ÓËÏÂÏË ×ÏËÒÕÇ ×ÙÒÁÖÅÎÉÊ ×ÏÚ×ÒÁÔÁ"
+21 "POSIX ÎÅ ÒÁÚÒÅÛÁÅÔ ÉÓÐÏÌØÚÏ×ÁÔØ ÓÌÅÄÕÀÝÉÊ ÏÐÅÒÁÔÏÒ: %s"
+22 "POSIX ÎÅ ÒÁÚÒÅÛÁÅÔ ÏÐÅÒÁÔÏÒÁÍ ÓÒÁ×ÎÅÎÉÑ ×ÙÈÏÄÉÔØ ÚÁ ÐÒÅÄÅÌÙ, ÅÓÌÉ ÕÔ×ÅÒÖÄÅÎÉÑ ÉÌÉ ÃÉËÌÙ"
+23 "POSIX ÔÒÅÂÕÅÔ 0 ÉÌÉ 1 ÏÐÅÒÁÔÏÒÁ ÓÒÁ×ÎÅÎÉÑ ÎÁ ÕÓÌÏ×ÉÅ"
+24 "POSIX ÔÒÅÂÕÅÔ, ÞÔÏÂÙ ×ÓÅ 3 ÞÁÓÔÉ ÐÅÔÌÉ ÂÙÌÉ ÎÅÐÕÓÔÙÍÉ"
+25 "POSIX ÎÅ ÄÏÐÕÓËÁÅÔ ÜËÓÐÏÎÅÎÃÉÁÌØÎÏÊ ÎÏÔÁÃÉÉ"
+26 "POSIX ÎÅ ÄÏÐÕÓËÁÅÔ ÓÓÙÌËÉ ÎÁ ÍÁÓÓÉ× × ËÁÞÅÓÔ×Å ÐÁÒÁÍÅÔÒÏ× ÆÕÎËÃÉÉ"
+27 "POSIX ÔÒÅÂÕÅÔ, ÞÔÏÂÙ ÌÅ×ÁÑ ÓËÏÂËÁ ÂÙÌÁ ÎÁ ÔÏÊ ÖÅ ÌÉÎÉÉ, ÞÔÏ É ÚÁÇÏÌÏ×ÏË ÆÕÎËÃÉÉ"
+
+$ ïÛÉÂËÉ ×ÙÐÏÌÎÅÎÉÑ.
+$set 5
+
+1 "îÅÄÅÊÓÔ×ÉÔÅÌØÎÙÊ ibase: ÄÏÌÖÅÎ ÂÙÔØ [%lu, %lu]"
+2 "îÅÄÅÊÓÔ×ÉÔÅÌØÎÙÊ obase: ÄÏÌÖÅÎ ÂÙÔØ [%lu, %lu]"
+3 "ÎÅÄÅÊÓÔ×ÉÔÅÌØÎÁÑ scale: ÄÏÌÖÎÁ ÂÙÔØ [%lu, %lu]"
+4 "ÎÅÄÅÊÓÔ×ÉÔÅÌØÎÏÅ ×ÙÒÁÖÅÎÉÅ read()"
+5 "ÒÅËÕÒÓÉ×ÎÙÊ ×ÙÚÏ× read()"
+6 "ÐÅÒÅÍÅÎÎÁÑ ÉÌÉ ÜÌÅÍÅÎÔ ÍÁÓÓÉ×Á Ñ×ÌÑÅÔÓÑ ÎÅÐÒÁ×ÉÌØÎÙÍ ÔÉÐÏÍ"
+7 "ÓÔÏÐËÁ ÉÍÅÅÔ ÓÌÉÛËÏÍ ÍÁÌÏ ÜÌÅÍÅÎÔÏ×"
+8 "ÎÅÐÒÁ×ÉÌØÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÐÁÒÁÍÅÔÒÏ×; ÎÕÖÎÏ %zu, ÎÕÖÎÏ %zu"
+9 "ÎÅÏÐÒÅÄÅÌÅÎÎÁÑ ÆÕÎËÃÉÑ: %s()"
+10 "ÎÅ ÍÏÖÅÔ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÕÓÔÏÅ ÚÎÁÞÅÎÉÅ × ×ÙÒÁÖÅÎÉÉ"
+
+$ æÁÔÁÌØÎÙÅ ÏÛÉÂËÉ.
+$set 6
+
+1 "îÅ ÕÄÁÌÏÓØ ×ÙÄÅÌÉÔØ ÐÁÍÑÔØ"
+2 "ïÛÉÂËÁ ××ÏÄÁ/×Ù×ÏÄÁ"
+3 "ÎÅ ÓÍÏÇ ÏÔËÒÙÔØ ÆÁÊÌ: %s"
+4 "ÆÁÊÌ ÎÅ ASCII: %s"
+5 "ÐÕÔØ - ÜÔÏ ËÁÔÁÌÏÇ: %s"
+6 "ÎÅ×ÅÒÎÁÑ ÏÐÃÉÑ ËÏÍÁÎÄÎÏÊ ÓÔÒÏËÉ: '%c' (\"%s\")"
+
+$set 7
+
+1 "ÏÐÃÉÑ ÔÒÅÂÕÅÔ ÁÒÇÕÍÅÎÔÁ: '%c' (\"%s\")"
+2 "ÏÐÃÉÑ ÎÅ ÐÒÉÎÉÍÁÅÔ ÁÒÇÕÍÅÎÔÏ×: '%c' (\"%s\")"
diff --git a/locales/ru_RU.UTF-8.msg b/locales/ru_RU.UTF-8.msg
new file mode 100644
index 0000000..86b2eac
--- /dev/null
+++ b/locales/ru_RU.UTF-8.msg
@@ -0,0 +1,83 @@
+$quote "
+
+$ Разные сообщения.
+$set 1
+
+1 "Функция:"
+
+$ Типы ошибок.
+$set 2
+
+1 "Математическая ошибка:"
+2 "Ошибка при разборе:"
+3 "Ошибка выполнения:"
+4 "Фатальная ошибка:"
+5 "Предупреждение:"
+
+$ Математические ошибки.
+$set 3
+
+1 "отрицательное число"
+2 "неинтегрированное число"
+3 "переполнение: номер не помещается в аппаратный номер"
+4 "делить на 0"
+
+$ Ошибки при разборе.
+$set 4
+
+1 "конец файла"
+2 "недопустимый символ '%c'"
+3 "конец строки не найден"
+4 "конец комментария не найден"
+5 "недействительный жетон"
+6 "неправильное выражение"
+7 "пустое выражение"
+8 "заявление о недействительности печати"
+9 "определение недействительной функции"
+10 "неверное присвоение: левая сторона должна быть scale, ibase, obase, last, варом или элементом массива"
+11 "автоматическая переменная не найдена"
+12 "параметр функции или auto \"%s%s\" уже существует"
+13 "конец блока не найден"
+14 "не может вернуть значение из функции void: %s()"
+15 "var не может быть ссылкой: %s"
+16 "POSIX не допускает имен длиннее 1 символа: %s"
+17 "POSIX не допускает комментариев к сценарию '#'"
+18 "POSIX не допускает следующее ключевое слово: %s"
+19 "POSIX не допускает точку ('.') в качестве ярлыка для последнего результата"
+20 "POSIX требует скобок вокруг выражений возврата"
+21 "POSIX не разрешает использовать следующий оператор: %s"
+22 "POSIX не разрешает операторам сравнения выходить за пределы, если утверждения или циклы"
+23 "POSIX требует 0 или 1 оператора сравнения на условие"
+24 "POSIX требует, чтобы все 3 части петли были непустыми"
+25 "POSIX не допускает экспоненциальной нотации"
+26 "POSIX не допускает ссылки на массив в качестве параметров функции"
+27 "POSIX требует, чтобы левая скобка была на той же линии, что и заголовок функции"
+
+$ Ошибки выполнения.
+$set 5
+
+1 "Недействительный ibase: должен быть [%lu, %lu]"
+2 "Недействительный obase: должен быть [%lu, %lu]"
+3 "недействительная scale: должна быть [%lu, %lu]"
+4 "недействительное выражение read()"
+5 "рекурсивный вызов read()"
+6 "переменная или элемент массива является неправильным типом"
+7 "стопка имеет слишком мало элементов"
+8 "неправильное количество параметров; нужно %zu, нужно %zu"
+9 "неопределенная функция: %s()"
+10 "не может использовать пустое значение в выражении"
+
+$ Фатальные ошибки.
+$set 6
+
+1 "Не удалось выделить память"
+2 "Ошибка ввода/вывода"
+3 "не смог открыть файл: %s"
+4 "файл не ASCII: %s"
+5 "путь - это каталог: %s"
+6 "неверная опция командной строки: '%c' (\"%s\")"
+
+$set 7
+
+1 "опция требует аргумента: '%c' (\"%s\")"
+2 "опция не принимает аргументов: '%c' (\"%s\")"
diff --git a/locales/zh_CN.GB18030.msg b/locales/zh_CN.GB18030.msg
new file mode 100644
index 0000000..c04e324
--- /dev/null
+++ b/locales/zh_CN.GB18030.msg
@@ -0,0 +1,111 @@
+$ $
+$ Copyright (c) 2018-2020 Gavin D. Howard and contributors.
+$ $
+$ All rights reserved.
+$ $
+$ Redistribution and use in source and binary forms, with or without
+$ modification, are permitted provided that the following conditions are met:
+$ $
+$ * Redistributions of source code must retain the above copyright notice, this
+$   list of conditions and the following disclaimer.
+$ $
+$ * Redistributions in binary form must reproduce the above copyright notice,
+$   this list of conditions and the following disclaimer in the documentation
+$   and/or other materials provided with the distribution.
+$ $
+$ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+$ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+$ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+$ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+$ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+$ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+$ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+$ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+$ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+$ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+$ POSSIBILITY OF SUCH DAMAGE.
+$ $
+
+$quote "
+
+$ ÔÓÏîÐÅÏ¢¡£
+$set 1
+
+1 "º¯Êý£º"
+
+$ ´íÎóÀàÐÍ¡£
+$set 2
+
+1 "Êýѧ´íÎó£º"
+2 "½âÎö´íÎó£º"
+3 "ÔËÐÐʱ´íÎó£º"
+4 "ÖÂÃü´íÎó£º"
+5 "¾¯¸æ£º"
+
+$ Êýѧ´íÎó¡£
+$set 3
+
+1 "¸ºÊý"
+2 "·ÇÕûÊý"
+3 "Òç³ö£ºÊý×Ö²»·ûºÏÓ²¼þºÅÂë"
+4 "³ýÒÔ0"
+
+$ ½âÎö´íÎó¡£
+$set 4
+
+1 "Îļþ½áÊø"
+2 "ÎÞЧ×Ö·û'%c'"
+3 "ÕÒ²»µ½×Ö·û´®Î²²¿"
+4 "ÎÞ·¨ÕÒµ½ÆÀÂ۵Ľáβ"
+5 "ÎÞЧÁîÅÆ"
+6 "ÎÞЧ±í´ï"
+7 ¡°¿Õ±í´ï¡±
+8 "ÎÞЧ´òÓ¡ÉùÃ÷"
+9 "ÎÞЧµÄ¹¦Äܶ¨Òå"
+10 "ÎÞЧ·ÖÅ䣺×ó²à±ØÐëÊÇscale¡¢ibase¡¢obase¡¢last¡¢var»òÊý×éÔªËØ"
+11 "ûÓÐÕÒµ½×Ô¶¯±äÁ¿"
+12 "º¯Êý²ÎÊý»ò×Ô¶¯²ÎÊý \"%s%s\" ÒѾ­´æÔÚ"
+13 "ÕÒ²»µ½Çø¿éÄ©¶Ë"
+14 "²»ÄÜ´Óvoidº¯ÊýÖзµ»ØÒ»¸öÖµ£º%s()"
+15 "var²»ÄÜ×÷Ϊ²Î¿¼£º%s"
+16 "POSIX²»ÔÊÐíÃû×Ö³¬¹ý1¸ö×Ö·û£º%s"
+17 "POSIX²»ÔÊÐí'#'½Å±¾×¢ÊÍ"
+18 "POSIX²»ÔÊÐíʹÓÃÒÔϹؼü×Ö£º%s"
+19 "POSIX²»ÔÊÐíÓþäºÅ('.')×÷Ϊ×îºó½á¹ûµÄ¿ì½Ý·½Ê½"
+20 "POSIXÒªÇóÔÚ·µ»Ø±í´ïʽÖÜΧ¼ÓÀ¨ºÅ"
+21 "POSIX²»ÔÊÐíÒÔϲÙ×÷·û£º%s"
+22 "POSIX²»ÔÊÐíÔÚifÓï¾ä»òÑ­»·Ö®ÍâµÄ±È½ÏÔËËã·û"
+23 "POSIXÒªÇóÿ¸öÌõ¼þµÄ±È½ÏÔËËã·ûΪ0»ò1¸ö"
+24 "POSIXÒªÇóforÑ­»·µÄËùÓÐ3¸ö²¿·Ö±ØÐëÊÇ·Ç¿ÕµÄ"
+25 "POSIX²»ÔÊÐíʹÓÃÖ¸Êý·ûºÅ"
+26 "POSIX²»ÔÊÐíÊý×éÒýÓÃ×÷Ϊº¯Êý²ÎÊý"
+27 "POSIXÒªÇó×ó±ßµÄÀ¨ºÅºÍº¯ÊýÍ·ÔÚͬһÐÐÉÏ"
+
+$ ÔËÐÐʱ´íÎó¡£
+$set 5
+
+1 "ÎÞЧµÄibase: ±ØÐëÊÇ[%lu, %lu]"
+2 "ÎÞЧµÄobase£º±ØÐëÊÇ[%lu£¬%lu]"
+3 "ÎÞЧµÄscale£º±ØÐëÊÇ[%lu£¬%lu]"
+4 "ÎÞЧµÄread()±í´ïʽ"
+5 "µÝ¹é¶ÁÈ¡()µ÷ÓÃ"
+6 "±äÁ¿»òÊý×éÔªËØÊÇ´íÎóµÄÀàÐÍ"
+7 "¶ÑÕ»µÄÔªËØÌ«ÉÙ"
+8 "²ÎÊýÊýÁ¿´íÎó£»ÐèÒª%zu£¬ÓÐ%zu"
+9 "䶨ÒåµÄº¯Êý£º%s()"
+10 ¡°²»ÄÜÔÚ±í´ïʽÖÐʹÓÿÕÖµ¡±
+
+$ ÖÂÃü´íÎó¡£
+$set 6
+
+1 "ÄÚ´æ·ÖÅäʧ°Ü"
+2 "I/O´íÎó"
+3 "ÎÞ·¨´ò¿ªÎļþ¡£%s"
+4 "Îļþ²»ÊÇASCII: %s"
+5 "·¾¶ÊÇÒ»¸öĿ¼£º%s"
+6 "ÎÞЧµÄÃüÁîÐÐÑ¡Ï'%c'(\"%s\")"
+
+$set 7
+
+1 "Ñ¡ÏîÐèÒªÒ»¸ö²ÎÊý£º'%c'(\"%s\")"
+2 "Ñ¡Ïî²»ÐèÒª²ÎÊý¡£'%c'(\"%s\")"
diff --git a/locales/zh_CN.GB2312.msg b/locales/zh_CN.GB2312.msg
new file mode 100644
index 0000000..c04e324
--- /dev/null
+++ b/locales/zh_CN.GB2312.msg
@@ -0,0 +1,111 @@
+$ $
+$ Copyright (c) 2018-2020 Gavin D. Howard and contributors.
+$ $
+$ All rights reserved.
+$ $
+$ Redistribution and use in source and binary forms, with or without
+$ modification, are permitted provided that the following conditions are met:
+$ $
+$ * Redistributions of source code must retain the above copyright notice, this
+$   list of conditions and the following disclaimer.
+$ $
+$ * Redistributions in binary form must reproduce the above copyright notice,
+$   this list of conditions and the following disclaimer in the documentation
+$   and/or other materials provided with the distribution.
+$ $
+$ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+$ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+$ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+$ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+$ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+$ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+$ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+$ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+$ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+$ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+$ POSSIBILITY OF SUCH DAMAGE.
+$ $
+
+$quote "
+
+$ ÔÓÏîÐÅÏ¢¡£
+$set 1
+
+1 "º¯Êý£º"
+
+$ ´íÎóÀàÐÍ¡£
+$set 2
+
+1 "Êýѧ´íÎó£º"
+2 "½âÎö´íÎó£º"
+3 "ÔËÐÐʱ´íÎó£º"
+4 "ÖÂÃü´íÎó£º"
+5 "¾¯¸æ£º"
+
+$ Êýѧ´íÎó¡£
+$set 3
+
+1 "¸ºÊý"
+2 "·ÇÕûÊý"
+3 "Òç³ö£ºÊý×Ö²»·ûºÏÓ²¼þºÅÂë"
+4 "³ýÒÔ0"
+
+$ ½âÎö´íÎó¡£
+$set 4
+
+1 "Îļþ½áÊø"
+2 "ÎÞЧ×Ö·û'%c'"
+3 "ÕÒ²»µ½×Ö·û´®Î²²¿"
+4 "ÎÞ·¨ÕÒµ½ÆÀÂ۵Ľáβ"
+5 "ÎÞЧÁîÅÆ"
+6 "ÎÞЧ±í´ï"
+7 ¡°¿Õ±í´ï¡±
+8 "ÎÞЧ´òÓ¡ÉùÃ÷"
+9 "ÎÞЧµÄ¹¦Äܶ¨Òå"
+10 "ÎÞЧ·ÖÅ䣺×ó²à±ØÐëÊÇscale¡¢ibase¡¢obase¡¢last¡¢var»òÊý×éÔªËØ"
+11 "ûÓÐÕÒµ½×Ô¶¯±äÁ¿"
+12 "º¯Êý²ÎÊý»ò×Ô¶¯²ÎÊý \"%s%s\" ÒѾ­´æÔÚ"
+13 "ÕÒ²»µ½Çø¿éÄ©¶Ë"
+14 "²»ÄÜ´Óvoidº¯ÊýÖзµ»ØÒ»¸öÖµ£º%s()"
+15 "var²»ÄÜ×÷Ϊ²Î¿¼£º%s"
+16 "POSIX²»ÔÊÐíÃû×Ö³¬¹ý1¸ö×Ö·û£º%s"
+17 "POSIX²»ÔÊÐí'#'½Å±¾×¢ÊÍ"
+18 "POSIX²»ÔÊÐíʹÓÃÒÔϹؼü×Ö£º%s"
+19 "POSIX²»ÔÊÐíÓþäºÅ('.')×÷Ϊ×îºó½á¹ûµÄ¿ì½Ý·½Ê½"
+20 "POSIXÒªÇóÔÚ·µ»Ø±í´ïʽÖÜΧ¼ÓÀ¨ºÅ"
+21 "POSIX²»ÔÊÐíÒÔϲÙ×÷·û£º%s"
+22 "POSIX²»ÔÊÐíÔÚifÓï¾ä»òÑ­»·Ö®ÍâµÄ±È½ÏÔËËã·û"
+23 "POSIXÒªÇóÿ¸öÌõ¼þµÄ±È½ÏÔËËã·ûΪ0»ò1¸ö"
+24 "POSIXÒªÇóforÑ­»·µÄËùÓÐ3¸ö²¿·Ö±ØÐëÊÇ·Ç¿ÕµÄ"
+25 "POSIX²»ÔÊÐíʹÓÃÖ¸Êý·ûºÅ"
+26 "POSIX²»ÔÊÐíÊý×éÒýÓÃ×÷Ϊº¯Êý²ÎÊý"
+27 "POSIXÒªÇó×ó±ßµÄÀ¨ºÅºÍº¯ÊýÍ·ÔÚͬһÐÐÉÏ"
+
+$ ÔËÐÐʱ´íÎó¡£
+$set 5
+
+1 "ÎÞЧµÄibase: ±ØÐëÊÇ[%lu, %lu]"
+2 "ÎÞЧµÄobase£º±ØÐëÊÇ[%lu£¬%lu]"
+3 "ÎÞЧµÄscale£º±ØÐëÊÇ[%lu£¬%lu]"
+4 "ÎÞЧµÄread()±í´ïʽ"
+5 "µÝ¹é¶ÁÈ¡()µ÷ÓÃ"
+6 "±äÁ¿»òÊý×éÔªËØÊÇ´íÎóµÄÀàÐÍ"
+7 "¶ÑÕ»µÄÔªËØÌ«ÉÙ"
+8 "²ÎÊýÊýÁ¿´íÎó£»ÐèÒª%zu£¬ÓÐ%zu"
+9 "䶨ÒåµÄº¯Êý£º%s()"
+10 ¡°²»ÄÜÔÚ±í´ïʽÖÐʹÓÿÕÖµ¡±
+
+$ ÖÂÃü´íÎó¡£
+$set 6
+
+1 "ÄÚ´æ·ÖÅäʧ°Ü"
+2 "I/O´íÎó"
+3 "ÎÞ·¨´ò¿ªÎļþ¡£%s"
+4 "Îļþ²»ÊÇASCII: %s"
+5 "·¾¶ÊÇÒ»¸öĿ¼£º%s"
+6 "ÎÞЧµÄÃüÁîÐÐÑ¡Ï'%c'(\"%s\")"
+
+$set 7
+
+1 "Ñ¡ÏîÐèÒªÒ»¸ö²ÎÊý£º'%c'(\"%s\")"
+2 "Ñ¡Ïî²»ÐèÒª²ÎÊý¡£'%c'(\"%s\")"
diff --git a/locales/zh_CN.GBK.msg b/locales/zh_CN.GBK.msg
new file mode 100644
index 0000000..c04e324
--- /dev/null
+++ b/locales/zh_CN.GBK.msg
@@ -0,0 +1,111 @@
+$ $
+$ Copyright (c) 2018-2020 Gavin D. Howard and contributors.
+$ $
+$ All rights reserved.
+$ $
+$ Redistribution and use in source and binary forms, with or without
+$ modification, are permitted provided that the following conditions are met:
+$ $
+$ * Redistributions of source code must retain the above copyright notice, this
+$   list of conditions and the following disclaimer.
+$ $
+$ * Redistributions in binary form must reproduce the above copyright notice,
+$   this list of conditions and the following disclaimer in the documentation
+$   and/or other materials provided with the distribution.
+$ $
+$ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+$ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+$ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+$ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+$ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+$ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+$ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+$ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+$ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+$ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+$ POSSIBILITY OF SUCH DAMAGE.
+$ $
+
+$quote "
+
+$ ÔÓÏîÐÅÏ¢¡£
+$set 1
+
+1 "º¯Êý£º"
+
+$ ´íÎóÀàÐÍ¡£
+$set 2
+
+1 "Êýѧ´íÎó£º"
+2 "½âÎö´íÎó£º"
+3 "ÔËÐÐʱ´íÎó£º"
+4 "ÖÂÃü´íÎó£º"
+5 "¾¯¸æ£º"
+
+$ Êýѧ´íÎó¡£
+$set 3
+
+1 "¸ºÊý"
+2 "·ÇÕûÊý"
+3 "Òç³ö£ºÊý×Ö²»·ûºÏÓ²¼þºÅÂë"
+4 "³ýÒÔ0"
+
+$ ½âÎö´íÎó¡£
+$set 4
+
+1 "Îļþ½áÊø"
+2 "ÎÞЧ×Ö·û'%c'"
+3 "ÕÒ²»µ½×Ö·û´®Î²²¿"
+4 "ÎÞ·¨ÕÒµ½ÆÀÂ۵Ľáβ"
+5 "ÎÞЧÁîÅÆ"
+6 "ÎÞЧ±í´ï"
+7 ¡°¿Õ±í´ï¡±
+8 "ÎÞЧ´òÓ¡ÉùÃ÷"
+9 "ÎÞЧµÄ¹¦Äܶ¨Òå"
+10 "ÎÞЧ·ÖÅ䣺×ó²à±ØÐëÊÇscale¡¢ibase¡¢obase¡¢last¡¢var»òÊý×éÔªËØ"
+11 "ûÓÐÕÒµ½×Ô¶¯±äÁ¿"
+12 "º¯Êý²ÎÊý»ò×Ô¶¯²ÎÊý \"%s%s\" ÒѾ­´æÔÚ"
+13 "ÕÒ²»µ½Çø¿éÄ©¶Ë"
+14 "²»ÄÜ´Óvoidº¯ÊýÖзµ»ØÒ»¸öÖµ£º%s()"
+15 "var²»ÄÜ×÷Ϊ²Î¿¼£º%s"
+16 "POSIX²»ÔÊÐíÃû×Ö³¬¹ý1¸ö×Ö·û£º%s"
+17 "POSIX²»ÔÊÐí'#'½Å±¾×¢ÊÍ"
+18 "POSIX²»ÔÊÐíʹÓÃÒÔϹؼü×Ö£º%s"
+19 "POSIX²»ÔÊÐíÓþäºÅ('.')×÷Ϊ×îºó½á¹ûµÄ¿ì½Ý·½Ê½"
+20 "POSIXÒªÇóÔÚ·µ»Ø±í´ïʽÖÜΧ¼ÓÀ¨ºÅ"
+21 "POSIX²»ÔÊÐíÒÔϲÙ×÷·û£º%s"
+22 "POSIX²»ÔÊÐíÔÚifÓï¾ä»òÑ­»·Ö®ÍâµÄ±È½ÏÔËËã·û"
+23 "POSIXÒªÇóÿ¸öÌõ¼þµÄ±È½ÏÔËËã·ûΪ0»ò1¸ö"
+24 "POSIXÒªÇóforÑ­»·µÄËùÓÐ3¸ö²¿·Ö±ØÐëÊÇ·Ç¿ÕµÄ"
+25 "POSIX²»ÔÊÐíʹÓÃÖ¸Êý·ûºÅ"
+26 "POSIX²»ÔÊÐíÊý×éÒýÓÃ×÷Ϊº¯Êý²ÎÊý"
+27 "POSIXÒªÇó×ó±ßµÄÀ¨ºÅºÍº¯ÊýÍ·ÔÚͬһÐÐÉÏ"
+
+$ ÔËÐÐʱ´íÎó¡£
+$set 5
+
+1 "ÎÞЧµÄibase: ±ØÐëÊÇ[%lu, %lu]"
+2 "ÎÞЧµÄobase£º±ØÐëÊÇ[%lu£¬%lu]"
+3 "ÎÞЧµÄscale£º±ØÐëÊÇ[%lu£¬%lu]"
+4 "ÎÞЧµÄread()±í´ïʽ"
+5 "µÝ¹é¶ÁÈ¡()µ÷ÓÃ"
+6 "±äÁ¿»òÊý×éÔªËØÊÇ´íÎóµÄÀàÐÍ"
+7 "¶ÑÕ»µÄÔªËØÌ«ÉÙ"
+8 "²ÎÊýÊýÁ¿´íÎó£»ÐèÒª%zu£¬ÓÐ%zu"
+9 "䶨ÒåµÄº¯Êý£º%s()"
+10 ¡°²»ÄÜÔÚ±í´ïʽÖÐʹÓÿÕÖµ¡±
+
+$ ÖÂÃü´íÎó¡£
+$set 6
+
+1 "ÄÚ´æ·ÖÅäʧ°Ü"
+2 "I/O´íÎó"
+3 "ÎÞ·¨´ò¿ªÎļþ¡£%s"
+4 "Îļþ²»ÊÇASCII: %s"
+5 "·¾¶ÊÇÒ»¸öĿ¼£º%s"
+6 "ÎÞЧµÄÃüÁîÐÐÑ¡Ï'%c'(\"%s\")"
+
+$set 7
+
+1 "Ñ¡ÏîÐèÒªÒ»¸ö²ÎÊý£º'%c'(\"%s\")"
+2 "Ñ¡Ïî²»ÐèÒª²ÎÊý¡£'%c'(\"%s\")"
diff --git a/locales/zh_CN.UTF-8.msg b/locales/zh_CN.UTF-8.msg
new file mode 100644
index 0000000..08b5a9d
--- /dev/null
+++ b/locales/zh_CN.UTF-8.msg
@@ -0,0 +1,111 @@
+$ $
+$ Copyright (c) 2018-2020 Gavin D. Howard and contributors.
+$ $
+$ All rights reserved.
+$ $
+$ Redistribution and use in source and binary forms, with or without
+$ modification, are permitted provided that the following conditions are met:
+$ $
+$ * Redistributions of source code must retain the above copyright notice, this
+$   list of conditions and the following disclaimer.
+$ $
+$ * Redistributions in binary form must reproduce the above copyright notice,
+$   this list of conditions and the following disclaimer in the documentation
+$   and/or other materials provided with the distribution.
+$ $
+$ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+$ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+$ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+$ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+$ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+$ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+$ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+$ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+$ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+$ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+$ POSSIBILITY OF SUCH DAMAGE.
+$ $
+
+$quote "
+
+$ 杂项信息。
+$set 1
+
+1 "函数:"
+
+$ 错误类型。
+$set 2
+
+1 "数学错误:"
+2 "解析错误:"
+3 "运行时错误:"
+4 "致命错误:"
+5 "警告:"
+
+$ 数学错误。
+$set 3
+
+1 "负数"
+2 "非整数"
+3 "溢出:数字不符合硬件号码"
+4 "除以0"
+
+$ 解析错误。
+$set 4
+
+1 "文件结束"
+2 "无效字符'%c'"
+3 "找不到字符串尾部"
+4 "无法找到评论的结尾"
+5 "无效令牌"
+6 "无效表达"
+7 “空表达”
+8 "无效打印声明"
+9 "无效的功能定义"
+10 "无效分配:左侧必须是scale、ibase、obase、last、var或数组元素"
+11 "没有找到自动变量"
+12 "函数参数或自动参数 \"%s%s\" 已经存在"
+13 "找不到区块末端"
+14 "不能从void函数中返回一个值:%s()"
+15 "var不能作为参考:%s"
+16 "POSIX不允许名字超过1个字符:%s"
+17 "POSIX不允许'#'脚本注释"
+18 "POSIX不允许使用以下关键字:%s"
+19 "POSIX不允许用句号('.')作为最后结果的快捷方式"
+20 "POSIX要求在返回表达式周围加括号"
+21 "POSIX不允许以下操作符:%s"
+22 "POSIX不允许在if语句或循环之外的比较运算符"
+23 "POSIX要求每个条件的比较运算符为0或1个"
+24 "POSIX要求for循环的所有3个部分必须是非空的"
+25 "POSIX不允许使用指数符号"
+26 "POSIX不允许数组引用作为函数参数"
+27 "POSIX要求左边的括号和函数头在同一行上"
+
+$ 运行时错误。
+$set 5
+
+1 "无效的ibase: 必须是[%lu, %lu]"
+2 "无效的obase:必须是[%lu,%lu]"
+3 "无效的scale:必须是[%lu,%lu]"
+4 "无效的read()表达式"
+5 "递归读取()调用"
+6 "变量或数组元素是错误的类型"
+7 "堆栈的元素太少"
+8 "参数数量错误;需要%zu,有%zu"
+9 "未定义的函数:%s()"
+10 “不能在表达式中使用空值”
+
+$ 致命错误。
+$set 6
+
+1 "内存分配失败"
+2 "I/O错误"
+3 "无法打开文件。%s"
+4 "文件不是ASCII: %s"
+5 "路径是一个目录:%s"
+6 "无效的命令行选项:'%c'(\"%s\")"
+
+$set 7
+
+1 "选项需要一个参数:'%c'(\"%s\")"
+2 "选项不需要参数。'%c'(\"%s\")"
diff --git a/locales/zh_CN.eucCN.msg b/locales/zh_CN.eucCN.msg
new file mode 100644
index 0000000..c04e324
--- /dev/null
+++ b/locales/zh_CN.eucCN.msg
@@ -0,0 +1,111 @@
+$ $
+$ Copyright (c) 2018-2020 Gavin D. Howard and contributors.
+$ $
+$ All rights reserved.
+$ $
+$ Redistribution and use in source and binary forms, with or without
+$ modification, are permitted provided that the following conditions are met:
+$ $
+$ * Redistributions of source code must retain the above copyright notice, this
+$   list of conditions and the following disclaimer.
+$ $
+$ * Redistributions in binary form must reproduce the above copyright notice,
+$   this list of conditions and the following disclaimer in the documentation
+$   and/or other materials provided with the distribution.
+$ $
+$ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+$ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+$ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+$ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+$ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+$ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+$ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+$ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+$ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+$ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+$ POSSIBILITY OF SUCH DAMAGE.
+$ $
+
+$quote "
+
+$ ÔÓÏîÐÅÏ¢¡£
+$set 1
+
+1 "º¯Êý£º"
+
+$ ´íÎóÀàÐÍ¡£
+$set 2
+
+1 "Êýѧ´íÎó£º"
+2 "½âÎö´íÎó£º"
+3 "ÔËÐÐʱ´íÎó£º"
+4 "ÖÂÃü´íÎó£º"
+5 "¾¯¸æ£º"
+
+$ Êýѧ´íÎó¡£
+$set 3
+
+1 "¸ºÊý"
+2 "·ÇÕûÊý"
+3 "Òç³ö£ºÊý×Ö²»·ûºÏÓ²¼þºÅÂë"
+4 "³ýÒÔ0"
+
+$ ½âÎö´íÎó¡£
+$set 4
+
+1 "Îļþ½áÊø"
+2 "ÎÞЧ×Ö·û'%c'"
+3 "ÕÒ²»µ½×Ö·û´®Î²²¿"
+4 "ÎÞ·¨ÕÒµ½ÆÀÂ۵Ľáβ"
+5 "ÎÞЧÁîÅÆ"
+6 "ÎÞЧ±í´ï"
+7 ¡°¿Õ±í´ï¡±
+8 "ÎÞЧ´òÓ¡ÉùÃ÷"
+9 "ÎÞЧµÄ¹¦Äܶ¨Òå"
+10 "ÎÞЧ·ÖÅ䣺×ó²à±ØÐëÊÇscale¡¢ibase¡¢obase¡¢last¡¢var»òÊý×éÔªËØ"
+11 "ûÓÐÕÒµ½×Ô¶¯±äÁ¿"
+12 "º¯Êý²ÎÊý»ò×Ô¶¯²ÎÊý \"%s%s\" ÒѾ­´æÔÚ"
+13 "ÕÒ²»µ½Çø¿éÄ©¶Ë"
+14 "²»ÄÜ´Óvoidº¯ÊýÖзµ»ØÒ»¸öÖµ£º%s()"
+15 "var²»ÄÜ×÷Ϊ²Î¿¼£º%s"
+16 "POSIX²»ÔÊÐíÃû×Ö³¬¹ý1¸ö×Ö·û£º%s"
+17 "POSIX²»ÔÊÐí'#'½Å±¾×¢ÊÍ"
+18 "POSIX²»ÔÊÐíʹÓÃÒÔϹؼü×Ö£º%s"
+19 "POSIX²»ÔÊÐíÓþäºÅ('.')×÷Ϊ×îºó½á¹ûµÄ¿ì½Ý·½Ê½"
+20 "POSIXÒªÇóÔÚ·µ»Ø±í´ïʽÖÜΧ¼ÓÀ¨ºÅ"
+21 "POSIX²»ÔÊÐíÒÔϲÙ×÷·û£º%s"
+22 "POSIX²»ÔÊÐíÔÚifÓï¾ä»òÑ­»·Ö®ÍâµÄ±È½ÏÔËËã·û"
+23 "POSIXÒªÇóÿ¸öÌõ¼þµÄ±È½ÏÔËËã·ûΪ0»ò1¸ö"
+24 "POSIXÒªÇóforÑ­»·µÄËùÓÐ3¸ö²¿·Ö±ØÐëÊÇ·Ç¿ÕµÄ"
+25 "POSIX²»ÔÊÐíʹÓÃÖ¸Êý·ûºÅ"
+26 "POSIX²»ÔÊÐíÊý×éÒýÓÃ×÷Ϊº¯Êý²ÎÊý"
+27 "POSIXÒªÇó×ó±ßµÄÀ¨ºÅºÍº¯ÊýÍ·ÔÚͬһÐÐÉÏ"
+
+$ ÔËÐÐʱ´íÎó¡£
+$set 5
+
+1 "ÎÞЧµÄibase: ±ØÐëÊÇ[%lu, %lu]"
+2 "ÎÞЧµÄobase£º±ØÐëÊÇ[%lu£¬%lu]"
+3 "ÎÞЧµÄscale£º±ØÐëÊÇ[%lu£¬%lu]"
+4 "ÎÞЧµÄread()±í´ïʽ"
+5 "µÝ¹é¶ÁÈ¡()µ÷ÓÃ"
+6 "±äÁ¿»òÊý×éÔªËØÊÇ´íÎóµÄÀàÐÍ"
+7 "¶ÑÕ»µÄÔªËØÌ«ÉÙ"
+8 "²ÎÊýÊýÁ¿´íÎó£»ÐèÒª%zu£¬ÓÐ%zu"
+9 "䶨ÒåµÄº¯Êý£º%s()"
+10 ¡°²»ÄÜÔÚ±í´ïʽÖÐʹÓÿÕÖµ¡±
+
+$ ÖÂÃü´íÎó¡£
+$set 6
+
+1 "ÄÚ´æ·ÖÅäʧ°Ü"
+2 "I/O´íÎó"
+3 "ÎÞ·¨´ò¿ªÎļþ¡£%s"
+4 "Îļþ²»ÊÇASCII: %s"
+5 "·¾¶ÊÇÒ»¸öĿ¼£º%s"
+6 "ÎÞЧµÄÃüÁîÐÐÑ¡Ï'%c'(\"%s\")"
+
+$set 7
+
+1 "Ñ¡ÏîÐèÒªÒ»¸ö²ÎÊý£º'%c'(\"%s\")"
+2 "Ñ¡Ïî²»ÐèÒª²ÎÊý¡£'%c'(\"%s\")"
diff --git a/manuals/bc.1 b/manuals/bc.1
index 49db3a9..bb4eda2 100644
--- a/manuals/bc.1
+++ b/manuals/bc.1
@@ -23,7 +23,7 @@
 .
 .TP
 \fB\-g\fR, \fB\-\-global\-stacks\fR
-Turns the globals \fBibase\fR, \fBobase\fR, and \fBscale\fR into stacks\.
+Turns the globals \fBibase\fR, \fBobase\fR, and \fBscale\fR into stacks\. This includes \fBseed\fR if bc(1) was built with the extra math option\.
 .
 .IP
 This has the effect that a copy of the current value of all three are pushed onto a stack for every function call, as well as popped when every function returns\. This means that functions can assign to any and all of those globals without worrying that the change will affect other functions\. Thus, \fBoutput(x,b)\fR (in the \fIextended library\fR) could have been written like this:
@@ -53,7 +53,16 @@
 Second, if the purpose of a function is to set \fBibase\fR, \fBobase\fR, or \fBscale\fR globally for any other purpose, it could be split into one to three functions (based on how many globals it sets) and each of those functions could return the desired value for a global\.
 .
 .IP
-If this behavior is desired for every run of bc(1), then users could make sure to define \fBBC_ENV_ARGS\fR and include this option (see the ENVIRONMENT VARIABLES section for more details)\.
+For functions that set \fBseed\fR, the value assigned to \fBseed\fR is not propagated to parent functions\. This means that the sequence of pseudo\-random numbers that they see will not be the same sequence of pseudo\-random numbers that any parent sees\. This is only the case once \fBseed\fR has been set\.
+.
+.IP
+If a function desires to not affect the sequence of pseudo\-random numbers of its parents, but wants to use the same \fBseed\fR, it can use the following line:
+.
+.IP
+\fBseed = seed\fR
+.
+.IP
+If the behavior of this option is desired for every run of bc(1), then users could make sure to define \fBBC_ENV_ARGS\fR and include this option (see the ENVIRONMENT VARIABLES section for more details)\.
 .
 .IP
 If \fB\-s\fR, \fB\-w\fR, or any equivalents are used, this option is ignored\.
@@ -238,6 +247,35 @@
 Number 6 is a \fBnon\-portable extension\fR\.
 .
 .P
+If bc(1) was built with the extra math option, the following is also a named expression:
+.
+.IP "1." 4
+\fBseed\fR
+.
+.IP "" 0
+.
+.P
+The meaning of \fBseed\fR is dependent on the current pseudo\-random number generator but is guaranteed to not change except for new major versions\.
+.
+.P
+The \fBscale\fR of the value may be significant\.
+.
+.P
+If a previously used \fBseed\fR value is assigned to \fBseed\fR and used again, the pseudo\-random number generator is guaranteed to produce the same sequence of pseudo\-random numbers as it did when the \fBseed\fR value was previously used\.
+.
+.P
+The exact value assigned to \fBseed\fR is not guaranteed to be returned if \fBseed\fR is queried again immediately\. However, if \fBseed\fR \fIdoes\fR return a different value, both values, when assigned to \fBseed\fR, are guaranteed to produce the same sequence of pseudo\-random numbers\. This means that certain values assigned to \fBseed\fR will not produce unique sequences of pseudo\-random numbers\. The value of \fBseed\fR will change after any use of the \fBrand()\fR and \fBirand(E)\fR operands, except if the parameter passed to \fBirand(E)\fR is \fB0\fR or \fB1\fR\.
+.
+.P
+There is no limit to the length (number of significant decimal digits) or \fIscale\fR of the value that can be assigned to \fBseed\fR\.
+.
+.P
+This command is only available if bc(1) was built with the extra math option\.
+.
+.P
+This is a \fBnon\-portable extension\fR\.
+.
+.P
 Variables and arrays do not interfere; users can have arrays named the same as variables\. This also applies to functions (see the FUNCTIONS section), so a user can have a variable, array, and function that all have the same name, and they will not shadow each other\.
 .
 .P
@@ -291,6 +329,26 @@
 .IP "" 0
 .
 .P
+If bc(1) was built with the extra math option, the following are also valid operands:
+.
+.IP "1." 4
+\fBrand()\fR: A pseudo\-random integer between \fB0\fR (inclusive) and \fBBC_RAND_MAX\fR (inclusive)\. Using this operand will change the value of \fBseed\fR\. This is a \fBnon\-portable extension\fR\.
+.
+.IP "2." 4
+\fBirand(E)\fR: A pseudo\-random integer between \fB0\fR (inclusive) and the value of \fBE\fR (exclusive)\. If \fBE\fR is negative or is a non\-integer (\fBscale\fR is not \fB0\fR), an error is raised, and bc(1) resets (see the RESET section)\. If \fBE\fR is larger than \fBBC_RAND_MAX\fR, the higher bound is honored by generating several pseudo\-random integers, multiplying them by appropriate powers of \fBBC_RAND_MAX + 1\fR, and adding them together\. Thus, the size of integer that can be generated with this operand is unbounded\. Using this operand will change the value of \fBseed\fR\. If \fBE\fR is \fB0\fR or \fB1\fR, then \fB0\fR is returned, and \fBseed\fR is not changed\. This is a \fBnon\-portable extension\fR\.
+.
+.IP "3." 4
+\fBmaxrand()\fR: The max integer returned by \fBrand()\fR\. This is a \fBnon\-portable extension\fR\.
+.
+.IP "" 0
+.
+.P
+The integers generated by \fBrand()\fR and \fBirand(E)\fR are guaranteed to be as unbiased as possible, subject to the limitations of the pseudo\-random number generator\.
+.
+.P
+\fBNote\fR: The values returned by the pseudo\-random number generator with \fBrand()\fR and \fBirand(E)\fR are guaranteed to \fBNOT\fR be cryptographically\-secure\. This is a consequence of using a seeded pseudo\-random number generator\. However, they \fBare\fR guaranteed to be reproducible with identical \fBseed\fR values\.
+.
+.P
  \fI\fR
 .
 .SS "Numbers"
@@ -1031,6 +1089,22 @@
 This is a \fItranscendental function\fR\.
 .
 .TP
+\fBfrand(p)\fR
+Generates a pseudo\-random number between \fB0\fR (inclusive) and \fB1\fR (exclusive) with the number of decimal digits after the decimal point equal to the truncated absolute value of \fBp\fR\. If \fBp\fR is not \fB0\fR, then calling this function will change the value of \fBseed\fR\. If \fBp\fR is \fB0\fR, then \fB0\fR is returned, and \fBseed\fR is not changed\.
+.
+.TP
+\fBifrand(i, p)\fR
+Generates a pseudo\-random number that is between \fB0\fR (inclusive) and the truncated absolute value of \fBi\fR (exclusive) with the number of decimal digits after the decimal point equal to the truncated absolute value of \fBp\fR\. If the absolute value of \fBi\fR is greater than or equal to \fB2\fR, and \fBp\fR is not \fB0\fR, then calling this function will change the value of \fBseed\fR, otherwise, \fB0\fR is returned and \fBseed\fR is not changed\.
+.
+.TP
+\fBsrand(x)\fR
+Returns \fBx\fR with its sign flipped with probability \fB0\.5\fR\. In other words, it randomizes the sign of \fBx\fR\.
+.
+.TP
+\fBbrand()\fR
+Returns a random boolean value (either \fB0\fR or \fB1\fR)\.
+.
+.TP
 \fBubytes(x)\fR
 Returns the numbers of unsigned integer bytes required to hold the truncated absolute value of \fBx\fR\.
 .
@@ -1348,6 +1422,10 @@
 The maximum length of a number (in decimal digits), which includes digits after the decimal point\. Set at \fBBC_OVERFLOW_MAX\-1\fR\.
 .
 .TP
+\fBBC_RAND_MAX\fR
+The maximum integer (inclusive) returned by the \fBrand()\fR operand, if bc(1) has been built with the extra math option\. Set at \fB2^BC_LONG_BIT\-1\fR\.
+.
+.TP
 Exponent
 The maximum allowable exponent (positive or negative)\. Set at \fBBC_OVERFLOW_MAX\fR\.
 .
@@ -1370,7 +1448,13 @@
 .
 .TP
 \fBBC_ENV_ARGS\fR
-This is another way to give command\-line arguments to bc(1)\. They should be in the same format as all other command\-line arguments\. These are always processed first, so any files given in \fBBC_ENV_ARGS\fR will be processed before files given on the command\-line\. This gives the user the ability to set up "standard" options and files to be used at every invocation\. The most useful thing for such files to contain would be useful functions that the user might want every time bc(1) runs\.
+This is another way to give command\-line arguments to bc(1)\. They should be in the same format as all other command\-line arguments\. These are always processed first, so any files given in \fBBC_ENV_ARGS\fR will be processed before arguments and files given on the command\-line\. This gives the user the ability to set up "standard" options and files to be used at every invocation\. The most useful thing for such files to contain would be useful functions that the user might want every time bc(1) runs\.
+.
+.IP
+The code that parses \fBBC_ENV_ARGS\fR will correctly handle quoted arguments, but it does not understand escape sequences\. For example, the string \fB"/home/gavin/some bc file\.bc"\fR will be correctly parsed, but the string \fB"/home/gavin/some \e"bc\e" file\.bc"\fR will include the backslashes\.
+.
+.IP
+The quote parsing will handle either kind of quotes, \fB'\fR or \fB"\fR\. Thus, if you have a file with any number of single quotes in the name, you can use double quotes as the outside quotes, as in \fB"some \'bc\' file\.bc"\fR, and vice versa if you have a file with double quotes\. However, handling a file with both kinds of quotes in \fBDC_ENV_ARGS\fR is not supported due to the complexity of the parsing, though such files are still supported on the command\-line where the parsing is done by the shell\.
 .
 .TP
 \fBBC_LINE_LENGTH\fR
@@ -1392,7 +1476,7 @@
 A math error occurred\. This follows standard practice of using \fB1\fR for expected errors, since math errors will happen in the process of normal execution\.
 .
 .IP
-Math errors include divide by \fB0\fR, taking the square root of a negative number, attempting to convert a negative number to a hardware integer, overflow when converting a number to a hardware integer, and attempting to use a non\-integer where an integer is required\.
+Math errors include divide by \fB0\fR, taking the square root of a negative number, using a negative number as a bound for the pseudo\-random number generator, attempting to convert a negative number to a hardware integer, overflow when converting a number to a hardware integer, and attempting to use a non\-integer where an integer is required\.
 .
 .IP
 Converting to a hardware integer happens for the second operand of the power (\fB^\fR), places (\fB@\fR), left shift (\fB<<\fR), and right shift (\fB>>\fR) operators and their corresponding assignment operators\.
diff --git a/manuals/bc.1.ronn b/manuals/bc.1.ronn
index 04fb43c..4a3d71a 100644
--- a/manuals/bc.1.ronn
+++ b/manuals/bc.1.ronn
@@ -31,7 +31,8 @@
 The following are the options that bc(1) accepts.
 
   * `-g`, `--global-stacks`:
-    Turns the globals `ibase`, `obase`, and `scale` into stacks.
+    Turns the globals `ibase`, `obase`, and `scale` into stacks. This includes
+    `seed` if bc(1) was built with the extra math option.
 
     This has the effect that a copy of the current value of all three are pushed
     onto a stack for every function call, as well as popped when every function
@@ -63,8 +64,20 @@
     functions (based on how many globals it sets) and each of those functions
     could return the desired value for a global.
 
-    If this behavior is desired for every run of bc(1), then users could make
-    sure to define `BC_ENV_ARGS` and include this option (see the
+    For functions that set `seed`, the value assigned to `seed` is not
+    propagated to parent functions. This means that the sequence of
+    pseudo-random numbers that they see will not be the same sequence of
+    pseudo-random numbers that any parent sees. This is only the case once
+    `seed` has been set.
+
+    If a function desires to not affect the sequence of pseudo-random numbers
+    of its parents, but wants to use the same `seed`, it can use the following
+    line:
+
+    `seed = seed`
+
+    If the behavior of this option is desired for every run of bc(1), then users
+    could make sure to define `BC_ENV_ARGS` and include this option (see the
     ENVIRONMENT VARIABLES section for more details).
 
     If `-s`, `-w`, or any equivalents are used, this option is ignored.
@@ -266,6 +279,35 @@
 
 Number 6 is a **non-portable extension**.
 
+If bc(1) was built with the extra math option, the following is also a named
+expression:
+
+1.	`seed`
+
+The meaning of `seed` is dependent on the current pseudo-random number generator
+but is guaranteed to not change except for new major versions.
+
+The **scale** of the value may be significant.
+
+If a previously used `seed` value is assigned to `seed` and used again, the
+pseudo-random number generator is guaranteed to produce the same sequence of
+pseudo-random numbers as it did when the `seed` value was previously used.
+
+The exact value assigned to `seed` is not guaranteed to be returned if `seed` is
+queried again immediately. However, if `seed` *does* return a different value,
+both values, when assigned to `seed`, are guaranteed to produce the same
+sequence of pseudo-random numbers. This means that certain values assigned to
+`seed` will not produce unique sequences of pseudo-random numbers. The value of
+`seed` will change after any use of the `rand()` and `irand(E)` operands, except
+if the parameter passed to `irand(E)` is `0` or `1`.
+
+There is no limit to the length (number of significant decimal digits) or
+*scale* of the value that can be assigned to `seed`.
+
+This command is only available if bc(1) was built with the extra math option.
+
+This is a **non-portable extension**.
+
 Variables and arrays do not interfere; users can have arrays named the same as
 variables. This also applies to functions (see the FUNCTIONS section), so a user
 can have a variable, array, and function that all have the same name, and they
@@ -303,6 +345,33 @@
 13.	`maxscale()`: The max allowable `scale`. This is a **non-portable
 	extension**.
 
+If bc(1) was built with the extra math option, the following are also valid
+operands:
+
+1.	`rand()`: A pseudo-random integer between `0` (inclusive) and `BC_RAND_MAX`
+	(inclusive). Using this operand will change the value of `seed`. This is a
+	**non-portable extension**.
+2.	`irand(E)`: A pseudo-random integer between `0` (inclusive) and the
+	value of `E` (exclusive). If `E` is negative or is a non-integer (**scale**
+	is not `0`), an error is raised, and bc(1) resets (see the RESET section).
+	If `E` is larger than `BC_RAND_MAX`, the higher bound is honored by
+	generating several pseudo-random integers, multiplying them by appropriate
+	powers of `BC_RAND_MAX + 1`, and adding them together. Thus, the size of
+	integer that can be generated with this operand is unbounded. Using this
+	operand will change the value of `seed`. If `E` is `0` or `1`, then `0` is
+	returned, and `seed` is not changed. This is a **non-portable extension**.
+3.	`maxrand()`: The max integer returned by `rand()`. This is a **non-portable
+	extension**.
+
+The integers generated by `rand()` and `irand(E)` are guaranteed to be as
+unbiased as possible, subject to the limitations of the pseudo-random number
+generator.
+
+**Note**: The values returned by the pseudo-random number generator with
+`rand()` and `irand(E)` are guaranteed to **NOT** be cryptographically-secure.
+This is a consequence of using a seeded pseudo-random number generator. However,
+they **are** guaranteed to be reproducible with identical `seed` values.
+
 <a name="bc-numbers"/>
 
 ### Numbers
@@ -946,6 +1015,28 @@
 
     This is a [transcendental function][5].
 
+  * `frand(p)`:
+    Generates a pseudo-random number between `0` (inclusive) and `1` (exclusive)
+    with the number of decimal digits after the decimal point equal to the
+    truncated absolute value of `p`. If `p` is not `0`, then calling this
+    function will change the value of `seed`. If `p` is `0`, then `0` is
+    returned, and `seed` is not changed.
+
+  * `ifrand(i, p)`:
+    Generates a pseudo-random number that is between `0` (inclusive) and the
+    truncated absolute value of `i` (exclusive) with the number of decimal
+    digits after the decimal point equal to the truncated absolute value of `p`.
+    If the absolute value of `i` is greater than or equal to `2`, and `p` is not
+    `0`, then calling this function will change the value of `seed`, otherwise,
+    `0` is returned and `seed` is not changed.
+
+  * `srand(x)`:
+    Returns `x` with its sign flipped with probability `0.5`. In other words, it
+    randomizes the sign of `x`.
+
+  * `brand()`:
+    Returns a random boolean value (either `0` or `1`).
+
   * `ubytes(x)`:
     Returns the numbers of unsigned integer bytes required to hold the truncated
     absolute value of `x`.
@@ -1232,6 +1323,10 @@
     The maximum length of a number (in decimal digits), which includes digits
     after the decimal point. Set at `BC_OVERFLOW_MAX-1`.
 
+  * `BC_RAND_MAX`:
+    The maximum integer (inclusive) returned by the `rand()` operand, if bc(1)
+    has been built with the extra math option. Set at `2^BC_LONG_BIT-1`.
+
   * Exponent:
     The maximum allowable exponent (positive or negative). Set at
     `BC_OVERFLOW_MAX`.
@@ -1259,10 +1354,23 @@
     This is another way to give command-line arguments to bc(1). They should be
     in the same format as all other command-line arguments. These are always
     processed first, so any files given in `BC_ENV_ARGS` will be processed
-    before files given on the command-line. This gives the user the ability to
-    set up "standard" options and files to be used at every invocation. The most
-    useful thing for such files to contain would be useful functions that the
-    user might want every time bc(1) runs.
+    before arguments and files given on the command-line. This gives the user
+    the ability to set up "standard" options and files to be used at every
+    invocation. The most useful thing for such files to contain would be useful
+    functions that the user might want every time bc(1) runs.
+
+    The code that parses `BC_ENV_ARGS` will correctly handle quoted arguments,
+    but it does not understand escape sequences. For example, the string
+    `"/home/gavin/some bc file.bc"` will be correctly parsed, but the string
+    `"/home/gavin/some \"bc\" file.bc"` will include the backslashes.
+
+    The quote parsing will handle either kind of quotes, `'` or `"`. Thus, if
+    you have a file with any number of single quotes in the name, you can use
+    double quotes as the outside quotes, as in `"some 'bc' file.bc"`, and vice
+    versa if you have a file with double quotes. However, handling a file with
+    both kinds of quotes in `DC_ENV_ARGS` is not supported due to the complexity
+    of the parsing, though such files are still supported on the command-line
+    where the parsing is done by the shell.
 
   * `BC_LINE_LENGTH`:
     If this environment variable exists and contains an integer that is greater
@@ -1289,7 +1397,8 @@
     execution.
 
     Math errors include divide by `0`, taking the square root of a negative
-    number, attempting to convert a negative number to a hardware integer,
+    number, using a negative number as a bound for the pseudo-random number
+    generator, attempting to convert a negative number to a hardware integer,
     overflow when converting a number to a hardware integer, and attempting to
     use a non-integer where an integer is required.
 
diff --git a/manuals/benchmarks.md b/manuals/benchmarks.md
index ea49c9f..39a8e94 100644
--- a/manuals/benchmarks.md
+++ b/manuals/benchmarks.md
@@ -1,44 +1,35 @@
 # Benchmarks
 
-These are the results of benchmarks comparing this `bc` (at version `2.1.0`) and
-GNU `bc` (at version `1.07.1`).
+These are the results of benchmarks comparing this `bc` (at version `2.7.0`) and
+GNU `bc` (at version `1.07.1`), both compiled with `clang 9` at `-O2`.
 
 Note: all benchmarks were run four times, and the fastest run is the one shown.
 Also, `[bc]` means whichever `bc` was being run, and the assumed working
 directory is the root directory of this repository. Also, this `bc` was built at
 `-O2`.
 
-Note: some mistakes were made when updating these benchmarks for `2.1.0`.
-First, I did not update this `bc`'s version in this file. Second, I ran this
-`bc` after compiling with `clang` when the GNU `bc` was almost certainly
-compiled with `gcc`. Those mistakes have been fixed.
-
 ### Addition
 
 The command used was:
 
 ```
-tests/script.sh bc add.bc 0 1 1 [bc]
+tests/script.sh bc add.bc 1 0 1 1 [bc]
 ```
 
 For GNU `bc`:
 
 ```
-Running bc script: add.bc
-
 real 2.06
-user 1.09
-sys 0.96
+user 1.11
+sys 0.95
 ```
 
 For this `bc`:
 
 ```
-Running bc script: add.bc
-
-real 0.95
-user 0.90
-sys 0.05
+real 0.98
+user 0.95
+sys 0.02
 ```
 
 ### Subtraction
@@ -46,27 +37,23 @@
 The command used was:
 
 ```
-tests/script.sh bc subtract.bc 0 1 1 [bc]
+tests/script.sh bc subtract.bc 1 0 1 1 [bc]
 ```
 
 For GNU `bc`:
 
 ```
-Running bc script: subtract.bc
-
-real 2.08
-user 1.13
-sys 0.94
+real 2.04
+user 1.04
+sys 0.99
 ```
 
 For this `bc`:
 
 ```
-Running bc script: subtract.bc
-
-real 0.92
-user 0.88
-sys 0.04
+real 1.02
+user 1.00
+sys 0.01
 ```
 
 ### Multiplication
@@ -74,27 +61,23 @@
 The command used was:
 
 ```
-tests/script.sh bc multiply.bc 0 1 1 [bc]
+tests/script.sh bc multiply.bc 1 0 1 1 [bc]
 ```
 
 For GNU `bc`:
 
 ```
-Running bc script: multiply.bc
-
-real 5.54
-user 3.72
-sys 1.81
+real 5.96
+user 4.27
+sys 1.68
 ```
 
 For this `bc`:
 
 ```
-Running bc script: multiply.bc
-
-real 2.06
-user 2.01
-sys 0.05
+real 2.15
+user 2.11
+sys 0.04
 ```
 
 ### Division
@@ -102,27 +85,23 @@
 The command used was:
 
 ```
-tests/script.sh bc divide.bc 0 1 1 [bc]
+tests/script.sh bc divide.bc 1 0 1 1 [bc]
 ```
 
 For GNU `bc`:
 
 ```
-Running bc script: divide.bc
-
-real 2.80
-user 1.68
-sys 1.11
+real 2.74
+user 1.84
+sys 0.89
 ```
 
 For this `bc`:
 
 ```
-Running bc script: divide.bc
-
-real 1.45
-user 1.42
-sys 0.02
+real 1.49
+user 1.48
+sys 0.00
 ```
 
 ### Power
@@ -136,16 +115,16 @@
 For GNU `bc`:
 
 ```
-real 11.46
-user 11.45
-sys 0.00
+real 9.60
+user 9.58
+sys 0.01
 ```
 
 For this `bc`:
 
 ```
-real 0.75
-user 0.75
+real 0.67
+user 0.66
 sys 0.00
 ```
 
@@ -155,9 +134,9 @@
 patch was applied:
 
 ```
---- tests/bc/scripts/timeconst.bc	2018-09-28 11:32:22.808669000 -0600
+--- ../timeconst.bc	2018-09-28 11:32:22.808669000 -0600
 +++ ../timeconst.bc	2019-06-07 07:26:36.359913078 -0600
-@@ -108,8 +108,10 @@
+@@ -110,8 +110,10 @@
  
  		print "#endif /* KERNEL_TIMECONST_H */\n"
  	}
@@ -182,16 +161,16 @@
 For GNU `bc`:
 
 ```
-real 15.16
-user 14.59
-sys 0.56
+real 15.26
+user 14.60
+sys 0.64
 ```
 
 For this `bc`:
 
 ```
-real 11.63
-user 11.63
+real 11.24
+user 11.23
 sys 0.00
 ```
 
@@ -219,16 +198,16 @@
 For GNU `bc`:
 
 ```
-real 12.84
-user 12.84
+real 14.89
+user 14.88
 sys 0.00
 ```
 
 For this `bc`:
 
 ```
-real 21.20
-user 21.20
+real 22.19
+user 22.18
 sys 0.00
 ```
 
@@ -257,21 +236,21 @@
 For GNU `bc`:
 
 ```
-real 13.80
-user 13.80
-sys 0.00
+real 42.92
+user 32.70
+sys 10.19
 ```
 
 For this `bc`:
 
 ```
-real 14.90
-user 14.90
-sys 0.00
+real 28.50
+user 28.44
+sys 0.02
 ```
 
-It seems that my `bc` runs `while` loops faster than `for` loops. I don't know
-why it does that because both loops are using the same code underneath the hood.
+I have no idea why the performance of both `bc`'s fell off a cliff, especially
+the dismal showing by the GNU `bc`.
 
 Note that, when running the benchmarks, the optimization used is not the one I
 recommend, which is `-O3 -flto -march=native`. This `bc` separates its code into
@@ -281,33 +260,75 @@
 optimized at link time and inlined. There are other functions that matter as
 well.
 
-When compiling this `bc` with the recommended optimizations, the results are as
-follows.
+When compiling both `bc`'s with the recommended optimizations, the results are
+as follows.
 
-For the first script:
+For the first script, the command was:
 
 ```
-real 9.85
-user 9.85
+time -p [bc] ../timeconst.bc > /dev/null
+```
+
+For GNU `bc`:
+
+```
+real 14.01
+user 13.41
+sys 0.59
+```
+
+For this `bc`:
+
+```
+real 9.40
+user 9.39
 sys 0.00
 ```
 
-For the second script:
+For the second script, the command was:
 
 ```
-real 18.04
-user 18.04
+time -p [bc] ../test.bc > /dev/null
+```
+
+For GNU `bc`:
+
+```
+real 12.58
+user 12.58
 sys 0.00
 ```
 
-For the third script:
+For this `bc`:
 
 ```
-real 12.66
-user 12.66
+real 17.99
+user 17.98
 sys 0.00
 ```
 
+For the third script, the command was:
+
+```
+time -p [bc] ../test2.bc > /dev/null
+```
+
+For GNU `bc`:
+
+```
+real 39.74
+user 27.28
+sys 12.44
+```
+
+For this `bc`:
+
+```
+real 23.31
+user 23.27
+sys 0.02
+```
+
 This is more competitive.
 
 In addition, when compiling with the above recommendation, this `bc` gets even
@@ -315,7 +336,8 @@
 
 ### Recommended Compiler
 
-When I ran these benchmarks with my `bc` compiled under `clang`, it performed
-much better. I recommend compiling this `bc` with `clang`.
+When I ran these benchmarks with my `bc` compiled under `clang` vs. `gcc`, it
+performed much better under `clang`. I recommend compiling this `bc` with
+`clang`.
 
 [1]: https://github.com/torvalds/linux/blob/master/kernel/time/timeconst.bc
diff --git a/manuals/build.md b/manuals/build.md
index b9949ca..1df3bfd 100644
--- a/manuals/build.md
+++ b/manuals/build.md
@@ -412,8 +412,14 @@
 The assignment versions of the above operators are not available in `dc`, but
 the others are, as the operators `$`, `@`, `H`, and `h`, respectively.
 
-Extra operators can be disabled by passing either the `-E` flag or the
-`--disable-extra-math` option to `configure.sh`, as follows:
+In addition, this `bc` has the option of outputting in scientific notation or
+engineering notation. It can also take input in scientific or engineering
+notation. On top of that, it has a pseudo-random number generator. (See the
+[full manual](./bc.md) for more details.)
+
+Extra operators, scientific notation, engineering notation, and the
+pseudo-random number generator can be disabled by passing either the `-E` flag
+or the `--disable-extra-math` option to `configure.sh`, as follows:
 
 ```
 ./configure.sh -E
@@ -422,9 +428,9 @@
 
 Both commands are equivalent.
 
-This `bc` also has a larger library that is only enabled if extra operators are.
-More information about the functions can be found in the
-[Extended Library](./bc.md#extended-library) section of the
+This `bc` also has a larger library that is only enabled if extra operators and
+the pseudo-random number generator are. More information about the functions can
+be found in the [Extended Library](./bc.md#extended-library) section of the
 [full manual](./bc.md).
 
 ### Manpages
@@ -569,14 +575,14 @@
 ## Binary Size
 
 When built with both calculators, all available features, and `-Os` using clang,
-the executable is 113 kb (113,248 bytes) on x86_64. That isn't much for what is
-contained in the binary, but if necessary, it can be reduced.
+the executable is 125.4 kb (125,400 bytes) on `x86_64`. That isn't much for what
+is contained in the binary, but if necessary, it can be reduced.
 
 The single largest user of space is the `bc` calculator. If just `dc` is needed,
-the size can be reduced to 80 kb (80,432 bytes).
+the size can be reduced to 96.7 kb (96,680 bytes).
 
 The next largest user of space is history support. If that is not needed, size
-can be reduced (for a build with both calculators) to 97 kb (97,760 bytes).
+can be reduced (for a build with both calculators) to 108.9 kb (108,912 bytes).
 
 There are several reasons that history is a bigger user of space than `dc`
 itself:
@@ -589,17 +595,17 @@
 * History has a lot of const data for supporting `UTF-8` terminals.
 
 The next biggest user is `dc`, so if just `bc` is needed, the size can be
-reduced to 101 kb (100,960 bytes) with history and 84 kb (84,472 bytes) without
-history.
+reduced to 113.1 kb (113,112 bytes) with history and 96.6 kb (96,624 bytes)
+without history.
 
-The next biggest user is signal handling. Without it, the size (with both
-calculators) is reduced to 109 kb (109,120 bytes) with history and 93 kb (93,632
-bytes) without history.
+The next biggest user is extra math support. Without it, the size (with both
+calculators) is reduced to 117.2 kb (117,168 bytes) with history and 96.6 kb
+(96,584 bytes) without history.
 
-The next largest user is extra math support. If this is not needed, the size
-(with both calculators) can be reduced to 105 kb (105,048 bytes) with history
-and signal handling, 89 kb (88,560 bytes) without history, 101 kb (100,920
-bytes) without signal handling, and 84 kb (84,432 bytes) without both.
+The next largest user is signal handling. If this is not needed, the size (with
+both calculators) can be reduced to 121.3 kb (121,288 bytes) with history and
+extra math support, 104.8 kb (104,784 bytes) without history, 113.1 kb (113,056
+bytes) without extra math support, and 92.5 kb (92,456 bytes) without both.
 
 ## Testing
 
diff --git a/manuals/dc.1 b/manuals/dc.1
index 7640ae4..7b4a1cc 100644
--- a/manuals/dc.1
+++ b/manuals/dc.1
@@ -112,6 +112,18 @@
 .P
 Each item in the input source code, either a number (see the NUMBERS section) or a command (see the COMMANDS section), is processed and executed, in order\. Input is processed immediately when entered\.
 .
+.P
+If dc(1) was built with the extra math option, there is an additional register named \fBseed\fR\. This is the current seed used by the pseudo\-random number generator\. If the current value of \fBseed\fR is queried and stored, then if it is assigned to \fBseed\fR later, the pseudo\-random number generator is guaranteed to produce the same sequence of pseudo\-random numbers that were generated after the value of \fBseed\fR was first queried\.
+.
+.P
+Multiple values assigned to \fBseed\fR can produce the same sequence of pseudo\-random numbers\. Likewise, when a value is assigned to \fBseed\fR, it is not guaranteed that querying \fBseed\fR immediately after will return the same value\. In addition, the value of \fBseed\fR will change after any call to the \fB'\fR or \fB"\fR commands\. The maximum integer returned by the \fB'\fR command can be queried with the \fBW\fR command\.
+.
+.P
+\fBNote\fR: The values returned by the pseudo\-random number generator with the \fB'\fR and \fB"\fR commands are guaranteed to \fBNOT\fR be cryptographically\-secure\. This is a consequence of using a seeded pseudo\-random number generator\. However, they \fBare\fR guaranteed to be reproducible with identical \fBseed\fR values\.
+.
+.P
+The pseudo\-random number generator, \fBseed\fR, and all associated operations are \fBnon\-portable extensions\fR\.
+.
 .SS "Comments"
 Comments go from \fB#\fR until, and not including, the next newline\. This is a \fBnon\-portable extension\fR\.
 .
@@ -352,6 +364,41 @@
 .IP
 This is a \fBnon\-portable extension\fR\.
 .
+.SS "Pseudo\-Random Number Generator"
+If dc(1) was built with the extra math option, it has a built\-in pseudo\-random number generator\. These commands query the pseudo\-random number generator\. (See Parameters for more information about the \fBseed\fR value that controls the pseudo\-random number generator\.)
+.
+.P
+The pseudo\-random number generator is guaranteed to \fBNOT\fR be cryptographically\-secure\.
+.
+.TP
+\fB'\fR
+Generates an integer between 0 and \fBDC_RAND_MAX\fR, inclusive (see the LIMITS section)\.
+.
+.IP
+The generated integer is made as unbiased as possible, subject to the limitations of the pseudo\-random number generator\.
+.
+.IP
+This command is only available if dc(1) was built with the extra math option\.
+.
+.IP
+This is a \fBnon\-portable extension\fR\.
+.
+.TP
+\fB"\fR
+Pops a value off of the stack, which is used as an \fBexclusive\fR upper bound on the integer that will be generated\. If the bound is negative or is a non\-integer, an error is raised, and dc(1) resets (see the RESET section)\. If the bound is larger than \fBDC_RAND_MAX\fR, the higher bound is honored by generating several pseudo\-random integers, multiplying them by appropriate powers of \fBDC_RAND_MAX + 1\fR, and adding them together\. Thus, the size of integer that can be generated with this command is unbounded\. Using this command will change the value of \fBseed\fR\.
+.
+.IP
+If the operand is \fB0\fR or \fB1\fR, then the result pushed onto the stack is \fB0\fR, and \fBseed\fR is not changed\.
+.
+.IP
+The generated integer is made as unbiased as possible, subject to the limitations of the pseudo\-random number generator\.
+.
+.IP
+This command is only available if dc(1) was built with the extra math option\.
+.
+.IP
+This is a \fBnon\-portable extension\fR\.
+.
 .SS "Stack Control"
 These commands control the stack\.
 .
@@ -415,6 +462,28 @@
 If the value on top of the stack has any \fBscale\fR, the \fBscale\fR is ignored\.
 .
 .TP
+\fBj\fR
+Pops the value off of the top of the stack and uses it to set \fBseed\fR\. The meaning of \fBseed\fR is dependent on the current pseudo\-random number generator but is guaranteed to not change except for new major versions\.
+.
+.IP
+The \fBscale\fR of the value may be significant\.
+.
+.IP
+If a previously used \fBseed\fR value is used again, the pseudo\-random number generator is guaranteed to produce the same sequence of pseudo\-random numbers as it did when the \fBseed\fR value was previously used\.
+.
+.IP
+The exact value assigned to \fBseed\fR is not guaranteed to be returned if the \fBJ\fR command is used\. However, if \fBseed\fR \fIdoes\fR return a different value, both values, when assigned to \fBseed\fR, are guaranteed to produce the same sequence of pseudo\-random numbers\. This means that certain values assigned to \fBseed\fR will not produce unique sequences of pseudo\-random numbers\.
+.
+.IP
+There is no limit to the length (number of significant decimal digits) or \fIscale\fR of the value that can be assigned to \fBseed\fR\.
+.
+.IP
+This command is only available if dc(1) was built with the extra math option\.
+.
+.IP
+This is a \fBnon\-portable extension\fR\.
+.
+.TP
 \fBI\fR
 Pushes the current value of \fBibase\fR onto the main stack\.
 .
@@ -427,6 +496,16 @@
 Pushes the current value of \fBscale\fR onto the main stack\.
 .
 .TP
+\fBJ\fR
+Pushes the current value of \fBseed\fR onto the main stack\.
+.
+.IP
+This command is only available if dc(1) was built with the extra math option\.
+.
+.IP
+This is a \fBnon\-portable extension\fR\.
+.
+.TP
 \fBT\fR
 Pushes the maximum allowable value of \fBibase\fR onto the main stack\.
 .
@@ -447,6 +526,16 @@
 .IP
 This is a \fBnon\-portable extension\fR\.
 .
+.TP
+\fBW\fR
+Pushes the maximum (inclusive) integer that can be generated with the \fB'\fR pseudo\-random number generator command\.
+.
+.IP
+This command is only available if dc(1) was built with the extra math option\.
+.
+.IP
+This is a \fBnon\-portable extension\fR\.
+.
 .SS "Strings"
 The following commands control strings\.
 .
@@ -698,6 +787,10 @@
 The maximum length of a number (in decimal digits), which includes digits after the decimal point\. Set at \fBDC_OVERFLOW_MAX\-1\fR\.
 .
 .TP
+\fBDC_RAND_MAX\fR
+The maximum integer (inclusive) returned by the \fB'\fR command, if dc(1) has been built with the extra math option\. Set at \fB2^DC_LONG_BIT\-1\fR\.
+.
+.TP
 Exponent
 The maximum allowable exponent (positive or negative)\. Set at \fBDC_OVERFLOW_MAX\fR\.
 .
@@ -713,7 +806,13 @@
 .
 .TP
 \fBDC_ENV_ARGS\fR
-This is another way to give command\-line arguments to dc(1)\. They should be in the same format as all other command\-line arguments\. These are always processed first, so any files given in \fBDC_ENV_ARGS\fR will be processed before files given on the command\-line\. This gives the user the ability to set up "standard" options and files to be used at every invocation\. The most useful thing for such files to contain would be useful functions that the user might want every time dc(1) runs\. Another use would be to use the \fB\-e\fR option to set \fBscale\fR to a value other than \fB0\fR\.
+This is another way to give command\-line arguments to dc(1)\. They should be in the same format as all other command\-line arguments\. These are always processed first, so any files given in \fBDC_ENV_ARGS\fR will be processed before arguments and files given on the command\-line\. This gives the user the ability to set up "standard" options and files to be used at every invocation\. The most useful thing for such files to contain would be useful functions that the user might want every time dc(1) runs\. Another use would be to use the \fB\-e\fR option to set \fBscale\fR to a value other than \fB0\fR\.
+.
+.IP
+The code that parses \fBDC_ENV_ARGS\fR will correctly handle quoted arguments, but it does not understand escape sequences\. For example, the string \fB"/home/gavin/some dc file\.dc"\fR will be correctly parsed, but the string \fB"/home/gavin/some \e"dc\e" file\.dc"\fR will include the backslashes\.
+.
+.IP
+The quote parsing will handle either kind of quotes, \fB'\fR or \fB"\fR\. Thus, if you have a file with any number of single quotes in the name, you can use double quotes as the outside quotes, as in \fB"some \'bc\' file\.bc"\fR, and vice versa if you have a file with double quotes\. However, handling a file with both kinds of quotes in \fBDC_ENV_ARGS\fR is not supported due to the complexity of the parsing, though such files are still supported on the command\-line where the parsing is done by the shell\.
 .
 .TP
 \fBDC_LINE_LENGTH\fR
@@ -735,7 +834,7 @@
 A math error occurred\. This follows standard practice of using \fB1\fR for expected errors, since math errors will happen in the process of normal execution\.
 .
 .IP
-Math errors include divide by \fB0\fR, taking the square root of a negative number, attempting to convert a negative number to a hardware integer, overflow when converting a number to a hardware integer, and attempting to use a non\-integer where an integer is required\.
+Math errors include divide by \fB0\fR, taking the square root of a negative number, using a negative number as a bound for the pseudo\-random number generator, attempting to convert a negative number to a hardware integer, overflow when converting a number to a hardware integer, and attempting to use a non\-integer where an integer is required\.
 .
 .IP
 Converting to a hardware integer happens for the second operand of the power (\fB^\fR), places (\fB@\fR), left shift (\fBH\fR), and right shift (\fBh\fR) operators\.
diff --git a/manuals/dc.1.ronn b/manuals/dc.1.ronn
index 342f9d9..3d98598 100644
--- a/manuals/dc.1.ronn
+++ b/manuals/dc.1.ronn
@@ -154,6 +154,28 @@
 a command (see the COMMANDS section), is processed and executed, in order. Input
 is processed immediately when entered.
 
+If dc(1) was built with the extra math option, there is an additional register
+named `seed`. This is the current seed used by the pseudo-random number
+generator. If the current value of `seed` is queried and stored, then if it is
+assigned to `seed` later, the pseudo-random number generator is guaranteed to
+produce the same sequence of pseudo-random numbers that were generated after the
+value of `seed` was first queried.
+
+Multiple values assigned to `seed` can produce the same sequence of
+pseudo-random numbers. Likewise, when a value is assigned to `seed`, it is not
+guaranteed that querying `seed` immediately after will return the same value.
+In addition, the value of `seed` will change after any call to the `'` or `"`
+commands. The maximum integer returned by the `'` command can be queried with
+the `W` command.
+
+**Note**: The values returned by the pseudo-random number generator with the
+`'` and `"` commands are guaranteed to **NOT** be cryptographically-secure.
+This is a consequence of using a seeded pseudo-random number generator. However,
+they **are** guaranteed to be reproducible with identical `seed` values.
+
+The pseudo-random number generator, `seed`, and all associated operations are
+**non-portable extensions**.
+
 ### Comments
 
 Comments go from `#` until, and not including, the next newline. This is a
@@ -407,6 +429,49 @@
 
     This is a **non-portable extension**.
 
+### Pseudo-Random Number Generator
+
+If dc(1) was built with the extra math option, it has a built-in pseudo-random
+number generator. These commands query the pseudo-random number generator. (See
+Parameters for more information about the `seed` value that controls the
+pseudo-random number generator.)
+
+The pseudo-random number generator is guaranteed to **NOT** be
+cryptographically-secure.
+
+  * `'`:
+    Generates an integer between 0 and `DC_RAND_MAX`, inclusive (see the LIMITS
+    section).
+
+    The generated integer is made as unbiased as possible, subject to the
+    limitations of the pseudo-random number generator.
+
+    This command is only available if dc(1) was built with the extra math
+    option.
+
+    This is a **non-portable extension**.
+
+  * `"`:
+    Pops a value off of the stack, which is used as an **exclusive** upper bound
+    on the integer that will be generated. If the bound is negative or is a
+    non-integer, an error is raised, and dc(1) resets (see the RESET section).
+    If the bound is larger than `DC_RAND_MAX`, the higher bound is honored by
+    generating several pseudo-random integers, multiplying them by appropriate
+    powers of `DC_RAND_MAX + 1`, and adding them together. Thus, the size of
+    integer that can be generated with this command is unbounded. Using this
+    command will change the value of `seed`.
+
+    If the operand is `0` or `1`, then the result pushed onto the stack is `0`,
+    and `seed` is not changed.
+
+    The generated integer is made as unbiased as possible, subject to the
+    limitations of the pseudo-random number generator.
+
+    This command is only available if dc(1) was built with the extra math
+    option.
+
+    This is a **non-portable extension**.
+
 ### Stack Control
 
 These commands control the stack.
@@ -471,6 +536,31 @@
     If the value on top of the stack has any **scale**, the **scale** is
     ignored.
 
+  * `j`:
+    Pops the value off of the top of the stack and uses it to set `seed`. The
+    meaning of `seed` is dependent on the current pseudo-random number
+    generator but is guaranteed to not change except for new major versions.
+
+    The **scale** of the value may be significant.
+
+    If a previously used `seed` value is used again, the pseudo-random number
+    generator is guaranteed to produce the same sequence of pseudo-random
+    numbers as it did when the `seed` value was previously used.
+
+    The exact value assigned to `seed` is not guaranteed to be returned if the
+    `J` command is used. However, if `seed` *does* return a different value,
+    both values, when assigned to `seed`, are guaranteed to produce the same
+    sequence of pseudo-random numbers. This means that certain values assigned
+    to `seed` will not produce unique sequences of pseudo-random numbers.
+
+    There is no limit to the length (number of significant decimal digits) or
+    *scale* of the value that can be assigned to `seed`.
+
+    This command is only available if dc(1) was built with the extra math
+    option.
+
+    This is a **non-portable extension**.
+
   * `I`:
     Pushes the current value of `ibase` onto the main stack.
 
@@ -480,6 +570,14 @@
   * `K`:
     Pushes the current value of `scale` onto the main stack.
 
+  * `J`:
+    Pushes the current value of `seed` onto the main stack.
+
+    This command is only available if dc(1) was built with the extra math
+    option.
+
+    This is a **non-portable extension**.
+
   * `T`:
     Pushes the maximum allowable value of `ibase` onto the main stack.
 
@@ -495,6 +593,15 @@
 
     This is a **non-portable extension**.
 
+  * `W`:
+    Pushes the maximum (inclusive) integer that can be generated with the `'`
+    pseudo-random number generator command.
+
+    This command is only available if dc(1) was built with the extra math
+    option.
+
+    This is a **non-portable extension**.
+
 ### Strings
 
 The following commands control strings.
@@ -764,6 +871,10 @@
     The maximum length of a number (in decimal digits), which includes digits
     after the decimal point. Set at `DC_OVERFLOW_MAX-1`.
 
+  * `DC_RAND_MAX`:
+    The maximum integer (inclusive) returned by the `'` command, if dc(1) has
+    been built with the extra math option. Set at `2^DC_LONG_BIT-1`.
+
   * Exponent:
     The maximum allowable exponent (positive or negative). Set at
     `DC_OVERFLOW_MAX`.
@@ -785,11 +896,24 @@
     This is another way to give command-line arguments to dc(1). They should be
     in the same format as all other command-line arguments. These are always
     processed first, so any files given in `DC_ENV_ARGS` will be processed
-    before files given on the command-line. This gives the user the ability to
-    set up "standard" options and files to be used at every invocation. The most
-    useful thing for such files to contain would be useful functions that the
-    user might want every time dc(1) runs. Another use would be to use the `-e`
-    option to set `scale` to a value other than `0`.
+    before arguments and files given on the command-line. This gives the user
+    the ability to set up "standard" options and files to be used at every
+    invocation. The most useful thing for such files to contain would be useful
+    functions that the user might want every time dc(1) runs. Another use would
+    be to use the `-e` option to set `scale` to a value other than `0`.
+
+    The code that parses `DC_ENV_ARGS` will correctly handle quoted arguments,
+    but it does not understand escape sequences. For example, the string
+    `"/home/gavin/some dc file.dc"` will be correctly parsed, but the string
+    `"/home/gavin/some \"dc\" file.dc"` will include the backslashes.
+
+    The quote parsing will handle either kind of quotes, `'` or `"`. Thus, if
+    you have a file with any number of single quotes in the name, you can use
+    double quotes as the outside quotes, as in `"some 'bc' file.bc"`, and vice
+    versa if you have a file with double quotes. However, handling a file with
+    both kinds of quotes in `DC_ENV_ARGS` is not supported due to the complexity
+    of the parsing, though such files are still supported on the command-line
+    where the parsing is done by the shell.
 
   * `DC_LINE_LENGTH`:
     If this environment variable exists and contains an integer that is greater
@@ -816,7 +940,8 @@
     execution.
 
     Math errors include divide by `0`, taking the square root of a negative
-    number, attempting to convert a negative number to a hardware integer,
+    number, using a negative number as a bound for the pseudo-random number
+    generator, attempting to convert a negative number to a hardware integer,
     overflow when converting a number to a hardware integer, and attempting to
     use a non-integer where an integer is required.
 
diff --git a/release.sh b/release.sh
index 0a7c396..b2ce056 100755
--- a/release.sh
+++ b/release.sh
@@ -58,7 +58,7 @@
 	_configure_CC="$1"
 	shift
 
-	_configure_configure_flags="-f $1"
+	_configure_configure_flags="$1"
 	shift
 
 	_configure_GEN_HOST="$1"
@@ -225,6 +225,9 @@
 				"$_runconfigseries_configure_flags" 0 64 "$_runconfigseries_run_tests"
 		fi
 
+		runconfigtests "$_runconfigseries_CFLAGS -DBC_RAND_BUILTIN=0" "$_runconfigseries_CC" \
+			"$_runconfigseries_configure_flags" 1 64 "$_runconfigseries_run_tests"
+
 	fi
 
 	runconfigtests "$_runconfigseries_CFLAGS" "$_runconfigseries_CC" \
@@ -465,6 +468,9 @@
 	defcc="c99"
 fi
 
+export ASAN_OPTIONS="abort_on_error=1"
+export UBSAN_OPTIONS="print_stack_trace=1,silence_unsigned_overflow=1"
+
 build "$debug" "$defcc" "-g" "1" "$bits"
 
 header "Running math library under --standard"
@@ -522,7 +528,8 @@
 		printf 'Then run the GitHub release script as follows:\n'
 		printf '\n'
 		printf '    <github_release> %s .travis.yml codecov.yml release.sh \\\n' "$version"
-		printf '    RELEASE.md tests/afl.py tests/randmath.py tests/bc/scripts/timeconst.bc\n'
+		printf '    RELEASE.md tests/afl.py tests/radamsa.sh tests/radamsa.txt tests/randmath.py \\\n'
+		printf '    tests/bc/scripts/timeconst.bc\n'
 
 	fi
 
diff --git a/run-bc-tests-on-android.sh b/run-bc-tests-on-android.sh
index 7fa4c89..3be7bb8 100755
--- a/run-bc-tests-on-android.sh
+++ b/run-bc-tests-on-android.sh
@@ -12,4 +12,4 @@
   dash_t=""
 fi
 
-exec adb shell $dash_t /data/local/tmp/bc-tests/tests/all.sh bc 0 1 1 0 1 bc
+exec adb shell $dash_t /data/local/tmp/bc-tests/tests/all.sh bc 0 1 0 0 bc
diff --git a/src/args.c b/src/args.c
index 419b2b8..40481ef 100644
--- a/src/args.c
+++ b/src/args.c
@@ -33,6 +33,7 @@
  *
  */
 
+#include <assert.h>
 #include <ctype.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -41,47 +42,35 @@
 
 #include <unistd.h>
 
-#if BC_ENABLE_LONG_OPTIONS
-#include <getopt.h>
-#endif // BC_ENABLE_LONG_OPTIONS
-
 #include <status.h>
 #include <vector.h>
 #include <read.h>
 #include <vm.h>
 #include <args.h>
+#include <opt.h>
 
-#if BC_ENABLE_LONG_OPTIONS
-static const struct option bc_args_lopt[] = {
+static const BcOptLong bc_args_lopt[] = {
 
-	{ "expression", required_argument, NULL, 'e' },
-	{ "file", required_argument, NULL, 'f' },
-	{ "help", no_argument, NULL, 'h' },
-	{ "interactive", no_argument, NULL, 'i' },
-	{ "no-prompt", no_argument, NULL, 'P' },
+	{ "expression", BC_OPT_REQUIRED, 'e' },
+	{ "file", BC_OPT_REQUIRED, 'f' },
+	{ "help", BC_OPT_NONE, 'h' },
+	{ "interactive", BC_OPT_NONE, 'i' },
+	{ "no-prompt", BC_OPT_NONE, 'P' },
 #if BC_ENABLED
-	{ "global-stacks", no_argument, NULL, 'g' },
-	{ "mathlib", no_argument, NULL, 'l' },
-	{ "quiet", no_argument, NULL, 'q' },
-	{ "standard", no_argument, NULL, 's' },
-	{ "warn", no_argument, NULL, 'w' },
+	{ "global-stacks", BC_OPT_BC_ONLY, 'g' },
+	{ "mathlib", BC_OPT_BC_ONLY, 'l' },
+	{ "quiet", BC_OPT_BC_ONLY, 'q' },
+	{ "standard", BC_OPT_BC_ONLY, 's' },
+	{ "warn", BC_OPT_BC_ONLY, 'w' },
 #endif // BC_ENABLED
-	{ "version", no_argument, NULL, 'v' },
+	{ "version", BC_OPT_NONE, 'v' },
+	{ "version", BC_OPT_NONE, 'V' },
 #if DC_ENABLED
-	{ "extended-register", no_argument, NULL, 'x' },
+	{ "extended-register", BC_OPT_DC_ONLY, 'x' },
 #endif // DC_ENABLED
-	{ 0, 0, 0, 0 },
+	{ NULL, 0, 0 },
 
 };
-#endif //BC_ENABLE_LONG_OPTIONS
-
-#if !BC_ENABLED
-static const char* const bc_args_opt = "e:f:hiPvVx";
-#elif !DC_ENABLED
-static const char* const bc_args_opt = "e:f:ghilPqsvVw";
-#else // BC_ENABLED && DC_ENABLED
-static const char* const bc_args_opt = "e:f:ghilPqsvVwx";
-#endif // BC_ENABLED && DC_ENABLED
 
 static void bc_args_exprs(BcVec *exprs, const char *str) {
 	bc_vec_concat(exprs, str);
@@ -107,16 +96,14 @@
 BcStatus bc_args(int argc, char *argv[]) {
 
 	BcStatus s = BC_STATUS_SUCCESS;
-	int c, i, err = 0;
+	int c;
+	size_t i;
 	bool do_exit = false, version = false;
+	BcOpt opts;
 
-	optind = 1;
+	bc_opt_init(&opts, argv);
 
-#if BC_ENABLE_LONG_OPTIONS
-	i = 0;
-#endif // BC_ENABLE_LONG_OPTIONS
-
-	while ((c = getopt_long(argc, argv, bc_args_opt, bc_args_lopt, &i)) != -1) {
+	while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1) {
 
 		switch (c) {
 
@@ -128,13 +115,13 @@
 
 			case 'e':
 			{
-				bc_args_exprs(&vm->exprs, optarg);
+				bc_args_exprs(&vm->exprs, opts.optarg);
 				break;
 			}
 
 			case 'f':
 			{
-				s = bc_args_file(&vm->exprs, optarg);
+				s = bc_args_file(&vm->exprs, opts.optarg);
 				if (BC_ERR(s)) return s;
 				break;
 			}
@@ -161,35 +148,35 @@
 #if BC_ENABLED
 			case 'g':
 			{
-				if (BC_ERR(!BC_IS_BC)) err = c;
+				assert(BC_IS_BC);
 				vm->flags |= BC_FLAG_G;
 				break;
 			}
 
 			case 'l':
 			{
-				if (BC_ERR(!BC_IS_BC)) err = c;
+				assert(BC_IS_BC);
 				vm->flags |= BC_FLAG_L;
 				break;
 			}
 
 			case 'q':
 			{
-				if (BC_ERR(!BC_IS_BC)) err = c;
+				assert(BC_IS_BC);
 				vm->flags |= BC_FLAG_Q;
 				break;
 			}
 
 			case 's':
 			{
-				if (BC_ERR(!BC_IS_BC)) err = c;
+				assert(BC_IS_BC);
 				vm->flags |= BC_FLAG_S;
 				break;
 			}
 
 			case 'w':
 			{
-				if (BC_ERR(!BC_IS_BC)) err = c;
+				assert(BC_IS_BC);
 				vm->flags |= BC_FLAG_W;
 				break;
 			}
@@ -205,13 +192,13 @@
 #if DC_ENABLED
 			case 'x':
 			{
-				if (BC_ERR(BC_IS_BC)) err = c;
+				assert(!BC_IS_BC);
 				vm->flags |= DC_FLAG_X;
 				break;
 			}
 #endif // DC_ENABLED
 
-			// Getopt printed an error message, but we should exit.
+			// An error message has been printed, but we should exit.
 			case '?':
 			case ':':
 			default:
@@ -219,31 +206,14 @@
 				return BC_STATUS_ERROR_FATAL;
 			}
 		}
-
-		if (BC_ERR(err)) {
-
-			const char *name;
-
-#if BC_ENABLE_LONG_OPTIONS
-			for (i = 0; bc_args_lopt[i].name != NULL; ++i) {
-				if (bc_args_lopt[i].val == err) break;
-			}
-
-			name = bc_args_lopt[i].name;
-#else // BC_ENABLE_LONG_OPTIONS
-			name = "<long options disabled>";
-#endif // BC_ENABLE_LONG_OPTIONS
-
-			return bc_vm_verr(BC_ERROR_FATAL_OPTION, err, name);
-		}
 	}
 
 	if (version) bc_vm_info(NULL);
 	if (do_exit) exit((int) s);
 	if (vm->exprs.len > 1 || !BC_IS_BC) vm->flags |= BC_FLAG_Q;
-	if (argv[optind] != NULL && !strcmp(argv[optind], "--")) ++optind;
 
-	for (i = optind; i < argc; ++i) bc_vec_push(&vm->files, argv + i);
+	for (i = opts.optind; i < (size_t) argc; ++i)
+		bc_vec_push(&vm->files, argv + i);
 
 	return s;
 }
diff --git a/src/bc/parse.c b/src/bc/parse.c
index 75dabfc..9e93461 100644
--- a/src/bc/parse.c
+++ b/src/bc/parse.c
@@ -1110,12 +1110,24 @@
 		case BC_LEX_KW_LENGTH:
 		case BC_LEX_KW_OBASE:
 		case BC_LEX_KW_SCALE:
+#if BC_ENABLE_EXTRA_MATH
+		case BC_LEX_KW_SEED:
+#endif // BC_ENABLE_EXTRA_MATH
 		case BC_LEX_KW_SQRT:
 		case BC_LEX_KW_ABS:
+#if BC_ENABLE_EXTRA_MATH
+		case BC_LEX_KW_IRAND:
+#endif // BC_ENABLE_EXTRA_MATH
 		case BC_LEX_KW_READ:
+#if BC_ENABLE_EXTRA_MATH
+		case BC_LEX_KW_RAND:
+#endif // BC_ENABLE_EXTRA_MATH
 		case BC_LEX_KW_MAXIBASE:
 		case BC_LEX_KW_MAXOBASE:
 		case BC_LEX_KW_MAXSCALE:
+#if BC_ENABLE_EXTRA_MATH
+		case BC_LEX_KW_MAXRAND:
+#endif // BC_ENABLE_EXTRA_MATH
 		{
 			s = bc_parse_expr_status(p, BC_PARSE_PRINT, bc_parse_next_expr);
 			break;
@@ -1184,6 +1196,9 @@
 			bc_vm_printf("BC_STRING_MAX    = %lu\n", BC_MAX_STRING);
 			bc_vm_printf("BC_NAME_MAX      = %lu\n", BC_MAX_NAME);
 			bc_vm_printf("BC_NUM_MAX       = %lu\n", BC_MAX_NUM);
+#if BC_ENABLE_EXTRA_MATH
+			bc_vm_printf("BC_RAND_MAX      = %lu\n", BC_MAX_RAND);
+#endif // BC_ENABLE_EXTRA_MATH
 			bc_vm_printf("MAX Exponent     = %lu\n", BC_MAX_EXP);
 			bc_vm_printf("Number of vars   = %lu\n", BC_MAX_VARS);
 
@@ -1445,6 +1460,9 @@
 			case BC_LEX_KW_IBASE:
 			case BC_LEX_KW_LAST:
 			case BC_LEX_KW_OBASE:
+#if BC_ENABLE_EXTRA_MATH
+			case BC_LEX_KW_SEED:
+#endif // BC_ENABLE_EXTRA_MATH
 			{
 				if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
 					return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
@@ -1462,6 +1480,9 @@
 			case BC_LEX_KW_LENGTH:
 			case BC_LEX_KW_SQRT:
 			case BC_LEX_KW_ABS:
+#if BC_ENABLE_EXTRA_MATH
+			case BC_LEX_KW_IRAND:
+#endif // BC_ENABLE_EXTRA_MATH
 			{
 				if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
 					return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
@@ -1474,9 +1495,15 @@
 			}
 
 			case BC_LEX_KW_READ:
+#if BC_ENABLE_EXTRA_MATH
+			case BC_LEX_KW_RAND:
+#endif // BC_ENABLE_EXTRA_MATH
 			case BC_LEX_KW_MAXIBASE:
 			case BC_LEX_KW_MAXOBASE:
 			case BC_LEX_KW_MAXSCALE:
+#if BC_ENABLE_EXTRA_MATH
+			case BC_LEX_KW_MAXRAND:
+#endif // BC_ENABLE_EXTRA_MATH
 			{
 				if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
 					return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
diff --git a/src/data.c b/src/data.c
index 0d8c885..9f18530 100644
--- a/src/data.c
+++ b/src/data.c
@@ -39,6 +39,7 @@
 #include <bc.h>
 #include <dc.h>
 #include <num.h>
+#include <rand.h>
 #include <program.h>
 #include <vm.h>
 
@@ -74,7 +75,7 @@
 	BC_ERR_IDX_MATH, BC_ERR_IDX_MATH, BC_ERR_IDX_MATH, BC_ERR_IDX_MATH,
 
 	BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL,
-	BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL,
+	BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL,
 
 	BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC,
 	BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC,
@@ -104,10 +105,12 @@
 
 	"memory allocation failed",
 	"I/O error",
-	"could not open file: %s",
+	"cannot open file: %s",
 	"file is not ASCII: %s",
 	"path is a directory: %s",
-	"bad command-line option: '%c' (\"%s\")",
+	"bad command-line option: \"%s\"",
+	"option requires an argument: '%c' (\"%s\")",
+	"option takes no arguments: '%c' (\"%s\")",
 
 	"bad ibase: must be [%lu, %lu]",
 	"bad obase: must be [%lu, %lu]",
@@ -130,8 +133,8 @@
 
 	"end of file",
 	"bad character '%c'",
-	"string end could not be found",
-	"comment end could not be found",
+	"string end cannot be found",
+	"comment end cannot be found",
 	"bad token",
 #if BC_ENABLED
 	"bad expression",
@@ -139,10 +142,10 @@
 	"bad print statement",
 	"bad function definition",
 	"bad assignment: left side must be scale, ibase, "
-		"obase, last, var, or array element",
+		"obase, seed, last, var, or array element",
 	"no auto variable found",
 	"function parameter or auto \"%s%s\" already exists",
-	"block end could not be found",
+	"block end cannot be found",
 	"cannot return a value from void function: %s()",
 	"var cannot be a reference: %s",
 
@@ -652,6 +655,12 @@
 };
 #endif // BC_DEBUG_CODE
 
+#if BC_ENABLE_EXTRA_MATH
+
+const BcRandState bc_rand_multiplier = BC_RAND_MULTIPLIER;
+
+#endif // BC_ENABLE_EXTRA_MATH
+
 #if BC_ENABLED
 const BcLexKeyword bc_lex_kws[] = {
 	BC_LEX_KW_ENTRY("auto", 4, true),
@@ -668,15 +677,27 @@
 	BC_LEX_KW_ENTRY("ibase", 5, true),
 	BC_LEX_KW_ENTRY("obase", 5, true),
 	BC_LEX_KW_ENTRY("scale", 5, true),
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_ENTRY("seed", 4, false),
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_KW_ENTRY("length", 6, true),
 	BC_LEX_KW_ENTRY("print", 5, false),
 	BC_LEX_KW_ENTRY("sqrt", 4, true),
 	BC_LEX_KW_ENTRY("abs", 3, false),
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_ENTRY("irand", 5, false),
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_KW_ENTRY("quit", 4, true),
 	BC_LEX_KW_ENTRY("read", 4, false),
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_ENTRY("rand", 4, false),
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_KW_ENTRY("maxibase", 8, false),
 	BC_LEX_KW_ENTRY("maxobase", 8, false),
 	BC_LEX_KW_ENTRY("maxscale", 8, false),
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_ENTRY("maxrand", 7, false),
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_KW_ENTRY("else", 4, false),
 };
 
@@ -695,8 +716,9 @@
 	BC_PARSE_EXPR_ENTRY(true, true, false, false, true, true, false, false),
 	BC_PARSE_EXPR_ENTRY(false, false, false, false, false, true, true, false),
 	BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, false, false),
-	BC_PARSE_EXPR_ENTRY(false, true, true, true, true, true, false, true),
-	BC_PARSE_EXPR_ENTRY(true, false, true, true, true, true, false, 0),
+	BC_PARSE_EXPR_ENTRY(false, true, true, true, true, true, true, false),
+	BC_PARSE_EXPR_ENTRY(true, true, true, false, true, true, true, true),
+	BC_PARSE_EXPR_ENTRY(true, true, false, 0, 0, 0, 0, 0)
 #else // BC_ENABLE_EXTRA_MATH
 	BC_PARSE_EXPR_ENTRY(true, true, true, false, false, true, true, false),
 	BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, true, true),
@@ -757,13 +779,24 @@
 
 const uchar dc_lex_tokens[] = {
 #if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_IRAND,
+#else // BC_ENABLE_EXTRA_MATH
+	BC_LEX_INVALID,
+#endif // BC_ENABLE_EXTRA_MATH
+	BC_LEX_INVALID,
+#if BC_ENABLE_EXTRA_MATH
 	BC_LEX_OP_TRUNC,
 #else // BC_ENABLE_EXTRA_MATH
 	BC_LEX_INVALID,
 #endif // BC_ENABLE_EXTRA_MATH
-	BC_LEX_OP_MODULUS, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_LPAREN,
-	BC_LEX_RPAREN, BC_LEX_OP_MULTIPLY, BC_LEX_OP_PLUS, BC_LEX_INVALID,
-	BC_LEX_OP_MINUS, BC_LEX_INVALID, BC_LEX_OP_DIVIDE,
+	BC_LEX_OP_MODULUS, BC_LEX_INVALID,
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_RAND,
+#else // BC_ENABLE_EXTRA_MATH
+	BC_LEX_INVALID,
+#endif // BC_ENABLE_EXTRA_MATH
+	BC_LEX_LPAREN, BC_LEX_RPAREN, BC_LEX_OP_MULTIPLY, BC_LEX_OP_PLUS,
+	BC_LEX_INVALID, BC_LEX_OP_MINUS, BC_LEX_INVALID, BC_LEX_OP_DIVIDE,
 	BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID,
 	BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID,
 	BC_LEX_INVALID, BC_LEX_INVALID,
@@ -781,10 +814,21 @@
 #else // BC_ENABLE_EXTRA_MATH
 	BC_LEX_INVALID,
 #endif // BC_ENABLE_EXTRA_MATH
-	BC_LEX_KW_IBASE, BC_LEX_INVALID, BC_LEX_KW_SCALE, BC_LEX_LOAD_POP,
-	BC_LEX_OP_BOOL_AND, BC_LEX_OP_BOOL_NOT, BC_LEX_KW_OBASE,
-	BC_LEX_PRINT_STREAM, BC_LEX_NQUIT, BC_LEX_POP, BC_LEX_STORE_PUSH,
-	BC_LEX_KW_MAXIBASE, BC_LEX_KW_MAXOBASE, BC_LEX_KW_MAXSCALE, BC_LEX_INVALID,
+	BC_LEX_KW_IBASE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_SEED,
+#else // BC_ENABLE_EXTRA_MATH
+	BC_LEX_INVALID,
+#endif // BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_SCALE, BC_LEX_LOAD_POP, BC_LEX_OP_BOOL_AND, BC_LEX_OP_BOOL_NOT,
+	BC_LEX_KW_OBASE, BC_LEX_PRINT_STREAM, BC_LEX_NQUIT, BC_LEX_POP,
+	BC_LEX_STORE_PUSH, BC_LEX_KW_MAXIBASE, BC_LEX_KW_MAXOBASE,
+	BC_LEX_KW_MAXSCALE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_KW_MAXRAND,
+#else // BC_ENABLE_EXTRA_MATH
+	BC_LEX_INVALID,
+#endif // BC_ENABLE_EXTRA_MATH
 	BC_LEX_SCALE_FACTOR,
 	BC_LEX_INVALID, BC_LEX_KW_LENGTH, BC_LEX_INVALID, BC_LEX_INVALID,
 	BC_LEX_INVALID, BC_LEX_OP_POWER, BC_LEX_NEG, BC_LEX_INVALID,
@@ -795,7 +839,13 @@
 #else // BC_ENABLE_EXTRA_MATH
 	BC_LEX_INVALID,
 #endif // BC_ENABLE_EXTRA_MATH
-	BC_LEX_STORE_IBASE, BC_LEX_INVALID, BC_LEX_STORE_SCALE, BC_LEX_LOAD,
+	BC_LEX_STORE_IBASE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_LEX_STORE_SEED,
+#else // BC_ENABLE_EXTRA_MATH
+	BC_LEX_INVALID,
+#endif // BC_ENABLE_EXTRA_MATH
+	BC_LEX_STORE_SCALE, BC_LEX_LOAD,
 	BC_LEX_OP_BOOL_OR, BC_LEX_PRINT_POP, BC_LEX_STORE_OBASE, BC_LEX_KW_PRINT,
 	BC_LEX_KW_QUIT, BC_LEX_SWAP, BC_LEX_OP_ASSIGN, BC_LEX_INVALID,
 	BC_LEX_INVALID, BC_LEX_KW_SQRT, BC_LEX_INVALID, BC_LEX_EXECUTE,
@@ -839,18 +889,54 @@
 	BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
 	BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
 #endif // BC_ENABLED
-	BC_INST_IBASE, BC_INST_OBASE, BC_INST_SCALE, BC_INST_LENGTH, BC_INST_PRINT,
-	BC_INST_SQRT, BC_INST_ABS, BC_INST_QUIT, BC_INST_INVALID, BC_INST_MAXIBASE,
-	BC_INST_MAXOBASE, BC_INST_MAXSCALE, BC_INST_INVALID,
+	BC_INST_IBASE, BC_INST_OBASE, BC_INST_SCALE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_INST_SEED,
+#endif // BC_ENABLE_EXTRA_MATH
+	BC_INST_LENGTH, BC_INST_PRINT,
+	BC_INST_SQRT, BC_INST_ABS,
+#if BC_ENABLE_EXTRA_MATH
+	BC_INST_IRAND,
+#endif // BC_ENABLE_EXTRA_MATH
+	BC_INST_QUIT, BC_INST_INVALID,
+#if BC_ENABLE_EXTRA_MATH
+	BC_INST_RAND,
+#endif // BC_ENABLE_EXTRA_MATH
+	BC_INST_MAXIBASE,
+	BC_INST_MAXOBASE, BC_INST_MAXSCALE,
+#if BC_ENABLE_EXTRA_MATH
+	BC_INST_MAXRAND,
+#endif // BC_ENABLE_EXTRA_MATH
+	BC_INST_INVALID,
 	BC_INST_REL_EQ, BC_INST_MODEXP, BC_INST_DIVMOD, BC_INST_INVALID,
 	BC_INST_EXECUTE, BC_INST_PRINT_STACK, BC_INST_CLEAR_STACK,
 	BC_INST_STACK_LEN, BC_INST_DUPLICATE, BC_INST_SWAP, BC_INST_POP,
-	BC_INST_ASCIIFY, BC_INST_PRINT_STREAM, BC_INST_INVALID, BC_INST_INVALID,
-	BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
+	BC_INST_ASCIIFY, BC_INST_PRINT_STREAM,
+	BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
+#if BC_ENABLE_EXTRA_MATH
+	BC_INST_INVALID,
+#endif // BC_ENABLE_EXTRA_MATH
+	BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
 	BC_INST_PRINT_POP, BC_INST_NQUIT, BC_INST_SCALE_FUNC,
 };
 #endif // DC_ENABLED
 
+#if BC_LONG_BIT >= 64
+const BcDig bc_num_bigdigMax[] = {
+	709551616U,
+	446744073U,
+	18U
+};
+#else // BC_LONG_BIT >= 64
+const BcDig bc_num_bigdigMax[] = {
+	7296U,
+	9496U,
+	42U,
+};
+#endif // BC_LONG_BIT >= 64
+
+const size_t bc_num_bigdigMax_size = sizeof(bc_num_bigdigMax) / sizeof(BcDig);
+
 const char bc_parse_one[] = "1";
 
 const char bc_num_hex_digits[] = "0123456789ABCDEF";
diff --git a/src/dc/lex.c b/src/dc/lex.c
index bd1cb93..11f199c 100644
--- a/src/dc/lex.c
+++ b/src/dc/lex.c
@@ -119,8 +119,8 @@
 		if (l->last == dc_lex_regs[i]) return dc_lex_register(l);
 	}
 
-	if (c >= '$' && c <= '~' &&
-	    (l->t = dc_lex_tokens[(c - '$')]) != BC_LEX_INVALID)
+	if (c >= '"' && c <= '~' &&
+	    (l->t = dc_lex_tokens[(c - '"')]) != BC_LEX_INVALID)
 	{
 		return s;
 	}
diff --git a/src/dc/parse.c b/src/dc/parse.c
index e710c60..32d0c29 100644
--- a/src/dc/parse.c
+++ b/src/dc/parse.c
@@ -194,6 +194,9 @@
 		case BC_LEX_STORE_IBASE:
 		case BC_LEX_STORE_OBASE:
 		case BC_LEX_STORE_SCALE:
+#if BC_ENABLE_EXTRA_MATH
+		case BC_LEX_STORE_SEED:
+#endif // BC_ENABLE_EXTRA_MATH
 		{
 			inst = (uchar) (t - BC_LEX_STORE_IBASE + BC_INST_IBASE);
 			s = dc_parse_mem(p, inst, false, true);
diff --git a/src/lang.c b/src/lang.c
index c338919..f3d63b5 100644
--- a/src/lang.c
+++ b/src/lang.c
@@ -195,6 +195,9 @@
 		case BC_RESULT_IBASE:
 		case BC_RESULT_SCALE:
 		case BC_RESULT_OBASE:
+#if BC_ENABLE_EXTRA_MATH
+		case BC_RESULT_SEED:
+#endif // BC_ENABLE_EXTRA_MATH
 		{
 			bc_num_createCopy(&d->d.n, &src->d.n);
 			break;
@@ -249,6 +252,9 @@
 		case BC_RESULT_IBASE:
 		case BC_RESULT_SCALE:
 		case BC_RESULT_OBASE:
+#if BC_ENABLE_EXTRA_MATH
+		case BC_RESULT_SEED:
+#endif // BC_ENABLE_EXTRA_MATH
 		{
 			bc_num_free(&r->d.n);
 			break;
diff --git a/src/num.c b/src/num.c
index 86f4ede..1abe31f 100644
--- a/src/num.c
+++ b/src/num.c
@@ -43,6 +43,7 @@
 
 #include <status.h>
 #include <num.h>
+#include <rand.h>
 #include <vm.h>
 
 static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
@@ -270,10 +271,11 @@
 	a_int = bc_num_int(a);
 	b_int = bc_num_int(b);
 	a_int -= b_int;
-	a_max = (a->rdx > b->rdx);
 
 	if (a_int) return neg ? -((ssize_t) a_int) : (ssize_t) a_int;
 
+	a_max = (a->rdx > b->rdx);
+
 	if (a_max) {
 		min = b->rdx;
 		diff = a->rdx - b->rdx;
@@ -812,9 +814,9 @@
 	memset(c->num, 0, BC_NUM_SIZE(c->len));
 
 	s = bc_num_sub(&h1, &l1, &m1, 0);
-	if (BC_ERR(s)) goto err;
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
 	s = bc_num_sub(&l2, &h2, &m2, 0);
-	if (BC_ERR(s)) goto err;
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
 
 	if (BC_NUM_NONZERO(&h1) && BC_NUM_NONZERO(&h2)) {
 
@@ -1243,7 +1245,7 @@
 	b->neg = false;
 	s = bc_num_bigdig(b, &pow);
 	b->neg = neg;
-	if (s) return s;
+	if (BC_ERR(s)) return s;
 
 	bc_num_createCopy(&copy, a);
 
@@ -1913,10 +1915,8 @@
 		if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
 		if (fracp2.len < fracp2.rdx) fracp2.len = fracp2.rdx;
 
-		// Will never fail (except for signals) because fracp is
-		// guaranteed to be non-negative and small enough.
-		s = bc_num_bigdig(&fracp2, &dig);
-		if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
+		// fracp is guaranteed to be non-negative and small enough.
+		bc_num_bigdig2(&fracp2, &dig);
 
 		bc_num_bigdig2num(&digit, dig);
 		s = bc_num_sub(&fracp2, &digit, &fracp1, 0);
@@ -2085,28 +2085,48 @@
 	return s;
 }
 
-BcStatus bc_num_bigdig(const BcNum *restrict n, BcBigDig *result) {
+void bc_num_bigdig2(const BcNum *restrict n, BcBigDig *result) {
 
-	size_t i;
-	BcBigDig r;
+	// This function returns no errors because it's guaranteed to succeed if
+	// its preconditions are met. Those preconditions include both parameters
+	// being non-NULL, n being non-negative, and n being less than vm->max. If
+	// all of that is true, then we can just convert without worrying about
+	// negative errors or overflow. We also don't care about signals because
+	// this function should execute in only a few iterations, meaning that
+	// ignoring signals here should be fine.
+
+	BcBigDig r = 0;
+
+	assert(n != NULL && result != NULL);
+	assert(!n->neg);
+	assert(bc_num_cmp(n, &vm->max) < 0);
+	assert(n->len - n->rdx <= 3);
+
+	// There is a small speed win from unrolling the loop here, and since it
+	// only adds 53 bytes, I decided that it was worth it.
+	switch (n->len - n->rdx) {
+		case 3:
+			r = (BcBigDig) n->num[n->rdx + 2];
+			// Fallthrough.
+		case 2:
+			r = r * BC_BASE_POW + (BcBigDig) n->num[n->rdx + 1];
+			// Fallthrough.
+		case 1:
+			r = r * BC_BASE_POW + (BcBigDig) n->num[n->rdx];
+	}
+
+	*result = r;
+}
+
+BcStatus bc_num_bigdig(const BcNum *restrict n, BcBigDig *result) {
 
 	assert(n != NULL && result != NULL);
 
 	if (BC_ERR(n->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
+	if (BC_ERR(bc_num_cmp(n, &vm->max) >= 0))
+		return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
 
-	for (r = 0, i = n->len; i > n->rdx;) {
-
-		BcBigDig prev = r * BC_BASE_POW;
-
-		if (BC_ERR(prev / BC_BASE_POW != r))
-			return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
-
-		r = prev + (BcBigDig) n->num[--i];
-
-		if (BC_ERR(r < prev)) return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
-	}
-
-	*result = r;
+	bc_num_bigdig2(n, result);
 
 	return BC_STATUS_SUCCESS;
 }
@@ -2130,6 +2150,272 @@
 	n->len = i;
 }
 
+#if BC_ENABLE_EXTRA_MATH
+BcStatus bc_num_rng(const BcNum *restrict n, BcRNG *rng) {
+
+	BcStatus s;
+	BcNum pow, temp, temp2, intn, frac;
+	BcRand state1, state2, inc1, inc2;
+	BcDig pow_num[BC_RAND_NUM_SIZE];
+
+	bc_num_setup(&pow, pow_num, sizeof(pow_num) / sizeof(BcDig));
+
+	bc_num_init(&temp, n->len);
+	bc_num_init(&temp2, n->len);
+	bc_num_init(&frac, n->rdx);
+	bc_num_init(&intn, bc_num_int(n));
+
+	s = bc_num_mul(&vm->max, &vm->max, &pow, 0);
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+	memcpy(frac.num, n->num, BC_NUM_SIZE(n->rdx));
+	frac.len = n->rdx;
+	frac.rdx = n->rdx;
+	frac.scale = n->scale;
+
+	s = bc_num_mul(&frac, &pow, &temp, 0);
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+	bc_num_truncate(&temp, temp.scale);
+	bc_num_copy(&frac, &temp);
+
+	memcpy(intn.num, n->num + n->rdx, BC_NUM_SIZE(bc_num_int(n)));
+	intn.len = bc_num_int(n);
+
+	if (BC_NUM_NONZERO(&frac)) {
+
+		s = bc_num_divmod(&frac, &vm->max, &temp, &temp2, 0);
+		if (BC_ERR(s)) goto err;
+
+		// frac is guaranteed to be smaller than vm->max * vm->max (pow).
+		// This means that when dividing frac by vm->max, as above, the
+		// quotient and remainder are both guaranteed to be less than vm->max,
+		// which means we can use bc_num_bigdig2() here and not worry about
+		// overflow.
+		bc_num_bigdig2(&temp2, (BcBigDig*) &state1);
+		bc_num_bigdig2(&temp, (BcBigDig*) &state2);
+	}
+	else state1 = state2 = 0;
+
+	if (BC_NUM_NONZERO(&intn)) {
+
+		s = bc_num_divmod(&intn, &vm->max, &temp, &temp2, 0);
+		if (BC_ERR(s)) goto err;
+
+		// Because temp2 is the mod of vm->max, from above, it is guaranteed
+		// to be small enough to use bc_num_bigdig2().
+		bc_num_bigdig2(&temp2, (BcBigDig*) &inc1);
+
+		if (bc_num_cmp(&temp, &vm->max) >= 0) {
+			bc_num_copy(&temp2, &temp);
+			s = bc_num_mod(&temp2, &vm->max, &temp, 0);
+			if (BC_ERR(s)) goto err;
+		}
+
+		// The if statement above ensures that temp is less than vm->max, which
+		// means that we can use bc_num_bigdig2() here.
+		bc_num_bigdig2(&temp, (BcBigDig*) &inc2);
+	}
+	else inc1 = inc2 = 0;
+
+	bc_rand_seed(rng, state1, state2, inc1, inc2);
+
+err:
+	bc_num_free(&intn);
+	bc_num_free(&frac);
+	bc_num_free(&temp2);
+	bc_num_free(&temp);
+	return s;
+}
+
+BcStatus bc_num_createFromRNG(BcNum *restrict n, BcRNG *rng) {
+
+	BcStatus s;
+	BcRand s1, s2, i1, i2;
+	BcNum pow, conv, temp1, temp2, temp3;
+	BcDig pow_num[BC_RAND_NUM_SIZE];
+	BcDig temp1_num[BC_RAND_NUM_SIZE], temp2_num[BC_RAND_NUM_SIZE];
+	BcDig conv_num[BC_NUM_BIGDIG_LOG10];
+
+	bc_num_init(n, 2 * BC_RAND_NUM_SIZE);
+	bc_num_init(&temp3, 2 * BC_RAND_NUM_SIZE);
+
+	bc_num_setup(&pow, pow_num, sizeof(pow_num) / sizeof(BcDig));
+	bc_num_setup(&temp1, temp1_num, sizeof(temp1_num) / sizeof(BcDig));
+	bc_num_setup(&temp2, temp2_num, sizeof(temp2_num) / sizeof(BcDig));
+	bc_num_setup(&conv, conv_num, sizeof(conv_num) / sizeof(BcDig));
+
+	s = bc_num_mul(&vm->max, &vm->max, &pow, 0);
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+	bc_rand_getRands(rng, &s1, &s2, &i1, &i2);
+
+	bc_num_bigdig2num(&conv, (BcBigDig) s2);
+
+	s = bc_num_mul(&conv, &vm->max, &temp1, 0);
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+	bc_num_bigdig2num(&conv, (BcBigDig) s1);
+
+	s = bc_num_add(&conv, &temp1, &temp2, 0);
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+	s = bc_num_div(&temp2, &pow, &temp3, BC_RAND_STATE_BITS);
+	if (BC_ERR(s)) goto err;
+
+	bc_num_bigdig2num(&conv, (BcBigDig) i2);
+
+	s = bc_num_mul(&conv, &vm->max, &temp1, 0);
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+	bc_num_bigdig2num(&conv, (BcBigDig) i1);
+
+	s = bc_num_add(&conv, &temp1, &temp2, 0);
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+	s = bc_num_add(&temp2, &temp3, n, 0);
+	if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+err:
+	bc_num_free(&temp3);
+	if (BC_ERR(s)) bc_num_free(n);
+	return s;
+}
+
+BcStatus bc_num_irand(const BcNum *restrict a, BcNum *restrict b,
+                      BcRNG *restrict rng)
+{
+	BcStatus s = BC_STATUS_SUCCESS;
+	BcRand r;
+	BcBigDig modl;
+	BcNum pow, pow2, cp, cp2, mod, temp1, temp2, rand;
+	BcNum *p1, *p2, *t1, *t2, *c1, *c2, *tmp;
+	BcDig rand_num[BC_NUM_BIGDIG_LOG10];
+	bool carry;
+	ssize_t cmp;
+
+	assert(a != b);
+
+	if (BC_ERR(a->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
+	if (BC_ERR(a->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
+	if (BC_NUM_ZERO(a) || BC_NUM_ONE(a)) return s;
+
+	cmp = bc_num_cmp(a, &vm->max);
+
+	if (cmp <= 0) {
+
+		BcRand bits = 0;
+
+		if (cmp < 0) bc_num_bigdig2(a, (BcBigDig*) &bits);
+
+		// This condition means that bits is a power of 2. In that case, we
+		// can just grab a full-size int and mask out the unneeded bits.
+		// Also, this condition says that 0 is a power of 2, which works for
+		// us, since a value of 0 means a == rng->max. The bitmask will mask
+		// nothing in that case as well.
+		if (!(bits & (bits - 1))) r = bc_rand_int(rng) & (bits - 1);
+		else r = bc_rand_bounded(rng, bits);
+
+		// We made sure that r is less than vm->max,
+		// so we can use bc_num_bigdig2() here.
+		bc_num_bigdig2num(b, r);
+
+		return s;
+	}
+
+	// In the case where a is less than rng->max, we have to make sure we have
+	// an exclusive bound. This ensures that it happens. (See below.)
+	carry = (cmp < 0);
+
+	bc_num_createCopy(&cp, a);
+
+	bc_num_init(&cp2, cp.len);
+	bc_num_init(&mod, BC_NUM_BIGDIG_LOG10);
+	bc_num_init(&temp1, BC_NUM_DEF_SIZE);
+	bc_num_init(&temp2, BC_NUM_DEF_SIZE);
+	bc_num_init(&pow2, BC_NUM_DEF_SIZE);
+	bc_num_init(&pow, BC_NUM_DEF_SIZE);
+	bc_num_one(&pow);
+	bc_num_setup(&rand, rand_num, sizeof(rand_num) / sizeof(BcDig));
+
+	p1 = &pow;
+	p2 = &pow2;
+	t1 = &temp1;
+	t2 = &temp2;
+	c1 = &cp;
+	c2 = &cp2;
+
+	while (BC_NUM_NONZERO(c1)) {
+
+		s = bc_num_divmod(c1, &vm->max, c2, &mod, 0);
+		if (BC_ERR(s)) goto err;
+
+		// Because mod is the mod of vm->max, it is guaranteed to be smaller,
+		// which means we can use bc_num_bigdig2() here.
+		bc_num_bigdig(&mod, &modl);
+
+		if (bc_num_cmp(c1, &vm->max) < 0) {
+
+			// In this case, if there is no carry, then we know we can generate
+			// an integer *equal* to modl. Thus, we add one if there is no
+			// carry. Otherwise, we add zero, and we are still bounded properly.
+			// Since the last portion is guaranteed to be greater than 1, we
+			// know modl isn't 0 unless there is no carry.
+			modl += !carry;
+
+			if (modl == 1) r = 0;
+			else if (!modl) r = bc_rand_int(rng);
+			else r = bc_rand_bounded(rng, (BcRand) modl);
+		}
+		else {
+			if (modl) modl -= carry;
+			r = bc_rand_int(rng);
+			carry = (r >= (BcRand) modl);
+		}
+
+		bc_num_bigdig2num(&rand, r);
+
+		s = bc_num_mul(&rand, p1, p2, 0);
+		if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+		s = bc_num_add(p2, t1, t2, 0);
+		if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+		if (BC_NUM_NONZERO(c2)) {
+
+			s = bc_num_mul(&vm->max, p1, p2, 0);
+			if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
+
+			tmp = p1;
+			p1 = p2;
+			p2 = tmp;
+
+			tmp = c1;
+			c1 = c2;
+			c2 = tmp;
+		}
+		else c1 = c2;
+
+		tmp = t1;
+		t1 = t2;
+		t2 = tmp;
+	}
+
+	bc_num_copy(b, t1);
+	bc_num_clean(b);
+
+err:
+	bc_num_free(&pow);
+	bc_num_free(&pow2);
+	bc_num_free(&temp2);
+	bc_num_free(&temp1);
+	bc_num_free(&mod);
+	bc_num_free(&cp2);
+	bc_num_free(&cp);
+	return s;
+}
+#endif // BC_ENABLE_EXTRA_MATH
+
 size_t bc_num_addReq(const BcNum *a, const BcNum *b, size_t scale) {
 
 	size_t aint, bint, ardx, brdx;
diff --git a/src/opt.c b/src/opt.c
new file mode 100644
index 0000000..c9f82f4
--- /dev/null
+++ b/src/opt.c
@@ -0,0 +1,261 @@
+/*
+ * *****************************************************************************
+ *
+ * Copyright (c) 2018-2020 Gavin D. Howard and contributors.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * *****************************************************************************
+ *
+ * Adapted from https://github.com/skeeto/optparse
+ *
+ * *****************************************************************************
+ *
+ * Code for getopt_long() replacement. It turns out that getopt_long() has
+ * different behavior on different platforms.
+ *
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <status.h>
+#include <opt.h>
+#include <vm.h>
+
+static bool bc_opt_longoptsEnd(const BcOptLong *longopts, size_t i) {
+	return !longopts[i].name && !longopts[i].val;
+}
+
+static const char* bc_opt_longopt(const BcOptLong *longopts, int c) {
+
+	size_t i;
+
+	for (i = 0; !bc_opt_longoptsEnd(longopts, i); ++i) {
+		if (longopts[i].val == c) return longopts[i].name;
+	}
+
+	return "NULL";
+}
+
+static int bc_opt_error(BcError err, int c, const char *str) {
+
+	int result;
+
+	if (err == BC_ERROR_FATAL_OPTION) {
+		bc_vm_error(err, 0, str);
+		result = (int) '?';
+	}
+	else {
+		bc_vm_error(err, 0, (int) c, str);
+		result = (int) (err == BC_ERROR_FATAL_OPTION_ARG ? '\0' : ':');
+	}
+
+	return result;
+}
+
+static int bc_opt_type(const BcOptLong *longopts, char c) {
+
+	size_t i;
+
+	if (c == ':') return -1;
+
+	for (i = 0; !bc_opt_longoptsEnd(longopts, i) && longopts[i].val != c; ++i);
+
+	if (bc_opt_longoptsEnd(longopts, i)) return -1;
+
+	return (int) longopts[i].type;
+}
+
+static int bc_opt_parseShort(BcOpt *o, const BcOptLong *longopts) {
+
+	int type;
+	char *next;
+	char *option = o->argv[o->optind];
+
+	o->optopt = 0;
+	o->optarg = NULL;
+
+	option += o->subopt + 1;
+	o->optopt = option[0];
+
+	type = bc_opt_type(longopts, option[0]);
+	next = o->argv[o->optind + 1];
+
+	switch (type) {
+
+		case -1:
+		case BC_OPT_BC_ONLY:
+		case BC_OPT_DC_ONLY:
+		{
+			if (type == -1 || (type == BC_OPT_BC_ONLY && !BC_IS_BC) ||
+			    (type == BC_OPT_DC_ONLY && BC_IS_BC))
+			{
+				char str[2] = {0, 0};
+
+				str[0] = option[0];
+				o->optind += 1;
+
+				return bc_opt_error(BC_ERROR_FATAL_OPTION, option[0], str);
+			}
+		}
+		// Fallthrough.
+		case BC_OPT_NONE:
+		{
+			if (option[1]) o->subopt += 1;
+			else {
+				o->subopt = 0;
+				o->optind += 1;
+			}
+
+			return option[0];
+		}
+
+		case BC_OPT_REQUIRED:
+		{
+			o->subopt = 0;
+			o->optind += 1;
+
+			if (option[1]) o->optarg = option + 1;
+			else if (next != NULL) {
+				o->optarg = next;
+				o->optind += 1;
+			}
+			else {
+				return bc_opt_error(BC_ERROR_FATAL_OPTION_NO_ARG, option[0],
+				                    bc_opt_longopt(longopts, option[0]));
+			}
+
+			return option[0];
+		}
+	}
+
+	return 0;
+}
+
+static bool bc_opt_longoptsMatch(const char *name, const char *option) {
+
+	const char *a = option, *n = name;
+
+	if (name == NULL) return false;
+
+	for (; *a && *n && *a != '='; ++a, ++n) {
+		if (*a != *n) return false;
+	}
+
+	return (*n == '\0' && (*a == '\0' || *a == '='));
+}
+
+static char* bc_opt_longoptsArg(char *option) {
+
+	for (; *option && *option != '='; ++option);
+
+	if (*option == '=') return option + 1;
+	else return NULL;
+}
+
+int bc_opt_parse(BcOpt *o, const BcOptLong *longopts) {
+
+	size_t i;
+	char *option;
+	bool empty;
+
+	do {
+
+		option = o->argv[o->optind];
+		if (option == NULL) return -1;
+
+		empty = !strcmp(option, "");
+		o->optind += empty;
+
+	} while (empty);
+
+	if (BC_OPT_ISDASHDASH(option)) {
+
+		// Consume "--".
+		o->optind += 1;
+		return -1;
+	}
+	else if (BC_OPT_ISSHORTOPT(option)) {
+		return bc_opt_parseShort(o, longopts);
+	}
+	else if (!BC_OPT_ISLONGOPT(option)) {
+		return -1;
+	}
+
+	o->optopt = 0;
+	o->optarg = NULL;
+
+	// Skip "--" at beginning of the option.
+	option += 2;
+	o->optind += 1;
+
+	for (i = 0; !bc_opt_longoptsEnd(longopts, i); i++) {
+
+		const char *name = longopts[i].name;
+
+		if (bc_opt_longoptsMatch(name, option)) {
+
+			char *arg;
+
+			o->optopt = longopts[i].val;
+			arg = bc_opt_longoptsArg(option);
+
+			if ((longopts[i].type == BC_OPT_BC_ONLY && !BC_IS_BC) ||
+			    (longopts[i].type == BC_OPT_DC_ONLY && BC_IS_BC))
+			{
+				return bc_opt_error(BC_ERROR_FATAL_OPTION, o->optopt, name);
+			}
+
+			if (longopts[i].type == BC_OPT_NONE && arg != NULL)
+			{
+				return bc_opt_error(BC_ERROR_FATAL_OPTION_ARG, o->optopt, name);
+			}
+
+			if (arg != NULL) o->optarg = arg;
+			else if (longopts[i].type == BC_OPT_REQUIRED) {
+
+				o->optarg = o->argv[o->optind];
+
+				if (o->optarg != NULL) o->optind += 1;
+				else return bc_opt_error(BC_ERROR_FATAL_OPTION_NO_ARG,
+					                     o->optopt, name);
+			}
+
+			return o->optopt;
+		}
+	}
+
+	return bc_opt_error(BC_ERROR_FATAL_OPTION, 0, option);
+}
+
+void bc_opt_init(BcOpt *o, char *argv[]) {
+	o->argv = argv;
+	o->optind = 1;
+	o->subopt = 0;
+	o->optarg = NULL;
+}
diff --git a/src/program.c b/src/program.c
index a4b6604..62f992a 100644
--- a/src/program.c
+++ b/src/program.c
@@ -38,6 +38,7 @@
 #include <string.h>
 
 #include <signal.h>
+#include <time.h>
 
 #include <read.h>
 #include <parse.h>
@@ -111,9 +112,15 @@
 }
 
 static void bc_program_prepGlobals(BcProgram *p) {
+
 	size_t i;
+
 	for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)
 		bc_vec_push(p->globals_v + i, p->globals + i);
+
+#if BC_ENABLE_EXTRA_MATH
+	bc_rand_push(&p->rng);
+#endif // BC_ENABLE_EXTRA_MATH
 }
 
 #if BC_ENABLED
@@ -208,6 +215,9 @@
 		case BC_RESULT_IBASE:
 		case BC_RESULT_SCALE:
 		case BC_RESULT_OBASE:
+#if BC_ENABLE_EXTRA_MATH
+		case BC_RESULT_SEED:
+#endif // BC_ENABLE_EXTRA_MATH
 		{
 			n = &r->d.n;
 			break;
@@ -488,6 +498,21 @@
 	return s;
 }
 
+#if BC_ENABLE_EXTRA_MATH
+static void bc_program_rand(BcProgram *p) {
+
+	BcRand rand;
+	BcResult res;
+
+	rand = bc_rand_int(&p->rng);
+
+	res.t = BC_RESULT_TEMP;
+	bc_num_createFromBigdig(&res.d.n, rand);
+
+	bc_vec_push(&p->results, &res);
+}
+#endif // BC_ENABLE_EXTRA_MATH
+
 static void bc_program_printChars(const char *str) {
 	const char *nl;
 	vm->nchars += bc_vm_printf("%s", str);
@@ -895,6 +920,12 @@
 		*ptr = val;
 		*ptr_t = val;
 	}
+#if BC_ENABLE_EXTRA_MATH
+	else if (left->t == BC_RESULT_SEED) {
+		s = bc_num_rng(l, &p->rng);
+		if (BC_ERR(s)) return s;
+	}
+#endif // BC_ENABLE_EXTRA_MATH
 
 	if (use_val) {
 		bc_num_createCopy(&res.d.n, l);
@@ -1141,6 +1172,10 @@
 			bc_vec_pop(v);
 			p->globals[i] = BC_PROG_GLOBAL(v);
 		}
+
+#if BC_ENABLE_EXTRA_MATH
+		bc_rand_pop(&p->rng);
+#endif // BC_ENABLE_EXTRA_MATH
 	}
 
 	bc_vec_push(&p->results, &res);
@@ -1158,7 +1193,11 @@
 	BcNum *num, *resn = &res.d.n;
 	bool len = (inst == BC_INST_LENGTH);
 
+#if BC_ENABLE_EXTRA_MATH
+	assert(inst >= BC_INST_LENGTH && inst <= BC_INST_IRAND);
+#else // BC_ENABLE_EXTRA_MATH
 	assert(inst >= BC_INST_LENGTH && inst <= BC_INST_ABS);
+#endif // BC_ENABLE_EXTRA_MATH
 
 	s = bc_program_operand(p, &opd, &num, 0);
 	if (BC_ERR(s)) return s;
@@ -1180,14 +1219,32 @@
 		bc_num_createCopy(resn, num);
 		resn->neg = false;
 	}
+#if BC_ENABLE_EXTRA_MATH
+	else if (inst == BC_INST_IRAND) {
+		bc_num_init(resn, num->len - num->rdx);
+		s = bc_num_irand(num, resn, &p->rng);
+		if (BC_ERR(s)) {
+			bc_num_free(resn);
+			return s;
+		}
+	}
+#endif // BC_ENABLE_EXTRA_MATH
 	else {
 
 		BcBigDig val = 0;
 
 		if (len) {
 #if BC_ENABLED
-			if (BC_IS_BC && opd->t == BC_RESULT_ARRAY)
-				val = (BcBigDig) ((BcVec*) num)->len;
+			if (BC_IS_BC && opd->t == BC_RESULT_ARRAY) {
+
+				BcVec *v = (BcVec*) num;
+
+				if (v->size == sizeof(uchar)) v = bc_program_dereference(p, v);
+
+				assert(v->size == sizeof(BcNum));
+
+				val = (BcBigDig) v->len;
+			}
 			else
 #endif // BC_ENABLED
 			{
@@ -1324,8 +1381,7 @@
 		// This is also guaranteed to not error because num is in the range
 		// [0, UCHAR_MAX], which is definitely in range for a BcBigDig. And
 		// it is not negative.
-		s = bc_num_bigdig(&num, &val);
-		assert(!s || s == BC_STATUS_SIGNAL);
+		bc_num_bigdig2(&num, &val);
 #if BC_ENABLE_SIGNALS
 		if (BC_ERROR_SIGNAL_ONLY(s)) goto num_err;
 #endif // BC_ENABLE_SIGNALS
@@ -1557,6 +1613,19 @@
 	bc_program_pushBigDig(p, p->globals[inst - BC_INST_IBASE], t);
 }
 
+#if BC_ENABLE_EXTRA_MATH
+static void bc_program_pushSeed(BcProgram *p) {
+
+	BcResult res;
+
+	res.t = BC_RESULT_SEED;
+
+	bc_num_createFromRNG(&res.d.n, &p->rng);
+
+	bc_vec_push(&p->results, &res);
+}
+#endif // BC_ENABLE_EXTRA_MATH
+
 #ifndef NDEBUG
 void bc_program_free(BcProgram *p) {
 
@@ -1583,6 +1652,10 @@
 	}
 #endif // BC_ENABLED
 
+#if BC_ENABLE_EXTRA_MATH
+	bc_rand_free(&p->rng);
+#endif // BC_ENABLE_EXTRA_MATH
+
 #if DC_ENABLED
 	if (!BC_IS_BC) bc_vec_free(&p->tail_calls);
 #endif // DC_ENABLED
@@ -1620,6 +1693,11 @@
 	}
 #endif // DC_ENABLED
 
+#if BC_ENABLE_EXTRA_MATH
+	srand((unsigned int) time(NULL));
+	bc_rand_init(&p->rng);
+#endif // BC_ENABLE_EXTRA_MATH
+
 	bc_num_setup(&p->one, p->one_num, BC_PROG_ONE_CAP);
 	bc_num_one(&p->one);
 
@@ -1818,9 +1896,20 @@
 				break;
 			}
 
+#if BC_ENABLE_EXTRA_MATH
+			case BC_INST_RAND:
+			{
+				bc_program_rand(p);
+				break;
+			}
+#endif // BC_ENABLE_EXTRA_MATH
+
 			case BC_INST_MAXIBASE:
 			case BC_INST_MAXOBASE:
 			case BC_INST_MAXSCALE:
+#if BC_ENABLE_EXTRA_MATH
+			case BC_INST_MAXRAND:
+#endif // BC_ENABLE_EXTRA_MATH
 			{
 				BcBigDig dig = vm->maxes[inst - BC_INST_MAXIBASE];
 				bc_program_pushBigDig(p, dig, BC_RESULT_TEMP);
@@ -1850,10 +1939,21 @@
 				break;
 			}
 
+#if BC_ENABLE_EXTRA_MATH
+			case BC_INST_SEED:
+			{
+				bc_program_pushSeed(p);
+				break;
+			}
+#endif // BC_ENABLE_EXTRA_MATH
+
 			case BC_INST_LENGTH:
 			case BC_INST_SCALE_FUNC:
 			case BC_INST_SQRT:
 			case BC_INST_ABS:
+#if BC_ENABLE_EXTRA_MATH
+			case BC_INST_IRAND:
+#endif // BC_ENABLE_EXTRA_MATH
 			{
 				s = bc_program_builtin(p, inst);
 				break;
diff --git a/src/rand/rand.c b/src/rand/rand.c
new file mode 100644
index 0000000..01e878f
--- /dev/null
+++ b/src/rand/rand.c
@@ -0,0 +1,400 @@
+/*
+ * *****************************************************************************
+ *
+ * Copyright (c) 2018-2019 Gavin D. Howard and contributors.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * *****************************************************************************
+ *
+ * Parts of this code are adapted from the following:
+ *
+ * PCG, A Family of Better Random Number Generators.
+ *
+ * You can find the original source code at:
+ *   https://github.com/imneme/pcg-c
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Parts of this code are also under the following license:
+ *
+ * Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * *****************************************************************************
+ *
+ * Code for the RNG.
+ *
+ */
+
+#if BC_ENABLE_EXTRA_MATH
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <status.h>
+#include <num.h>
+#include <rand.h>
+#include <vm.h>
+
+#if !BC_RAND_BUILTIN
+
+static BcRandState bc_rand_addition(uint_fast64_t a, uint_fast64_t b) {
+
+	BcRandState res;
+
+	res.lo = a + b;
+	res.hi = (res.lo < a);
+
+	return res;
+}
+
+static BcRandState bc_rand_addition2(BcRandState a, BcRandState b) {
+
+	BcRandState temp, res;
+
+	res = bc_rand_addition(a.lo, b.lo);
+	temp = bc_rand_addition(a.hi, b.hi);
+	res.hi += temp.lo;
+
+	return res;
+}
+
+static BcRandState bc_rand_multiply(uint_fast64_t a, uint_fast64_t b) {
+
+	uint_fast64_t al, ah, bl, bh, c0, c1, c2, c3;
+	BcRandState carry, res;
+
+	al = BC_RAND_TRUNC32(a);
+	ah = BC_RAND_CHOP32(a);
+	bl = BC_RAND_TRUNC32(b);
+	bh = BC_RAND_CHOP32(b);
+
+	c0 = al * bl;
+	c1 = al * bh;
+	c2 = ah * bl;
+	c3 = ah * bh;
+
+	carry = bc_rand_addition(c1, c2);
+
+	res = bc_rand_addition(c0, (BC_RAND_TRUNC32(carry.lo)) << 32);
+	res.hi += BC_RAND_CHOP32(carry.lo) + c3 + (carry.hi << 32);
+
+	return res;
+}
+
+static BcRandState bc_rand_multiply2(BcRandState a, BcRandState b) {
+
+	BcRandState c0, c1, c2, carry;
+
+	c0 = bc_rand_multiply(a.lo, b.lo);
+	c1 = bc_rand_multiply(a.lo, b.hi);
+	c2 = bc_rand_multiply(a.hi, b.lo);
+
+	carry = bc_rand_addition2(c1, c2);
+	carry.hi = carry.lo;
+	carry.lo = 0;
+
+	return bc_rand_addition2(c0, carry);
+}
+
+#endif // BC_RAND_BUILTIN
+
+static void bc_rand_setModified(BcRNGData *r) {
+
+#if BC_RAND_BUILTIN
+	r->inc |= (BcRandState) 1UL;
+#else // BC_RAND_BUILTIN
+	r->inc.lo |= (uint_fast64_t) 1UL;
+#endif // BC_RAND_BUILTIN
+}
+
+static void bc_rand_clearModified(BcRNGData *r) {
+
+#if BC_RAND_BUILTIN
+	r->inc &= ~((BcRandState) 1UL);
+#else // BC_RAND_BUILTIN
+	r->inc.lo &= ~(1UL);
+#endif // BC_RAND_BUILTIN
+}
+
+static void bc_rand_copy(BcRNGData *d, BcRNGData *s) {
+	bool unmod = BC_RAND_NOTMODIFIED(d);
+	memcpy(d, s, sizeof(BcRNGData));
+	if (!unmod) bc_rand_setModified(d);
+	else if (!BC_RAND_NOTMODIFIED(s)) bc_rand_clearModified(d);
+}
+
+static uchar bc_rand_frand(void *ptr) {
+	return (uchar) fgetc((FILE*) ptr);
+}
+
+static uchar bc_rand_rand(void *ptr) {
+	BC_UNUSED(ptr);
+	return (uchar) (unsigned int) rand();
+}
+
+static BcRandState bc_rand_inc(BcRNGData *r) {
+
+	BcRandState inc;
+
+#if BC_RAND_BUILTIN
+	inc = r->inc | 1;
+#else // BC_RAND_BUILTIN
+	inc.lo = r->inc.lo | 1;
+	inc.hi = r->inc.hi;
+#endif // BC_RAND_BUILTIN
+
+	return inc;
+}
+
+static void bc_rand_setInc(BcRNGData *r) {
+
+#if BC_RAND_BUILTIN
+	r->inc <<= 1UL;
+#else // BC_RAND_BUILTIN
+	r->inc.hi <<= 1UL;
+	r->inc.hi |= (r->inc.lo & (1UL << (BC_LONG_BIT - 1))) >> (BC_LONG_BIT - 1);
+	r->inc.lo <<= 1UL;
+#endif // BC_RAND_BUILTIN
+}
+
+static void bc_rand_seedState(BcRandState *state, ulong val1, ulong val2) {
+
+#if BC_RAND_BUILTIN
+	*state = ((BcRandState) val1) | ((BcRandState) val2) << (BC_LONG_BIT);
+#else // BC_RAND_BUILTIN
+	state->lo = val1;
+	state->hi = val2;
+#endif // BC_RAND_BUILTIN
+}
+
+static void bc_rand_seedRNG(BcRNGData *r, ulong state1, ulong state2,
+                            ulong inc1, ulong inc2)
+{
+	bc_rand_seedState(&r->state, state1, state2);
+	bc_rand_seedState(&r->inc, inc1, inc2);
+	bc_rand_setInc(r);
+}
+
+static ulong bc_rand_fillUlong(BcRandChar fchar, void *ptr) {
+
+	size_t i;
+	ulong res = 0;
+
+	for (i = 0; i < sizeof(ulong); ++i)
+		res |= ((ulong) fchar(ptr)) << (i * CHAR_BIT);
+
+	return res;
+}
+
+static void bc_rand_fill(BcRNGData *r, BcRandChar fchar, void *ptr) {
+
+	ulong state1, state2, inc1, inc2;
+
+	state1 = bc_rand_fillUlong(fchar, ptr);
+	state2 = bc_rand_fillUlong(fchar, ptr);
+
+	inc1 = bc_rand_fillUlong(fchar, ptr);
+	inc2 = bc_rand_fillUlong(fchar, ptr);
+
+	bc_rand_seedRNG(r, state1, state2, inc1, inc2);
+}
+
+static void bc_rand_step(BcRNGData *r) {
+	BcRandState temp = bc_rand_mul2(r->state, bc_rand_multiplier);
+	r->state = bc_rand_add2(temp, bc_rand_inc(r));
+}
+
+static BcRand bc_rand_output(BcRNGData *r) {
+	return BC_RAND_ROT(BC_RAND_FOLD(r->state), BC_RAND_ROTAMT(r->state));
+}
+
+static void bc_rand_seedZeroes(BcRNG *r, BcRNGData *rng, size_t idx) {
+
+	BcRNGData *rng2;
+
+	if (r->v.len <= idx) return;
+
+	rng2 = bc_vec_item_rev(&r->v, idx);
+
+	if (BC_RAND_ZERO(rng2)) {
+		size_t i;
+		for (i = 1; i < r->v.len; ++i)
+			bc_rand_copy(bc_vec_item_rev(&r->v, i), rng);
+	}
+}
+
+static void bc_rand_srand(BcRNG *r) {
+
+	BcRNGData *rng = bc_vec_top(&r->v);
+
+	if (BC_ERR(BC_RAND_ZERO(rng))) {
+
+		FILE* rand = fopen("/dev/urandom", "r");
+
+		if (BC_NO_ERR(rand != NULL)) {
+			bc_rand_fill(rng, bc_rand_frand, rand);
+			fclose(rand);
+		}
+	}
+
+	while (BC_ERR(BC_RAND_ZERO(rng))) bc_rand_fill(rng, bc_rand_rand, NULL);
+}
+
+static void bc_rand_propagate(BcRNG *r, BcRNGData *rng) {
+
+	if (r->v.len <= 1) return;
+
+	if (BC_RAND_NOTMODIFIED(rng)) {
+
+		size_t i;
+		bool go = true;
+
+		for (i = 1; go && i < r->v.len; ++i) {
+			BcRNGData *rng2 = bc_vec_item_rev(&r->v, i);
+			go = BC_RAND_NOTMODIFIED(rng2);
+			bc_rand_copy(rng2, rng);
+		}
+
+		bc_rand_seedZeroes(r, rng, i);
+	}
+	else bc_rand_seedZeroes(r, rng, 1);
+}
+
+BcRand bc_rand_int(BcRNG *r) {
+
+	BcRNGData *rng;
+
+	bc_rand_srand(r);
+
+	rng = bc_vec_top(&r->v);
+	bc_rand_step(rng);
+	bc_rand_propagate(r, rng);
+
+	return bc_rand_output(rng);
+}
+
+BcRand bc_rand_bounded(BcRNG *r, BcRand bound) {
+
+	BcRand rand, threshold = (0 - bound) % bound;
+
+	do {
+		rand = bc_rand_int(r);
+	} while (rand < threshold);
+
+	return rand % bound;
+}
+
+void bc_rand_seed(BcRNG *r, ulong state1, ulong state2, ulong inc1, ulong inc2)
+{
+	BcRNGData *rng = bc_vec_top(&r->v);
+
+	bc_rand_seedState(&rng->inc, inc1, inc2);
+	bc_rand_setInc(rng);
+	bc_rand_setModified(rng);
+
+	if (!state1 && !state2) {
+		memcpy(&rng->state, &rng->inc, sizeof(BcRandState));
+		bc_rand_step(rng);
+	}
+	else bc_rand_seedState(&rng->state, state1, state2);
+
+	bc_rand_propagate(r, rng);
+}
+
+static BcRandState bc_rand_getInc(BcRNGData *r) {
+
+	BcRandState res;
+
+#if BC_RAND_BUILTIN
+	res = r->inc >> 1;
+#else // BC_RAND_BUILTIN
+	res = r->inc;
+	res.lo >>= 1;
+	res.lo |= (res.hi & 1) << (BC_LONG_BIT - 1);
+	res.hi >>= 1;
+#endif // BC_RAND_BUILTIN
+
+	return res;
+}
+
+void bc_rand_getRands(BcRNG *r, BcRand *s1, BcRand *s2, BcRand *i1, BcRand *i2)
+{
+	BcRNGData *rng;
+	BcRandState inc;
+
+	bc_rand_srand(r);
+
+	rng = bc_vec_top(&r->v);
+	inc = bc_rand_getInc(rng);
+
+	*s1 = BC_RAND_TRUNC(rng->state);
+	*s2 = BC_RAND_CHOP(rng->state);
+
+	*i1 = BC_RAND_TRUNC(inc);
+	*i2 = BC_RAND_CHOP(inc);
+}
+
+void bc_rand_push(BcRNG *r) {
+	BcRNGData rng;
+	memset(&rng, 0, sizeof(BcRNGData));
+	if (r->v.len > 0) bc_rand_copy(&rng, bc_vec_top(&r->v));
+	bc_vec_push(&r->v, &rng);
+}
+
+void bc_rand_pop(BcRNG *r) {
+	bc_vec_pop(&r->v);
+}
+
+void bc_rand_init(BcRNG *r) {
+	bc_vec_init(&r->v, sizeof(BcRNGData), NULL);
+	bc_rand_push(r);
+}
+
+void bc_rand_free(BcRNG *r) {
+	bc_vec_free(&r->v);
+}
+
+#endif // BC_ENABLE_EXTRA_MATH
diff --git a/src/vm.c b/src/vm.c
index efbb290..7e9f1e7 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -165,41 +165,57 @@
 static BcStatus bc_vm_envArgs(const char* const env_args_name) {
 
 	BcStatus s;
-	BcVec v;
-	char *env_args = getenv(env_args_name), *buffer, *buf;
+	char *env_args = getenv(env_args_name), *buf, *start;
+	char instr = '\0';
 
 	if (env_args == NULL) return BC_STATUS_SUCCESS;
 
-	buffer = bc_vm_strdup(env_args);
-	buf = buffer;
+	start = buf = vm->env_args_buffer = bc_vm_strdup(env_args);
 
-	bc_vec_init(&v, sizeof(char*), NULL);
-	bc_vec_push(&v, &env_args_name);
+	bc_vec_init(&vm->env_args, sizeof(char*), NULL);
+	bc_vec_push(&vm->env_args, &env_args_name);
 
 	while (*buf) {
 
 		if (!isspace(*buf)) {
 
-			bc_vec_push(&v, &buf);
+			if (*buf == '"' || *buf == '\'') {
 
-			while (*buf && !isspace(*buf)) buf += 1;
+				instr = *buf;
+				buf += 1;
 
-			if (*buf) {
-				*buf = '\0';
+				if (*buf == instr) {
+					buf += 1;
+					continue;
+				}
+			}
+
+			bc_vec_push(&vm->env_args, &buf);
+
+			while (*buf && ((!instr && !isspace(*buf)) ||
+			                (instr && *buf != instr)))
+			{
 				buf += 1;
 			}
+
+			if (*buf) {
+
+				if (instr) instr = '\0';
+
+				*buf = '\0';
+				buf += 1;
+				start = buf;
+			}
+			else if (instr) return bc_vm_error(BC_ERROR_FATAL_OPTION, 0, start);
 		}
 		else buf += 1;
 	}
 
 	// Make sure to push a NULL pointer at the end.
 	buf = NULL;
-	bc_vec_push(&v, &buf);
+	bc_vec_push(&vm->env_args, &buf);
 
-	s = bc_args((int) v.len - 1, bc_vec_item(&v, 0));
-
-	bc_vec_free(&v);
-	free(buffer);
+	s = bc_args((int) vm->env_args.len - 1, bc_vec_item(&vm->env_args, 0));
 
 	return s;
 }
@@ -234,6 +250,8 @@
 	bc_history_free(&vm->history);
 #endif // BC_ENABLE_HISTORY
 #ifndef NDEBUG
+	bc_vec_free(&vm->env_args);
+	free(vm->env_args_buffer);
 	bc_vec_free(&vm->files);
 	bc_vec_free(&vm->exprs);
 	bc_program_free(&vm->prog);
@@ -648,6 +666,11 @@
 #endif // _WIN32
 #endif // BC_ENABLE_SIGNALS
 
+	memcpy(vm->max_num, bc_num_bigdigMax,
+	       bc_num_bigdigMax_size * sizeof(BcDig));
+	bc_num_setup(&vm->max, vm->max_num, BC_NUM_BIGDIG_LOG10);
+	vm->max.len = bc_num_bigdigMax_size;
+
 	vm->file = NULL;
 
 	bc_vm_gettext();
@@ -689,6 +712,10 @@
 	vm->maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE;
 	vm->maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE;
 
+#if BC_ENABLE_EXTRA_MATH
+	vm->maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1;
+#endif // BC_ENABLE_EXTRA_MATH
+
 	if (BC_IS_BC && !BC_IS_POSIX)
 		vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE;
 
diff --git a/tests/all.sh b/tests/all.sh
index aa50f93..c1d91e6 100755
--- a/tests/all.sh
+++ b/tests/all.sh
@@ -38,7 +38,7 @@
 	d="$1"
 	shift
 else
-	err_exit "usage: $script dir [run_extra_tests] [run_stack_tests] [use_long_options] [gen_tests] [time_tests] [exec args...]" 1
+	err_exit "usage: $script dir [run_extra_tests] [run_stack_tests] [gen_tests] [time_tests] [exec args...]" 1
 fi
 
 if [ "$#" -lt 1 ]; then
@@ -56,13 +56,6 @@
 fi
 
 if [ "$#" -lt 1 ]; then
-	use_long_options=1
-else
-	use_long_options="$1"
-	shift
-fi
-
-if [ "$#" -lt 1 ]; then
 	generate_tests=1
 else
 	generate_tests="$1"
@@ -92,13 +85,16 @@
 	halt="q"
 fi
 
+unset BC_ENV_ARGS
+unset BC_LINE_LENGTH
+
 printf '\nRunning %s tests...\n\n' "$d"
 
 while read t; do
 
 	if [ "$extra" -eq 0  ]; then
 		if [ "$t" = "trunc" -o "$t" = "places" -o "$t" = "shift" -o "$t" = "lib2" \
-		     -o "$t" = "scientific" -o "$t" = "engineering" ]
+		     -o "$t" = "scientific" -o "$t" = "engineering" -o "$t" = "rand" ]
 		then
 			printf 'Skipping %s %s\n' "$d" "$t"
 			continue
@@ -111,7 +107,7 @@
 
 sh "$testdir/stdin.sh" "$d" "$exe" "$@"
 
-sh "$testdir/scripts.sh" "$d" "$run_stack_tests" "$generate_tests" "$time_tests" "$exe" "$@"
+sh "$testdir/scripts.sh" "$d" "$extra" "$run_stack_tests" "$generate_tests" "$time_tests" "$exe" "$@"
 sh "$testdir/read.sh" "$d" "$exe" "$@"
 sh "$testdir/errors.sh" "$d" "$exe" "$@"
 
@@ -194,11 +190,7 @@
 
 printf '%s\n%s\n%s\n%s\n' "$results" "$results" "$results" "$results" > "$out1"
 
-if [ "$use_long_options" -ne 0 ]; then
-	"$exe" "$@" -e "$exprs" -f "$f" --expression "$exprs" --file "$f" -e "$halt" > "$out2"
-else
-	"$exe" "$@" -e "$exprs" -f "$f" -e "$exprs" -f "$f" -e "$halt" > "$out2"
-fi
+"$exe" "$@" -e "$exprs" -f "$f" --expression "$exprs" --file "$f" -e "$halt" > "$out2"
 
 diff "$out1" "$out2"
 
@@ -223,28 +215,20 @@
 
 checktest "$d" "$err" "invalid option argument" "$out2" "$d"
 
-if [ "$use_long_options" -ne 0 ]; then
+"$exe" "$@" "--$lopt" -e "$exprs" > /dev/null 2> "$out2"
+err="$?"
 
-	"$exe" "$@" "--$lopt" -e "$exprs" > /dev/null 2> "$out2"
-	err="$?"
-
-	checktest "$d" "$err" "invalid long option argument" "$out2" "$d"
-
-fi
+checktest "$d" "$err" "invalid long option argument" "$out2" "$d"
 
 "$exe" "$@" "-u" -e "$exprs" > /dev/null 2> "$out2"
 err="$?"
 
 checktest "$d" "$err" "unrecognized option argument" "$out2" "$d"
 
-if [ "$use_long_options" -ne 0 ]; then
+"$exe" "$@" "--uniform" -e "$exprs" > /dev/null 2> "$out2"
+err="$?"
 
-	"$exe" "$@" "--uniform" -e "$exprs" > /dev/null 2> "$out2"
-	err="$?"
-
-	checktest "$d" "$err" "unrecognized long option argument" "$out2" "$d"
-
-fi
+checktest "$d" "$err" "unrecognized long option argument" "$out2" "$d"
 
 printf 'pass\n'
 
diff --git a/tests/bc/all.txt b/tests/bc/all.txt
index 134e82f..069e609 100644
--- a/tests/bc/all.txt
+++ b/tests/bc/all.txt
@@ -40,4 +40,5 @@
 misc4
 misc5
 void
+rand
 lib2
diff --git a/tests/bc/errors.txt b/tests/bc/errors.txt
index 56f88dd..7977a8c 100644
--- a/tests/bc/errors.txt
+++ b/tests/bc/errors.txt
@@ -147,6 +147,8 @@
 root(5, 0)
 root(13, -5)
 root(1548, 0)
+irand(-4)
+irand(3289.10827340)
 scale = 10000000000000000000000000000000000
 obase += 999999999999999999999999999999999999999999999999999999999999999999999999
 ibase *= 9999999999999999999999999999999999999999999999999999999999999.9
diff --git a/tests/bc/errors/17.txt b/tests/bc/errors/17.txt
new file mode 100644
index 0000000..3f861a0
--- /dev/null
+++ b/tests/bc/errors/17.txt
@@ -0,0 +1,313 @@
+print "Gathering array...\n"
+
+s =	seed
+
+sum = 0
+
+for (i = 0; i < 100; ++i) {
+	a[i] = rand()
+	sum += a[i]
+	b[i] = irand(sum)
+}
+
+print "Testing implementation...\n"
+
+if (maxrand() >= 2^64 - 1) {
+
+	seed = 54.86785590782347282592869373784717814475564948862907968939159536927733440\
+	          901359008180088183692646452982444316148757934570312500000
+
+	ibase = G
+	obase = G
+
+	for (i = 0; i < 64; ++i) {
+		rand()
+	}
+
+	7B47F409
+	BA1D3330
+	83D2F293
+	BFA4784B
+	CBED606E
+	BFC6A3AD
+	812FFF6D
+	E61F305A
+	F9384B90
+	32DB86FE
+	1DC035F9
+	ED786826
+	3822441D
+	2BA113D7
+	1C5B818B
+	A233956A
+	84DA65E3
+	CED67292
+	B2C0FE06
+	91817130
+
+	55FE8917
+	47E92091
+	486AF299
+	B1E882BB
+	C261E845
+	1A9B90F6
+	7964E884
+	5F36D7A4
+	1EE2052D
+	8519F5D5
+	293D4E4F
+	6D8F99FC
+	C3421509
+	A06CD7C6
+	E43064D3
+	E20F9BF0
+	401B50B7
+	8EF1FF3E
+	E357E2B2
+	A4AEEE37
+
+	2AD4426A
+	9D11BE94
+	7290C556
+	6E6F3787
+	050C2EE3
+	4FD73703
+	C6FF478B
+	4B1CA1E1
+	1654EA91
+	CD08B2F2
+	F7FF3DA8
+	78B1B8DA
+	A100602C
+	9588585F
+	DA028873
+	66B4F376
+	0E6B4B9A
+	48167094
+	0D58CDA0
+	8F7238BE
+
+	F79983F3
+	07E5D324
+	AD78DF52
+	1532BA74
+	1E4899E2
+	6C75DF64
+	171DDC36
+	F2D8D74A
+	24E6D907
+	4780FD32
+	9ADF408C
+	A25544CF
+	EFC6A738
+	1AA23A54
+	C5A13EBB
+	F739EDC9
+	C3A015FA
+	3D5E1511
+	AFC4D7FB
+	3F413B5E
+
+	4660CB73
+	88FC773F
+	D6BED59C
+	63B3B54A
+	D67D3DDE
+	23394F8B
+	13384B44
+	DD8B3ABC
+	FF59A21E
+	3BB16D7E
+	6E01CB68
+	EC34790E
+	B26C42AD
+	D723C830
+	DFD10FCA
+	7E362AA1
+	826FF323
+	CB8F63B5
+	9B3227E5
+	9A61E339
+}
+else {
+
+	ibase = G
+	obase = G
+
+	86B1DA1D72062B68
+	1304AA46C9853D39
+	A3670E9E0DD50358
+	F9090E529A7DAE00
+	C85B9FD837996F2C
+	606121F8E3919196
+	7CE1C7FF478354BA
+	CBC4AC70E541310E
+	74BE71999EC37F2C
+	B81F9C99A934F1A7
+	120E9901A900C97F
+	0F983BAD4B19F493
+	5934619363660D96
+	D5A7FE2717A2014E
+	6E437241C9E6676E
+	6A75C9DD6329CD29
+	2D9E477683673437
+	51FB0CF3D4405437
+	217BB90392D08B20
+	47C528A018B07A82
+
+	1B4E474C418C835E
+	BDB2BDA74A119ED6
+	C6DB79D0B9E43493
+	C3CF4834E94A41D1
+	AB8312FC7877C7DC
+	094B108133E8B5EC
+	37CA97AC830113BD
+	EF02D7347F9192BF
+	959517DD9896C53A
+	7A80EB7629EFE9F9
+	AE53C23F2B1CF57C
+	CA605CD189F6D5CD
+	921C2704886A9622
+	B68C9FBF826AF7AA
+	73F8C733124772C3
+	6B57F7E459EFBCDF
+	9DE7696DDB6B8E18
+	02CA67560DC26877
+	A24E353080777DEC
+	4D600156763FD65C
+
+	5CDF9C7E26DD2C38
+	6A32443BBBB16774
+	3D8415FFECFB8B7F
+	3090ED9C47@6
+	6DBF241361C3E652
+	2CA9EF5A2AD971FC
+	44FBE937A1CF0FFC
+	DB17CF0577CB7853
+	AA3747D98D31B24C
+	5D9A104C5D7F43F7
+	BAE65E3E293B2C7B
+	16A396F0DB4EF984
+	6DD2BACDC4445A05
+	7B7A13D1858E5CA8
+	F73722BCAA52447C
+	31A2C7BBE77CBA00
+	7FC8AF9003BA1ACE
+	5703F11DD3F235EF
+	FA1952267EF836C7
+	BBFA558C9E2D51E2
+
+	3A29661D8145AF36
+	608DEA6358DABD7C
+	9E34E9E53431B447
+	325A05E35EA524EB
+	63A87CCF0C80ABB1
+	8EA183287A46F292
+	E2AA5F119CBF2A08
+	2F3BEB0DE8B730C8
+	4B8006A928CF8F5B
+	57B8BA85069C201C
+	3422D962DDF59474
+	FD744940BA7366A1
+	23D24B06B5DA4F6F
+	AA187C608319D1DC
+	DC60CA6FEA738B8A
+	C9FC61DF96A769FE
+	82E2457708658A20
+	2BECEC9B3E7D93EC
+	1340DAEC04588952
+	F533446AD5C50B1D
+
+	31FD1C7F434A62CE
+	D16DAEDD1F281A39
+	6B5D9648931D7057
+	62FEE3392DBB06D5
+	0358BC87B00DF25A
+	F3C882D22946175D
+	65BA8F11B4516EFE
+	2DA5A96E626DA4FE
+	DCC669F4CD6121F0
+	7A47FAC054319CA2
+	9661CFEE277284C8
+	01E483A14F4EB23A
+	ADDC115507390607
+	5AB47C343BD3B0BD
+	4882FB3A3957B11F
+	615B7C9C3626DD44
+	F79CF49562969219
+	88C32C194EA78D27
+	DA8AFFE1353FF352
+	A7A3C331A64CB146
+
+	ibase = A
+
+	seed = 54.0950779151573258314404657465246373249101452529430389404296875000
+
+	ibase = G
+
+	for (i = 0; i < 64; ++i) {
+		rand()
+	}
+}
+
+print "Testing array...\n"
+
+ibase = A
+
+seed = s
+
+sum = 0
+
+for (i = 0; i < 100; ++i) {
+	a[i] == rand()
+	sum += a[i]
+	b[i] == irand(sum)
+}
+
+print "Exercising irand()...\n"
+
+scale = 256
+
+pow = (maxrand() + 1) ^ 4
+s =!2^256 + 2^128 + (irand(pow) / pow)
+seed = s
+seed < s
+
+s = -459.125
+seed = s
+seed == -s
+
+irand(0)
+irand(1)
+seed == -s
+irand(maxrand() + 1) <= maxrand()
+
+for (i = 0; i < 200; ++i) {
+	irand(20) < 20
+}
+
+seed = 738
+seed != 738
+
+s = 2398@0625
+seed = s
+seed != s
+
+pow = (maxrand() + 1) ^ 4
+s = 2^2560 + 2^128 + (irand(pow) / pow)
+seed = s
+seed < s
+
+b = 0
+m = maxrand() + 1
+n = m + 1
+
+for (i = 0; !b && i < 100; ++i) {
+	c = irand(n)
+	b = (c != 0 && c != m)
+	if (c >= n) print "irand() result is too large.\n"
+}
+
+b
+
+sqrt(-1)
diff --git a/tests/bc/lib2.txt b/tests/bc/lib2.txt
index e1f9022..f742602 100644
--- a/tests/bc/lib2.txt
+++ b/tests/bc/lib2.txt
@@ -449,3 +449,14 @@
 uint(-3)
 uint(3.928375)
 int(4.000000)
+b = brand()
+b < 2
+b >= 0
+i = irand(maxrand() + 1)
+i <= maxrand()
+i >= 0
+f = frand(10)
+scale(f) == 10
+fi = ifrand(123, 28)
+scale(fi) == 28
+fi < 128
diff --git a/tests/bc/lib2_results.txt b/tests/bc/lib2_results.txt
index b440fc8..c08d8c7 100644
--- a/tests/bc/lib2_results.txt
+++ b/tests/bc/lib2_results.txt
@@ -688,3 +688,10 @@
 Error: -3 is negative.
 Error: 3.928375 is not an integer.
 Error: 4.000000 is not an integer.
+1
+1
+1
+1
+1
+1
+1
diff --git a/tests/bc/rand.txt b/tests/bc/rand.txt
new file mode 100644
index 0000000..cdd8976
--- /dev/null
+++ b/tests/bc/rand.txt
@@ -0,0 +1,311 @@
+print "Gathering array...\n"
+
+s = seed
+
+sum = 0
+
+for (i = 0; i < 100; ++i) {
+	a[i] = rand()
+	sum += a[i]
+	b[i] = irand(sum)
+}
+
+print "Testing implementation...\n"
+
+if (maxrand() >= 2^64 - 1) {
+
+	seed = 54.86785590782347282592869373784717814475564948862907968939359536927733440\
+	          901359008180088183692646452982444316148757934570312500000
+
+	ibase = G
+	obase = G
+
+	for (i = 0; i < 64; ++i) {
+		rand()
+	}
+
+	7B47F409
+	BA1D3330
+	83D2F293
+	BFA4784B
+	CBED606E
+	BFC6A3AD
+	812FFF6D
+	E61F305A
+	F9384B90
+	32DB86FE
+	1DC035F9
+	ED786826
+	3822441D
+	2BA113D7
+	1C5B818B
+	A233956A
+	84DA65E3
+	CED67292
+	B2C0FE06
+	91817130
+
+	55FE8917
+	47E92091
+	486AF299
+	B1E882BB
+	C261E845
+	1A9B90F6
+	7964E884
+	5F36D7A4
+	1EE2052D
+	8519F5D5
+	293D4E4F
+	6D8F99FC
+	C3421509
+	A06CD7C6
+	E43064D3
+	E20F9BF0
+	401B50B7
+	8EF1FF3E
+	E357E2B2
+	A4AEEE37
+
+	2AD4426A
+	9D11BE94
+	7290C556
+	6E6F3787
+	050C2EE3
+	4FD73703
+	C6FF478B
+	4B1CA1E1
+	1654EA91
+	CD08B2F2
+	F7FF3DA8
+	78B1B8DA
+	A100602C
+	9588585F
+	DA028873
+	66B4F376
+	0E6B4B9A
+	48167094
+	0D58CDA0
+	8F7238BE
+
+	F79983F3
+	07E5D324
+	AD78DF52
+	1532BA74
+	1E4899E2
+	6C75DF64
+	171DDC36
+	F2D8D74A
+	24E6D907
+	4780FD32
+	9ADF408C
+	A25544CF
+	EFC6A738
+	1AA23A54
+	C5A13EBB
+	F739EDC9
+	C3A015FA
+	3D5E1511
+	AFC4D7FB
+	3F413B5E
+
+	4660CB73
+	88FC773F
+	D6BED59C
+	63B3B54A
+	D67D3DDE
+	23394F8B
+	13384B44
+	DD8B3ABC
+	FF59A21E
+	3BB16D7E
+	6E01CB68
+	EC34790E
+	B26C42AD
+	D723C830
+	DFD10FCA
+	7E362AA1
+	826FF323
+	CB8F63B5
+	9B3227E5
+	9A61E339
+}
+else {
+
+	ibase = G
+	obase = G
+
+	86B1DA1D72062B68
+	1304AA46C9853D39
+	A3670E9E0DD50358
+	F9090E529A7DAE00
+	C85B9FD837996F2C
+	606121F8E3919196
+	7CE1C7FF478354BA
+	CBC4AC70E541310E
+	74BE71999EC37F2C
+	B81F9C99A934F1A7
+	120E9901A900C97F
+	0F983BAD4B19F493
+	5934619363660D96
+	D5A7FE2717A2014E
+	6E437241C9E6676E
+	6A75C9DD6329CD29
+	2D9E477683673437
+	51FB0CF3D4405437
+	217BB90392D08B20
+	47C528A018B07A82
+
+	1B4E474C418C835E
+	BDB2BDA74A119ED6
+	C6DB79D0B9E43493
+	C3CF4834E94A41D1
+	AB8312FC7877C7DC
+	094B108133E8B5EC
+	37CA97AC830113BD
+	EF02D7347F9192BF
+	959517DD9896C53A
+	7A80EB7629EFE9F9
+	AE53C23F2B1CF57C
+	CA605CD189F6D5CD
+	921C2704886A9622
+	B68C9FBF826AF7AA
+	73F8C733124772C3
+	6B57F7E459EFBCDF
+	9DE7696DDB6B8E18
+	02CA67560DC26877
+	A24E353080777DEC
+	4D600156763FD65C
+
+	5CDF9C7E26DD2C38
+	6A32443BBBB16774
+	3D8415FFECFB8B7F
+	3090ED9C475635A3
+	6DBF241361C3E652
+	2CA9EF5A2AD971FC
+	44FBE937A1CF0FFC
+	DB17CF0577CB7853
+	AA3747D98D31B24C
+	5D9A104C5D7F43F7
+	BAE65E3E293B2C7B
+	16A396F0DB4EF984
+	6DD2BACDC4445A05
+	7B7A13D1858E5CA8
+	F73722BCAA52447C
+	31A2C7BBE77CBA00
+	7FC8AF9003BA1ACE
+	5703F11DD3F235EF
+	FA1952267EF836C7
+	BBFA558C9E2D51E2
+
+	3A29661D8145AF36
+	608DEA6358DABD7C
+	9E34E9E53431B447
+	325A05E35EA524EB
+	63A87CCF0C80ABB1
+	8EA183287A46F292
+	E2AA5F119CBF2A08
+	2F3BEB0DE8B730C8
+	4B8006A928CF8F5B
+	57B8BA85069C201C
+	3422D962DDF59474
+	FD744940BA7366A1
+	23D24B06B5DA4F6F
+	AA187C608319D1DC
+	DC60CA6FEA738B8A
+	C9FC61DF96A769FE
+	82E2457708658A20
+	2BECEC9B3E7D93EC
+	1340DAEC04588952
+	F533446AD5C50B1D
+
+	31FD1C7F434A62CE
+	D16DAEDD1F281A39
+	6B5D9648931D7057
+	62FEE3392DBB06D5
+	0358BC87B00DF25A
+	F3C882D22946175D
+	65BA8F11B4516EFE
+	2DA5A96E626DA4FE
+	DCC669F4CD6121F0
+	7A47FAC054319CA2
+	9661CFEE277284C8
+	01E483A14F4EB23A
+	ADDC115507390607
+	5AB47C343BD3B0BD
+	4882FB3A3957B11F
+	615B7C9C3626DD44
+	F79CF49562969219
+	88C32C194EA78D27
+	DA8AFFE1353FF352
+	A7A3C331A64CB146
+
+	ibase = A
+
+	seed = 54.0950779151573258314404657465246373249101452529430389404296875000
+
+	ibase = G
+
+	for (i = 0; i < 64; ++i) {
+		rand()
+	}
+}
+
+print "Testing array...\n"
+
+ibase = A
+
+seed = s
+
+sum = 0
+
+for (i = 0; i < 100; ++i) {
+	a[i] == rand()
+	sum += a[i]
+	b[i] == irand(sum)
+}
+
+print "Exercising irand()...\n"
+
+scale = 256
+
+pow = (maxrand() + 1) ^ 4
+s = 2^256 + 2^128 + (irand(pow) / pow)
+seed = s
+seed < s
+
+s = -459.125
+seed = s
+seed == -s
+
+irand(0)
+irand(1)
+seed == -s
+irand(maxrand() + 1) <= maxrand()
+
+for (i = 0; i < 200; ++i) {
+	irand(20) < 20
+}
+
+seed = 738
+seed != 738
+
+s = 2398@0625
+seed = s
+seed != s
+
+pow = (maxrand() + 1) ^ 4
+s = 2^2560 + 2^128 + (irand(pow) / pow)
+seed = s
+seed < s
+
+b = 0
+m = maxrand() + 1
+n = m + 1
+
+for (i = 0; !b && i < 100; ++i) {
+	c = irand(n)
+	b = (c != 0 && c != m)
+	if (c >= n) print "irand() result is too large.\n"
+}
+
+b
diff --git a/tests/bc/rand_results.txt b/tests/bc/rand_results.txt
new file mode 100644
index 0000000..187434f
--- /dev/null
+++ b/tests/bc/rand_results.txt
@@ -0,0 +1,614 @@
+Gathering array...
+Testing implementation...
+86B1DA1D72062B68
+1304AA46C9853D39
+A3670E9E0DD50358
+F9090E529A7DAE00
+C85B9FD837996F2C
+606121F8E3919196
+7CE1C7FF478354BA
+CBC4AC70E541310E
+74BE71999EC37F2C
+B81F9C99A934F1A7
+120E9901A900C97F
+F983BAD4B19F493
+5934619363660D96
+D5A7FE2717A2014E
+6E437241C9E6676E
+6A75C9DD6329CD29
+2D9E477683673437
+51FB0CF3D4405437
+217BB90392D08B20
+47C528A018B07A82
+1B4E474C418C835E
+BDB2BDA74A119ED6
+C6DB79D0B9E43493
+C3CF4834E94A41D1
+AB8312FC7877C7DC
+94B108133E8B5EC
+37CA97AC830113BD
+EF02D7347F9192BF
+959517DD9896C53A
+7A80EB7629EFE9F9
+AE53C23F2B1CF57C
+CA605CD189F6D5CD
+921C2704886A9622
+B68C9FBF826AF7AA
+73F8C733124772C3
+6B57F7E459EFBCDF
+9DE7696DDB6B8E18
+2CA67560DC26877
+A24E353080777DEC
+4D600156763FD65C
+5CDF9C7E26DD2C38
+6A32443BBBB16774
+3D8415FFECFB8B7F
+3090ED9C475635A3
+6DBF241361C3E652
+2CA9EF5A2AD971FC
+44FBE937A1CF0FFC
+DB17CF0577CB7853
+AA3747D98D31B24C
+5D9A104C5D7F43F7
+BAE65E3E293B2C7B
+16A396F0DB4EF984
+6DD2BACDC4445A05
+7B7A13D1858E5CA8
+F73722BCAA52447C
+31A2C7BBE77CBA00
+7FC8AF9003BA1ACE
+5703F11DD3F235EF
+FA1952267EF836C7
+BBFA558C9E2D51E2
+3A29661D8145AF36
+608DEA6358DABD7C
+9E34E9E53431B447
+325A05E35EA524EB
+63A87CCF0C80ABB1
+8EA183287A46F292
+E2AA5F119CBF2A08
+2F3BEB0DE8B730C8
+4B8006A928CF8F5B
+57B8BA85069C201C
+3422D962DDF59474
+FD744940BA7366A1
+23D24B06B5DA4F6F
+AA187C608319D1DC
+DC60CA6FEA738B8A
+C9FC61DF96A769FE
+82E2457708658A20
+2BECEC9B3E7D93EC
+1340DAEC04588952
+F533446AD5C50B1D
+31FD1C7F434A62CE
+D16DAEDD1F281A39
+6B5D9648931D7057
+62FEE3392DBB06D5
+358BC87B00DF25A
+F3C882D22946175D
+65BA8F11B4516EFE
+2DA5A96E626DA4FE
+DCC669F4CD6121F0
+7A47FAC054319CA2
+9661CFEE277284C8
+1E483A14F4EB23A
+ADDC115507390607
+5AB47C343BD3B0BD
+4882FB3A3957B11F
+615B7C9C3626DD44
+F79CF49562969219
+88C32C194EA78D27
+DA8AFFE1353FF352
+A7A3C331A64CB146
+7B47F409
+BA1D3330
+83D2F293
+BFA4784B
+CBED606E
+BFC6A3AD
+812FFF6D
+E61F305A
+F9384B90
+32DB86FE
+1DC035F9
+ED786826
+3822441D
+2BA113D7
+1C5B818B
+A233956A
+84DA65E3
+CED67292
+B2C0FE06
+91817130
+55FE8917
+47E92091
+486AF299
+B1E882BB
+C261E845
+1A9B90F6
+7964E884
+5F36D7A4
+1EE2052D
+8519F5D5
+293D4E4F
+6D8F99FC
+C3421509
+A06CD7C6
+E43064D3
+E20F9BF0
+401B50B7
+8EF1FF3E
+E357E2B2
+A4AEEE37
+2AD4426A
+9D11BE94
+7290C556
+6E6F3787
+50C2EE3
+4FD73703
+C6FF478B
+4B1CA1E1
+1654EA91
+CD08B2F2
+F7FF3DA8
+78B1B8DA
+A100602C
+9588585F
+DA028873
+66B4F376
+E6B4B9A
+48167094
+D58CDA0
+8F7238BE
+F79983F3
+7E5D324
+AD78DF52
+1532BA74
+1E4899E2
+6C75DF64
+171DDC36
+F2D8D74A
+24E6D907
+4780FD32
+9ADF408C
+A25544CF
+EFC6A738
+1AA23A54
+C5A13EBB
+F739EDC9
+C3A015FA
+3D5E1511
+AFC4D7FB
+3F413B5E
+4660CB73
+88FC773F
+D6BED59C
+63B3B54A
+D67D3DDE
+23394F8B
+13384B44
+DD8B3ABC
+FF59A21E
+3BB16D7E
+6E01CB68
+EC34790E
+B26C42AD
+D723C830
+DFD10FCA
+7E362AA1
+826FF323
+CB8F63B5
+9B3227E5
+9A61E339
+Testing array...
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+Exercising irand()...
+1
+1
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
diff --git a/tests/bc/scripts/len.bc b/tests/bc/scripts/len.bc
new file mode 100644
index 0000000..ec931f2
--- /dev/null
+++ b/tests/bc/scripts/len.bc
@@ -0,0 +1,48 @@
+define fast_gcd(a, b) {
+
+	if (a == b) return a;
+	if (a > b) return fast_gcd(a - b, b)
+
+	return fast_gcd(a, b - a);
+}
+
+define void r_reduce(*r[]) {
+
+	auto g,s;
+
+	if (length(r[]) != 2) sqrt(-1);
+	if (scale(r[0])) 2^r[0];
+	if (scale(r[1])) 2^r[1];
+
+	if (r[0] >= 0 && r[1] >= 0) g = fast_gcd(r[0], r[1]);
+	else g = gcd(r[0], r[1]);
+
+	s = scale;
+	scale = 0;
+
+	r[0] /= g;
+	r[1] /= g;
+
+	scale = s;
+}
+
+define void r_init(*r[], a, b) {
+	r[0] = a;
+	r[1] = b;
+	r_reduce(r[]);
+}
+
+define void r_initi(*r[], i, a, b) {
+
+	length(r[]);
+
+	r[0] = i * b + a;
+	r[1] = b;
+
+	length(r[]);
+
+	r_reduce(r[]);
+}
+
+length(a[])
+r_initi(a[], 5, 63, 94);
diff --git a/tests/bc/scripts/len.txt b/tests/bc/scripts/len.txt
new file mode 100644
index 0000000..3328062
--- /dev/null
+++ b/tests/bc/scripts/len.txt
@@ -0,0 +1,3 @@
+1
+1
+2
diff --git a/tests/bc/scripts/rand.bc b/tests/bc/scripts/rand.bc
new file mode 100644
index 0000000..54173c7
--- /dev/null
+++ b/tests/bc/scripts/rand.bc
@@ -0,0 +1,97 @@
+#! /usr/bin/bc
+
+define x(x) {
+	seed = x
+	seed@20
+	return seed
+}
+
+define y(x) {
+	auto s
+	seed@20
+	s = x(x)
+	seed@20
+	return s
+}
+
+define void u(x) {
+	seed = x
+	seed@20
+}
+
+define void v(x) {
+	u(x)
+	seed@20
+}
+
+define g(x) {
+	auto s
+	s = irand(x)
+	s < x
+	return seed
+}
+
+define h(x) {
+	auto s
+	s = g(x)
+	s == seed
+	return s
+}
+
+define j(x) {
+	auto s, r
+	seed@20
+	s = seed
+	r = rand()
+	seed = x
+	s != seed
+	return rand()
+}
+
+define k(x) {
+	auto s, r
+	s = seed
+	seed@20
+	r = j(x)
+	s != seed
+	seed = x
+	rand() == r
+	return r
+}
+
+define m(*a[]) {
+	auto i
+	seed = seed
+	for (i = 0; i < 100; ++i) {
+		a[i] = rand()
+	}
+	return seed
+}
+
+v(50.5)
+seed@20
+
+s = y(75.25)
+s@20
+seed@20
+
+r = rand()
+i = irand(r)
+
+i < r
+
+s = h(maxrand() ^ 4)
+s == seed
+
+seed = 2398.0625
+r = k(38.45)
+seed = 38.45
+r == rand()
+
+s = m(a[])
+
+for (i = 0; i < 100; ++i) {
+	rand() == a[i]
+}
+
+s == seed
diff --git a/tests/bc/scripts/rand.txt b/tests/bc/scripts/rand.txt
new file mode 100644
index 0000000..886daca
--- /dev/null
+++ b/tests/bc/scripts/rand.txt
@@ -0,0 +1,119 @@
+50.50000000000000000000
+50.50000000000000000000
+50.50000000000000000000
+50.50000000000000000000
+75.25000000000000000000
+50.50000000000000000000
+75.25000000000000000000
+50.50000000000000000000
+1
+1
+1
+1
+2398.06250000000000000000
+2398.06250000000000000000
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
diff --git a/tests/dc/all.txt b/tests/dc/all.txt
index a0afcb2..6879541 100644
--- a/tests/dc/all.txt
+++ b/tests/dc/all.txt
@@ -19,3 +19,4 @@
 vars
 misc
 strings
+rand
diff --git a/tests/dc/rand.txt b/tests/dc/rand.txt
new file mode 100644
index 0000000..250da02
--- /dev/null
+++ b/tests/dc/rand.txt
@@ -0,0 +1 @@
+J ss ' sr lr " st ls j ' lr GpR lr " lt GpR W W GpR
diff --git a/tests/dc/rand_results.txt b/tests/dc/rand_results.txt
new file mode 100644
index 0000000..e8183f0
--- /dev/null
+++ b/tests/dc/rand_results.txt
@@ -0,0 +1,3 @@
+1
+1
+1
diff --git a/tests/radamsa.sh b/tests/radamsa.sh
new file mode 100755
index 0000000..345ed62
--- /dev/null
+++ b/tests/radamsa.sh
@@ -0,0 +1,86 @@
+#! /bin/bash
+#
+# Copyright (c) 2018-2020 Gavin D. Howard and contributors.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+#   list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+getentry() {
+
+	if [ $# -gt 0 ]; then
+		entnum="$1"
+	else
+		entnum=0
+	fi
+
+	e=$(cat -)
+	num=$(printf '%s\n' "$e" | wc -l)
+
+	if [ "$entnum" -eq 0 ]; then
+		rand=$(printf 'irand(%s) + 1\n' "$num" | "$bcdir/bc")
+	else
+		rand="$entnum"
+	fi
+
+	ent=$(printf '%s\n' "$e" | tail -n +$rand | head -n 1)
+
+	printf '%s\n' "$ent"
+}
+
+script="$0"
+
+dir=$(dirname "$script")
+
+bcdir="$dir/../bin"
+
+export ASAN_OPTIONS="abort_on_error=1"
+
+entries=$(cat "$dir/radamsa.txt")
+
+IFS=$'\n'
+
+while [ 1 ]; do
+
+	entry=$(cat -- "$dir/radamsa.txt" | getentry)
+	items=$(printf '%s\n' "$entry" | radamsa -n 10)
+
+	printf '%s\n' "$items"
+
+	for i in `seq 1 10`; do
+
+		item=$(printf '%s\n' "$items" | getentry "$i")
+
+		export BC_ENV_ARGS="$item"
+		echo 'halt' | bin/bc
+		err=$?
+
+		if [ "$err" -gt 127 ]; then
+			printf 'bc crashed with error %d on the following:\n' "$err"
+			printf '%s\n' "$item"
+			exit 1
+		fi
+	done
+
+done
diff --git a/tests/radamsa.txt b/tests/radamsa.txt
new file mode 100644
index 0000000..4bf2890
--- /dev/null
+++ b/tests/radamsa.txt
@@ -0,0 +1,17 @@
+-lq '/home/gavin/.bcrc'
+-lq "/home/gavin/.bcrc"
+-lqg '/home/gavin/bc stuff.bc'
+-lqg "/home/gavin/bc stuff.bc"
+-lqg '/home/gavin/"bc" stuff.bc'
+-lqg "/home/gavin/'bc' stuff.bc"
+-lqg '/home/gavin/bc stuff.bc
+-lqg "/home/gavin/bc stuff.bc
+-lqg '/home/gavin/"bc" stuff.bc
+-lqg "/home/gavin/'bc' stuff.bc
+--mathlib --expand
+--file="/home/gavin/.bcrc"
+--file=/home/gavin/.bcrc
+--file="/home/gavin/bc stuff.bc"
+--file
+--expression "4+4"
+-e "irand(128)" -f /home/gavin/.bcrc
diff --git a/tests/script.sh b/tests/script.sh
index b8d3107..4d1b235 100755
--- a/tests/script.sh
+++ b/tests/script.sh
@@ -34,7 +34,7 @@
 testdir=$(dirname "${script}")
 
 if [ "$#" -lt 2 ]; then
-	printf 'usage: %s dir script [run_stack_tests] [generate_tests] [time_tests] [exec args...]\n' "$script"
+	printf 'usage: %s dir script [run_extra_tests] [run_stack_tests] [generate_tests] [time_tests] [exec args...]\n' "$script"
 	exit 1
 fi
 
@@ -45,6 +45,13 @@
 shift
 
 if [ "$#" -gt 0 ]; then
+	run_extra_tests="$1"
+	shift
+else
+	run_extra_tests=1
+fi
+
+if [ "$#" -gt 0 ]; then
 	run_stack_tests="$1"
 	shift
 else
@@ -97,9 +104,16 @@
 	exit 0
 fi
 
+if [ "$run_extra_tests" -eq 0 ]; then
+	if [ "$f" = "rand.bc" ]; then
+		printf 'Skipping %s script: %s\n' "$d" "$f"
+		exit 0
+	fi
+fi
+
 if [ "$run_stack_tests" -eq 0 ]; then
 
-	if [ "$f" = "globals.bc" -o "$f" = "references.bc" ]; then
+	if [ "$f" = "globals.bc" -o "$f" = "references.bc" -o "$f" = "rand.bc" ]; then
 		printf 'Skipping %s script: %s\n' "$d" "$f"
 		exit 0
 	fi
@@ -128,7 +142,7 @@
 
 if [ "$time_tests" -ne 0 ]; then
 	printf '\n'
-	printf '%s\n' "$halt" | time -p "$exe" "$@" $options "$s" > "$out"
+	printf '%s\n' "$halt" | /usr/bin/time -p "$exe" "$@" $options "$s" > "$out"
 	printf '\n'
 else
 	printf '%s\n' "$halt" | "$exe" "$@" $options "$s" > "$out"
diff --git a/tests/scripts.sh b/tests/scripts.sh
index f8a9e53..13932fc 100755
--- a/tests/scripts.sh
+++ b/tests/scripts.sh
@@ -34,7 +34,7 @@
 testdir=$(dirname "${script}")
 
 if [ "$#" -eq 0 ]; then
-	printf 'usage: %s dir [run_stack_tests] [generate_tests] [time_tests] [exec args...]\n' "$script"
+	printf 'usage: %s dir [run_extra_tests] [run_stack_tests] [generate_tests] [time_tests] [exec args...]\n' "$script"
 	exit 1
 else
 	d="$1"
@@ -42,6 +42,13 @@
 fi
 
 if [ "$#" -gt 0 ]; then
+	run_extra_tests="$1"
+	shift
+else
+	run_extra_tests=1
+fi
+
+if [ "$#" -gt 0 ]; then
 	run_stack_tests="$1"
 	shift
 else
@@ -74,6 +81,6 @@
 for s in $scriptdir/*.$d; do
 
 	f=$(basename "$s")
-	sh "$testdir/script.sh" "$d" "$f" "$run_stack_tests" "$generate" "$time_tests" "$exe" "$@"
+	sh "$testdir/script.sh" "$d" "$f" "$run_extra_tests" "$run_stack_tests" "$generate" "$time_tests" "$exe" "$@"
 
 done
diff --git a/tests/test.sh b/tests/test.sh
index 8eb2134..a8e7dde 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -119,7 +119,7 @@
 
 if [ "$time_tests" -ne 0 ]; then
 	printf '\n'
-	printf '%s\n' "$halt" | time -p "$exe" "$@" $options "$name" > "$out"
+	printf '%s\n' "$halt" | /usr/bin/time -p "$exe" "$@" $options "$name" > "$out"
 	printf '\n'
 else
 	printf '%s\n' "$halt" | "$exe" "$@" $options "$name" > "$out"