Skip to content

Commit

Permalink
version 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
cellocgw authored and cran-robot committed Jun 25, 2020
0 parents commit 271ea36
Show file tree
Hide file tree
Showing 10 changed files with 434 additions and 0 deletions.
14 changes: 14 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Package: Rfractran
Type: Package
Title: A 'FRACTRAN' Interpreter and Some Helper Functions
Version: 1.0
Date: 2020-06-09
Author: Carl Witthoft
Maintainer: Carl Witthoft <carl@witthoft.com>
Depends: gmp
Description: 'FRACTRAN' is an obscure yet tantalizing programming language invented by John Conway of 'Game of Life' fame. The code consists of a sequence of fractions. The rules are simple. First, select an integer to initialize the process. Second, multiply the integer by the first fraction. If an integer results, start again with the new integer. If not, try the next fraction. Finally, if no such multiplication yields an integer, terminate the program. For more information, see <https://en.wikipedia.org/wiki/FRACTRAN> .
License: LGPL-3
NeedsCompilation: no
Packaged: 2020-06-24 00:56:29 UTC; cgw
Repository: CRAN
Date/Publication: 2020-06-25 16:20:11 UTC
9 changes: 9 additions & 0 deletions MD5
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
358bbba639e47280b22acd0f0c866aff *DESCRIPTION
7bd8c8b436803e87fe5b18e94be130f0 *NAMESPACE
6a5c6eaf701920b9bd0be475cbc37638 *R/fractran.r
e15afcdc07fad3f71403c5d429e15820 *build/partial.rdb
bb390e6903920f06875d329e98b2f114 *man/Rfractran-package.Rd
48f447d32b95f30ba4092c8052ab990d *man/fracAns.Rd
f9c584539de28071e1944500dffee4e9 *man/fracDo.Rd
498c6136ec954d3bcf135f3079b67ad3 *man/fracMake.Rd
87337f547db5a96bba60052ebdbd91f1 *man/stopPrime.Rd
4 changes: 4 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export("fracAns", "fracDo", "fracMake", "stopPrime")
importFrom("grDevices", "xy.coords")
importFrom("utils", "flush.console")
import("gmp")
141 changes: 141 additions & 0 deletions R/fractran.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# for the first fraction f in the list for which nf is an integer, replace n by nf
# repeat this rule until no fraction in the list produces an integer when multiplied by n, then halt.

# validation tests:
# addition: { 3/2 } . enter 2^a*3^b and get 3^(a+b) answer when terminated

# multiplication: complicated because the sequence "bounces" between two subsets of the fraction set. Notice that the bigger denom, 33=11*3 in this case, ALWAYS has to be listed first before the denom 3, or it will never be reached.
# { 455/33, 11/13, 1/11, ; 3/7, 11/2, 1/3 } ( ";" there for emphasis only)

# then 2^a*3^b terminates with 5^(a*b)
# fracmul <-makefrac(c(455,11,1,3,11,1),c(33,13,11,7,2,3))


# prime generator; moves a lot faster/farther than Conway's
#start with 10, feed {7/3 99/98 13/49 39/35 36/91 10/143 49/13 7/11 1/2 91/1} and result includes a series of values 10^p , p prime
# fracp10 <- fracMake(c(7,99,13,39,36,10,49,7,1,91), c(3,98,49,35,91,143,13,11,2,1))

#Conway's original prime gen : feed it 2, get 2^(primes) here and ther
#fracpc <-makefrac(c(17,78,19,23,29,77,95,77,1,11,13,15,1,55), c(91,85,51,38,33,29,23,19,17,13,11,2,7,1))

#Remember - if you run N 'tries' you can restart with the Nth output integer.




# first, a "set maker" which takes n,d vectors or matrix and makes the right bigq out of them .
fracMake <- function(n, d = NULL){
#notice that entering a vector 'n' and no 'd' will produce the sequence 1/n[1],2/n[2], ...
nd <-xy.coords(as.bigz(n),as.bigz(d))
return(invisible(as.bigq(nd$x,nd$y)))
}

# A Q&D logulator to pull numbers of interest out of the returned set of bigqs

fracAns <- function(intvec, logbase, logprec = 1e-5) {
logall <- log(numerator(intvec),logbase)
frist <- which(abs(logall - round(logall)) < logprec ) # use 1e-4 as a solid place
return(invisible(logall[frist]))

}

