Here are my notes and links to useful resources on surreals.

Please don’t use this to do any sums for safety critical systems ;-)

This is a helper function:

is_integer <- \(x) x == as.integer(x)
is_integer(42)
## [1] TRUE
is_integer(pi)
## [1] FALSE

I represent surreals as lists. This is another helper to make them and apply the class “surreal”.

as.surreal <- function(l) {
  res <- l
  
  if(is.null(res) || length(l) == 0)
    res <- nullset
  
  class(res) <- "surreal"
  res
}

Rather than sets of surreals, I’m using lists and ignoring duplicates and order in the output…

as.surreal_list <- function(sl) {
  res <- sl
  class(res) <- "surreal_list"
  res
}

Some useful predicates:

is.surreal <- function(s)
  class(s) == "surreal"

is.surreal_list <- function(sl)
  class(sl) == "surreal_list"

is.nullset <- function(s) {
  is.surreal(s) && length(s) == 0
}

And the main way to construct surreals and lists of surreals:

surreal <- function(l, r) {
  list(l = l,
       r = r) |> as.surreal()
}

surreals <- function(...) {
  res <- list(...) |> unique()
  class(res) <- "surreal_list"
  res
}

How to peek into the left or right pair of a surreal. Note that if there’s nothing there to peek at, I just return an empty set.

L <- function(s) {
  stopifnot(class(s) == "surreal")
  
  if (is.null(s) || length(s) == 0)
    nullset
  else
    s$l
}

R <- function(s) {
  stopifnot(class(s) == "surreal")
  
  if (is.null(s) || length(s) == 0)
    nullset
  else
    s$r
}

And the empty set. Well, list.

nullset <- list()
class(nullset) <- "surreal"

This seems to work… At present it just removes duplicates and nullsets. It could also go through and remove elements in L that are smaller than the largest surreal and elements of R that are larger than the smallest surreal.

simplify_surreals <- function(s) {
  if (is.surreal(s)) {
    if (is.nullset(s))
      nullset
    else
      surreal(simplify_surreals(s |> L()),
              simplify_surreals(s |> R()))
  }
  else if (is.surreal_list(s)) {
    if (length(s) == 0)
      nullset
    else if (length(s) == 1)
      simplify_surreals(s[[1]])
    else {
      # find nulls and zap them
      res <- c()
      for (one_s in s) {
        one_res <- simplify_surreals(one_s)
        if (!is.nullset(one_res))
          res <- c(res, list(one_res))
      }
      res |> unique() |> as.surreal_list()
    }
  }
  else
    stop("No idea what to do with that, sorry")
}

This simplifies dyadic numbers, \(n/2^k\).

simplify_dyadic <- \(n, k) {
  stopifnot(is_integer(n))
  stopifnot(is_integer(k))
  stopifnot(k >= 0)
  
  while (k > 0 && n %% 2 == 0) {
    n <- n/2
    k <- k - 1
  }
  
  c(n, k)
}

This is Tøndering’s “dali” function that builds a surreal from a dyadic number.

# x = n/2^k
dali <- \(n, k, loud = FALSE) {
  stopifnot(is_integer(n))
  stopifnot(is_integer(k))
  stopifnot(k >= 0)
  
  if (loud) {
    cat("dali called with ")
    cat(n); cat("/"); cat(2^k)
  }
  
  n_old <- n
  k_old <- k
  
  simple <- simplify_dyadic(n,k)
  n <- simple[1]
  k <- simple[2]
  
  if (loud && (n != n_old || k != k_old)) {
    cat(" = "); cat(n); cat("/"); cat(2^k)
  }
  
  if (loud) {
    cat(" = "); cat(n/2^k); cat("\n")
  }
  
  res <- NA
  
  if (n == 0) {
    res <- list(l = nullset, r = nullset)
  }
  else if (k == 0) {
    if (n > 0)
      res <- list(l = dali(n - 1, 0), r = nullset)
    else
      res <- list(l = nullset, r = dali(n + 1, 0))      
  }
  else {
    res <- list(l = dali(n-1, k), r = dali(n+1, k))
  }
  
  class(res) <- "surreal"
  res
}

Print surreals:

