- // Before we can insert a number into the universe, we must first determine
- // if it is newly discovered. We do this by looking at its ancestors, all
- // guaranteed to be on the number line by construction, finding three
- // cases:
- //
- // 1. There are zero ancestors.
- // We have rediscovered 0 and can abort the insertion unless creating
- // a brand new universe.
- //
- // 2. There is one ancestor.
- // If the number extends the number line, it is new, otherwise it will
- // be similar to a form with two ancestors and should be ignored since
- // the two ancestor form is a better visual representation.
- //
- // 3. There are two ancestors.
- // The number is only new if both ancestors are side-by-side on the
- // number line.
-
- ancestorCount := num.leftSet.cardinality() + num.rightSet.cardinality()
- switch ancestorCount {
- case 0:
- if u.cardinality() == 0 {
- num.value = 0.0
- num.identifier = u.nextUniqueID
- u.nextUniqueID++
- u.numbers = append(u.numbers, num)
- }
- case 1:
- if num.leftSet.cardinality() == 1 {
- index := u.getIndex(num.leftSet.members[0])
- if index+1 == u.cardinality() {
- num.value = u.numbers[u.cardinality()-1].value + 1.0
- num.identifier = u.nextUniqueID
- u.nextUniqueID++
- u.numbers = append(u.numbers, num)
- }
- } else {
- index := u.getIndex(num.rightSet.members[0])
- if index == 0 {
- num.value = u.numbers[0].value - 1.0
- num.identifier = u.nextUniqueID
- u.nextUniqueID++
- u.numbers = append(u.numbers[:1], u.numbers...)
- u.numbers[0] = num
- }
- }
- case 2:
- leftIndex := u.getIndex(num.leftSet.members[0])
- rightIndex := u.getIndex(num.rightSet.members[0])
- if leftIndex+1 == rightIndex {
- num.value = (num.leftSet.members[0].value + num.rightSet.members[0].value) / 2
- num.identifier = u.nextUniqueID
- u.nextUniqueID++
- u.numbers = append(u.numbers[:rightIndex+1], u.numbers[rightIndex:]...)
- u.numbers[rightIndex] = num
- }
- }
+ // Before we can insert a number into the universe, we must first determine
+ // if it is newly discovered. We do this by looking at its ancestors, all
+ // guaranteed to be on the number line by construction, finding three
+ // cases:
+ //
+ // 1. There are zero ancestors.
+ // We have rediscovered 0 and can abort the insertion unless creating
+ // a brand new universe.
+ //
+ // 2. There is one ancestor.
+ // If the number extends the number line, it is new, otherwise it will
+ // be similar to a form with two ancestors and should be ignored since
+ // the two ancestor form is a better visual representation.
+ //
+ // 3. There are two ancestors.
+ // The number is only new if both ancestors are side-by-side on the
+ // number line.
+
+ ancestorCount := num.leftSet.cardinality() + num.rightSet.cardinality()
+ switch ancestorCount {
+ case 0:
+ if u.cardinality() == 0 {
+ num.value = 0.0
+ num.identifier = u.nextUniqueID
+ u.nextUniqueID++
+ u.numbers = append(u.numbers, num)
+ }
+ case 1:
+ if num.leftSet.cardinality() == 1 {
+ index := u.getIndex(num.leftSet.members[0])
+ if index+1 == u.cardinality() {
+ num.value = u.numbers[u.cardinality()-1].value + 1.0
+ num.identifier = u.nextUniqueID
+ u.nextUniqueID++
+ u.numbers = append(u.numbers, num)
+ }
+ } else {
+ index := u.getIndex(num.rightSet.members[0])
+ if index == 0 {
+ num.value = u.numbers[0].value - 1.0
+ num.identifier = u.nextUniqueID
+ u.nextUniqueID++
+ u.numbers = append(u.numbers[:1], u.numbers...)
+ u.numbers[0] = num
+ }
+ }
+ case 2:
+ leftIndex := u.getIndex(num.leftSet.members[0])
+ rightIndex := u.getIndex(num.rightSet.members[0])
+ if leftIndex+1 == rightIndex {
+ num.value = (num.leftSet.members[0].value + num.rightSet.members[0].value) / 2
+ num.identifier = u.nextUniqueID
+ u.nextUniqueID++
+ u.numbers = append(u.numbers[:rightIndex+1], u.numbers[rightIndex:]...)
+ u.numbers[rightIndex] = num
+ }
+ }
- // By construction, no number in this program will ever have more than one
- // element in its right or left sets, nor will those elements be of a form
- // other than that 'registered' with the universe. That allows direct
- // comparisons using the surrealNumber.identifier field via u.getIndex().
-
- // ---------------------------------
-
- // First we implement the check "no member of the first number's left set
- // is greater than or equal to the second number".
- if numA.leftSet.cardinality() == 1 {
- if u.getIndex(numA.leftSet.members[0]) >= u.getIndex(numB) {
- return false
- }
- }
-
- // Now we implement the check "no member of the second number's right set
- // is less than or equal to the first number".
- if numB.rightSet.cardinality() == 1 {
- if u.getIndex(numB.rightSet.members[0]) <= u.getIndex(numA) {
- return false
- }
- }
-
- return true
+ // By construction, no number in this program will ever have more than one
+ // element in its right or left sets, nor will those elements be of a form
+ // other than that 'registered' with the universe. That allows direct
+ // comparisons using the surrealNumber.identifier field via u.getIndex().
+
+ // ---------------------------------
+
+ // First we implement the check "no member of the first number's left set
+ // is greater than or equal to the second number".
+ if numA.leftSet.cardinality() == 1 {
+ if u.getIndex(numA.leftSet.members[0]) >= u.getIndex(numB) {
+ return false
+ }
+ }
+
+ // Now we implement the check "no member of the second number's right set
+ // is less than or equal to the first number".
+ if numB.rightSet.cardinality() == 1 {
+ if u.getIndex(numB.rightSet.members[0]) <= u.getIndex(numA) {
+ return false
+ }
+ }
+
+ return true
- // Flags to enable various performance profiling options.
- cpuProfile := flag.String("cpuprofile", "", "Filename for saving CPU profile output.")
- memProfile := flag.String("memprofile", "", "Filename for saving memory profile output.")
- suppressOutput := flag.Bool("silent", false, "Suppress printing of numberline at end of simulation. Useful when profiling.")
-
- // Obtain termination conditions from user.
- totalGenerations := flag.Int("generations", 2, "Number of generations of surreal numbers to breed.")
- flag.Parse()
- if *totalGenerations < 1 {
- log.Fatal("ERROR: The argument to '-generations' must be greater than zero.")
- }
- remainingGenerations := *totalGenerations - 1
-
- // Setup any CPU performance profiling requested by the user. This will run
- // throughout program execution.
- if *cpuProfile != "" {
- cpuProFile, err := os.Create(*cpuProfile)
- if err != nil {
- log.Fatal("ERROR: Unable to open CPU profiling output file:", err)
- }
- pprof.StartCPUProfile(cpuProFile)
- defer pprof.StopCPUProfile()
- }
-
- // Build a universe to contain all the surreal numbers we breed.
- // Seed it by hand with the number zero as generation-0.
- var universe surrealUniverse
- universe.nextUniqueID = 0
- fmt.Println("Seeding generation 0 by hand.")
- universe.insert(surrealNumber{surrealSet{}, surrealSet{}, 0.0, 0, 0})
-
- // Breed however many generations of numbers were requested by the user and
- // add them all to the universe.
- fmt.Printf("Breeding Generation:")
- for generation := 1; generation <= remainingGenerations; generation++ {
- // Give the user some idea of overall progress during long jobs.
- if generation != 1 {
- fmt.Printf(",")
- }
- fmt.Printf(" %d", generation)
-
- // First generate all possible new numbers in this generation.
- potentiallyNewNumbers := permuteExistingNumbers(generation, universe)
- // Attempt to add the new numbers to the universe. Any duplicates will
- // be weeded out in the attempt.
- addNumbersToUniverse(potentiallyNewNumbers, &universe)
- }
- fmt.Printf(".\n")
-
- // Setup any memory profiling requested by the user. This will snapshot
- // the heap only at this point, after all numbers were generated.
- if *memProfile != "" {
- memProFile, err := os.Create(*memProfile)
- if err != nil {
- log.Fatal("ERROR: Unable to open heap profile output file:", err)
- }
- pprof.WriteHeapProfile(memProFile)
- memProFile.Close()
- }
-
- // Print the number line with generation on the horizontal axis and
- // magnitude on the vertical axis.
- if !(*suppressOutput) {
- for i := 0; i < universe.cardinality(); i++ {
- printlnNumber(universe.numbers[i])
- }
- }
- fmt.Println("After", *totalGenerations, "generations, the universe contains", len(universe.numbers), "numbers.")
- fmt.Println("If output looks poor, ensure tabstop is eight spaces and that output doesn't wrap.")
+ // Flags to enable various performance profiling options.
+ cpuProfile := flag.String("cpuprofile", "", "Filename for saving CPU profile output.")
+ memProfile := flag.String("memprofile", "", "Filename for saving memory profile output.")
+ suppressOutput := flag.Bool("silent", false, "Suppress printing of numberline at end of simulation. Useful when profiling.")
+
+ // Obtain termination conditions from user.
+ totalGenerations := flag.Int("generations", 2, "Number of generations of surreal numbers to breed.")
+ flag.Parse()
+ if *totalGenerations < 1 {
+ log.Fatal("ERROR: The argument to '-generations' must be greater than zero.")
+ }
+ remainingGenerations := *totalGenerations - 1
+
+ // Setup any CPU performance profiling requested by the user. This will run
+ // throughout program execution.
+ if *cpuProfile != "" {
+ cpuProFile, err := os.Create(*cpuProfile)
+ if err != nil {
+ log.Fatal("ERROR: Unable to open CPU profiling output file:", err)
+ }
+ pprof.StartCPUProfile(cpuProFile)
+ defer pprof.StopCPUProfile()
+ }
+
+ // Build a universe to contain all the surreal numbers we breed.
+ // Seed it by hand with the number zero as generation-0.
+ var universe surrealUniverse
+ universe.nextUniqueID = 0
+ fmt.Println("Seeding generation 0 by hand.")
+ universe.insert(surrealNumber{surrealSet{}, surrealSet{}, 0.0, 0, 0})
+
+ // Breed however many generations of numbers were requested by the user and
+ // add them all to the universe.
+ fmt.Printf("Breeding Generation:")
+ for generation := 1; generation <= remainingGenerations; generation++ {
+ // Give the user some idea of overall progress during long jobs.
+ if generation != 1 {
+ fmt.Printf(",")
+ }
+ fmt.Printf(" %d", generation)
+
+ // First generate all possible new numbers in this generation.
+ potentiallyNewNumbers := permuteExistingNumbers(generation, universe)
+ // Attempt to add the new numbers to the universe. Any duplicates will
+ // be weeded out in the attempt.
+ addNumbersToUniverse(potentiallyNewNumbers, &universe)
+ }
+ fmt.Printf(".\n")
+
+ // Setup any memory profiling requested by the user. This will snapshot
+ // the heap only at this point, after all numbers were generated.
+ if *memProfile != "" {
+ memProFile, err := os.Create(*memProfile)
+ if err != nil {
+ log.Fatal("ERROR: Unable to open heap profile output file:", err)
+ }
+ pprof.WriteHeapProfile(memProFile)
+ memProFile.Close()
+ }
+
+ // Print the number line with generation on the horizontal axis and
+ // magnitude on the vertical axis.
+ if !(*suppressOutput) {
+ for i := 0; i < universe.cardinality(); i++ {
+ printlnNumber(universe.numbers[i])
+ }
+ }
+ fmt.Println("After", *totalGenerations, "generations, the universe contains", len(universe.numbers), "numbers.")
+ fmt.Println("If output looks poor, ensure tabstop is eight spaces and that output doesn't wrap.")
- // Profiling indicates that most calls to growSlice() and memMove()
- // originate in this function. Allocating a sufficiently large slice
- // upfront results in 2x speed improvement and 1/3 reduction in heap usage.
- numberOfPermutations := ((universe.cardinality()+1) * (universe.cardinality()+1)) - 1
- numbers := make([]surrealNumber, 0, numberOfPermutations)
-
- // First build permutations with 1 element in each set.
- for i := 0; i < universe.cardinality(); i++ {
- for j := 0; j < universe.cardinality(); j++ {
- var leftSet, rightSet surrealSet
- leftSet.insert(universe.numbers[i], universe)
- rightSet.insert(universe.numbers[j], universe)
- newSurrealNumber := surrealNumber{leftSet,rightSet,0.0,0,generation}
- if isValidSurrealNumber(newSurrealNumber, universe) {
- numbers = append(numbers, newSurrealNumber)
- }
- }
- }
- // Now build permutations with one empty set and one 1-element set.
- for i := 0; i < universe.cardinality(); i++ {
- var tempSet surrealSet
- tempSet.insert(universe.numbers[i], universe)
- newSurrealNumber := surrealNumber{tempSet,surrealSet{},0.0,0,generation}
- numbers = append(numbers, newSurrealNumber)
- newSurrealNumber = surrealNumber{surrealSet{},tempSet,0.0,0,generation}
- numbers = append(numbers, newSurrealNumber)
- }
-
- return numbers
+ // Profiling indicates that most calls to growSlice() and memMove()
+ // originate in this function. Allocating a sufficiently large slice
+ // upfront results in 2x speed improvement and 1/3 reduction in heap usage.
+ numberOfPermutations := ((universe.cardinality() + 1) * (universe.cardinality() + 1)) - 1
+ numbers := make([]surrealNumber, 0, numberOfPermutations)
+
+ // First build permutations with 1 element in each set.
+ for i := 0; i < universe.cardinality(); i++ {
+ for j := 0; j < universe.cardinality(); j++ {
+ var leftSet, rightSet surrealSet
+ leftSet.insert(universe.numbers[i], universe)
+ rightSet.insert(universe.numbers[j], universe)
+ newSurrealNumber := surrealNumber{leftSet, rightSet, 0.0, 0, generation}
+ if isValidSurrealNumber(newSurrealNumber, universe) {
+ numbers = append(numbers, newSurrealNumber)
+ }
+ }
+ }
+ // Now build permutations with one empty set and one 1-element set.
+ for i := 0; i < universe.cardinality(); i++ {
+ var tempSet surrealSet
+ tempSet.insert(universe.numbers[i], universe)
+ newSurrealNumber := surrealNumber{tempSet, surrealSet{}, 0.0, 0, generation}
+ numbers = append(numbers, newSurrealNumber)
+ newSurrealNumber = surrealNumber{surrealSet{}, tempSet, 0.0, 0, generation}
+ numbers = append(numbers, newSurrealNumber)
+ }
+
+ return numbers
- // For alignment reasons, we never print more than three digits. Thus, any
- // number greater than (26^3)-1 is out of range.
- if num >= 26*26*26 {
- base26String = []byte("xxx")
- } else {
- var firstByte, secondByte, thirdByte byte
- thirdByte = byte((num % 26) + uint('a'));
- num = num / 26;
- secondByte = byte((num % 26) + uint('a'));
- num = num / 26;
- firstByte = byte((num % 26) + uint('a'));
- num = num / 26;
- base26String = append(base26String, firstByte)
- base26String = append(base26String, secondByte)
- base26String = append(base26String, thirdByte)
- }
- return base26String
+ // For alignment reasons, we never print more than three digits. Thus, any
+ // number greater than (26^3)-1 is out of range.
+ if num >= 26*26*26 {
+ base26String = []byte("xxx")
+ } else {
+ var firstByte, secondByte, thirdByte byte
+ thirdByte = byte((num % 26) + uint('a'))
+ num = num / 26
+ secondByte = byte((num % 26) + uint('a'))
+ num = num / 26
+ firstByte = byte((num % 26) + uint('a'))
+ num = num / 26
+ base26String = append(base26String, firstByte)
+ base26String = append(base26String, secondByte)
+ base26String = append(base26String, thirdByte)
+ }
+ return base26String