Commits (48)
**/*.pyc
**/*.pyo
pythlib/*.so
dd0e0p
from ._version import __version__
from .autocompile import load_rules, compile_rules, reset_tree, run_tests
#ifndef LIFELIB_VERSION /*
__version__=[x.replace('"', '') for x in '''
*/
#define LIFELIB_VERSION "ll1.65"
// '''.split() if ('ll' in x)][0]
#define LIFELIB_VERSION "ll2.0.0"
// '''.split() if ('ll' in x)][0][2:]
#endif
import os
import re
import sys
import os
import subprocess
from . import genera
__all__ = ['lifelib_dir', 'compile_rules', 'load_rules', 'reset_tree']
lifelib_dir = os.path.dirname(os.path.abspath(__file__))
cygwin_dirs = []
def get_cygwin_dir():
cygwin_possibilities = cygwin_dirs + ['C:\\cygwin64', 'C:\\cygwin']
for c in cygwin_possibilities:
if os.path.exists(c):
return c
raise ValueError("Cygwin directory unknown; please call lifelib.autocompile.add_cygdir('D:\\\\path\\\\to\\\\cygwin64')")
def get_local_bash():
if (os.name == 'nt'):
return [os.path.join(get_cygwin_dir(), 'bin', 'bash.exe'), os.path.join(lifelib_dir, 'cygbash.sh')]
else:
return ['bash', os.path.join(lifelib_dir, 'cygbash.sh')]
def get_compiler():
if (os.name == 'nt'):
return get_local_bash() + ['g++']
else:
return ['g++']
def run_tests():
print("Warning: if you are in a Jupyter notebook you will not see output until the tests complete.")
print("Use the terminal to view periodic progress...")
subprocess.check_call(get_local_bash() + ['tests/test_all.sh'])
print("Completed tests successfully.")
def compile_rules(*rules, **kwargs):
if (len(rules) == 0):
rules = ['b3s23']
if (len(rules) > 8):
raise ValueError("Each lifelib session may have up to 8 rules.")
soname = os.path.join(lifelib_dir, 'pythlib', 'lifelib_%s.so' % '_'.join(rules))
def get_script_path():
return os.path.dirname(os.path.realpath(sys.argv[0]))
force_compile = kwargs.get('force_compile', False)
if (force_compile or not os.path.exists(soname)):
try:
cwd = os.path.abspath(os.getcwd())
print("Generating code for rules %s..." % str(list(rules)))
generate_code(rules)
os.chdir(lifelib_dir)
print("Compiling lifelib shared object (this may take a while)...")
po = subprocess.Popen(get_compiler() + ["-std=c++11", "-march=native", "-O3",
"-Wall", "-Wextra", "-fPIC", "-shared", "-o", soname, "lifelib.cpp"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
g_stdout, g_stderr = po.communicate()
status = po.returncode
if (status != 0):
sys.stderr.write("Compiler stdout: %s\n" % g_stdout)
sys.stderr.write("Compiler stderr: %s\n" % g_stderr)
raise RuntimeError("g++ returned nonzero exit code %d" % status)
finally:
os.chdir(cwd)
reset_tree()
return soname
def load_rules(*rules, **kwargs):
soname = compile_rules(*rules, **kwargs)
from .pythlib import Session
use_indirection = (os.name == 'nt') or kwargs.get('force_indirect', False)
local_bash = get_local_bash() if use_indirection else None
local_python = kwargs.get('local_python', 'python')
return Session(soname, rules=rules, local_bash=local_bash, local_python=local_python)
def write_all_iterators(f, hist, rules, families):
......@@ -84,28 +178,39 @@ def write_all_iterators(f, hist, rules, families):
f.write('\n\n#endif\n\n')
def main(logic_directory='avxlife/lifelogic'):
def reset_tree(rule='b3s23'):
# Check command-line arguments:
rules = list(sys.argv)[1:]
if (len(rules) == 0):
print("Usage:")
print("python rule2asm.py b3s23 b38s23 g4b2s345")
exit(1)
if (len(rules) > 8):
print("lifelib supports a maximum of 8 different rules")
exit(1)
try:
cwd = os.path.abspath(os.getcwd())
generate_code([rule], clean_before=True)
except:
import warnings
warnings.warn("Could not reset tree -- check permissions?")
finally:
os.chdir(cwd)
def generate_code(rules, clean_before=False):
logic_directory = 'avxlife/lifelogic'
# Determine rule families from genera:
try:
families = [genera.rule_property(r, 'family') for r in rules]
except ImportError:
print("ImportError; current directory == %s" % os.getcwd())
raise
os.chdir(lifelib_dir)
if clean_before and os.path.exists(logic_directory):
import shutil
shutil.rmtree(logic_directory)
# Setup directories and import genera:
os.chdir(get_script_path())
import genera
if not os.path.exists(logic_directory):
os.makedirs(logic_directory)
os.chdir(logic_directory)
# Determine rule families from genera:
families = [genera.rule_property(r, 'family') for r in rules]
# Obtain integers describing valid mantissae:
def mant2int(m):
if hasattr(m, '__iter__'):
......@@ -160,7 +265,5 @@ def main(logic_directory='avxlife/lifelogic'):
genera.create_rule(r)
os.chdir(lifelib_dir)
if __name__ == '__main__':
main()
#include "ltl.h"
#include <iostream>
int main() {
uint8_t a[1024];
uint8_t b[256];
uint8_t c[256];
for (uint64_t i = 0; i < 1024; i++) { a[i] = ((i == 300) || (i == 303) || (i == 200)) ? 1 : 0; }
/*
apg::transpose_16x16_sse2(a, b, true);
for (uint64_t i = 0; i < 512; i++) { std::cout << ((int) a[i]) << " "; if (i % 16 == 15) { std::cout << std::endl; } }
std::cout << std::endl;
apg::inplace_cumsum_sse2(a);
for (uint64_t i = 0; i < 512; i++) { std::cout << ((int) a[i]) << " "; if (i % 16 == 15) { std::cout << std::endl; } }
std::cout << std::endl;
*/
// apg::convolve2d((uint64_t*) a, (uint64_t*) b, 3);
apg::ntsum2d((uint64_t*) a, (uint64_t*) b);
for (uint64_t i = 0; i < 256; i++) { std::cout << ((int) b[i]) << " "; if (i % 16 == 15) { std::cout << std::endl; } }
uint64_t x[4] = {0x0123456789abcdefull, 0, 0x0011223344556677ull, 0x8899aabbccddeeffull};
apg::bits2bytes(x, (uint64_t*) b);
apg::bits2bytes_sse2(x, (uint64_t*) c);
std::cout << std::endl;
for (uint64_t i = 0; i < 256; i++) { std::cout << ((int) b[i]) << " "; if (i % 16 == 15) { std::cout << std::endl; } }
std::cout << std::endl;
for (uint64_t i = 0; i < 256; i++) { std::cout << ((int) c[i]) << " "; if (i % 16 == 15) { std::cout << std::endl; } }
return 0;
}
#include "stdint.h"
#include <iostream>
int main() {
uint8_t a[64]; for (int i = 0; i < 64; i++) { a[i] = i; }
uint8_t b[64]; for (int i = 0; i < 64; i++) { b[i] = 0; }
uint8_t c[64]; for (int i = 0; i < 64; i++) { c[i] = 0; }
asm (
"vmovdqu (%0), %%ymm0 \n\t"
"vmovdqu 32(%0), %%xmm14 \n\t"
"vshufi32x4 $68, %%zmm14, %%zmm0, %%zmm0 \n\t"
"vmovdqu64 %%zmm0, (%1) \n\t"
"vshufi32x4 $78, %%zmm0, %%zmm0, %%zmm15 \n\t"
"vmovdqu %%ymm0, (%2) \n\t"
"vmovdqu %%xmm15, 32(%2) \n\t"
: /* no output operands -- implicitly volatile */
: "r" (a), "r" (b), "r" (c)
: "xmm0", "xmm14", "xmm15", "memory" );
for (int i = 0; i < 64; i++) { std::cout << ((int) a[i]) << " "; }
std::cout << std::endl;
for (int i = 0; i < 64; i++) { std::cout << ((int) b[i]) << " "; }
std::cout << std::endl;
for (int i = 0; i < 64; i++) { std::cout << ((int) c[i]) << " "; }
std::cout << std::endl;
uint32_t d[32]; for (int i = 0; i < 32; i++) { d[i] = i*i; }
uint32_t e[16] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
asm (
"vmovdqu64 (%0), %%zmm0 \n\t"
"vmovdqu64 64(%0), %%zmm1 \n\t"
"vmovdqu64 (%1), %%zmm2 \n\t"
"vpermi2d %%zmm1, %%zmm0, %%zmm2 \n\t"
"vmovdqu64 %%zmm2, (%1) \n\t"
: /* no output operands -- implicitly volatile */
: "r" (d), "r" (e)
: "xmm0", "xmm1", "xmm2", "memory" );
for (int i = 0; i < 16; i++) { std::cout << ((int) e[i]) << " "; }
std::cout << std::endl;
return 0;
}
#include "iterators_b3s23.h"
#include "iterators_b36s23.h"
#include <iostream>
int main() {
uint32_t d[32];
uint32_t h[32];
for (int i = 0; i < 32; i++) {
d[i] = 0; h[i] = 0;
}
d[15] = 31 << 14;
d[16] = 17 << 14;
std::cout << "--------" << std::endl;
// clock_t start_time = clock();
for (int k = 0; k < 10; k++) {
for (int i = 8; i < 24; i++) {
uint32_t x = d[i];
int j = 32;
while (j --> 0) {
if (1 & (x >> j)) {
std::cout << "*";
} else {
std::cout << ".";
}
}
std::cout << std::endl;
}
b3s23::iterate_var_sse2(8, d);
// std::cout << "Value: " << apg::iter8_sse2(d, h) << std::endl;
}
for (int i = 0; i < 32; i++) {
d[i] = 0; h[i] = 0;
}
d[14] = 14 << 14;
d[15] = 1 << 14;
d[16] = 1 << 14;
d[17] = 1 << 14;
std::cout << "--------" << std::endl;
// clock_t start_time = clock();
for (int k = 0; k < 10; k++) {
for (int i = 8; i < 24; i++) {
uint32_t x = d[i];
int j = 32;
while (j --> 0) {
if (1 & (x >> j)) {
std::cout << "*";
} else {
std::cout << ".";
}
}
std::cout << std::endl;
}
b36s23::iterate_var_sse2(8, d);
// std::cout << "Value: " << apg::iter8_sse2(d, h) << std::endl;
}
// clock_t end_time = clock();
// std::cout << "iter8 in " << ((double) (end_time - start_time) / CLOCKS_PER_SEC) << " us." << std::endl;
return 0;
}
#include <iostream>
#include <stdint.h>
#include "lifeperm.h"
#include "eors.h"
int main() {
// std::cout << "Best instruction set: " << apg::best_instruction_set() << std::endl;
std::cout << "CPU name: " << apg::cpu_name() << std::endl;
uint64_t a[16];
for (int i = 0; i < 16; i++) {
a[i] = 0x0807060504030201ull + (0x0808080808080808ull * i);
}
uint32_t b1[32];
uint32_t b2[32];
uint32_t b3[32];
uint64_t c1[4];
uint64_t c2[4];
uint64_t c3[4];
apg::z64_to_r32_sse2(a, b1);
apg::z64_to_r32_sse2(a, b2);
// apg::z64_to_r32_avx2(a, b3);
for (int i = 0; i < 32; i++) {
uint32_t x = b1[i];
uint32_t y = b2[i];
// uint32_t z = b3[i];
for (int j = 0; j < 4; j++) {
std::cout << (x & 0xff) << ' '; x = x >> 8;
std::cout << (y & 0xff) << ' '; y = y >> 8;
// std::cout << (z & 0xff) << ' '; z = z >> 8;
}
std::cout << std::endl;
}
apg::r32_centre_to_z64_sse4(b1, c1);
apg::r32_centre_to_z64_ssse3(b2, c2);
// apg::r32_centre_to_z64_avx2(b3, c3);
for (int i = 0; i < 4; i++) {
uint64_t x = c1[i];
uint64_t y = c2[i];
// uint64_t z = c3[i];
for (int j = 0; j < 8; j++) {
std::cout << (x & 0xff) << ' '; x = x >> 8;
std::cout << (y & 0xff) << ' '; y = y >> 8;
// std::cout << (z & 0xff) << ' '; z = z >> 8;
}
std::cout << std::endl;
}
uint8_t d[64];
for (int i = 0; i < 64; i++) { d[i] = i + 1; }
uint8_t e[64];
apg::transpose_bytes_sse2((uint64_t*) d, (uint64_t*) e);
for (int i = 0; i < 64; i++) {
std::cout << ((int) e[i]) << " ";
if (i % 8 == 7) { std::cout << std::endl; }
}
return 0;
}
#!/bin/bash
# This allows a pathless Cygwin bash to bootstrap its way into finding
# a compiler:
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
cd "$( dirname "${BASH_SOURCE[0]}" )"
"$@"
import os
import re
from importlib import import_module
from .genuslist import genus_list
......@@ -15,6 +16,7 @@ def obtain_genus(rulestring):
def genus_to_module(genus):
m = import_module('.' + genus, __name__)
return m
def rule_property(rulestring, attribute):
......
......@@ -367,7 +367,7 @@ class iwriter(iwriter_base):
ruleint += (bee[i] << i)
ruleint += (ess[i] << (i + 8))
print(("Rule integer: "+str(ruleint)+stars))
# print(("Rule integer: "+str(ruleint)+stars))
regnames = [10, 8, 9, 12, 1, 0]
opnames = ["and", "or", "andn", "nonsense", "xor"]
rident = None
......@@ -391,8 +391,7 @@ class iwriter(iwriter_base):
if rident is None:
raise ValueError("Error: unrecognised rule")
else:
print(("Rule circuit: ["+rident+"]"+stars))
# print(("Rule circuit: ["+rident+"]"+stars))
rchars = list(rident)
for i in range(len(rchars)):
......
......@@ -21,7 +21,7 @@
namespace apg {
const static uint32_t klowbits = 12;
const static uint32_t klowbits = 8;
template <typename K, typename I, typename V>
struct kiventry {
......
#include <iostream>
#include "hypertree.h"
int main() {
typedef apg::nicearray<uint64_t, 4> uint64x4;
typedef apg::nicearray<uint32_t, 4> uint32x4;
apg::hypertree<uint32_t, 4, double, uint64x4, double> htree;
uint32_t k = htree.make_leaf(uint64x4(5, 6, 7, 8));
std::cout << k << std::endl;
std::cout << htree.make_nonleaf(uint32x4(0, 0, 0, 0)) << std::endl;
std::cout << htree.make_nonleaf(uint32x4(0, 0, k, 0)) << std::endl;
std::cout << htree.make_nonleaf(uint32x4(k, k, k, 0)) << std::endl;
std::cout << htree.make_nonleaf(uint32x4(0, 0, k, 0)) << std::endl;
htree.gc_mark(apg::hypernode<uint32_t>(2, 1));
}
#include "pattern2.h"
#include "streamlife.h"
#include <new>
extern "C"
{
void* CreateLifetree(int maxmem, int nlayers) {
/*
* Since we don't know at compile-time how many layers our lifetree
* has, we have explicit template instantiations for powers of two
* and round up to the next one.
*
* Negative integers are used to denote 'special' algorithms such
* as streamlife.
*/
if (nlayers == -1) {
return new(std::nothrow) apg::streamtree<uint32_t, 1>(maxmem);
} else if (nlayers > 32) {
return new(std::nothrow) apg::lifetree<uint32_t, 64>(maxmem);
} else if (nlayers > 16) {
return new(std::nothrow) apg::lifetree<uint32_t, 32>(maxmem);
} else if (nlayers > 8) {
return new(std::nothrow) apg::lifetree<uint32_t, 16>(maxmem);
} else if (nlayers > 4) {
return new(std::nothrow) apg::lifetree<uint32_t, 8>(maxmem);
} else if (nlayers > 2) {
return new(std::nothrow) apg::lifetree<uint32_t, 4>(maxmem);
} else if (nlayers > 1) {
return new(std::nothrow) apg::lifetree<uint32_t, 2>(maxmem);
} else {
return new(std::nothrow) apg::lifetree<uint32_t, 1>(maxmem);
}
}
void DeleteLifetree(void *ptr, int nlayers) {
if (nlayers == -1) {
delete reinterpret_cast<apg::streamtree<uint32_t, 1>*>(ptr);
} else if (nlayers > 32) {
delete reinterpret_cast<apg::lifetree<uint32_t, 64>*>(ptr);
} else if (nlayers > 16) {
delete reinterpret_cast<apg::lifetree<uint32_t, 32>*>(ptr);
} else if (nlayers > 8) {
delete reinterpret_cast<apg::lifetree<uint32_t, 16>*>(ptr);
} else if (nlayers > 4) {
delete reinterpret_cast<apg::lifetree<uint32_t, 8>*>(ptr);
} else if (nlayers > 2) {
delete reinterpret_cast<apg::lifetree<uint32_t, 4>*>(ptr);
} else if (nlayers > 1) {
delete reinterpret_cast<apg::lifetree<uint32_t, 2>*>(ptr);
} else {
delete reinterpret_cast<apg::lifetree<uint32_t, 1>*>(ptr);
}
}
int GetDiameterOfPattern(void *ptr) {
auto ppat = reinterpret_cast<apg::pattern*>(ptr);
return ppat->logdiam();
}
void DeletePattern(void *ptr) {
delete reinterpret_cast<apg::pattern*>(ptr);
}
void SavePatternRLE(void *ptr, const char *filename, const char *header, const char *footer) {
std::cerr << "filename: '" << filename << "'\n";
std::cerr << "header: '" << header << "'\n";
std::cerr << "footer: '" << footer << "'\n";
std::cerr << "--------" << std::endl;
std::ofstream out(filename);
auto ppat = reinterpret_cast<apg::pattern*>(ptr);
out << header;
ppat->write_rle(out);
out << footer << std::endl;
}
void SavePatternMC(void *ptr, const char *filename, const char *header, const char *footer) {
std::cerr << "filename: '" << filename << "'\n";
std::cerr << "header: '" << header << "'\n";
std::cerr << "footer: '" << footer << "'\n";
std::cerr << "--------" << std::endl;
std::ofstream out(filename);
auto ppat = reinterpret_cast<apg::pattern*>(ptr);
auto lab = ppat->getlab();
lab->write_macrocell_header(out);
out << header;
lab->write_macrocell_headerless(out, ppat->gethnode(), ppat->getrule());
out << footer << std::endl;
}
void* BooleanPatternImmutable(void* pat1, void* pat2, int op) {
auto ppat1 = reinterpret_cast<apg::pattern*>(pat1);
auto ppat2 = reinterpret_cast<apg::pattern*>(pat2);
auto lab = ppat1->getlab();
return new(std::nothrow) apg::pattern(lab, lab->boolean_universe(ppat1->gethnode(), ppat1->coerce(*ppat2), op), ppat1->getrule());
}
void BooleanPatternMutable(void* pat1, void* pat2, int op) {
auto ppat1 = reinterpret_cast<apg::pattern*>(pat1);
auto ppat2 = reinterpret_cast<apg::pattern*>(pat2);
auto lab = ppat1->getlab();
ppat1->changehnode(lab->boolean_universe(ppat1->gethnode(), ppat1->coerce(*ppat2), op));
}
void* CreatePatternFromFile(void* lt, const char *filename) {
auto lab = reinterpret_cast<apg::lifetree_abstract<uint32_t>*>(lt);
return new(std::nothrow) apg::pattern(lab, std::string(filename));
}
void* CreateRectangle(void* lt, int x, int y, int width, int height, const char *rule) {
auto lab = reinterpret_cast<apg::lifetree_abstract<uint32_t>*>(lt);
auto hnode = lab->rectangle(x, y, width, height);
return new(std::nothrow) apg::pattern(lab, hnode, std::string(rule));
}
void* CreatePatternFromRLE(void* lt, const char *rle, const char *rule) {
auto lab = reinterpret_cast<apg::lifetree_abstract<uint32_t>*>(lt);
return new(std::nothrow) apg::pattern(lab, std::string(rle), std::string(rule));
}
void* AdvancePattern(void* pat, int numgens, uint64_t exponent) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return new(std::nothrow) apg::pattern(ppat->advance2(numgens, exponent));
}
void* GetSemisolidForPattern(void* pat, int flags) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
auto lab = ppat->getlab();
uint64_t depth = ppat->gethnode().depth;
return new(std::nothrow) apg::pattern(lab, lab->semisolid(depth, flags), ppat->getrule());
}
void* ShiftPattern(void* pat, int x, int y, uint64_t exponent) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return new(std::nothrow) apg::pattern(ppat->shift(x, y, exponent));
}
void* TransformPattern(void* pat, const char *tfm) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return new(std::nothrow) apg::pattern(ppat->transform(std::string(tfm), 0, 0));
}
void* MatchLive(void* pat, void* pat1) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
auto ppat1 = reinterpret_cast<apg::pattern*>(pat1);
return new(std::nothrow) apg::pattern(ppat->match(*ppat1));
}
void* MatchLiveAndDead(void* pat, void* pat1, void* pat0) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
auto ppat1 = reinterpret_cast<apg::pattern*>(pat1);
auto ppat0 = reinterpret_cast<apg::pattern*>(pat0);
return new(std::nothrow) apg::pattern(ppat->match(*ppat1, *ppat0));
}
void* FindPeriodOrAdvance(void* pat, int exponent) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
apg::pattern x = ppat->pdetect(1ull << exponent);
if (ppat->dt != 0) {
ppat->ascertain_period();
return nullptr;
} else {
return new(std::nothrow) apg::pattern(x);
}
}
int GetPopulationOfPattern(void* pat, int modprime) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return ppat->popcount(modprime);
}
void GetPatternBox(void* pat, int64_t* bbox) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
ppat->getrect(bbox);
}
void GetRuleOfPattern(void* pat, char* buffer) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
std::string s = ppat->getrule();
s.copy(buffer, 2048);
}
void GetApgcodeOfPattern(void* pat, char* buffer) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
std::string s = ppat->apgcode();
s.copy(buffer, 2048);
}
int64_t GetDXOfPattern(void* pat) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return ppat->dx;
}
int64_t GetDYOfPattern(void* pat) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return ppat->dy;
}
int64_t GetDTOfPattern(void* pat) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return ppat->dt;
}
uint64_t GetBeszelIndex(void* pat) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return ppat->gethnode().index;
}
uint64_t GetUlqomaIndex(void* pat) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return ppat->gethnode().index2;
}
bool PatternNonempty(void* pat) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return ppat->gethnode().nonempty();
}
bool PatternEquality(void* pat1, void* pat2) {
auto ppat1 = reinterpret_cast<apg::pattern*>(pat1);
auto ppat2 = reinterpret_cast<apg::pattern*>(pat2);
return ((*ppat1) == (*ppat2));
}
}
......@@ -16,6 +16,9 @@ namespace apg {
class lifetree_generic : public lifetree_abstract<I> {
public:
virtual ~lifetree_generic() {}
hypertree<I, 4, J, nicearray<uint64_t, 4*N>, J > htree;
using lifetree_abstract<I>::breach;
......@@ -109,6 +112,146 @@ namespace apg {
}
}
void getcells_recurse(hypernode<I> hnode, uint64_t x, uint64_t y, std::map<std::pair<uint64_t, uint64_t>, uint64_t> &cells) {
if (hnode.index == 0) {
return;
} else if (hnode.depth != 0) {
uint64_t semisize = 8ull << hnode.depth;
getcells_recurse(getchild(hnode, 0), x, y, cells);
getcells_recurse(getchild(hnode, 1), x + semisize, y, cells);
getcells_recurse(getchild(hnode, 2), x, y + semisize, cells);
getcells_recurse(getchild(hnode, 3), x + semisize, y + semisize, cells);
} else {
kiventry<nicearray<uint64_t, 4*N>, I, J >* pptr = ind2ptr_leaf(hnode.index);
uint64_t bitmask[4] = {0ull};
for (uint64_t i = 0; i < 4*N; i++) {
bitmask[i & 3] |= pptr->key.x[i];
}
for (uint64_t j = 0; j < 4; j++) {
if (bitmask[j] == 0) { continue; }
for (uint64_t yi = 0; yi < 8; yi++) {
for (uint64_t xi = 0; xi < 8; xi++) {
if (bitmask[j] & (1ull << (xi + (yi << 3)))) {
uint64_t c = 0;
for (unsigned int i = 0; i < N; i++) {
uint64_t rel = pptr->key.x[4*i + j];
rel = rel >> xi;
rel = rel >> (yi << 3);
c |= ((rel & 1) << i);
}
std::pair<uint64_t, uint64_t> loc(y + yi + ((j & 2) << 2), x + xi + ((j & 1) << 3));
cells.emplace(loc, c);
}
}
}
}
}
}
void printrun(std::ostream &outstream, uint64_t n, std::string c) {
if (n >= 2) {
outstream << n << c;
} else if (n == 1) {
outstream << c;
}
}
void printrun(std::ostream &outstream, uint64_t n, std::string c, int &linelength) {
std::ostringstream x;
printrun(x, n, c);
std::string xs = x.str();
if (linelength + xs.length() > 66) {
outstream << '\n';
linelength = 0;
}
outstream << xs;
linelength += xs.length();
}
std::string state2string(uint64_t c) {
if (c == 0) {
return ((N == 1) ? "b" : ".");
} else if (c == 1) {
return ((N == 1) ? "o" : "A");
} else {
uint64_t d = c - 1;
std::string a(1, (char) ('A' + (d % 24)));
d = d / 24;
while (d > 0) {
a = std::string(1, (char) ('p' + ((d + 10) % 11))) + a;
d = d / 11;
}
return a;
}
}
void write_rle(std::ostream &outstream, hypernode<I> hnode, std::string rule) {
if (hnode.index == 0) {
outstream << "x = 1, y = 1, rule = " << gollyrule(rule) << std::endl;
outstream << state2string(0) << "!" << std::endl;
} else {
int linelength = 0;
std::map<std::pair<uint64_t, uint64_t>, uint64_t> cells;
getcells_recurse(hnode, 0, 0, cells);
uint64_t minx = -1;
uint64_t miny = -1;
uint64_t maxx = 0;
uint64_t maxy = 0;
for (auto it = cells.begin(); it != cells.end(); ++it) {
uint64_t y = it->first.first;
uint64_t x = it->first.second;
maxx = (x > maxx) ? x : maxx;
maxy = (y > maxy) ? y : maxy;
minx = (x < minx) ? x : minx;
miny = (y < miny) ? y : miny;
}
outstream << "x = " << (1 + maxx - minx) << ", y = " << (1 + maxy - miny);
outstream << ", rule = " << gollyrule(rule) << std::endl;
uint64_t lastx = 0; uint64_t lasty = 0; uint64_t lastc = 0;
uint64_t runlength = 0;
for (auto it = cells.begin(); it != cells.end(); ++it) {
uint64_t y = it->first.first - miny;
uint64_t x = it->first.second - minx;
uint64_t c = it->second;
if ((y > lasty) || (x > lastx) || (c != lastc)) {
printrun(outstream, runlength, state2string(lastc), linelength);
if (y > lasty) {
printrun(outstream, y - lasty, "$", linelength);
lasty = y;
lastx = 0;
}
if (x > lastx) {
printrun(outstream, x - lastx, state2string(0), linelength);
}
runlength = 0;
}
runlength += 1;
lastx = x + 1;
lastc = c;
}
printrun(outstream, runlength, state2string(lastc), linelength);
outstream << "!" << std::endl;
}
}
uint64_t write_macrocell_recurse(std::ostream &outstream, hypernode<I> hnode,
std::map<uint64_t, uint64_t> *subleaf2int,
std::map<std::pair<I, uint32_t>, uint64_t> *hnode2int,
......@@ -144,8 +287,11 @@ namespace apg {
}
}
void write_macrocell(std::ostream &outstream, hypernode<I> hnode, std::string rule) {
void write_macrocell_header(std::ostream &outstream) {
outstream << "[M2] (lifelib " << LIFELIB_VERSION << ")" << std::endl;
}
void write_macrocell_headerless(std::ostream &outstream, hypernode<I> hnode, std::string rule) {
if (rule != "") { outstream << "#R " << gollyrule(rule) << std::endl; }
std::map<uint64_t, uint64_t> subleaf2int;
std::map<std::pair<I, uint32_t>, uint64_t> hnode2int;
......@@ -153,6 +299,11 @@ namespace apg {
write_macrocell_recurse(outstream, breach(hnode), &subleaf2int, &hnode2int, linenum);
}
void write_macrocell(std::ostream &outstream, hypernode<I> hnode, std::string rule) {
write_macrocell_header(outstream);
write_macrocell_headerless(outstream, hnode, rule);
}
hypernode<I> read_macrocell(std::istream &instream, std::map<uint64_t, uint64_t> *lmap, std::string &rule) {
/*
* Returns a hypernode representing the contents of a macrocell
......@@ -423,7 +574,7 @@ namespace apg {
return bound_recurse(breach(hnode), direction, memmap);
}
uint64_t z = (direction & 2) ? 0 : (16 << hnode.depth);
uint64_t z = (direction & 2) ? 0 : (16ull << hnode.depth);
auto it = memmap->find(std::make_pair(hnode.index, hnode.depth));
if (it != memmap->end()) {
......@@ -1299,6 +1450,23 @@ namespace apg {
}
I __attribute__ ((noinline)) leaf_iteration(kiventry<nicearray<I, 4>, I, J >* pptr, int rule, int history, uint64_t mantissa) {
// Set up the memory locations:
nicearray<uint64_t, 4*N> outleaf = {0ull};
uint64_t* inleafxs[4];
for (int i = 0; i < 4; i++) {
inleafxs[i] = ind2ptr_leaf(pptr->key.x[i])->key.x;
}
universal_leaf_iterator<N>(rule, history, mantissa, inleafxs, outleaf.x);
return make_leaf(outleaf);
}
hypernode<I> iterate_recurse1(hypernode<I> hnode, uint64_t mantissa, uint64_t exponent, int rule, int history) {
/*
* Given a 2^n-by-2^n square represented by a hypernode, return the
......@@ -1339,18 +1507,7 @@ namespace apg {
if (hnode.depth == 1) {
// Set up the memory locations:
nicearray<uint64_t, 4*N> outleaf = {0ull};
uint64_t* inleafxs[4];
for (int i = 0; i < 4; i++) {
inleafxs[i] = ind2ptr_leaf(pptr->key.x[i])->key.x;
}
universal_leaf_iterator<N>(rule, history, mantissa, inleafxs, outleaf.x);
I finalnode = make_leaf(outleaf);
I finalnode = leaf_iteration(pptr, rule, history, mantissa);
if (mantissa != 0) {
// Cache the result to save additional recomputation:
......
......@@ -52,6 +52,8 @@ namespace apg {
public:
virtual ~lifetree_abstract() {}
uint64_t gc_threshold;
virtual uint64_t newihandle(hypernode<I> hnode) = 0;
......@@ -67,6 +69,9 @@ namespace apg {
virtual bool threshold_gc(uint64_t threshold) = 0;
virtual uint64_t getcell_recurse(hypernode<I> hnode, uint64_t x, uint64_t y) = 0;
virtual void write_macrocell(std::ostream &outstream, hypernode<I> hnode, std::string rule) = 0;
virtual void write_macrocell_header(std::ostream &outstream) = 0;
virtual void write_macrocell_headerless(std::ostream &outstream, hypernode<I> hnode, std::string rule) = 0;
virtual void write_rle(std::ostream &outstream, hypernode<I> hnode, std::string rule) = 0;
virtual hypernode<I> _string32(std::string s) = 0;
virtual std::string _string32(hypernode<I> hnode) = 0;
......@@ -198,7 +203,28 @@ namespace apg {
}
}
virtual hypernode<I> tensor_recurse(hypernode<I> hnode, lifetree_abstract<I> *lab, uint32_t delta, std::vector<I> &v,
std::map<std::pair<I, uint32_t>, I> *memmap) = 0;
hypernode<I> tensor_recurse(hypernode<I> hnode, lifetree_abstract<I> *lab, uint32_t delta, std::vector<I> &v) {
std::map<std::pair<I, uint32_t>, I> memmap;
return tensor_recurse(hnode, lab, delta, v, &memmap);
}
hypernode<I> boolean_universe(hypernode<I> lnode, hypernode<I> rnode, int operation) {
if (operation == 4) {
hypernode<I> oncell = pyramid_down(breach(rnode));
std::vector<I> v;
v.push_back(0);
v.push_back(breach(rnode).index);
return tensor_recurse(lnode, this, oncell.depth + 4, v);
} else if (operation == 5) {
return convolve_universe(lnode, rnode, false);
} else if (operation == 6) {
return convolve_universe(lnode, rnode, true);
}
hypernode<I> lx = lnode;
hypernode<I> rx = rnode;
while (lx.depth < rx.depth) { lx = pyramid_up(lx); }
......@@ -325,8 +351,8 @@ namespace apg {
int64_t right = bound_recurse(hnode, 2);
int64_t bottom = bound_recurse(hnode, 3);
bbox[0] = left - (8 << hnode.depth);
bbox[1] = top - (8 << hnode.depth);
bbox[0] = left - (8ll << hnode.depth);
bbox[1] = top - (8ll << hnode.depth);
bbox[2] = 1 + right - left;
bbox[3] = 1 + bottom - top;
......@@ -343,14 +369,6 @@ namespace apg {
return h;
}
virtual hypernode<I> tensor_recurse(hypernode<I> hnode, lifetree_abstract<I> *lab, uint32_t delta, std::vector<I> &v,
std::map<std::pair<I, uint32_t>, I> *memmap) = 0;
hypernode<I> tensor_recurse(hypernode<I> hnode, lifetree_abstract<I> *lab, uint32_t delta, std::vector<I> &v) {
std::map<std::pair<I, uint32_t>, I> memmap;
return tensor_recurse(hnode, lab, delta, v, &memmap);
}
virtual hypernode<I> copy_recurse(hypernode<I> hnode, lifetree_abstract<I> *lab, std::map<std::pair<I, uint32_t>, I> *memmap) = 0;
hypernode<I> copy_recurse(hypernode<I> hnode, lifetree_abstract<I> *lab) {
......@@ -391,6 +409,18 @@ namespace apg {
return shift_universe(hnode_initial, x, y, 0);
}
hypernode<I> semisolid(uint64_t depth, uint64_t flags) {
hypernode<I> hnode = solid(depth);
I z = 0;
nicearray<I, 4> cc = {((flags & 1) ? hnode.index : z),
((flags & 2) ? hnode.index : z),
((flags & 4) ? hnode.index : z),
((flags & 8) ? hnode.index : z)};
return make_nonleaf_hn(hnode.depth + 1, cc);
}
hypernode<I> rectangle(uint64_t width, uint64_t height) {
if (width == 0 || height == 0) {
return hypernode<I>(0, 1);
......@@ -422,8 +452,10 @@ namespace apg {
* Apply an isometry of the plane which sends (0, 0) to (x, y) with
* an optional rotation and/or reflection.
*/
int64_t nx = (perm & 64) ? x : (x + 1);
int64_t ny = (perm & 128) ? y : (y + 1);
uint8_t invperm = (1 << ((perm & 12) >> 1)) | (2 << ((perm & 48) >> 3)) | (3 << ((perm & 192) >> 5));
int64_t nx = (invperm & 64) ? x : (x + 1);
int64_t ny = (invperm & 128) ? y : (y + 1);
hypernode<I> hn = (perm == 228) ? hnode : transform_recurse(hnode, perm);
return shift_universe(hn, nx, ny, 0);
}
......
......@@ -134,10 +134,22 @@ namespace apg {
}
}
basepattern<I> advance(std::string rule, uint64_t numgens, uint64_t exponent) {
if (rule == rulestring) {
return basepattern<I>(lab, lab->advance(hnode, rule, numgens, exponent), rule, dx, dy, dt, minp);
} else {
return basepattern<I>(lab, lab->advance(hnode, rule, numgens, exponent), rule);
}
}
basepattern<I> advance(uint64_t numgens) {
return advance(rulestring, numgens);
}
basepattern<I> advance(uint64_t numgens, uint64_t exponent) {
return advance(rulestring, numgens, exponent);
}
basepattern<I> operator[](std::string rule) {
return basepattern<I>(lab, hnode, rule);
}
......@@ -148,6 +160,10 @@ namespace apg {
return basepattern<I>(lab, lab->shift_universe(hnode, x, y), rulestring, dx, dy, dt, minp);
}
basepattern<I> shift(int64_t x, int64_t y, uint64_t exponent) {
return basepattern<I>(lab, lab->shift_universe(hnode, x, y, exponent), rulestring, dx, dy, dt, minp);
}
basepattern<I> getchild(uint32_t x) const {
return basepattern<I>(lab, lab->getchild(hnode, x), rulestring);
}
......@@ -206,6 +222,10 @@ namespace apg {
return lab->getbbox(hnode, bbox);
}
int logdiam() {
return (hnode.depth + 4);
}
void display(int64_t *bbox) {
int dim = 128;
if (hnode.depth == 2) { dim = 64; }
......@@ -263,6 +283,11 @@ namespace apg {
return *this;
}
basepattern<I>& operator*=(const basepattern<I> &other) {
changehnode(lab->boolean_universe(hnode, coerce(other), 4));
return *this;
}
// Set operations (immutable):
basepattern<I> conjunction(const basepattern<I> &other) {
......@@ -281,11 +306,15 @@ namespace apg {
return basepattern<I>(lab, lab->boolean_universe(hnode, coerce(other), 3), rulestring);
}
basepattern<I> kronecker(const basepattern<I> &other) {
return basepattern<I>(lab, lab->boolean_universe(hnode, coerce(other), 4), rulestring);
}
// Equality testing:
bool operator==(const basepattern<I> &other) const {
hypernode<I> l = lab->pyramid_down(hnode);
hypernode<I> r = lab->pyramid_down(coerce(other));
hypernode<I> l = lab->pyramid_down(lab->breach(hnode));
hypernode<I> r = lab->pyramid_down(lab->breach(coerce(other)));
return (l == r);
}
......@@ -331,14 +360,14 @@ namespace apg {
return lab->digest_universe(hnode);
}
void pdetect(uint64_t gmax) {
basepattern<I> pdetect(uint64_t gmax) {
/*
* Detect periodicity. This does not necessarily find the minimum
* period, but instead can return a multiple of the actual period.
* For the latter, use the slightly slower ascertain_period().
*/
if (dt != 0) { return; }
if (dt != 0) { return advance(0); }
int64_t bbox[4] = {0};
int64_t bbox_orig[4] = {0};
......@@ -350,10 +379,10 @@ namespace apg {
this->dt = 8;
this->dx = 0;
this->dy = 0;
return;
return x;
}
int lowbits = 10;
int lowbits = 30;
while (((1ull << lowbits) << lowbits) > gmax) { lowbits -= 1; }
uint64_t g = 8;
......@@ -374,25 +403,25 @@ namespace apg {
// std::cout << "Possible super-period of " << delta << std::endl;
basepattern<I> y = advance(delta);
y.getrect(bbox);
hypernode<I> hnode2 = y.shift(bbox_orig[0] - bbox[0], bbox_orig[1] - bbox[1]).gethnode();
if (hnode == hnode2) {
basepattern<I> shifted = y.shift(bbox_orig[0] - bbox[0], bbox_orig[1] - bbox[1]);
if (hnode == shifted.gethnode()) {
// std::cout << "Yes!" << std::endl;
this->minp = (rulestring[1] == '0') ? 2 : 1;
this->dt = delta;
this->dx = bbox[0] - bbox_orig[0];
this->dy = bbox[1] - bbox_orig[1];
return;
break;
} else {
basepattern<I> y1 = advance(g);
y1.getrect(bbox);
hypernode<I> hnode3 = y1.shift(-bbox[0], -bbox[1]).gethnode();
basepattern<I> y3 = y1.shift(-bbox[0], -bbox[1]);
basepattern<I> y2 = advance(g2);
y2.getrect(bbox);
hypernode<I> hnode4 = y2.shift(-bbox[0], -bbox[1]).gethnode();
if (hnode3 == hnode4) {
basepattern<I> y4 = y2.shift(-bbox[0], -bbox[1]);
if (y3.gethnode() == y4.gethnode()) {
// Pattern is eventually periodic but not initially periodic.
// As such, it cannot possibly return to its initial state.
return;
break;
}
}
} else {
......@@ -402,15 +431,17 @@ namespace apg {
x = x.advance(i);
g += i;
if ((i << lowbits) == 0) { return; }
if ((i << lowbits) == 0) { break; }
// Increase the step size:
if (g % (i << lowbits) == 0) { i *= 2; }
}