print.surreal <- function(s) {
  stopifnot(class(s) == "surreal")
  stopifnot(length(s) %in% c(0,2))
  
  if (length(s) == 0) {
    cat("∅")
  }
  else if ((length(s) == 2) &&
           all(names(s) == c("l", "r"))) {
    cat("(")
    print(s |> L())
    cat(" | ")
    print(s |> R())
    cat(")")    
  }
  else
    stop("I don't know how to print that surreal")
}

And lists of surreals:

print.surreal_list <- function(surs) {
  stopifnot(class(surs) == "surreal_list")
  
  if (length(surs) == 0)
    cat("∅")
  else {
    i <- 1
    for (s in surs) {
      print(s)
      if (i < length(surs))
        cat(", ")
      i <- i+1
    }
  }
}

(In)equalities:

less_equal <- function(x, y) {
  greater_equal(y, x)
}

greater_equal <- function(x, y) {
  if (is.nullset(x) || is.nullset(y))
    FALSE # BEWARE: I don't know why this isn't TRUE...
          # but it seems to work as is 
  else {
    res <- c() 

    if (is.surreal(x |> R()))
      res <- c(res, less_equal(x |> R(), y))
    else
      for (x_R in x |> R())
        res <- c(res, less_equal(x_R, y))
    
    if (is.surreal(y |> L()))
      res <- c(res, less_equal(x, y |> L()))
    else    
      for (y_L in y |> L())
        res <- c(res, less_equal(x, y_L))

    !any(res)
  }
}
equal <- function(x,y)
  greater_equal(x,y) && less_equal(x,y)

less <- function(x,y)
  less_equal(x,y) && !equal(x,y)

greater <- function(x,y)
  greater_equal(x,y) && !equal(x,y)

Given all the above, addition is relatively painless. You will see I haven’t done multiplication yet.

This can be improved very quickly by removing duplicates (TODO). Also it doesn’t work with sets as inputs. It’s fine for adding dalis.

add <- function(a, b) {
  if (length(a) == 0 || length(b) == 0) {
    nullset
  }
  else {
    surreal(l = surreals(add(a |> L(), b),
                         add(a, b |> L())),
            r = surreals(add(a |> R(), b),
                         add(a, b |> R())))
  }
}

And some examples:

add(dali(0,0), dali(0,0))
## (∅ | ∅)
add(dali(1,0), dali(1,0)) |> simplify_surreals()
## (((∅ | ∅) | ∅) | ∅)
add(dali(1,0), dali(1,1))
## ((∅, (∅ | ∅) | ∅, (∅, (∅ | ∅) | ∅)), ((∅ | ∅), ∅ | ∅) | ∅, ((∅, (∅ | ∅) | ∅), ((∅ | ∅), ∅ | ∅) | ∅))
add(dali(1,0), dali(1,1)) |> simplify_surreals()
## (((∅ | ∅) | ((∅ | ∅) | ∅)), ((∅ | ∅) | ∅) | (((∅ | ∅) | ∅) | ∅))
dali(1,0) # 1
## ((∅ | ∅) | ∅)
dali(2,0) # 2
## (((∅ | ∅) | ∅) | ∅)
# 1+1 = 2
add(dali(1,0), dali(1,0)) |> simplify_surreals()
## (((∅ | ∅) | ∅) | ∅)

Hurrah, so 1+1 = 2. Great.

add(dali(2,0), dali(-2,0)) |> simplify_surreals()
## (((∅ | (∅ | (∅ | ∅))) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) | (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ∅) | ∅)))

That mess above is equal to zero.

equal(add(dali(2,0), dali(-2,0)), dali(0,0))
## [1] TRUE

It’s easy to see as left and right are mirror images of each other.

(((∅ | (∅ | (∅ | ∅))) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) | (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ∅) | ∅)))