# 2) Figure out how to get it at least 10X faster.
# 1) Notice that this returns nothing (empty) if the value of 'input' is a winner.
# Change so first returned value is the input value
# Here's the fraccinator engine.
# 'tries' is default terminator so things don't go blooey
fracDo <-function(input, fractions=NULL, nums, denoms, tries = 100, stopFun=NULL, liveUpdate=TRUE , ...) {
input <- as.bigq(input) # for safety, do everything in bigq rather than bigz
if (length(fractions)) {
set <- fractions
if (!is.bigq(set)) stop('fraction set must be bigq')
} else {
set <-as.bigq(floor(nums), floor(denoms))
}
setlen <- length(set)
intvec = input # will store 'wins' here # but bigq(NULL) is weird.
itry = 1
while (itry <= tries) {
if (liveUpdate) {
if(!itry%%1000){
cat(' .')
# moved inside to reduce if-loading
if (!itry%%20000) {
cat('\n')
# flush.console()
}
flush.console()
} #, appendLF = FALSE)
}
idx = 1 # index for grabbing a fraction
gotit = 1 # set to zero if new integer found
while (gotit) {
tmpterm <- mul.bigq(input, set[idx])
if (denominator(tmpterm ) == 1){
intvec <- c(intvec,tmpterm)
# if termination desired, do it here
if(!is.null(stopFun)) {
foundit <- stopFun(tmpterm, ...) #must return a single logical
flush.console()
if (foundit){
return(invisible(intvec))
}
}
gotit <- 0
input <- tmpterm # start again with new integer
} else {
idx <- idx + 1
}
if (idx > setlen){
gotit = 0
itry <- tries + 1 # terminate because didn't find another int
}
} #end of gotit
itry = itry + 1 # work on new "input" integer
} # end of tries
if (liveUpdate) cat('\n') # just to get a newline
return(invisible(intvec ))
}

# Let's create a "stopFun" whose only job is to cat() a found prime without stopping the main function.
# TODO:
#2: see if simple unique(factorize) test works better than my goofball code
#1: modify to work with either the 10^prime or the 2^prime code.
# Logb x = Loga x/Loga b so log(x,mant) = log(x)/log(mant)
# this will require 'mantissa' to be in the '...' of fracDo .See if neater way

# cheap time test. catPrime:
# user system elapsed
# 64.613 0.712 65.737
# stopPrime
# user system elapsed
# 63.588 0.398 63.879

stopPrime <- function(x,mantissa,returnVal = 0) {
mantissa = round(mantissa) # safety
manfac <- factorize(mantissa)
facs <- factorize(numerator(x)) # x is a bigq
# cyclically remove sets of 'facs' which make up one manfac
if (prod(unique(manfac) == unique(facs)) ) {
# no extraneous values in factorization
manle <-rle(as.numeric(manfac))
numle <- rle(as.numeric(facs))
# see if all factors are proportional quantities
if (length(unique(manle$lengths/numle$lengths)) == 1) {
cat(c('found ',log(numerator(x) )/log(mantissa),'\n') )
flush.console()
}
}
return(invisible(returnVal)) # typically don't want to terminate fracDo
}

# catPrime <-function(x){
# # x will always be a bigq
# aa <- as.numeric(unlist(strsplit(as.character(numerator(x)),'')))
# if (aa[1] == 1 && sum(aa[2:length(aa)]) == 0) {
# cat(c('found ',length(aa)-1),'\n')
# flush.console()
# }
# return(invisible(0)) # Since I don't want to terminate fracDo
# }

