"flag"
"fmt"
"log"
+ "os"
+ "runtime/pprof"
+ "sort"
+ "strconv"
)
// =============================================================================
}
func (u *surrealUniverse) getIndex(num surrealNumber) int {
- for i := 0; i < len(u.numbers); i++ {
- if num.identifier == u.numbers[i].identifier {
- return i
- }
- }
- return -1 // This should be unreachable.
+ // Since getIndex() is only ever called on 'num' which already exists in
+ // the universe, we return the result directly, without bothering to
+ // confirm an exact match.
+ //
+ // Also, we can get away with a direct comparison of these floating point
+ // numbers since there will be an exact match, guaranteed since 'num' is
+ // already in the universe.
+ return sort.Search(u.cardinality(), func(i int) bool {return u.numbers[i].value >= num.value})
}
func (u *surrealUniverse) cardinality() int {
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{
+ if num.leftSet.cardinality() == 1 {
index := u.getIndex(num.leftSet.members[0])
- num.identifier = u.nextUniqueID
- u.nextUniqueID++
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])
- num.identifier = u.nextUniqueID
- u.nextUniqueID++
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
}
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:]...)
type surrealNumber struct {
leftSet surrealSet
rightSet surrealSet
+ value float64
identifier uint
generation int
}
// =============================================================================
func main() {
+ // Flags to enable various performance profiling options.
+ cpuProfile := flag.String("cpuprofile", "", "Filename for saving CPU profile output.")
+ memProfilePrefix := flag.String("memprofileprefix", "", "Filename PREFIX 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()
}
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})
+ 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 reduced form symbols per Axiom 1.
potentialNumbers := permuteExistingNumbers(generation, universe)
// Now prune out any symbols which are NOT valid numbers per Axiom 2.
// Attempt to add the new numbers to the universe. Any duplicates will
// be weeded out in the attempt.
addNumbersToUniverse(validNumbers, &universe)
+
+ // Setup any memory profiling requested by the user. This will snapshot
+ // the heap at the end of every generation.
+ if *memProfilePrefix != "" {
+ memProFile, err := os.Create(*memProfilePrefix + "-gen" + strconv.Itoa(generation) + ".mprof")
+ if err != nil {
+ log.Fatal("ERROR: Unable to write heap profile to disk:", err)
+ }
+ pprof.WriteHeapProfile(memProFile)
+ memProFile.Close()
+ }
}
fmt.Printf(".\n")
+
// Print the number line with generation on the horizontal axis and
// magnitude on the vertical axis.
- for i := 0; i < universe.cardinality(); i++ {
- printlnNumber(universe.numbers[i])
+ 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.")
var leftSet, rightSet surrealSet
leftSet.insert(universe.numbers[i], universe)
rightSet.insert(universe.numbers[j], universe)
- newSurrealNumber := surrealNumber{leftSet,rightSet,0,generation}
+ newSurrealNumber := surrealNumber{leftSet,rightSet,0.0,0,generation}
numbers = append(numbers, newSurrealNumber)
}
}
for i := 0; i < universe.cardinality(); i++ {
var tempSet surrealSet
tempSet.insert(universe.numbers[i], universe)
- newSurrealNumber := surrealNumber{tempSet,surrealSet{},0,generation}
+ newSurrealNumber := surrealNumber{tempSet,surrealSet{},0.0,0,generation}
numbers = append(numbers, newSurrealNumber)
- newSurrealNumber = surrealNumber{surrealSet{},tempSet,0,generation}
+ newSurrealNumber = surrealNumber{surrealSet{},tempSet,0.0,0,generation}
numbers = append(numbers, newSurrealNumber)
}