raw_sum <- add(dali(2,2), dali(2,2))
simp_sum <- raw_sum |> simplify_surreals()
correct_ans <- dali(1,0)
raw_sum
## ((∅, (∅ | ∅) | ∅, (∅, (∅ | ∅) | ∅)), ((∅ | ∅), ∅ | ((∅ | ∅), ∅ | ∅), ∅) | ((∅, (∅ | ∅) | ∅, (∅, (∅ | ∅) | ∅)), ((∅ | ∅), ∅ | ∅) | ∅, ((∅, (∅ | ∅) | ∅), ((∅ | ∅), ∅ | ∅) | ∅)), ((∅, (∅ | ∅) | ∅), ((∅ | ∅), ∅ | ((∅ | ∅), ∅ | ∅), ∅) | ((∅, (∅ | ∅) | ∅), ((∅ | ∅), ∅ | ∅) | ∅), ∅))
simp_sum
## (((∅ | ∅) | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ((∅ | ∅) | ∅)), ((∅ | ∅) | ∅) | (((∅ | ∅) | ∅) | ∅)), (((∅ | ∅) | ∅), ((∅ | ∅) | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ∅) | ∅)))
correct_ans
## ((∅ | ∅) | ∅)
equal(raw_sum, simp_sum)
## [1] TRUE
equal(simp_sum, correct_ans)
## [1] TRUE

Let’s check whether a putative surreal really is:

legit_surreal <- function(s) {
  !greater_equal(s |> L(), s |> R())
}

YES:

legit_surreal(dali(0,1))
## [1] TRUE

NOPE:

legit_surreal(surreal(dali(0,1), dali(0,1)))
## [1] FALSE
nullset
## ∅

Try building surreals systematically

new_surreals <- function(old_list) {
  cat("Starting with: ")
  print(old_list)
  cat("\n")
  res <- list()
  
  checked <- 0
  legit   <- 0
  
  for (l in old_list)
    for (r in old_list) {
      candidate <- surreal(l, r)
      
      # There must be an easier way that works!
      deja_vu <- (length(c(old_list, list(candidate)) 
                         |> unique()) == length(old_list))
      
      if (!deja_vu) {
        cat("Checking: ")
        print(candidate)
        checked <- checked + 1
  
        if (legit_surreal(candidate)) {
          res <- c(res, list(candidate))
          cat(" - Yes\n")
          legit <- legit + 1
        }
        else
          cat(" - No\n")
      }
    }
  cat("\n")
  cat(legit)
  cat("/")
  cat(checked)
  cat(" were valid\n")
  
  res |> as.surreal_list()
}
cat("Let's make some surreal numbers!\n\n")
## Let's make some surreal numbers!
last_day = 3
so_far <- surreals(nullset)