Binary file added build/partial.rdb
Binary file not shown.
46 changes: 46 additions & 0 deletions man/Rfractran-package.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
\name{Rfractran-package}
\alias{Rfractran-package}
\alias{Rfractran}
\docType{package}
\title{
\packageTitle{Rfractran}
}
\description{
\packageDescription{Rfractran}
}
\details{
The DESCRIPTION file:
\packageDESCRIPTION{Rfractran}
\packageIndices{Rfractran}
FRACTRAN is an obscure yet tantalizing programming "language"
invented by John Conway of "Game of Life" fame. The code consists
of a sequence of fractions. The operation is simple.
1 - Initalize with an integer
2 - Multiply the integer by the first fraction. If an integer results,
start again with the new integer. If not, try the next fraction.
3 - If no such multiplication yields an integer, terminate the program.
One warning: there is a FRACTRAN program that can be found on the web
which is supposed to generate the digits of pi. Unfortunately, it's
known to have a bug which causes it not to work.
So far nobody has found a correction for it.
}
\author{
\packageAuthor{Rfractran}
Maintainer: \packageMaintainer{Rfractran}
}
\references{
\url{https://esolangs.org/wiki/Fractran}
\url{https://oeis.org/wiki/List_of_FRACTRAN_programs_to_compute_core_sequences}
}
\examples{
##Not Run
# addition: { 3/2 } . Enter 2^a*3^b and get 3^(a+b) answer when terminated
# multiplication:
# { 455/33, 11/13, 1/11, 3/7, 11/2, 1/3 }
# then enter 2^a*3^b Terminates with 5^(a*b)
# prime generator (This function never terminates.)
#start with 10, feed to [7/3 99/98 13/49 39/35 36/91 10/143 49/13 7/11 1/2 91/1]
# and whenever the result of a run is of the form 10^p , p is prime
}
44 changes: 44 additions & 0 deletions man/fracAns.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
\name{fracAns}
\alias{fracAns}

\title{
A Q&D "Logulator" to Find Numbers of Interest From \code{fracDo} Output
}
\description{
In nearly all cases FRACTRAN's "result" is the exponent of some number.
This function takes the logarithm of the specified base and
identifies values which are integers (or nearly so, to the specified precision).
}
\usage{
fracAns(intvec, logbase, logprec = 1e-05)
}
\arguments{
\item{intvec}{
A vector of \code{bigq} values, as returned from \code{fracDo}
}
\item{logbase}{
The base of the desired logarithm, e.g. 2 or 10 in many cases.
}
\item{logprec}{
A reasonably small value used to check whether the returned double should be considered to be an integer (thus ignoring binary precision errors)
}
}
\value{
A vector of the integer values found
}
\author{
Carl Witthoft <carl@witthoft.com>
}
\examples{
##---The prime generator doesn't terminate, so look for values.
#start with 10, and result includes a series of values 10^p , p prime
fracp10 <- fracMake(c(7,99,13,39,36,10,49,7,1,91), c(3,98,49,35,91,143,13,11,2,1))
p10 <-fracDo(10,fractions = fracp10, tries = 1000)
foundp10 <-fracAns(p10,logbase = 10)
# [1] 1 2 3 5 7 # sorry about the "1" :-)

}
88 changes: 88 additions & 0 deletions man/fracDo.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
\name{fracDo}
\alias{fracDo}
%- Also NEED an '\alias' for EACH other topic documented here.
\title{
The FRACTRAN Interpreter Function
}
\description{
This implements the FRACTRAN process, to wit
For the first fraction f in the list for which nf is an integer,
replace n by nf.
Repeat this rule until no fraction in the list produces
an integer when multiplied by n, then halt.
}
\usage{
fracDo(input, fractions = NULL, nums, denoms, tries = 100,
stopFun = NULL, liveUpdate = TRUE, ...)
}
%- maybe also 'usage' for other objects documented here.
\arguments{
\item{input}{
The starting integer for the given FRACTRAN code (fraction sequence)
}
\item{fractions}{
The sequence of fractions. Must be in \code{bigq} form. If NULL, then
\code{nums} and \code{denoms} must be supplied.
}
\item{nums}{
Vector of integers (or \code{bigz} values ) representing the numerators
of the FRACTRAN fractions. If \code{fractions} is supplied, this is ignored.
}
\item{denoms}{
Vector of integers (or \code{bigz} values ) representing the denominators
of the FRACTRAN fractions. If \code{fractions} is supplied, this is ignored.
}
\item{tries}{
A 'safety' limiter on the number of times to process the next integer
generated. This avoids possible infinite runs or other time-wasters.
}
\item{stopFun}{
Optional user-supplied function that can be used to terminate a FRACTRAN
run early, or to take min-run actions such as sending information to
the console. See Details for more information.
}
\item{liveUpdate}{
If set to TRUE, a few dots and words are sent to the console to indicate
that the algorithm is still running.
}
\item{\dots}{
Possible additional arguments for \code{stopFun} or future use.
}
}
\details{
Some FRACTRAN programs do not terminate, most famously the prime generators.
If a specific value is being looked for, an appropriate \code{stopFun}
can be supplied to check for that value. \code{stopFun} must return a
single logical value (R, as always will convert numerics to logical if
necessary) indicating success as TRUE. The first argument
to \code{stopFun} must accept a single \code{bigq} value.
If there are more arguments, they must be entered after all named
arguments. Note that this function does not have to send TRUE; it could be
used solely to execute other commands mid-run.
See \code{\link[Rfractran]{stopPrime}} for one such example.

}
\value{
A vector of all the \code{bigq} values generated.
These all have denominator == 1, as they are the integers found in
each iteration of the FRACTRAN algorithm.
}
\references{
\url{https://esolangs.org/wiki/Fractran}
\url{https://oeis.org/wiki/List_of_FRACTRAN_programs_to_compute_core_sequences}
}
\author{
Carl Witthoft <carl@witthoft.com
}


\seealso{
\code{\link[Rfractran]{fracMake}}
}
\examples{
# addition: { 3/2 } . enter 2^a*3^b and get 3^(a+b) answer when terminated
addit <- fracDo(2^5*3^8,nums = 3, denoms = 2)
# Last value returned is what we want. But can't take log(bigq)
log(numerator(addit[length(addit)]),3)
}

32 changes: 32 additions & 0 deletions man/fracMake.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
\name{fracMake}
\alias{fracMake}
%- Also NEED an '\alias' for EACH other topic documented here.
\title{
Function to Create a Sequence of \code{bigq} Fractions
}
\description{
Feed this function a collection of numerator and denominator values;
get a vector of \code{bigq} fractions suitable for use as a FRACTRAN program.
}
\usage{
fracMake(n, d = NULL)
}
%- maybe also 'usage' for other objects documented here.
\arguments{
\item{n}{
Vector of integers to be used as numerators, or a Nx2 array of integers
where each row contains a num, denom pair. If this is an arry, the input
\code{d} is ignored.
}
\item{d}{
Vector of integers to be used as denominators. Ignored if \code{n} is an array
}
}

\value{
Vector of \code{bigq} fractions.
}

\author{
Carl Witthoft <carl@witthoft.com>
}

0 comments on commit 271ea36

Please sign in to comment.