for (i in 1:(last_day+1)) {
  cat("Day ")
  cat(i-1)
  cat("\n")
  so_far <- c(so_far, new_surreals(so_far)) |> unique() |>
    as.surreal_list()
  cat("\n")
}
## Day 0
## Starting with: ∅
## Checking: (∅ | ∅) - Yes
## 
## 1/1 were valid
## 
## Day 1
## Starting with: ∅, (∅ | ∅)
## Checking: (∅ | (∅ | ∅)) - Yes
## Checking: ((∅ | ∅) | ∅) - Yes
## Checking: ((∅ | ∅) | (∅ | ∅)) - No
## 
## 2/3 were valid
## 
## Day 2
## Starting with: ∅, (∅ | ∅), (∅ | (∅ | ∅)), ((∅ | ∅) | ∅)
## Checking: (∅ | (∅ | (∅ | ∅))) - Yes
## Checking: (∅ | ((∅ | ∅) | ∅)) - Yes
## Checking: ((∅ | ∅) | (∅ | ∅)) - No
## Checking: ((∅ | ∅) | (∅ | (∅ | ∅))) - No
## Checking: ((∅ | ∅) | ((∅ | ∅) | ∅)) - Yes
## Checking: ((∅ | (∅ | ∅)) | ∅) - Yes
## Checking: ((∅ | (∅ | ∅)) | (∅ | ∅)) - Yes
## Checking: ((∅ | (∅ | ∅)) | (∅ | (∅ | ∅))) - No
## Checking: ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) - Yes
## Checking: (((∅ | ∅) | ∅) | ∅) - Yes
## Checking: (((∅ | ∅) | ∅) | (∅ | ∅)) - No
## Checking: (((∅ | ∅) | ∅) | (∅ | (∅ | ∅))) - No
## Checking: (((∅ | ∅) | ∅) | ((∅ | ∅) | ∅)) - No
## 
## 7/13 were valid
## 
## Day 3
## Starting with: ∅, (∅ | ∅), (∅ | (∅ | ∅)), ((∅ | ∅) | ∅), (∅ | (∅ | (∅ | ∅))), (∅ | ((∅ | ∅) | ∅)), ((∅ | ∅) | ((∅ | ∅) | ∅)), ((∅ | (∅ | ∅)) | ∅), ((∅ | (∅ | ∅)) | (∅ | ∅)), ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)), (((∅ | ∅) | ∅) | ∅)
## Checking: (∅ | (∅ | (∅ | (∅ | ∅)))) - Yes
## Checking: (∅ | (∅ | ((∅ | ∅) | ∅))) - Yes
## Checking: (∅ | ((∅ | ∅) | ((∅ | ∅) | ∅))) - Yes
## Checking: (∅ | ((∅ | (∅ | ∅)) | ∅)) - Yes
## Checking: (∅ | ((∅ | (∅ | ∅)) | (∅ | ∅))) - Yes
## Checking: (∅ | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - Yes
## Checking: (∅ | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: ((∅ | ∅) | (∅ | ∅)) - No
## Checking: ((∅ | ∅) | (∅ | (∅ | ∅))) - No
## Checking: ((∅ | ∅) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: ((∅ | ∅) | (∅ | ((∅ | ∅) | ∅))) - No
## Checking: ((∅ | ∅) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - Yes
## Checking: ((∅ | ∅) | ((∅ | (∅ | ∅)) | ∅)) - No
## Checking: ((∅ | ∅) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - No
## Checking: ((∅ | ∅) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - No
## Checking: ((∅ | ∅) | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: ((∅ | (∅ | ∅)) | (∅ | (∅ | ∅))) - No
## Checking: ((∅ | (∅ | ∅)) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: ((∅ | (∅ | ∅)) | (∅ | ((∅ | ∅) | ∅))) - Yes
## Checking: ((∅ | (∅ | ∅)) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - Yes
## Checking: ((∅ | (∅ | ∅)) | ((∅ | (∅ | ∅)) | ∅)) - Yes
## Checking: ((∅ | (∅ | ∅)) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - Yes
## Checking: ((∅ | (∅ | ∅)) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - Yes
## Checking: ((∅ | (∅ | ∅)) | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: (((∅ | ∅) | ∅) | (∅ | ∅)) - No
## Checking: (((∅ | ∅) | ∅) | (∅ | (∅ | ∅))) - No
## Checking: (((∅ | ∅) | ∅) | ((∅ | ∅) | ∅)) - No
## Checking: (((∅ | ∅) | ∅) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: (((∅ | ∅) | ∅) | (∅ | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | ∅) | ∅) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | ∅) | ∅) | ((∅ | (∅ | ∅)) | ∅)) - No
## Checking: (((∅ | ∅) | ∅) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - No
## Checking: (((∅ | ∅) | ∅) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | ∅) | ∅) | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | ∅) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | (∅ | ∅)) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | (∅ | (∅ | ∅))) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | ((∅ | ∅) | ∅)) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: ((∅ | (∅ | (∅ | ∅))) | (∅ | ((∅ | ∅) | ∅))) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | ((∅ | (∅ | ∅)) | ∅)) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - Yes
## Checking: ((∅ | (∅ | (∅ | ∅))) | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: ((∅ | ((∅ | ∅) | ∅)) | ∅) - Yes
## Checking: ((∅ | ((∅ | ∅) | ∅)) | (∅ | ∅)) - No
## Checking: ((∅ | ((∅ | ∅) | ∅)) | (∅ | (∅ | ∅))) - No
## Checking: ((∅ | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ∅)) - Yes
## Checking: ((∅ | ((∅ | ∅) | ∅)) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: ((∅ | ((∅ | ∅) | ∅)) | (∅ | ((∅ | ∅) | ∅))) - No
## Checking: ((∅ | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - Yes
## Checking: ((∅ | ((∅ | ∅) | ∅)) | ((∅ | (∅ | ∅)) | ∅)) - No
## Checking: ((∅ | ((∅ | ∅) | ∅)) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - No
## Checking: ((∅ | ((∅ | ∅) | ∅)) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - No
## Checking: ((∅ | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | ∅) - Yes
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | (∅ | ∅)) - No
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | (∅ | (∅ | ∅))) - No
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ∅)) - Yes
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | (∅ | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | ((∅ | (∅ | ∅)) | ∅)) - No
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - No
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | ∅) | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: (((∅ | (∅ | ∅)) | ∅) | ∅) - Yes
## Checking: (((∅ | (∅ | ∅)) | ∅) | (∅ | ∅)) - No
## Checking: (((∅ | (∅ | ∅)) | ∅) | (∅ | (∅ | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | ∅) | ((∅ | ∅) | ∅)) - Yes
## Checking: (((∅ | (∅ | ∅)) | ∅) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: (((∅ | (∅ | ∅)) | ∅) | (∅ | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | ∅) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - Yes
## Checking: (((∅ | (∅ | ∅)) | ∅) | ((∅ | (∅ | ∅)) | ∅)) - No
## Checking: (((∅ | (∅ | ∅)) | ∅) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | ∅) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | ∅) | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | ∅) - Yes
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | (∅ | ∅)) - Yes
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | (∅ | (∅ | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | ((∅ | ∅) | ∅)) - Yes
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | (∅ | ((∅ | ∅) | ∅))) - Yes
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - Yes
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | ((∅ | (∅ | ∅)) | ∅)) - Yes
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - Yes
## Checking: (((∅ | (∅ | ∅)) | (∅ | ∅)) | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | ∅) - Yes
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | (∅ | ∅)) - No
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | (∅ | (∅ | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ∅)) - Yes
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | (∅ | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - Yes
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | ((∅ | (∅ | ∅)) | ∅)) - No
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - No
## Checking: (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ∅) | ∅)) - Yes
## Checking: ((((∅ | ∅) | ∅) | ∅) | ∅) - Yes
## Checking: ((((∅ | ∅) | ∅) | ∅) | (∅ | ∅)) - No
## Checking: ((((∅ | ∅) | ∅) | ∅) | (∅ | (∅ | ∅))) - No
## Checking: ((((∅ | ∅) | ∅) | ∅) | ((∅ | ∅) | ∅)) - No
## Checking: ((((∅ | ∅) | ∅) | ∅) | (∅ | (∅ | (∅ | ∅)))) - No
## Checking: ((((∅ | ∅) | ∅) | ∅) | (∅ | ((∅ | ∅) | ∅))) - No
## Checking: ((((∅ | ∅) | ∅) | ∅) | ((∅ | ∅) | ((∅ | ∅) | ∅))) - No
## Checking: ((((∅ | ∅) | ∅) | ∅) | ((∅ | (∅ | ∅)) | ∅)) - No
## Checking: ((((∅ | ∅) | ∅) | ∅) | ((∅ | (∅ | ∅)) | (∅ | ∅))) - No
## Checking: ((((∅ | ∅) | ∅) | ∅) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))) - No
## Checking: ((((∅ | ∅) | ∅) | ∅) | (((∅ | ∅) | ∅) | ∅)) - No
## 
## 50/111 were valid
cat("By the end of day ")
## By the end of day
cat(last_day)
## 3
cat(", we got the following surreals:\n")
## , we got the following surreals:
for (s in so_far) {
  print(s)
  cat("\n")
}
## ∅
## (∅ | ∅)
## (∅ | (∅ | ∅))
## ((∅ | ∅) | ∅)
## (∅ | (∅ | (∅ | ∅)))
## (∅ | ((∅ | ∅) | ∅))
## ((∅ | ∅) | ((∅ | ∅) | ∅))
## ((∅ | (∅ | ∅)) | ∅)
## ((∅ | (∅ | ∅)) | (∅ | ∅))
## ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅))
## (((∅ | ∅) | ∅) | ∅)
## (∅ | (∅ | (∅ | (∅ | ∅))))
## (∅ | (∅ | ((∅ | ∅) | ∅)))
## (∅ | ((∅ | ∅) | ((∅ | ∅) | ∅)))
## (∅ | ((∅ | (∅ | ∅)) | ∅))
## (∅ | ((∅ | (∅ | ∅)) | (∅ | ∅)))
## (∅ | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)))
## (∅ | (((∅ | ∅) | ∅) | ∅))
## ((∅ | ∅) | ((∅ | ∅) | ((∅ | ∅) | ∅)))
## ((∅ | ∅) | (((∅ | ∅) | ∅) | ∅))
## ((∅ | (∅ | ∅)) | (∅ | ((∅ | ∅) | ∅)))
## ((∅ | (∅ | ∅)) | ((∅ | ∅) | ((∅ | ∅) | ∅)))
## ((∅ | (∅ | ∅)) | ((∅ | (∅ | ∅)) | ∅))
## ((∅ | (∅ | ∅)) | ((∅ | (∅ | ∅)) | (∅ | ∅)))
## ((∅ | (∅ | ∅)) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)))
## ((∅ | (∅ | ∅)) | (((∅ | ∅) | ∅) | ∅))
## (((∅ | ∅) | ∅) | (((∅ | ∅) | ∅) | ∅))
## ((∅ | (∅ | (∅ | ∅))) | ∅)
## ((∅ | (∅ | (∅ | ∅))) | (∅ | ∅))
## ((∅ | (∅ | (∅ | ∅))) | (∅ | (∅ | ∅)))
## ((∅ | (∅ | (∅ | ∅))) | ((∅ | ∅) | ∅))
## ((∅ | (∅ | (∅ | ∅))) | (∅ | ((∅ | ∅) | ∅)))
## ((∅ | (∅ | (∅ | ∅))) | ((∅ | ∅) | ((∅ | ∅) | ∅)))
## ((∅ | (∅ | (∅ | ∅))) | ((∅ | (∅ | ∅)) | ∅))
## ((∅ | (∅ | (∅ | ∅))) | ((∅ | (∅ | ∅)) | (∅ | ∅)))
## ((∅ | (∅ | (∅ | ∅))) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)))
## ((∅ | (∅ | (∅ | ∅))) | (((∅ | ∅) | ∅) | ∅))
## ((∅ | ((∅ | ∅) | ∅)) | ∅)
## ((∅ | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ∅))
## ((∅ | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ((∅ | ∅) | ∅)))
## ((∅ | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ∅) | ∅))
## (((∅ | ∅) | ((∅ | ∅) | ∅)) | ∅)
## (((∅ | ∅) | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ∅))
## (((∅ | ∅) | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ∅) | ∅))
## (((∅ | (∅ | ∅)) | ∅) | ∅)
## (((∅ | (∅ | ∅)) | ∅) | ((∅ | ∅) | ∅))
## (((∅ | (∅ | ∅)) | ∅) | ((∅ | ∅) | ((∅ | ∅) | ∅)))
## (((∅ | (∅ | ∅)) | ∅) | (((∅ | ∅) | ∅) | ∅))
## (((∅ | (∅ | ∅)) | (∅ | ∅)) | ∅)
## (((∅ | (∅ | ∅)) | (∅ | ∅)) | (∅ | ∅))
## (((∅ | (∅ | ∅)) | (∅ | ∅)) | ((∅ | ∅) | ∅))
## (((∅ | (∅ | ∅)) | (∅ | ∅)) | (∅ | ((∅ | ∅) | ∅)))
## (((∅ | (∅ | ∅)) | (∅ | ∅)) | ((∅ | ∅) | ((∅ | ∅) | ∅)))
## (((∅ | (∅ | ∅)) | (∅ | ∅)) | ((∅ | (∅ | ∅)) | ∅))
## (((∅ | (∅ | ∅)) | (∅ | ∅)) | ((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)))
## (((∅ | (∅ | ∅)) | (∅ | ∅)) | (((∅ | ∅) | ∅) | ∅))
## (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | ∅)
## (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ∅))
## (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | ((∅ | ∅) | ((∅ | ∅) | ∅)))
## (((∅ | (∅ | ∅)) | ((∅ | ∅) | ∅)) | (((∅ | ∅) | ∅) | ∅))
## ((((∅ | ∅) | ∅) | ∅) | ∅)

This is fun and unexpected:

equal(surreal(dali(3,0), dali(17,0)), dali(10,0))
## [1] FALSE
equal(surreal(dali(3,0), dali(17,0)), dali(4,0))
## [1] TRUE