+#include "cell.h"
+#include "index.h"
+#include "IMap.h"
+
+namespace iProlog {
+
+ // https://stackoverflow.com/questions/108318/how-can-i-test-whether-a-number-is-a-power-of-2)
+
+ bool is_a_power_of_2(int size) {
+ return size > 0 && (size & (size - 1)) == 0;
+ }
+
+ IntSet *IntSet::init(IntSet* isp) {
+ int capacity = arraySize(init_cap, m_fillFactor_pc);
+
+ isp->set_mask(capacity);
+ isp->m_data = isp->alloc(capacity);
+ isp->m_threshold = (capacity * m_fillFactor_pc) / 100;
+ isp->m_size = 0;
+
+ return isp;
+ }
+
+ void IntSet::dealloc(IntSet* isp) {
+ if (isp == nullptr) abort();
+ isp->md_free(isp->m_data);
+ free((void*)isp);
+ }
+
+ IntSet::IntSet() {
+
+ assert(is_a_power_of_2(init_cap));
+
+ init(this);
+ }
+
+ void IntSet::rehash(size_t newCapacity) {
+#define TR if(0)
+ TR cout << " !!!!!!!!!!!!! calling rehash, newCapacity="
+ << to_string(newCapacity) << " !!!!!!!!!!!!!!!!!!" << endl;
+ m_threshold = (int)(newCapacity * ((float)m_fillFactor_pc/100));
+ set_mask(newCapacity);
+
+ TR cout << " m_mask is now " << m_mask << endl;
+
+ size_t oldCapacity = length();
+ Vec oldData = m_data;
+
+ TR for (int i = 0; i < oldCapacity; i++)
+ cout << " oldData[" << i << "].key=" << oldData[i].key
+ << " oldData[i].val=" << oldData[i].val << endl;
+
+ m_data = alloc(newCapacity); // sets md_capacity = newCapacity
+ m_size = m_hasFreeKey ? 1 : 0;
+
+ for (int i = 0; i < oldCapacity; i++) {
+ int oldKey = oldData[i].key;
+ if (oldKey != FREE_KEY) {
+ put(oldKey, oldData[i].val);
+ assert(contains(oldKey));
+ }
+ }
+ md_free(oldData);
+#undef TR
+ }
+
+ int IntSet::get(int key) const {
+#define TR if(0)
+ int ptr = mk_ptr(key);
+
+ TR cout << " IntSet::get(" << key << "), ptr="
+ << ptr << endl;
+
+ if (key == FREE_KEY) {
+ int r = m_hasFreeKey ? m_freeValue : NO_VALUE;
+ TR cout << " key==FREE_KEY m_hasFreeKey=" << m_hasFreeKey << endl;
+ return r;
+ }
+ int k = get_key_at(ptr);
+
+ if (k == FREE_KEY) {
+ // TR cout << " k==m_data[" << ptr << "]==" << k << " == FREE_KEY" << ", m_data[ptr + 1] = " << m_data[ptr + 1] << endl;
+ return NO_VALUE; //end of chain already
+ }
+
+ if (k == key) { //we check FREE prior to this call
+ // TR cout << " k==key, returning m_data[" << ptr + 1 << "]=" << m_data[ptr + 1] << endl;
+ return get_val_at(ptr);
+ }
+
+ while (true) {
+ // cout << "get loop" << endl;
+ ptr = next(ptr);
+ k = get_key_at(ptr);
+ if (k == FREE_KEY) {
+ // TR cout << " k=get_key_at(" << ptr << ")==FREE_KEY " << FREE_KEY << " m_data[ptr+1]="<< m_data[ptr+1] << endl;
+ return NO_VALUE;
+ }
+ if (k == key) {
+ TR cout << " k==key at end of chain==" << k << endl;
+ return get_val_at(ptr);
+ }
+ }
+#undef TR
+ }
+
+ int IntSet::put(int key, int value) {
+#define TR if(0)
+ TR cout << endl
+ << " IntSet::put(key=" << to_string(key)
+ << ", value=" << to_string(value) << ")"
+ << " md_capacity=" << to_string(md_capacity)
+ << " m_size()=" << to_string(m_size)
+ << " m_threshold=" << to_string(m_threshold)
+ << endl;
+
+ if (key == FREE_KEY) {
+ int ret = m_freeValue;
+ if (!m_hasFreeKey) {
+ ++m_size;
+ }
+ m_hasFreeKey = true;
+ m_freeValue = value;
+ TR cout << " (1) key == FREE_KEY, returning " << ret << endl;
+ return ret;
+ }
+
+ int ptr = mk_ptr(key);
+ int k = get_key_at(ptr); // m_data[ptr];
+ if (k == FREE_KEY) //end of chain already
+ {
+ set_key_at(ptr,key);
+ set_val_at(ptr,value);
+ if (m_size >= m_threshold) {
+ TR cout << "**** about to call rehash, m_size=" << m_size << " m_threshold=" << m_threshold << endl;
+ // TR cout << "**** m_data.capacity()=" << m_data.capacity() << endl;
+ rehash(length() * 2); //size is set inside
+ }
+ else {
+ ++m_size;
+ }
+ TR cout << " (2) k == FREE_KEY, returning NO_VALUE" << endl;
+ return NO_VALUE;
+ }
+ else if (k == key) //we check FREE prior to this call
+ {
+ int ret = get_val_at(ptr);
+ set_val_at(ptr, value);
+ TR cout << " (3) k == key, get_val_at(ptr)<-value==" << get_val_at(ptr) << " returning past value " << ret << endl;
+ return ret;
+ }
+
+ int count = 0;
+ while (true) {
+ TR cout << "ptr=" << to_string(ptr) << endl;
+ ptr = next(ptr);
+ k = get_key_at(ptr);
+ TR cout << "k=" << k << endl;
+ if (k == FREE_KEY) {
+ set_key_at(ptr, key);
+ set_val_at(ptr, value);
+ if (m_size >= m_threshold) {
+ TR cout << " needing to rehash" << endl;
+ rehash(length() * 2); //size is set inside
+ }
+ else {
+ ++m_size;
+ }
+ TR cout << " (4) at end of chain, returning NO_VALUE" << endl;
+ return NO_VALUE;
+ }
+ else if (k == key) {
+ int ret = get_val_at(ptr);
+ set_val_at(ptr, value);
+ TR cout << " (5) found something, returning " << ret << endl;
+ return ret;
+ }
+ }
+ }
+
+#if 0 // not called
+ int IntSet::remove(int key) {
+ if (key == FREE_KEY) {
+ if (!m_hasFreeKey)
+ return NO_VALUE;
+ m_hasFreeKey = false;
+ --m_size;
+ return m_freeValue; //value is not cleaned
+ }
+
+ int ptr = mk_ptr(key);
+ int k = get_key_at(ptr);
+ if (k == key) //we check FREE prior to this call
+ {
+ int res = get_val_at(ptr);
+ shiftKeys(ptr);
+ --m_size;
+ return res;
+ }
+ else if (k == FREE_KEY)
+ return NO_VALUE; //end of chain already
+ while (true) {
+ ptr = next(ptr);
+ k = get_key_at(ptr);
+ if (k == key) {
+ int res = get_val_at(ptr);
+ shiftKeys(ptr);
+ --m_size;
+ return res;
+ }
+ else if (k == FREE_KEY)
+ return NO_VALUE;
+ }
+ }
+
+ int IntSet::shiftKeys(int pos) {
+ // Shift entries with the same hash.
+ int last, slot;
+ int k;
+
+ while (true) {
+ cout << "shiftkeys loop" << endl;
+ last = pos;
+ pos = next(last);
+ while (true) {
+ cout << "shiftKeys subloop";
+ k = get_key_at(pos);
+ if (k == FREE_KEY) {
+ set_key_at(last,FREE_KEY);
+ return last;
+ }
+ slot = mk_ptr(k); //calculate the starting slot for the current key
+ if (last <= pos ? last >= slot || slot > pos : last >= slot && slot > pos) {
+ break;
+ }
+ pos = next(pos);
+ }
+ set_key_at(last) = k;
+ set_val_at(last, get_val_at(pos));
+ }
+ }
+#endif
+
+ /** Taken from FastUtil implementation */
+
+/** Return the least power of two greater than or equal to the specified value.
+ *
+ * Note that this function will return 1 when the argument is 0.
+ *
+ * @param x a long integer smaller than or equal to 262 .
+ * @return the least power of two greater than or equal to the specified value.
+ */
+ long IntSet::nextPowerOfTwo(long x) {
+ if (x == 0)
+ return 1;
+ x--;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return (x | x >> 32) + 1;
+ }
+
+ // always init with size = 2^n for n >= 0
+
+ /** Returns the least power of two smaller than or equal to 230
+ * and larger than or equal to Math.ceil( expected / f ).
+ *
+ * @param expected the expected number of elements in a hash table.
+ * @param f the load factor.
+ * @return the minimum possible size for a backing array.
+ * @throws IllegalArgumentException if the necessary size is larger than 230 .
+ */
+ int IntSet::arraySize(int expected, int f_pc) {
+ long s = std::max(2L, IntSet::nextPowerOfTwo((long)ceil(expected / ((float) f_pc/100))));
+ if (s > 1 << 30) {
+ string emsg = "Too large " + expected;
+ // emsg += " expected elements with load factor " + f;
+ throw new logic_error(emsg);
+ }
+ return (int)s;
+ }
+
+ // @Override
+string IntSet::show() const {
+ //return java.util.Arrays.toString(m_data);
+ string b = "{";
+ size_t l = md_capacity;
+ // cout << "IntSet::show() md_capacity=" << to_string(md_capacity) << endl;
+ bool first = true;
+ for (int i = 0; i < l; i++) {
+ int v = get_key_at(i);
+ if (v != FREE_KEY) {
+ if (!first) {
+ b += ", ";
+ }
+ first = false;
+#if 0
+ b += "[" + to_string(i);
+ b += "]:#";
+#endif
+ b += to_string(v);
+#if 0
+ b += "->" + to_string(get_val_at(i));
+#endif
+ }
+ }
+ b += "}";
+ return b;
+}
+}
\ No newline at end of file
diff --git a/IP/cpp/iProlog/IntSet.h b/IP/cpp/iProlog/IntSet.h
new file mode 100644
index 00000000..7fcd3c71
--- /dev/null
+++ b/IP/cpp/iProlog/IntSet.h
@@ -0,0 +1,139 @@
+#pragma once
+
+/* "for use as IntSet" - Paul Tarau
+ * iProlog / C++ [derived from Java version]
+ * Original (Java) author: Mikhail Vorontsov
+ * License: public domain
+ * https://github.com/mikvor/hashmapTest/blob/master/LICENSE
+ * Java code modified by Paul Tarau
+ *
+ * derived from code at https://github.com/mikvor/hashmapTest
+ *
+ * Article by Vorontsov:
+ * https://web.archive.org/web/20170606024914/http://java-performance.info/implementing-world-fastest-java-int-to-int-hash-map/
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "defs.h"
+
+#include
+
+#include "IMap.h"
+
+namespace iProlog {
+ class is_bucket {
+ public:
+ int key;
+ int val;
+ };
+
+ typedef is_bucket* Vec;
+
+ using namespace std;
+
+ // For short logic programs, these are already pretty bulky
+ // compared to their actual indexing maps.
+ // Parameterizing for different kinds of maps
+ // has an additional cost.
+ // Unsatisfactory, but solvable with C++ "concept"?
+ // If I stick with m_data size = 2^n, what are now some per-instance
+ // members could be separated into a "hash table header" class
+ // and each could have an index into a table of these.
+
+ class IntSet {
+ private:
+
+ static const int FREE_KEY = 0;
+ static const int NO_VALUE = 0;
+
+ static const int init_cap = (1 << 2);
+
+ /** Fill factor, must be between (0 and 100) */
+ static const int m_fillFactor_pc = 75;
+ /** We will resize a map once it reaches this size */
+ int m_threshold;
+
+ /** Keys and values */
+ Vec m_data;
+
+ private:
+ /** Do we have 'free' key in the map? */
+ bool m_hasFreeKey;
+ /** Value of 'free' key */
+ int m_freeValue;
+
+ /** Current map size */
+ int m_size;
+
+ /** Mask to calculate the original position */
+ int m_mask;
+
+ /** number of elts if ever full */
+ size_t md_capacity;
+
+ public:
+ IntSet();
+ ~IntSet() { md_free(m_data); }
+
+ inline static int no_value() { return NO_VALUE; }
+ inline size_t length() const { return md_capacity; }
+
+ inline bool is_free(int i) const { return m_data[i].key == FREE_KEY; }
+ inline int get_key_at(int i) const { return m_data[i].key; }
+ inline void set_key_at(int i, int v) { m_data[i].key = v; }
+ inline int get_val_at(int i) const { return m_data[i].val; }
+ inline void set_val_at(int i, int v) { m_data[i].val = v; }
+
+ inline bool contains(int key) const { return NO_VALUE != get(key); }
+ inline bool add_key(int key) { return NO_VALUE != put(key, 666); }
+ // inline bool delete_(int key) { return NO_VALUE != remove(key); }
+ inline bool isEmpty() const { return 0 == m_size; }
+ inline int size() const { return m_size; }
+
+ static IntSet* alloc_intset() { return (IntSet*)malloc(sizeof(IntSet)); }
+ static IntSet* init(IntSet *isp);
+ static void dealloc(IntSet* isp);
+
+ int get(int key) const;
+ int put(int key, int value);
+ // int remove(int key);
+ private:
+ inline void md_free(Vec vs) {
+ free(vs);
+ }
+ inline int wraparound(int i) const { return i & m_mask; }
+ inline int next(int ptr) const { return wraparound(ptr+1); }
+ inline void set_mask(int cap) { m_mask = cap - 1; }
+ inline int mk_ptr(int key) const { return (phiMix(key) & m_mask); }
+
+ inline Vec alloc(int cap) {
+ md_capacity = cap;
+ size_t sz = cap * sizeof(is_bucket);
+ void* p = malloc(sz);
+ memset(p, 0, sz);
+ return (Vec) p;
+ }
+
+ static long nextPowerOfTwo(long x);
+ static int arraySize(int expected, int f_pc);
+ int shiftKeys(int pos);
+ void rehash(size_t newCapacity);
+
+ //taken from FastUtil
+ static const int INT_PHI = 0x9E3779B9;
+
+ inline int phiMix(int x) const {
+ int h = x * INT_PHI;
+ return h ^ h >> 16;
+ }
+
+ public:
+ string show() const;
+ };
+}
\ No newline at end of file
diff --git a/IP/cpp/iProlog/Inty.h b/IP/cpp/iProlog/Inty.h
new file mode 100644
index 00000000..1f211b2d
--- /dev/null
+++ b/IP/cpp/iProlog/Inty.h
@@ -0,0 +1,29 @@
+#pragma once
+
+/* Inty -- for stronger typing in an "ints all the way down" datastructure
+ *
+ * It may make sense to make it a template class, so that there can
+ * be int16, int32, int64 in places where it matters for speed/space,
+ * even varying according to use in data structures.
+ */
+
+namespace iProlog {
+ template class Inty {
+ private:
+ int_type i;
+ public:
+
+ inline Inty() { i = 0; }
+ Inty(int_type x) : Inty() { i = x; }
+
+ inline int_type as_int() const { return i; }
+ inline bool operator == (Inty x) const { return i == x.as_int(); }
+ inline bool operator != (Inty x) const { return i != x.as_int(); }
+ inline Inty dec() const { return Inty(i - 1); }
+ // inline Inty& operator =(const Inty& x) { i = x.as_int(); return *this; }
+ inline void operator =(const Inty& x) { i = x.as_int(); }
+ inline bool operator<(const Inty& x) const { return i < x.as_int(); }
+ inline bool operator>(const Inty& x) const { return i > x.as_int(); }
+ };
+
+} // namespace
\ No newline at end of file
diff --git a/IP/cpp/iProlog/Object.cpp b/IP/cpp/iProlog/Object.cpp
new file mode 100644
index 00000000..9af5c0b8
--- /dev/null
+++ b/IP/cpp/iProlog/Object.cpp
@@ -0,0 +1,28 @@
+
+#include "Object.h"
+
+namespace iProlog {
+
+ using namespace std;
+
+ string Object::toString() const {
+ switch (type) {
+ case e_nullptr:
+ return "$null";
+ case e_integer:
+ return to_string(i);
+ case e_string:
+ return s;
+ case e_vector: {
+ string j;
+ for (auto a : v) {
+ if (!j.empty())
+ j += ",";
+ j += a.toString();
+ }
+ return "(" + j + ")";
+ }
+ }
+ throw logic_error("invalid term");
+ }
+} // end namespace
diff --git a/IP/cpp/iProlog/Object.h b/IP/cpp/iProlog/Object.h
new file mode 100644
index 00000000..b8fcce21
--- /dev/null
+++ b/IP/cpp/iProlog/Object.h
@@ -0,0 +1,31 @@
+#pragma once
+// not sure how derivative of iProlog this is
+#include
+#include
+#include "defs.h"
+
+namespace iProlog {
+
+ using namespace std;
+
+ struct Object {
+ enum { e_nullptr, e_integer, e_string, e_vector } type;
+ int i;
+ string s;
+ vector v;
+
+ Object() : type(e_nullptr), i(0), s("") {}
+
+ int& operator =(const int j) { i = j; type = e_integer; return i; }
+ explicit Object(int i) : type(e_integer), i(i) {}
+
+ string& operator =(const string s_new) { type = e_string, s = s_new; return s; }
+ explicit Object(string s) : type(e_string), i(int(0)), s(s) {}
+
+ vector& operator =(const vector vo) { type = e_vector; v = vo; return v; }
+ explicit Object(vector v) : type(e_vector), i(0), v(v) {}
+
+ // avoid bringing in string lib:
+ string toString() const;
+ };
+} // end namespace
diff --git a/IP/cpp/iProlog/RelocStack.h b/IP/cpp/iProlog/RelocStack.h
new file mode 100644
index 00000000..f0369ae3
--- /dev/null
+++ b/IP/cpp/iProlog/RelocStack.h
@@ -0,0 +1,129 @@
+#pragma once
+
+// WORK IN PROGRESS
+
+/* Defines a type-parameterized, addressable, relocatable stack with elements of type T,
+ * with optional range-checks. A replacement for STL vector.
+ *
+ * Purposes:
+ * - save space -- the STL vector header is pretty "fat", e.g. 24 bytes
+ * on my 64-bit machine. This one is 8 bytes on the same machine.
+ * iProlog uses a lot of small arrays, so this matters.
+ *
+ * - save time -- getting the C++ compiler to just fetch and use a pointer,
+ * rather than passing a whole struct around during parameter passing and
+ * assignment. The array can be bounds-checked in debug configs, but
+ * that overhead can be dispensed with, in release binaries.
+ * STL vectors also have one level of indirection, so a fetch
+ * is saved. Space can also be time when caching is considered.
+ *
+ * - flexibility -- C++ STL vector is very general. Here, however, we can
+ * allow that array element sizes can be expressed in 8 bits,
+ * and the number of elements in 16 bits. Other configurations
+ * could be specified with typedefs for header member types.
+ * Speed-for-space trades become possible. For example,
+ * if the elements are near enough to a power of two in size,
+ * the array index operator could do a single left shift
+ * on the index. Elements could be aligned on cache block
+ * addresses.
+ *
+ * If the class could consist of only a pointer, the
+ * optimizer has a better chance of treating an object (not a pointer to it)
+ * as a pointer. But there are other data: the storage capacity, and the index
+ * to the top of the stack. Relocatability imposes another constraint:
+ * sometimes you want the data locked in place prevent relocation, other
+ * times you don't care.
+ *
+ * The STL vector template type uses an indirect,
+ * and has a fairly long header with the pointer to the actual relocatable vector data.
+ * This might be solved in C style by using malloc, etc. but messily.
+ *
+ */
+
+#include "defs.h"
+#include
+
+namespace iProlog {
+ using namespace std;
+
+ template
+ class RelocStack
+#ifndef RAW_CELL_HEAP
+ : vector
+#endif
+ { /*start class body*/
+
+#ifdef RAW_CELL_HEAP
+ typedef struct {
+ public:
+ Ty* top; // fast access
+ uint_fast16_t cap; // checked often
+ uint_least16_t elt_size; // less often
+ uint_least8_t max_hdr_size; // for testing empty
+ bool lock; // reloc safety catch
+ } header;
+
+ Ty* data_;
+
+ public:
+ RelocStack(unsigned short min_no) {
+ header hd;
+
+ hd.elt_size = (uint_least16_t)sizeof(Ty);
+ hd.lock = false;
+ hd.cap = min_no;
+ //
+ // Worst case size:
+ // "Passing a size which is not an integral multiple of alignment
+ // or an alignment which is not valid or not supported by the implementation
+ // causes the function to fail and return a null pointer ..."
+ // https://en.cppreference.com/w/c/memory/aligned_alloc
+ //
+
+ size_t hdr_align = alignment_of(header);
+ size_t hdr_size = sizeof(header);
+ size_t Ty_align = alignment_of(Ty);
+ size_t requested = sizeof(Ty) * min_no;
+ //
+ // sizeof(Ty) could be less or greater than sizeof(header)
+ // if less, be sure to round up to an integral multiple
+ // of largest alignment
+ //
+ hd.max_hdr_size = hdr_size + (hdr_size & (Ty_align - 1));
+ // "-1" for lo-order bit mask; alignments are 2^n, n=1,2,3,4.
+ size_t real_size = requested + hd.max_hdr_size;
+
+ char* alloced = aligned_alloc(alignment_of(hdr_align), real_size);
+ //
+ // hd.top - like "top = -1" init in Java version.
+ // DANGER -- what if alloc can start at addr 0 and sizeof(Ty) > min_space?
+ // Testing for empty needs to be careful, or check aligned_alloc
+ // result to see if it's < sizeof(Ty)
+ //
+ assert((intptr_t)alloced < hd.max_hdr_size + sizeof(Ty));
+ // intptr_t "optional"
+ // https://en.cppreference.com/w/cpp/types/integer
+
+ hd.top = (Ty*)((char*)this + hd.max_hdr_size) - 1;
+ //
+ // set header and finally initialize pointer
+ //
+ *((header*)data_) = hd;
+ data_ = (Ty*)((char*)alloced + hd.max_hdr_size);
+}
+
+Ty* s_data_with_lock() {
+ header* bp = (header*)this;
+ bp->lock = true;
+ return data_;
+}
+
+void unlock_data() {
+ ((header*)this)->lock = false;
+}
+#else
+ int nothing;
+ void stub() {}
+#endif
+ };
+}
\ No newline at end of file
diff --git a/IP/cpp/iProlog/cell.h b/IP/cpp/iProlog/cell.h
new file mode 100644
index 00000000..1cd41511
--- /dev/null
+++ b/IP/cpp/iProlog/cell.h
@@ -0,0 +1,155 @@
+#pragma once
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+#include "defs.h"
+#include "Inty.h"
+#include
+
+#include
+
+// C++ 20 will have machine instructions generated for rotr and rotl
+// which may permit faster tag extract (thus smaller-footprint tag comparison)
+// for the hi-order tag styles.
+
+namespace iProlog {
+
+ using namespace std;
+
+ class cell : public Inty {
+
+ public:
+ static const int bitwidth = CHAR_BIT * sizeof(int);
+ cell() : Inty(0) { }
+ cell(int x) : Inty(x) { }
+ static inline cell nonval() { return cell(-1); }; // IFFY
+
+ // hi_order_tag=1 -> a bit slower on 32-bit, probably because gcc
+ // is generating fatter instructions to accommodate
+ // comparisons to constants that require more bits
+ // to express.
+ // Eventually try on 16-bit arch
+ static const int use_sign_bit = USE_SIGN_BIT;
+
+ static const int n_tag_bits = 3;
+ static const int n_ref_tags = 3;
+ static const int n_ref_bits = bitwidth - n_tag_bits;
+
+ // TODO: just have a shift & mask inline function?
+ // ....
+ static const int nonref_base = use_sign_bit ? 0 : n_ref_tags;
+ static const int signed_zeros = use_sign_bit << (bitwidth - 1);
+ static const int tag_shift = use_sign_bit ? n_ref_bits : 0;
+ static const int ref_shift = use_sign_bit ? 0 : n_tag_bits;
+ static const int unshifted_tag_mask = (1 << n_tag_bits) - 1;
+ static const int tag_mask = unshifted_tag_mask << tag_shift;
+ static const int ref_mask = ~tag_mask;
+
+ public:
+ static const int V_ = (0 << tag_shift) | signed_zeros;
+ static const int U_ = (1 << tag_shift) | signed_zeros;
+ static const int R_ = (2 << tag_shift) | signed_zeros;
+ static const int C_ = (0 + nonref_base) << tag_shift;
+ static const int N_ = (1 + nonref_base) << tag_shift;
+ static const int A_ = (2 + nonref_base) << tag_shift;
+
+ static const int BAD = (3 + nonref_base) << tag_shift;
+
+ // tag(BAD,0) used as "null pointer value" in prog.cpp,
+ // and as "null termination" in index vectors
+ static inline cell null() { return tag(BAD,0); }
+
+ // abs_var is "abstract variable" used in index matching
+ static inline cell abstr_var() { return tag(V_, 0); }
+
+ // For x64, and maybe 32-bit CPU case:
+ // By making BAD == 0, and always making sure there's a zero cell at the
+ // end of a copy, there's a low-overhead sentinel. This could outperform
+ // memcpy, since there must be set-up overhead for it, and the average
+ // size of copies (except for inline single-cell in construction)
+ // is somewhere around 5-6 cells. The copy could be
+ //
+ // (1) copy one 64-bit (8-byte) long long int, then
+ // (2) look at the copy count to see if more is needed.
+ // (3) If so, copy another, then fall into
+ // (4) unrolled-loop switch. [MT]
+ //
+ // If the first cell is zero, which makes no code sense,
+ // it could trap out and use following cells as arguments
+ // to the trap call. This would still leave one more cell
+ // tag available for other purposes that arise.
+
+ static inline cell tag(int t, size_t w) {
+ return cell((int)((w << ref_shift) | t));
+ }
+
+ inline int arg() const {
+ if (use_sign_bit)
+ return (as_int() & ref_mask);
+ else
+ return as_int() >> ref_shift;
+ }
+
+ inline int s_tag() const { return as_int() & tag_mask; }
+
+ inline bool is_var() const { int t = s_tag(); return t == U_ || t == V_; }
+ inline bool is_ref() const { return s_tag() == R_; }
+
+ inline bool is_reloc() const {
+ if (!use_sign_bit)
+ return is_var() || is_ref();
+ else
+ return as_int() < 0;
+ }
+
+ inline bool is_const() const { return s_tag() == C_; }
+ inline bool is_offset() const { return s_tag() == A_; }
+
+ // The offset b will be shifted according to the tag architecture
+ // chosen. When copies are forward in the cell heap, b > 0.
+ //
+ inline cell relocated_by(cell b) const {
+ if (!is_reloc()) return *this;
+ if (use_sign_bit) return as_int() + (b.as_int() & ref_mask);
+ else return as_int() + b.as_int();
+ }
+
+ static inline cell argOffset(size_t o) { return tag(A_, o); }
+ static inline cell reference(size_t r) { return tag(R_, r); }
+
+ // note that this can be in-place and overlapping
+ static inline void cp_cells(cell b, const cell *srcp, cell *dstp, int count) {
+# define STEP *dstp++ = (*srcp++).relocated_by(b)
+ while (count >= 4) { STEP; STEP; STEP; STEP; count -= 4; }
+ switch (count) {
+ case 3: STEP; case 2: STEP; case 1: STEP; case 0: ;
+ }
+# undef STEP
+ }
+
+ // Need to avoid string lib in small memory
+
+ static string tagSym(int t) {
+ if (t == V_) return "V";
+ if (t == U_) return "U";
+ if (t == R_) return "R";
+ if (t == C_) return "C";
+ if (t == N_) return "N";
+ if (t == A_) return "A";
+ if (t == BAD) return "!";
+ return "?";
+ }
+
+ string show() {
+ string s = tagSym(s_tag());
+ s += ":";
+ s += to_string(arg());
+ return s;
+ }
+
+ };
+
+} /* end namespace*/
diff --git a/IP/cpp/iProlog/clause.cpp b/IP/cpp/iProlog/clause.cpp
new file mode 100644
index 00000000..281ba8ce
--- /dev/null
+++ b/IP/cpp/iProlog/clause.cpp
@@ -0,0 +1,28 @@
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+#include "defs.h"
+#include "cell.h"
+#include "index.h"
+#include "clause.h"
+#include "spine.h"
+
+namespace iProlog {
+
+ using namespace std;
+
+ // need head cell plus body array, hg_len becomes . . . body length?
+ Clause::Clause(int len_0, clause_skel skel_0, size_t skel_len_0, int base_0, int neck_0) {
+ skel = skel_0;
+ skel_len = skel_len_0;
+ if (skel_len == 0) abort();
+ base = base_0;
+ len = len_0;
+ neck = neck_0;
+ for (int i = 0; i < IV_LEN; ++i)
+ index_vector[i] = cell::null();
+ }
+}
diff --git a/IP/cpp/iProlog/clause.h b/IP/cpp/iProlog/clause.h
new file mode 100644
index 00000000..d6767161
--- /dev/null
+++ b/IP/cpp/iProlog/clause.h
@@ -0,0 +1,41 @@
+#pragma once
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+#include "skeleton.h"
+
+namespace iProlog {
+ using namespace std;
+
+ /**
+ * "representation of a clause" [Clause.java].
+ */
+
+ class Clause {
+ // Skeletal elements for compiled form:
+ public:
+ int len; // length of heap slice
+ clause_skel skel; // "head+goals pointing to cells in clauses"
+ int skel_len; // (for raw array implementation)
+ int base; // the point in the heap where this clause starts
+ int neck; // first after the end of the head (=length of the head)
+ t_index_vector index_vector; // indexables in head. In the video, this is described as
+ // "the index vector containing dereferenced constants,
+ // numbers or array sizes as extracted from the outermost
+ // term of the head of the clause, with zero values
+ // marking variable positions."
+ // Should it be "outermost termS"?
+
+ Clause() : len(size_t(0)), skel_len(0), base(size_t(0)), neck(size_t(0)) {
+ for (int i = 0; i < IV_LEN; ++i)
+ index_vector[i] = cell::null();
+ init_skel(skel);
+ }
+
+ Clause(int len_0, clause_skel skel_0, size_t skel_len_0, int base_0, int neck_0);
+ };
+
+} // end namespace
diff --git a/IP/cpp/iProlog/config.h b/IP/cpp/iProlog/config.h
new file mode 100644
index 00000000..c3807265
--- /dev/null
+++ b/IP/cpp/iProlog/config.h
@@ -0,0 +1,75 @@
+#pragma once
+
+// #define NDEBUG
+#include
+
+#include "Inty.h"
+
+namespace iProlog {
+ const int MIN_HEAP_SIZE = 1 << 10;
+
+#define COUNTING_MATCHES
+
+ // For tuning speed/space tradeoffs.
+ // See https://en.cppreference.com/w/cpp/types/integer
+
+ typedef int cell_int; // could go down to int_least16_t but getting compile
+ // errors in xhash about IMap even with "short int"
+ typedef int cls_no_int; // could go down to uint_least8_t
+ // for logic programs with <256 clauses
+
+ typedef int sym_idx_int;
+
+ typedef Inty ClauseNumber; /* 1 ... clause array size */
+
+ typedef Inty ClauseIndex;
+
+ /* RAW=true says to go with a less-safe, faster implementation
+ * than STL vectors, with no bounds check, and less header info, to
+ * save a little space. The fast-copy cell heap-to-heap relocation may
+ * end up in this class eventually.
+ * "if (is_raw)..." ensures that code code gets checked by the
+ * compiler, except there there's some #ifdef conditional
+ * compilation. Constant-folding and dead code elimination
+ * does the rest.
+ */
+#define RAW_CELL_HEAP
+#ifdef RAW_CELL_HEAP
+ const bool has_raw_cell_heap = true;
+#else
+ const bool has_raw_cell_heap = false;
+#endif
+
+ // Some way to pass these on compiler command line????
+
+ const int MAXIND = 3; // "number of index args" [Engine.java]
+ const int IV_LEN = (MAXIND + 1); // for sentinel search
+ const int START_INDEX = 1; // "if # of clauses < START_INDEX,
+ // turn off indexing" [Engine.java]
+
+ const bool indexing = true;
+
+ const int INTMAP_INIT_SIZE = (1 << 2);
+ const float INTMAP_FILL_FACTOR = 0.75f;
+
+// use int *instead of vector for sorting in indexing
+#define RAW_QUICKSORT
+
+#define USE_SIGN_BIT true
+
+ const bool integrity_check = false;
+ void checkit_();
+ inline void checkit() { if (integrity_check) checkit_(); }
+
+#define COUNTING_MATCHES
+
+#define TRY_CATCHING
+
+#define RAW_IMAPS
+
+#define RAW_CELL_LIST
+#define MEASURE_CELL_LIST
+
+#define RAW_SKEL
+
+}
\ No newline at end of file
diff --git a/IP/cpp/iProlog/defs.h b/IP/cpp/iProlog/defs.h
new file mode 100644
index 00000000..923e8b9f
--- /dev/null
+++ b/IP/cpp/iProlog/defs.h
@@ -0,0 +1,39 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "config.h"
+
+namespace iProlog {
+
+ using namespace std;
+
+ // I need a more specific header file for this:
+ // typedef int ClauseNumber; /* 1 ... clause array size */
+
+ // If one of the goals is to avoid bringing in the
+ // string library, this might better be put elsewhere.
+ typedef const string cstr;
+ inline cstr operator+(cstr s, int i) { return s + to_string(i); }
+ inline cstr operator+(cstr s, size_t i) { return s + to_string(i); }
+ inline cstr operator+(cstr s, long i) { return s + to_string(i); }
+
+#ifdef TRY_CATCHING
+#define TRY try
+#define CATCH(s) catch (exception &e__) { cout<<"!!!"<"add"
+ * [7] r: 10 -- link to subterm: _0 holds(=) s X
+ * [8] v: 8 -- Y
+ * [9] r: 13 -- link to next subterm: _1 holds(=) s Z
+ *
+ * -- _0 holds(=) s X:
+ **[10] a: 2
+ * [11] c: ->"s"
+ * [12] v: 12 -- X
+ *
+ * -- _1 holds(=) s Z:
+ **[13] a: 2
+ * [14] c: ->"s"
+ * [15] v: 15 -- Z
+ *
+ * -- add X Y Z:
+ **[16] a: 4
+ * [17] c: ->"add"
+ * [18] u: 12 -- X
+ * [19] u: 8 -- Y
+ * [20] u: 15 -- Z
+ */
+
+Engine::~Engine() { }
+
+/**
+ * unfold - "transforms a spine [G] containing references to choice point and
+ * immutable list of goals into a new spine, by reducing the
+ * first goal in the list with a clause that successfully
+ * unifies with it - in which case places the goals of the
+ * clause at the top of the new list of goals, in reverse order"
+ */
+Spine* Engine::unfold(Spine *G) {
+#define TR if(0)
+ assert(G != nullptr);
+
+ if (CellList::isEmpty(G->the_goals))
+ return nullptr;
+
+ int trail_top = trail.getTop(); // top of trail
+ int saved_heap_top = heap.getTop();
+ int base = saved_heap_top + 1;
+ cell this_goal = CellList::head(G->the_goals);
+
+ TR cout << "unfold: this goal=" << this_goal.as_int()
+ << " this goal's tag= " << this_goal.s_tag()
+ << " this goal's arg = " << this_goal.arg() << endl;
+ TR cout << "unfold: about to call makeIndexArgs with current G->unifiables[0]=" << G->unifiables[0].as_int() << endl;
+
+ Ip->makeIndexArgs(heap, G, this_goal); // this goal is head(G->the_goals)
+
+ if (G->unifiables.size() > 0)
+ TR cout << "unfold: after makeIndexArgs with current G->unifiables[0]=" << G->unifiables[0].as_int() << endl;
+
+ size_t last = G->unifiables.size();
+ TR cout << "unfold: last=" << to_string(last) << endl;
+
+ TR cout << "G->last_clause_tried=" << G->last_clause_tried << endl;
+ for (int k = G->last_clause_tried; k < last; k++) {
+ TR cout << "G->unifiables[" << k << "]=" << G->unifiables[k].as_int() << endl;
+
+ Clause& C0 = clauses[G->unifiables[k].as_int()];
+
+ if (!Ip->possible_match(G, C0))
+ continue;
+
+ cell b = cell::tag(cell::V_, base - C0.base);
+ // pushHeadtoHeap:
+ CellStack::pushCells(heap, b, 0, C0.neck, C0.base);
+ cell head = C0.skel[0].relocated_by(b);
+
+ unify_stack_clear(); // "set up unification stack" [Engine.java]
+ unify_stack_push(head);
+ unify_stack_push(this_goal);
+
+ if (!unify(base)) {
+ unwindTrail(trail_top);
+ heap.setTop(saved_heap_top);
+ continue;
+ }
+
+ CL_p remaining_goals = CellList::tail(G->the_goals);
+ G->last_clause_tried = k + 1;
+
+ /** [was in pushBody()]
+ * "Copies and relocates body of clause at offset from heap to heap
+ * while also placing head as the first element of [a buffer array] that,
+ * when returned, contains references to the toplevel spine of the clause."
+ *
+ * Moved that buffering down into new_Spine to reduce copying
+ * and save space.
+ */
+ CellStack::pushCells(heap, b, C0.neck, C0.len, C0.base);
+
+ if ((int)C0.skel_len != 0 || remaining_goals != nullptr)
+ return Spine::new_Spine(C0, b, base, remaining_goals, trail_top, clause_list);
+ else
+ return answer(trail_top);
+ }
+ return nullptr;
+#undef TR
+}
+
+bool Engine::unify(int base) {
+ while (!unify_stack_isEmpty()) {
+ cell x1 = deref(unify_stack_pop());
+ cell x2 = deref(unify_stack_pop());
+
+ // cout << "unify: x1=" << x1.as_int() << ",x2=" << x2.as_int() << endl;
+
+ if (x1.as_int() != x2.as_int()) {
+ int t1 = x1.s_tag();
+ int t2 = x2.s_tag();
+ int w1 = x1.arg();
+ int w2 = x2.arg();
+ if (x1.is_var()) { /* "unb. var. v1" */
+ if (x2.is_var() && w2 > w1) { /* "unb. var. v2" */
+ set_cell(w2,x1);
+ if (w2 <= base) {
+ trail.push(x2);
+ }
+ } else { // "x2 nonvar or older"
+ set_cell(w1,x2);
+ if (w1 <= base) {
+ trail.push(x1);
+ }
+ }
+ } else if (x2.is_var()) { /* "x1 is NONVAR" */
+ set_cell(w2,x1);
+ if (w2 <= base) {
+ trail.push(x2);
+ }
+ } else if (cell::R_ == t1 && cell::R_ == t2) { // "both should be R"
+ if (!unify_args(w1, w2)) {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ // cout << "unify succeeded" << endl;
+ return true;
+}
+
+bool Engine::unify_args(int w1, int w2) { // w1 & w1 already detagged in unify()
+ assert(cell_at(w1).is_offset() && cell_at(w2).is_offset());
+
+ cell v1 = cell_at(w1);
+ cell v2 = cell_at(w2);
+
+ // "both should be A:"
+
+ if (!v1.is_offset()) abort();
+ if (!v2.is_offset()) abort();
+
+ int n1 = v1.arg();
+ int n2 = v2.arg();
+
+ if (n1 != n2)
+ return false;
+
+ int b1 = 1 + w1;
+ int b2 = 1 + w2;
+
+ for (int i = n1 - 1; i >= 0; i--) {
+ int i1 = b1 + i;
+ int i2 = b2 + i;
+
+ cell u1 = cell_at(i1);
+ cell u2 = cell_at(i2);
+
+ if (u1 == u2)
+ continue;
+
+ unify_stack_push(u2);
+ unify_stack_push(u1);
+ }
+ return true;
+}
+
+void Engine::clear() {
+ heap.setTop(-1);
+}
+
+ /**
+ * Extracts a query - by convention of the form
+ * goal(Vars):-body to be executed by the engine
+ */
+Clause Engine::getQuery() {
+ return clauses[clauses.size() - 1];
+}
+
+/**
+ * "Returns the initial spine built from the query from which execution starts."
+ */
+Spine *Engine::init() {
+ int base = heap_size();
+ Clause G = getQuery();
+ cell b = cell::tag(cell::V_, 0);
+
+ // clause_list: initially an array [0..clauses.length-1]
+ int l = (int)clauses.size();
+ clause_list = Spine::Unifiables(l);
+ for (int i = 0; i < l; i++)
+ clause_list[i] = ClauseIndex(i);
+
+ Spine *Q = Spine::new_Spine(G, b, base, nullptr, trail.getTop(), clause_list);
+ spines.push_back(Q);
+ return Q;
+}
+
+/**
+ * answer - "Returns an answer as a Spine while recording in it
+ * the top of the trail to allow the caller to retrieve
+ * more answers by forcing backtracking."
+ */
+Spine* Engine::answer(int trail_top) {
+ return Spine::new_Spine(spines[0]->head, trail_top);
+}
+
+/**
+ * hasClauses - "Detects availability of alternative clauses for the
+ * top goal of this spine."
+ */
+bool Engine::hasClauses(const Spine* S) const {
+ return S->last_clause_tried < S->unifiables.size();
+}
+
+Object Engine::exportTerm(cell x) const {
+#define TR if(0)
+ if (x == cell::tag(cell::BAD, 0))
+ return Object();
+
+ x = deref(x);
+ TR cout << "exportTerm: x=" << x.show() << endl;
+ int w = x.arg();
+
+ switch (x.s_tag()) {
+ case cell::C_: return Object(symTab.getSym(w));
+ case cell::N_: return Object(w);
+ case cell::V_: return Object(cstr("V") + w);
+ /*case U_:*/
+ case cell::R_: {
+ TR cout << "R_: " << endl;
+ cell a = cell_at(w);
+ if (!a.is_offset())
+ throw logic_error(cstr("*** should be A, found=") + showCell(a));
+ int n = a.arg();
+ vector arr;
+ int k = w + 1; // "offset to embedded array" [Engine.java]
+ for (int i = 0; i < n; i++) {
+ int j = k + i;
+ cell c = cell_at(j);
+ TR cout << "cell_at(" << j << ")=" << c.show() << endl;
+ Object o = exportTerm(c);
+ TR cout << "exportTerm(cell " << c.show() << ")="
+ << exportTerm(c).toString() << endl;
+ arr.push_back(o);
+ }
+ return Object(arr);
+ }
+ default:
+ throw logic_error(cstr("*BAD TERM*") + showCell(x));
+ }
+#undef TR
+}
+
+/**
+ * ask - "Retrieves an answer and ensures the engine can be resumed
+ * by unwinding the trail of the query Spine.
+ * Returns an external "human readable" representation of the answer.
+ *
+ * "A key element in the interpreter loop is to ensure that
+ * after an Engine yields an answer, it can, if asked to,
+ * resume execution and work on computing more answers. [...]
+ * A variable 'query' of type Spine, contains the top of the trail
+ * as it was before evaluation of the last goal,
+ * up to where bindings of the variables will have to be undone,
+ * before resuming execution." [HHG doc]
+ *
+ * MISTAKEN CHANGE:
+ * Moving to prog.cpp: "It also unpacks the actual answer term
+ * (by calling the method exportTerm) to a tree representation of a term,
+ * consisting of recursively embedded arrays hosting as leaves,
+ * an external representation of symbols, numbers and variables." [HHG doc]
+ */
+Object Engine::ask() {
+#define TR if(0)
+#if 0
+ set_engine(this); // for static checkit, usable in other scopes(?)
+ checkit();
+#endif
+ query = yield();
+ if (nullptr == query)
+ return Object();
+
+ TR cout << "ask: query->trail_top=" << query->trail_top << endl;
+ Spine *ans = answer(query->trail_top);
+
+ TR cout << "yield: " << ans->show() << endl;
+ cell result = ans->head;
+ /////////////////////////
+ Object R = exportTerm(result);
+ unwindTrail(query->trail_top);
+ Spine::free(ans);
+ Spine::free(query); // leaky to delete this?
+ query = nullptr;
+
+ return R;
+#undef TR
+}
+/**
+ * unwindTrail - "Removes binding for variable cells
+ * above savedTop." [Engine.java]
+ */
+void Engine::unwindTrail(int savedTop) {
+ while (savedTop < trail.getTop()) {
+ cell href = trail.pop();
+ // setRef(href, href);
+ setUnbound(href);
+ }
+}
+
+/**
+ * popSpine - "Removes this spine from the spine stack and
+ * resets trail and heap to where they were at its
+ * creation time - while undoing variable binding
+ * up to that point." [Engine.java]
+ */
+void Engine::popSpine() {
+
+ Spine *G = spines.back();
+ int new_base = int(G->base) - 1;
+ int savedTop = G->trail_top;
+ spines.pop_back();
+ free(G);
+
+ unwindTrail(savedTop);
+ heap.setTop(new_base);
+}
+
+/**
+ * yield "Main interpreter loop: starts from a spine and works
+ * though a stream of answers, returned to the caller one
+ * at a time, until the spines stack is empty - when it
+ * returns null." [Engine.java]
+ */
+Spine* Engine::yield() {
+ while (!spines.empty()) {
+ Spine* G = spines.back(); // was "peek()" in Java
+
+ if (!hasClauses(G)) {
+ popSpine();
+ continue;
+ }
+ Spine *C = unfold(G);
+ if (nullptr == C) {
+ popSpine(); // no matches
+ continue;
+ }
+ if (C->hasGoals()) {
+ spines.push_back(C);
+ continue;
+ }
+ return C; // answer
+ }
+ return nullptr;
+}
+
+string Engine::showCell(cell w) const {
+ int t = w.s_tag();
+ int val = w.arg();
+ string s = "";
+
+ switch (t) {
+ case cell::V_: s = cstr("v:") + val; break;
+ case cell::U_: s = cstr("u:") + val; break;
+ case cell::N_: s = cstr("n:") + val; break;
+ case cell::C_: s = cstr("c:") + symTab.getSym(val); break;
+ case cell::R_: s = cstr("r:") + val; break;
+ case cell::A_: s = cstr("a:") + val; break;
+ default: s = cstr("*BAD*=") + w.as_int();
+ }
+ return s;
+}
+
+/**
+ * Copies and relocates the head of clause C from heap to heap.
+ */
+cell Engine::pushHeadtoHeap(cell b, const Clause& C) {
+#define TR if(0)
+ TR cout << "push HeadtoHeap entered" << endl;
+ CellStack::pushCells(heap, b, 0, C.neck, C.base);
+ cell head = C.skel[0].relocated_by(b);
+
+ return head;
+#undef TR
+}
+
+
+} // namespace
diff --git a/IP/cpp/iProlog/file2string.h b/IP/cpp/iProlog/file2string.h
new file mode 100644
index 00000000..f6ee7f4a
--- /dev/null
+++ b/IP/cpp/iProlog/file2string.h
@@ -0,0 +1,20 @@
+#pragma once
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+#include
+#include
+#include
+#include
+
+std::string file2string(std::string path) {
+ std::ifstream f(path);
+ if (!f.good())
+ throw std::invalid_argument(path + " not found");
+ std::stringstream s;
+ s << f.rdbuf();
+ return s.str();
+}
diff --git a/IP/cpp/iProlog/iProlog.vcxproj b/IP/cpp/iProlog/iProlog.vcxproj
new file mode 100644
index 00000000..c01b4f51
--- /dev/null
+++ b/IP/cpp/iProlog/iProlog.vcxproj
@@ -0,0 +1,173 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {1203cb19-da21-43b9-9d0a-ca67e4ebe3ad}
+ iProlog
+ 10.0
+ iProlog
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ はい (/Ot)
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ false
+ _CONSOLE;%(PreprocessorDefinitions)
+ true
+ Speed
+ Default
+ MaxSpeed
+ Default
+ false
+ FastCall
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IP/cpp/iProlog/iProlog.vcxproj.filters b/IP/cpp/iProlog/iProlog.vcxproj.filters
new file mode 100644
index 00000000..b84a88c8
--- /dev/null
+++ b/IP/cpp/iProlog/iProlog.vcxproj.filters
@@ -0,0 +1,111 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+ ソース ファイル
+
+
+
\ No newline at end of file
diff --git a/IP/cpp/iProlog/iProlog.vcxproj.user b/IP/cpp/iProlog/iProlog.vcxproj.user
new file mode 100644
index 00000000..993cdd81
--- /dev/null
+++ b/IP/cpp/iProlog/iProlog.vcxproj.user
@@ -0,0 +1,19 @@
+
+
+
+ sud4x.pl true
+ WindowsLocalDebugger
+
+
+ perms.pl true
+ WindowsLocalDebugger
+
+
+ sud4x.pl
+ WindowsLocalDebugger
+
+
+ sud4x.pl
+ WindowsLocalDebugger
+
+
\ No newline at end of file
diff --git a/IP/cpp/iProlog/index.cpp b/IP/cpp/iProlog/index.cpp
new file mode 100644
index 00000000..e00bd6d1
--- /dev/null
+++ b/IP/cpp/iProlog/index.cpp
@@ -0,0 +1,482 @@
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+/*
+* From the ICLP 2017 conference paper (P. Tarau)
+*
+"The indexing algorithm is designed as an independent add-on to be
+plugged into the the main Prolog engine."
+
+This C++ code is an attempt to make iProlog indexing more "plug-in".
+
+"For each argument position in the head of a clause
+(up to a maximum that can be specified by the programmer)
+it associates to each indexable element (symbol, number or arity)
+the set of clauses where the indexable element occurs
+in that argument position."
+
+MAXIND is that maximum.
+*/
+
+#include
+#include
+/// #include "index.h"
+#include "Engine.h"
+
+namespace iProlog {
+
+ cell index::cell2index(CellStack& heap, cell c) const {
+ cell x = cell::abstr_var();
+ int t = c.s_tag();
+ switch (t) {
+ case cell::R_:
+ x = Engine::getRef(heap, c);
+ break;
+ case cell::C_:
+ case cell::N_:
+ x = c;
+ break;
+ }
+ return x;
+ }
+
+// "Indexing extensions - ony active if [there are] START_INDEX clauses or more."
+
+ inline void get_arg_start_and_n(CellStack& h, cell g, int &arg_start, int &n) {
+ arg_start = 1 + g.arg(); // "1" to be just after pred symbol ref
+ n = min(Engine::getRef(h, g).arg(), MAXIND);
+ }
+
+ void index::getIndexables(t_index_vector &index_vector, CellStack &heap, cell goal) {
+#define TR if(0)
+ TR cout << "getIndexables(...): " << endl;
+
+ int arg_start, n;
+ get_arg_start_and_n(heap, goal, arg_start, n);
+
+ for (int arg_pos = 0; arg_pos < n; arg_pos++) {
+ cell arg = Engine::cell_at(heap,arg_start + arg_pos);
+ cell c = Engine::deref(heap, arg);
+ index_vector[arg_pos] = cell2index(heap, c);
+
+ TR cout << "getIndexables: index_vector[" << arg_pos << "] <- " << index_vector[arg_pos].as_int() << endl;
+ }
+ for (int pos = n; pos < IV_LEN; ++pos) // sentinel-searched
+ index_vector[pos] = cell::null();
+
+ TR cout << "getIndexables returning" << endl;
+ }
+
+ index::index(CellStack &heap, vector& clauses) {
+#define TR if(0)
+
+ for (int i = 0; i < clauses.size(); ++i) {
+ cell hd = clauses[i].skel[0];
+ TR cout << " clauses[" << i << "].skel[0]=" << hd.show() << endl;
+ getIndexables(clauses[i].index_vector, heap, hd);
+ }
+
+ // was vcreate() in Java version:
+ // var_maps = vector(IV_LEN);
+
+ for (int arg_pos = 0; arg_pos < IV_LEN; arg_pos++) {
+ var_maps[arg_pos] = new cls_no_set();
+ }
+ // end vcreate() inlined
+
+ if (clauses.size() < START_INDEX) {
+#ifndef RAW_IMAPS
+ imaps = vector();
+#endif
+ return;
+ }
+#ifndef RAW_IMAPS
+ imaps = IMap::create(IV_LEN);
+#else
+ for (int i = 0; i < IV_LEN; ++i) {
+ imaps[i] = IMap();
+ }
+#endif
+
+ for (int i = 0; i < clauses.size(); i++) {
+ ClauseIndex ci(i);
+ TR cout << "in index ctor loop, i = " << to_string(i) << endl;
+ put(clauses[i].index_vector, to_clause_no(ci)); // "$$$ UGLY INC" in Java code
+ }
+#undef TR
+ }
+
+/**
+ * "Tests if the head of a clause, not yet copied to the heap
+ * for execution, could possibly match the current goal, an
+ * abstraction of which has been placed in [the index vectors]."
+ * ("abstraction of which" seems to refer to the variable
+ * positions. With the original tag format, these are V_:0 == 0)
+ */
+
+// Reasonable candidate for loop unrolling.
+// Sentinel search could also work if the sentinel
+// position is temporarily give two different
+// values in the two vectors. Only called from
+// one place in unfold(), so also a reasonable
+// candidate for inlining.
+
+ bool index::possible_match(const Spine* sp,
+ const Clause& cl)
+#ifndef COUNTING_MATCHES
+ const
+#endif
+ {
+ if (!indexing) return true;
+
+ for (size_t i = 0; i < MAXIND; i++) {
+ cell x = sp->index_vector[i];
+ cell y = cl.index_vector[i];
+
+ if (x == cell::abstr_var() || y == cell::abstr_var())
+ continue;
+ if (x != y)
+ break;
+ }
+#ifdef COUNTING_MATCHES
+ n_matches++;
+#endif
+ return true;
+ }
+
+ /* Tarau's IMap.java: K = Integer, a Java object reference.
+ * "key": index vector element.
+ * val : clause number (not index)
+ * Java automagically "boxes" key to an Integer.
+ * So we need to hash in imaps on the vector elt?
+ * How does this work with matching clauses in indexing?
+ /*
+ final boolean put(final K key, final int val) {
+ IntSet vals = map.get(key);
+ if (null == vals) {
+ vals = new IntSet();
+ map.put(key, vals);
+ }
+ return vals.add(val);
+ }
+ */
+
+ void index::put(const t_index_vector &iv, ClauseNumber cls_no) {
+
+#define TR if(0)
+ for (int arg_pos = 0; arg_pos < MAXIND; arg_pos++) {
+ cell vec_elt = iv[arg_pos];
+
+ TR cout << " arg_pos = " << to_string(arg_pos)
+ << " vec_elt = " << to_string(vec_elt.as_int()) << endl;
+
+ if (!(vec_elt == cell::null()) && !vec_elt.is_var()) { // in Java code, basically: != tag(V_,0)
+
+ // INDEX PARTLY FAILED BEFORE WHEN CELL SIGN BIT ON
+ // Probably because 0 is tag(V_,0) with sign bit off
+
+ TR cout << " imaps[" << arg_pos << "].put("
+ << cls_no.as_int() << ","
+ << vec_elt.as_int() << ")" << endl;
+ imaps[arg_pos].put(cls_no, vec_elt);
+ }
+ else {
+ // this can include vec_elt == cell::null case, but maybe that's OK
+ /* "If [var_maps[arg_pos]] denotes the set of clauses
+ * having variables in position [arg_pos], then any of them
+ * can also unify with our goal element"
+ */
+ int c = cls_no.as_int();
+
+ TR cout << " var_maps[" << arg_pos << "].add_key(" << c << ")" << endl;
+ var_maps[arg_pos]->add_key(c);
+ assert(var_maps[arg_pos]->contains(c));
+ }
+ }
+#undef TR
+ }
+
+/**
+ * "Makes, if needed, registers associated to top goal of a Spine.
+ * These registers will be reused when matching with candidate clauses.
+ * Note that [index_vector] contains dereferenced cells - this is done once for
+ * each goal's toplevel subterms." [Engine.java]
+ * This is in Engine, on-demand construction of index args,
+ * but it could be in initialization.
+ * The initial tests could be in an inline member function.
+ */
+ void index::makeIndexArgs(CellStack &heap, Spine *G, cell goal) {
+#define TR if(0)
+ if (!indexing) return;
+
+ if (!(G->index_vector[0] == cell::null())
+ || !G->hasGoals()
+ )
+ return;
+
+ getIndexables(G->index_vector, heap, goal);
+ G->unifiables = matching_clauses_(G->index_vector);
+#undef TR
+ }
+
+// copypasta from
+// https://prepinsta.com/data-structures-algorithms/sorting-of-array/
+// Ridiculous that I have to do this, but
+// (1) with the shorter vectors in C++, I needed to use std::sort()
+// but the CPU+RAM overhead of the vector is high compared to the
+// actual sorting steps and in-place sort memory requirements;
+// (2) even C's sort() takes a pointer to a comparison function,
+// when all that's needed here is int comparison;
+// (3) sorting seems to be called only once in all of this code
+// so the sort function can even be inlined.
+//
+// Steps to add:
+// Algorithm: special-casing of arrays of length 1 and 2,
+// maybe even 3.
+// In use: separately sort the two results of calling intersect
+// then do mergesort on them, although this can't be
+// done in-place.
+//
+//
+
+ // Function to swap two elements
+ inline void swap(ClauseNumber& a, ClauseNumber& b) {
+ ClauseNumber temp = a;
+ a = b;
+ b = temp;
+ }
+
+ // Partition the array and return the pivot index
+ inline int partition(ClauseNumber *arr, int low, int high) {
+ ClauseNumber pivot = arr[high]; // Choose the rightmost element as the pivot
+ int i = low - 1; // Index of the smaller element
+
+ for (int j = low; j <= high - 1; j++) {
+ if (arr[j].as_int() <= pivot.as_int()) {
+ i++;
+ swap(arr[i], arr[j]);
+ }
+ }
+
+ swap(arr[i + 1], arr[high]);
+ return i + 1; // Return the pivot index
+ }
+
+ // "Quick sort implementation"
+ inline void quickSort(ClauseNumber *arr, int low, int high) {
+ if (low < high) {
+ int pivotIndex = partition(arr, low, high);
+
+ // Recursively sort the left and right subarrays
+ quickSort(arr, low, pivotIndex - 1);
+ quickSort(arr, pivotIndex + 1, high);
+ }
+ }
+
+ /* "When looking for the clauses matching an element of
+ * the list of goals to solve, for an indexing element x occurring in position i,
+ * we fetch the set Cx,i of clauses associated to it.
+ * If Vi denotes the set of clauses having variables in position i,
+ * then any of them can also unify with our goal element.
+ * Thus we would need to compute the union of the sets Cx,i and Vi
+ * for each position i, and then intersect them
+ * to obtain the set of matching clauses.
+ * We will not actually compute the unions, however.
+ * Instead, for each element of the set of clauses corresponding to
+ * the gpredicate nameh (position 0), we retain only those which are
+ * either in Cx,i or in Vi for each i > 0.
+ * We do the same for each element for the set V0 of clauses
+ * having variables in predicate positions (if any)." [HHG/ICLP 2017]
+ */
+
+ typedef cls_no_set *cls_no_set_vec[IV_LEN];
+
+ ClauseNumber* intersect0_p( const cls_no_set* m, // maps[0] or vmaps[0]
+ const cls_no_set_vec& maps,
+ const cls_no_set_vec& vmaps,
+ ClauseNumber* cls_nos_p,
+ int push_count) {
+#define TR if(0)
+
+ TR cout << " intersect0_p: m->length()=" << to_string(m->length()) << endl;
+
+ ClauseNumber* cnp = cls_nos_p;
+
+ TR cout << " m->length()=" << to_string(m->length()) << endl;
+
+ for (int k = 0; k < m->length(); k++) {
+ if (!m->is_free(k)) {
+ ClauseNumber cn = m->get_key_at(k);
+ TR cout << "cn = " << to_string(cn.as_int()) << endl;
+ bool found = true;
+ for (int i = 1; i < push_count; i++) {
+ TR cout << "i loop: i = " << to_string(i) << endl;
+ ClauseNumber v0;
+ if (maps[i] == 0) v0 = cls_no_set::no_value();
+ else v0 = maps[i]->get(cn.as_int());
+ TR cout << " v = " << to_string(v0.as_int()) << endl;
+ if (v0 == cls_no_set::no_value()) {
+ ClauseNumber v1;
+ if (vmaps[i] == nullptr) v1 = cls_no_set::no_value();
+ else v1 = vmaps[i]->get(cn.as_int());
+ if (v1 == cls_no_set::no_value()) {
+ found = false;
+ break;
+ }
+ }
+ }
+ if (found) {
+ TR cout << " at data[" << to_string(k) << "] found key="
+ << to_string(cn.as_int()) << " to push to result" << endl;
+ *cnp++ = cn;
+ }
+ }
+ }
+
+ TR cout << "MAXIND*push_count=" << to_string(MAXIND*push_count) << " cnp - cls_nos_p = " << cnp - cls_nos_p << endl;
+ return cnp;
+#undef TR
+ }
+
+ /*
+ * This translation is from IMap.get, with ArrayList for ms & vms
+ */
+ // The iv loop continue condition was "== 0" in Java code.
+ // In that code, since V_ cells are never zero (even though V_ == 0),
+ // zero may be a kind of null- terminator for index vectors
+ // that are shorter than MAXIND.
+ // This works because V_ cells can't be at index 0 in the heapf.
+ //
+ // This complicates the idea of changing to relative addressing
+ // in variables, with a zero offset indicating that dereferencing
+ // has ended at a variable. I'll keep cell::BAD for now.
+ // If this is true, the mystery is why there's no loop break
+ // on zero.
+
+ vector index::matching_clauses_(t_index_vector& iv) {
+#define TR if(0)
+ TR cout << "Entering matching_clauses, IV_LEN=" << to_string(IV_LEN) << endl;
+
+ cls_no_set_vec msp;
+ cls_no_set_vec vmsp;
+
+ // init empty placeholders
+ for (int i = 0; i < IV_LEN; ++i)
+ msp[i] = vmsp[i] = nullptr;
+
+ TR cout << " ==== matching_clauses: start iv loop, imaps.size()=" << imaps.size() << endl;
+
+ int push_count = 0;
+
+ for (int i = 0; /*i < MAXIND*/; i++) { // sentinel stops this
+ if (iv[i] == cell::abstr_var())
+ continue;
+ else if (iv[i] == cell::null()) // "index vectors are null-terminated if < MAXIND"
+ break;
+ else {
+ TR cout << "OK, starting to add to msp & vmsp with i=" << to_string(i) << endl;
+ cls_no_set* m = imaps[i].map[iv[i]];
+ msp[push_count] = m;
+ vmsp[push_count] = var_maps[i];
+ ++push_count;
+
+ TR cout << " iv[" << i << "]=" << iv[i].show()
+ << ", ms:" << (m==nullptr? "_" : m->show())
+ << ", vms:" << (var_maps[i]==nullptr? "_" : var_maps[i]->show())
+ << ", push_count =" << push_count << endl;
+ }
+ }
+
+ TR cout << " ==== matching_clauses: rest of processing" << endl;
+
+ // vector cs;
+
+ int n_to_alloc = MAXIND * push_count; // ????
+
+ auto csp = (ClauseNumber*) _malloca(n_to_alloc * sizeof(ClauseNumber));
+ if (csp == nullptr) abort();
+ // cout << "csp len = " << n_to_alloc << endl;
+
+ // was IntMap.java intersect, expanded here:
+ TR cout << " msp[0].m_size=" << to_string(msp[0]->size()) << endl;
+ ClauseNumber *new_end = intersect0_p(msp[0], msp, vmsp, csp, push_count);
+
+ TR cout << " vms[0].m_size=" << to_string(vmsp[0]->size()) << endl;
+ new_end = intersect0_p(vmsp[0], msp, vmsp, new_end, push_count);
+ long cs_size = new_end - csp;
+
+ if (cs_size > n_to_alloc)
+ abort();
+
+#ifdef RAW_QUICKSORT
+ quickSort(csp, 0, cs_size - 1);
+#endif
+ // cout << " after intersect0_p new_end - csp=" << cs_size << endl;
+
+ // is: clause numbers converted to indices
+ vector is; /*= cs.toArray() in Java, emulated here but
+ * with conversion to indices. Could
+ * probably be done on-the-fly in intersect0. */
+ is.reserve(cs_size);
+
+ TR cout << " (1) is.size()=" << is.size() << endl;
+ TR cout << " (1) is.capacity()=" << is.capacity() << endl;
+
+ for (int i = 0; i < cs_size; ++i)
+ is.emplace_back(to_clause_idx(csp[i]));
+
+ TR cout << " is.size()=" << to_string(is.size()) << endl;
+
+ /* "Finally we sort the resulting set of clause numbers and
+ * hand it over to the main Prolog engine for unification
+ * and possible unfolding in case of success."
+ *
+ * I.e., respect standard Prolog clause ordering.
+ */
+ TR cout << " (2) is.size()=" << is.size() << endl;
+ TR cout << " (2) is.capacity()=" << is.capacity() << endl;
+#ifndef RAW_QUICKSORT
+ if (is.size() > 1)
+ std::sort(is.begin(), is.end());
+#endif
+ TR for (int i = 0; i < cs_size; ++i) {
+ cout << " is[" << i << "]=" << is[i].as_int() << endl;
+ }
+
+ TR cout << " ==== matching_clauses: exiting" << endl << endl;
+
+ _freea((void*)csp);
+
+ return is;
+#undef TR
+ }
+
+ string index::show(const t_index_vector& iv) const {
+ string s = "";
+ char d = '<';
+ for (int arg_pos = 0; arg_pos < MAXIND; ++arg_pos) {
+ s += d;
+ s += to_string(iv[arg_pos].as_int());
+ d = ',';
+ }
+ s += ">";
+ return s;
+ }
+
+ string index::show() const {
+ string s = "index::show():";
+
+ for (int i = 0; i < MAXIND; ++i) {
+ s += "\n INDEX: [" + to_string(i);
+ s += "]";
+ s += "\n imaps: " + imaps[i].show();
+ s += "\n var_maps: " + var_maps[i]->show();
+ }
+
+ s += "\n";
+ return s;
+ }
+} // namespace
diff --git a/IP/cpp/iProlog/index.h b/IP/cpp/iProlog/index.h
new file mode 100644
index 00000000..7e8d953e
--- /dev/null
+++ b/IP/cpp/iProlog/index.h
@@ -0,0 +1,85 @@
+#pragma once
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+using namespace std;
+
+#include
+#include
+#include "cell.h"
+
+namespace iProlog {
+ typedef array t_index_vector; // deref'd cells
+}
+
+#include "IMap.h"
+#include "clause.h"
+#include "spine.h"
+#include "CellStack.h"
+
+namespace iProlog {
+
+class Spine;
+class Engine;
+
+class index {
+
+public:
+
+ /* "For each argument position in the head of a clause
+ * (up to a maximum that can be specified by the programmer [MAXIND])
+ * it associates to each indexable element(symbol, number or arity)
+ * the set of clauses [indicated by clause number]
+ * where the indexable element occurs in that argument position."
+ */
+#ifndef RAW_IMAPS
+ typedef vector IMaps; // probably need safer subclass of vector
+ // e.g., use at() in overloaded [] op
+ IMaps imaps;
+#else
+ typedef array IMaps;
+ IMaps imaps;
+#endif
+
+ /* "The clauses having variables in an indexed argument position are also
+ * collected in a separate set for each argument position."
+ */
+ // vector var_maps;
+ array var_maps;
+
+ long n_matches = 0;
+
+ index(CellStack &heap, vector& clauses);
+
+ bool possible_match(const Spine *sp,
+ const Clause& cl)
+#ifndef COUNTING_MATCHES
+ const
+#endif
+ ;
+
+ void put(const t_index_vector& iv, ClauseNumber clause_no);
+
+ void makeIndexArgs(CellStack &heap, Spine *G, cell goal);
+
+ vector matching_clauses_(t_index_vector& iv);
+
+ static inline ClauseNumber to_clause_no(ClauseIndex i) { ClauseNumber x(i.as_int()+1); return x; }
+
+ static inline bool is_cls_no(ClauseNumber cls_no) { return cls_no.as_int() != 0; }
+
+ static inline ClauseIndex to_clause_idx(ClauseNumber cl_no) { return cl_no.dec(); }
+
+ void getIndexables(t_index_vector &index_vector, CellStack &heap, cell goal);
+
+ string show(const t_index_vector& iv) const;
+
+ cell cell2index(CellStack& heap, cell c) const;
+
+ string show() const;
+};
+
+} // namespace
diff --git a/IP/cpp/iProlog/main.cpp b/IP/cpp/iProlog/main.cpp
new file mode 100644
index 00000000..62c13629
--- /dev/null
+++ b/IP/cpp/iProlog/main.cpp
@@ -0,0 +1,526 @@
+/*
+ * iProlog/C++
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+#include
+#include
+#include
+#if 0
+#include
+#endif
+#include
+#include
+
+#include "prog.h"
+#include "toks.h"
+
+#include "Inty.h"
+#include "RelocStack.h"
+#include "sym_tab.h"
+
+
+std::string file2string(std::string path) {
+ std::ifstream f(path);
+ if (!f.good())
+ throw std::invalid_argument(path + " not found");
+ std::stringstream s;
+ s << f.rdbuf();
+ return s.str();
+}
+
+
+using namespace std;
+using namespace chrono;
+
+std::string current_working_directory()
+{
+#if 0
+ std::filesystem::__cxx11::path p = std::filesystem::current_path();
+ cout << "p = " << p << endl;
+#endif
+
+#ifdef CPP17
+ std::filesystem::path cwd = std::filesystem::current_path();
+ return cwd.string();
+#else
+ return "C:/Users/Michael Turner/Documents/Github/iProlog/IP/";
+#endif
+}
+
+namespace iProlog {
+
+
+void show(cstr h, int i) {
+ cout << h << i << " (oct)" << std::oct << i << endl;
+}
+
+ CellStack heap;
+ sym_tab sym;
+
+string showCell(cell w) {
+ int t = w.s_tag();
+ int val = w.arg();
+ string s = "";
+
+ switch (t) {
+ case cell::V_: s = cstr("v:") + val; break;
+ case cell::U_: s = cstr("u:") + val; break;
+ case cell::N_: s = cstr("n:") + val; break;
+ case cell::C_: s = cstr("c:") + val; break; //had getSym(...)
+ case cell::R_: s = cstr("r:") + val; break;
+ case cell::A_: s = cstr("a:") + val; break;
+ default: s = cstr("*BAD*=") + w.as_int();
+ }
+ return s;
+}
+
+/*static*/ vector &
+put_ref( const string arg,
+ unordered_map> &refs,
+ int clause_pos) {
+ vector& Is = refs[arg];
+ if (Is.empty()) {
+ Is = vector();
+ refs[arg] = Is;
+ }
+ Is.push_back(clause_pos);
+ return Is;
+}
+
+/*
+ * "Encodes string constants into symbols while leaving
+ * other data types untouched." [Engine.java]
+ */
+cell encode(int t, const string s) {
+ size_t w;
+ try {
+ w = stoi(s);
+ }
+ catch (const std::invalid_argument& e) {
+ if (t == cell::C_)
+ w = int(sym.addSym(s));
+ else {
+ cstr err = string("bad number form in encode=") + t + ":" + s + ", [" + e.what() + "]";
+ throw logic_error(err);
+ }
+ }
+ // cout << " encode: w=" << w << " returning " << cell::tag(t, w).as_int() << endl;
+ return cell::tag(t, w);
+}
+
+/**
+ * "Places a clause built by the Toks reader on the heap." [Engine.java]
+ */
+Clause putClause(vector cells, vector &vskel, int neck) {
+ int base = heap.getTop()+1;
+ cell b = cell::tag(cell::V_, base);
+ // ... because b is used later in '+' ops that would otherwise mangle tags.
+ int len = int(cells.size());
+ CellStack::pushCells(heap, b, 0, len, cells);
+
+ cell* src = vskel.data();
+#ifdef RAW_SKEL
+ cell* dst = (cell*)malloc(vskel.size() * sizeof(cell));
+#else
+ cell* dst = vskel.data();
+#endif
+
+ // for goals list being C++ vector, in-place reloc
+ if (has_raw_cell_heap)
+ cell::cp_cells(b, src, dst, (int)vskel.size());
+ else
+ for (size_t i = 0; i < vskel.size(); i++)
+ dst[i] = dst[i].relocated_by(b);
+
+#ifdef RAW_SKEL
+ Clause rc = Clause(len, dst, vskel.size(), base, neck);
+#else
+ Clause rc = Clause(len, vskel, vskel.size(), base, neck);
+#endif
+ return rc;
+}
+
+ void linker(unordered_map> refs,
+ vector &cells,
+ vector &goal_refs, // R_ (ref) cells only
+ vector &compiled_clauses) {
+ // Java:
+ // final Iterator K = refs.values().iterator();
+ // while (K.hasNext())
+
+ for (auto kIs = refs.begin(); kIs != refs.end(); ++kIs) {
+ vector Is = kIs->second;
+ if (Is.size() == 0)
+ continue;
+ assert(goal_refs.size() > 0);
+
+ // "finding the A among refs" [Engine.java]
+ bool found = false;
+ size_t leader = -1;
+ for (size_t j = 0; j < Is.size(); ++j)
+ if (/*cell::isArgOffset(cells[j])*/
+ cells[Is[j]].s_tag() == cell::A_) {
+ leader = Is[j];
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ // "for vars, first V others U" [Engine.java]
+ leader = Is[0];
+ for (size_t i = 0; i < Is.size(); ++i)
+ if (Is[i] == leader)
+ cells[Is[i]] = cell::tag(cell::V_, Is[i]);
+ else
+ cells[Is[i]] = cell::tag(cell::U_, leader);
+ }
+ else {
+ for (size_t i = 0; i < Is.size(); ++i) {
+ if (Is[i] == leader)
+ continue;
+ cells[Is[i]] = cell::tag(cell::R_, leader);
+ }
+ }
+ }
+
+ int neck;
+ if (1 == goal_refs.size())
+ neck = int(cells.size());
+ else
+ neck = goal_refs[1L].arg();
+
+ Clause C = putClause(cells, goal_refs, neck); // safe to pass all?
+
+ compiled_clauses.push_back(C);
+ }
+
+
+vector dload(const cstr s) {
+ vector>> clause_asm_list = Toks::toSentences(s);
+ vector compiled_clauses;
+
+ for (vector> unexpanded_clause : clause_asm_list) {
+ // map refs;
+ unordered_map> refs = unordered_map>();
+ vector cells;
+ vector goal_refs;
+ int k = 0;
+ for (vector clause_asm : Toks::mapExpand(unexpanded_clause)) {
+
+ size_t line_len = clause_asm.size();
+
+ goal_refs.push_back(cell::reference(k++));
+ cells.push_back(cell::argOffset(line_len));
+ for (string cell_asm_code : clause_asm) {
+ if (1 == cell_asm_code.length())
+ cell_asm_code = "c:" + cell_asm_code;
+ string arg = cell_asm_code.substr(2);
+
+ switch (cell_asm_code[0]) {
+ case 'c': cells.push_back(encode(cell::C_, arg)); k++; break;
+ case 'n': cells.push_back(encode(cell::N_, arg)); k++; break;
+ case 'v': put_ref(arg, refs, k);
+ cells.push_back(cell::tag(cell::BAD, k)); k++; break;
+ case 'h': refs[arg].push_back(k-1);
+ assert(k > 0);
+ cells[size_t(k-1)] = cell::argOffset(line_len-1);
+ goal_refs.pop_back(); break;
+ default: throw logic_error(cstr("FORGOTTEN=") + cell_asm_code);
+ }
+ }
+ }
+ linker(refs, cells, goal_refs, compiled_clauses);
+ }
+
+ size_t clause_count = compiled_clauses.size();
+ vector all_clauses = vector(clause_count);
+
+ for (int i = 0; i < clause_count; i++) {
+ all_clauses[i] = compiled_clauses[i];
+ }
+
+ return all_clauses;
+ }
+
+ void test_tagging() {
+ int n = -1;
+ assert(n >> 1 == -1);
+ // Need to be sure that V_ and U_ < R_,
+ // and the largest possible value of R_ < lowest possible value
+ // of the lowest type of cell.
+ //
+ cout << "n_ref_bits=" << cell::n_ref_bits << endl;
+
+ show("unshifted_tag_mask=", cell::unshifted_tag_mask);
+ assert(cell::V_ < cell::C_ && cell::V_ < cell::N_ && cell::V_ < cell::A_);
+ assert(cell::U_ < cell::C_ && cell::U_ < cell::N_ && cell::U_ < cell::A_);
+ assert(cell::R_ < cell::C_ && cell::R_ < cell::N_ && cell::R_ < cell::A_);
+
+ cout << "ref_mask=" << cell::ref_mask << " (oct)" << std::oct << cell::ref_mask << endl;
+ cout << "tag_mask=" << cell::tag_mask << " (oct)" << std::oct << cell::tag_mask << endl;
+
+ cell r = cell::tag(cell::R_, 1);
+ cout << "r=" << r.as_int() << " (oct)" << std::oct << r.as_int() << endl;
+ size_t dtr = r.arg();
+ int tor = r.s_tag();
+ cout << "arg of r)=" << dtr << " (oct)" << std::oct << dtr << endl;
+ cout << "tag of r)=" << tor << " (oct)" << std::oct << tor << endl;
+
+ assert(!r.is_var());
+ assert(!r.is_const());
+ assert(r.is_reloc());
+
+ cell rx = cell::tag(cell::R_, 0);
+ cell ry = cell::tag(cell::R_, 1);
+ // Int rz = tag(R_,-1);
+ assert(ry.as_int() > rx.as_int());
+ // assert(cell::hi_order_tag ? rx.as_int() < 0 : rx.as_int() > 0);
+ // assert (use_sign_bit ? ry < rz : ry > rz);
+
+ int max_unsigned_ref = (1 << cell::n_ref_bits) - 1;
+
+ cout << "max_unsigned_ref=" << max_unsigned_ref << endl;
+
+ assert(cell::C_ > cell::R_);
+ assert(cell::C_ < cell::A_ && cell::C_ < cell::N_);
+
+ cell rmax = cell::tag(cell::R_, max_unsigned_ref);
+ cout << "rmax=" << rmax.as_int() << " (oct)" << std::oct << rmax.as_int() << endl;
+
+ cell cmax = cell::tag(cell::C_, max_unsigned_ref);
+ // assert (cmax > rmax);
+ cout << "rx=" << rx.as_int() << " (oct)" << std::oct << rx.as_int() << endl;
+ cout << "cmax=" << cmax.as_int() << " (oct)" << std::oct << cmax.as_int() << endl;
+ // assert (cmax > rx); // fails with use_sign_bit because ...
+
+ int val = 7;
+ const int the_tag = cell::A_;
+ cout << "test_tagging: tag_mask = " << std::oct << cell::tag_mask << endl;
+ cell i = cell::tag(the_tag, val);
+ size_t w = i.arg();
+ int t = i.s_tag();
+ cout << "In test_tagging, w=" << std::oct << w << endl;
+ assert(t == the_tag);
+ assert(w == val);
+ cout << std::dec << endl;
+ cell bad = cell::tag(cell::BAD, val);
+ assert(!bad.is_ref());
+ assert(!bad.is_var());
+ assert(!bad.is_const());
+ assert(!bad.is_reloc());
+
+ cell a = cell::tag(cell::A_, 0);
+ if (a.is_var()) abort();
+ if (!a.is_offset()) abort();
+ }
+
+ void testSharedCellList() {
+ CL_p p;
+ cell h;
+ p = CellList::mk_shared();
+ CL_p q;
+ q = CellList::mk_shared(h);
+ }
+
+ // int * about 25%-30% faster than vector
+ void vec_bench()
+ {
+ using namespace chrono;
+
+ const int OC = 200000000;
+ const int IC = 10;
+
+ cout << "vectors benchmark" << endl;
+ {
+ int x = 0;
+ auto b = steady_clock::now();
+
+ vector is(IC);
+ for (int i = 0; i < OC; ++i) {
+ for (int j = 0; j < IC; ++j)
+ is[j] = j + x++;
+ for (int j = 0; j < IC; ++j)
+ is[j] = -is[j] + x--;
+ }
+
+ cout << "after run, x = " << to_string(x) << endl;
+ auto e = steady_clock::now();
+ long long d = duration_cast(e - b).count();
+ cout << "done in " << std::dec << duration_cast(e - b).count() << endl;
+ cout << "or " << (double)d / 1000 << endl;
+ }
+
+ cout << "int * benchmark" << endl;
+ {
+ int x = 0;
+ auto b = steady_clock::now();
+ int* is = (int*)malloc(sizeof(int) * IC);
+ for (int i = 0; i < OC; ++i) {
+ for (int j = 0; j < IC; ++j)
+ is[j] = j + x++;
+ for (int j = 0; j < IC; ++j)
+ is[j] = -is[j]+ x--;
+ }
+
+ cout << "after run, x = " << to_string(x) << endl;
+ auto e = steady_clock::now();
+ long long d = duration_cast(e - b).count();
+ cout << "done in " << std::dec << duration_cast(e - b).count() << endl;
+ cout << "or " << (double)d / 1000 << endl;
+ }
+ }
+
+ void int_bench()
+ {
+ using namespace chrono;
+ auto b = steady_clock::now();
+ cout << "before run" << endl;
+ //////////////
+ cout << "after run" << endl;
+ auto e = steady_clock::now();
+ long long d = duration_cast(e - b).count();
+ cout << "done in " << std::dec << duration_cast(e - b).count() << endl;
+ cout << "or " << (double)d / 1000 << endl;
+ }
+
+ void test_IntSet() {
+ IntSet im;
+ cout << endl << "************** test_IntSet() ***********************************" << endl;
+
+ for (int i = 1; i < 9; ++i) {
+ // cout << "about to call add_key";
+ im.add_key(i);
+ // cout << "about to call put";
+ im.put(i, i + 1);
+ // cout << " about to call get";
+ int v = im.get(i);
+ // cout << "test_IntSet i = " << to_string(i) << endl;
+ if (!(v == i + 1)) abort();
+ }
+
+ cout << "************** END test_IntSet() ***********************************" << endl;
+ }
+
+ void test_IMap() {
+#define TR if(1)
+ cout << "************** test_IMap() ***********************************" << endl;
+ IMap x;
+ cell c = cell::tag(cell::V_, 3);
+ ClauseNumber cls_no = 7;
+
+ TR cout << "===== calling x.put(cellbox->i=" << c.as_int() << ", cls_no=" << cls_no.as_int() << endl;
+
+ x.put(cls_no, c);
+
+ cls_no_set* the_intset = x.get_cls_no_set(c);
+ TR cout << " got intset, length=" << the_intset->length() << endl;
+ TR cout << "the_intset: " << the_intset->show() << endl;
+
+ TR cout << "Imap x is now " << x.show() << endl;
+
+ assert(the_intset->contains(cls_no.as_int()));
+
+ cout << endl << "************** END test_IMap() ***********************************" << endl;
+#undef TR
+ }
+
+ int do_with(int argc, char* argv[])
+ {
+#if 0
+ vec_bench();
+ exit(0);
+#endif
+ iProlog::test_IntSet();
+ // exit(0);
+
+ cout << "...starting execution of " << argv[0] << endl;
+ test_IMap();
+#if 0
+ test_tagging();
+ testSharedCellList();
+#endif
+ string where_i_am = current_working_directory();
+ string test_directory = where_i_am + "/progs/";
+ cout << "... in " << where_i_am << endl;
+
+ if (argc == 1) {
+ cerr << "Must supply name of a program in directory " << test_directory << endl;
+ exit(-1);
+ }
+ try {
+ string fname;
+ bool print_ans;
+
+ fname = argv[1];
+ print_ans = argc == 3 ? string(argv[2]) == "true" : false;
+
+ string pl_nl = test_directory + fname + ".nl";
+
+ cout << "==============================================================" << endl;
+
+ string source = file2string(pl_nl);
+ vector clauses = dload(source);
+
+ index* Ip = nullptr;
+
+ if (indexing) {
+ Ip = new index(heap, clauses);
+ cout << Ip->show() << endl;
+ }
+
+ Prog *p = new Prog(heap,clauses,sym,Ip); // any index-building done there
+
+ p->ppCode();
+
+ { using namespace chrono;
+ auto b = steady_clock::now();
+ cout << "before run" << endl;
+ p->run(print_ans);
+ cout << "after run" << endl;
+ auto e = steady_clock::now();
+ long long d = duration_cast(e - b).count();
+ cout << "done in " << std::dec << duration_cast(e - b).count() << endl;
+ cout << "or " << (double)d / 1000 << endl;
+ }
+
+ cout << p->stats() << endl;
+
+cout << "---------------------------------------" << endl;
+
+ delete p;
+ }
+ catch (exception& e) {
+ cout << e.what() << endl;
+ }
+
+ return 0;
+ }
+
+ Engine* ep = nullptr;
+ void set_engine(void* e) { ep = (Engine *) e; }
+
+ void checkit_() {
+#define TR if(0)
+ if (!integrity_check) return;
+ cout << "checkit..." << endl;
+ if (indexing) {
+ assert(ep->Ip != nullptr);
+ for (int i = 0; i < ep->clauses.size(); ++i) {
+ TRY{
+ cell hd = ep->clauses.at(i).skel[0];
+ assert(ep->clauses[i].base >= 0);
+ assert(ep->clauses[i].base < ep->heap.size());
+ } CATCH("blowing up in check it")
+ }
+ }
+#undef TR
+ }
+
+} // end iProlog
+
+int main(int argc, char* argv[]) {
+ return iProlog::do_with(argc, argv);
+}
+
diff --git a/IP/cpp/iProlog/makefile b/IP/cpp/iProlog/makefile
new file mode 100644
index 00000000..1bd30058
--- /dev/null
+++ b/IP/cpp/iProlog/makefile
@@ -0,0 +1,62 @@
+O= \
+ CellList.o \
+ CellStack.o \
+ clause.o \
+ engine.o \
+ IntMap.o \
+ main.o \
+ Object.o \
+ prog.o \
+ spine.o \
+ toks.o \
+ IMap.o \
+ index.o
+
+
+OPT=-Os
+#kOPT=-Ofast
+#OPT=
+
+# DBG=-g
+DBG=
+
+#PRF=-pg
+PRF=
+
+CPPFLAGS= $(DBG) $(OPT) $(PRF) --std=c++17
+
+a.out: $O
+ g++ $(CPPFLAGS) $O
+
+
+$O: makefile defs.h
+
+index.o: cell.h
+
+CellList.o: cell.h CellList.h
+
+CellStack.o: cell.h CellStack.h
+
+clause.o: cell.h index.h clause.h
+
+Engine.o: Engine.h
+
+Imap.o: Imap.h
+
+index.o: index.h
+
+IntMap.o: IntMap.h
+
+main.o: toks.h prog.h
+
+Object.o: Object.h
+
+prog.o: Engine.h prog.h
+
+spine.o: spine.h
+
+toks.o: toks.h cell.h
+
+
+clean:
+ rm $O a.out errs gmon.out
diff --git a/IP/cpp/iProlog/notes b/IP/cpp/iProlog/notes
new file mode 100644
index 00000000..cedb5587
--- /dev/null
+++ b/IP/cpp/iProlog/notes
@@ -0,0 +1,157 @@
+operator new (Inty vec alloc) in matching_clauses
+intersect_0p IntSet::get
+Hash equal_to
+unify CellStack::pop
+new_Spine alloc call op new & Inty alloc vec
+getIndexables
+
+Done:
+-- fixed: popSpine calling ~Spine?!?!?!
+-- removed redundant buffer for Spine ctor
+
+verification:
+
+try 16-bit cell ints
+review idea of relative addressing
+look at raw hashmap implementation
+look at how to put spines on heap
+figure out CellList threading
+figure out where to stack unify_list
+make unification not use C++ stack???
+ goal is at most two stacks, growing from
+ a base heap forward, and from the end
+ of available space backward
+external compiled form -- just symtab & "bytecode"
+
+
+Roadmap to iProlog with 4K of RAM, 32K of flash
+
+Goal: "Soft-realtime Natural Language Logic Programming on a Picosatellite"
+
+Get relative addressing working
+ - besides speedup, may make binary code more compressible
+ - LZ4 (about 2x) approaches RAM memcpy speed
+
+Put dload et al. in separate C++ class or prog.h/cpp
+ - don't need its buffers
+ - download ASCII opcodes to MSP/CC430
+
+Try to make unfold & unify[_args] "stackless"
+ - stacks that could b threaded into heap:
+ - C++ code stack
+ - ustack
+ - spine stack
+ - CellList
+
+Dispense with indexing, at least at first
+ - implement as callbacks, for full orthogonality?
+ - custom indexer?
+ - how to "heapify" it?
+
+Binary output from compiler
+ - endianess perhaps indicated in a header
+ - LZ4 compression
+
+Find out whether CC430 can write to its own flash
+ - if so, and the address space issue issue can be solved
+ look at how initial logic-programming code can be put in flash
+ to free up RAM -- which means a split heap, a little hairy
+
+"Garbage collect" initialization code for logic programs?
+ - need some control flow analysis to be sure of
+ what to dump and how soon to dump it
+
+Try compression
+ - probably need new "opcode" to trigger inflate
+ - larger (32K) code space enables dictionary compression
+ - but split of flash/RAM adddress space a problem
+
+Optimize "deterministic" clauses
+ - maybe extend to threaded code?
+
+------------------------------------------------------------
+
+cell2index does a getRef -- maybe bad news for the idea
+of relative addressing in heap cells
+
+pp.11-12 of paper:
+
+"For each argument position in the head of a clause (up to a maximum
+that can be specified by the programmer) ..."
+
+ MAXIND
+
+"...it associates to each indexable element (symbol, number or arity)...
+
+ C,N,A
+
+"...the set of clauses where the indexable element occurs in that argument
+position."
+
+ cell sub-type: indexable_elt?
+
+"For deep indexing, the argument position can be generalized to be the
+integer sequence defining the path leading to the indexable element in
+a compound term."
+
+ hence "leader"? (Or does he even do this?)
+ not clear how the integer sequence would be derived
+
+"The clauses having variables in an indexed argument position are also
+collected in a separate set for each argument position."
+
+ vmaps - maps var pos in clause head to ...?
+
+"Sets of clause numbers associated to each (tagged) indexable element are
+supported by an IntMap implemented as a fast int-to-int hash table (using
+linear probing)."
+
+ clause_no type?
+ indexable_elt -> {clause_no*}
+ clause_no -> indexable_elt?
+ indexable_elt -> clause_no?
+
+"An IntMap is associated to each indexable element by a HashMap."
+
+ This is where Integer comes in. But what is it an integer of?
+
+"The HashMaps are placed into an array indexed by the argument position
+to which they apply."
+
+ vector imaps
+
+"When looking for the clauses matching an element of the list of goals
+to solve, for an indexing element x occurring in position i, we fetch
+the set C[x,i] of clauses associated to it."
+
+ Presumably including the functor ("predicate name") of the clause...
+ See below.
+
+"If V[i] denotes the set of clauses having variables in position i,
+then any of them can also unify with our goal element."
+
+"Thus we would need to compute the union of the sets C[x,i] and V[i] for
+each position i,..."
+
+ Why?
+
+"...and then intersect them to obtain the set of matching
+clauses. We will not actually compute the unions, however. Instead,...
+
+"...for each element of the set of clauses corresponding to the
+“predicate name” (position 0), we retain only those which are either
+in C[x,i] or in V[i] for each i > 0."
+
+ the ms/vms in index()?
+ why i > 0? Because of functor of head?
+
+"We do the same for each element for the set V[0] of clauses having
+variables in predicate positions (if any)."
+
+ "predicate positions" meaning in the predicate name position?
+
+"Finally we sort the resulting set of clause numbers and hand it over
+to the main Prolog engine for unification and possible unfolding in case
+of success."
+
+ Is the sort order an optimization?
diff --git a/IP/cpp/iProlog/profiling b/IP/cpp/iProlog/profiling
new file mode 100644
index 00000000..427e80e1
--- /dev/null
+++ b/IP/cpp/iProlog/profiling
@@ -0,0 +1,13 @@
+big.pl
+ ucrtbase.dll -- huge, but malloc & free, etc. small?
+ 5.5% unfold
+ 1.7% matching clauses
+ 0.6% operator new
+ 2.3% allocate vector of cells
+ 1.7% new Spine
+ 6.9% unify
+ 2.7% concat
+ 2.1% intersect0
+ 0.7% push_cells
+ 2.3% CellStack pop
+ 1.8% cp_cells
diff --git a/IP/cpp/iProlog/prog.cpp b/IP/cpp/iProlog/prog.cpp
new file mode 100644
index 00000000..90b778bd
--- /dev/null
+++ b/IP/cpp/iProlog/prog.cpp
@@ -0,0 +1,238 @@
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+#include
+#include
+#include "Engine.h"
+#include "prog.h"
+
+using namespace std;
+
+namespace iProlog {
+ /**
+ * raw display of a term - to be overridden
+ */
+ /*virtual*/ string Prog::showTermCell(cell x) const {
+ return showTerm(exportTerm(x));
+ }
+
+ /**
+ * raw display of an externalized term
+ */
+ string Prog::showTerm(Object O) const {
+ if (O.type == Object::e_integer)
+ return O.toString();
+ if (O.type == Object::e_vector)
+ return st0(O.v);
+ return O.toString();
+ }
+
+ string Prog::show_index() const {
+ return Ip->show();
+ }
+
+ string Prog::showCells(int base, int len) const {
+ string buf;
+ for (int k = 0; k < len; k++) {
+ cell instr = cell_at(base + k);
+ buf += cstr("[") + (base + k) + "]" + showCell(instr) + " ";
+ }
+ return buf;
+ }
+
+ void Prog::ppTrail() {
+ assert(cell::V_ == 0);
+ for (int i = 0; i <= trail.getTop(); i++) {
+ cell t = trail.get(size_t(i));
+ // pp(cstr("trail_[") + i + "]=" + showCell(t) + ":" + showTermCell(t));
+ pp(cstr("trail_[") + i + "]=" + showCell(t) + ":"
+ + "*[showTermCell(cell) stub]*");
+ }
+ }
+
+ void Prog::pp(const string s) const {
+ std::cout << s << endl;
+ }
+#if 0
+ void Prog::pp(unordered_map syms) const {
+#else
+ void Prog::pp(sym_tab &syms) const {
+#endif
+ pp("pp(syms):");
+ cout << "syms.size()=" << syms.syms.size() << endl;
+ for (auto &kv : syms.syms)
+ cout << " " << kv.first << "," << kv.second << endl;
+ }
+
+ void Prog::ppGoals(CL_p bs) {
+ for (CL_p bp = bs; bp != nullptr; bp = CellList::tail(bs)) {
+ pp(showTerm(exportTerm(CellList::head(bp))));
+ }
+ }
+ void Prog::ppc(const Spine &S) {
+ CL_p bs = S.the_goals;
+ pp(cstr("\nppc: t=") + S.trail_top + ",last_clause_tried=" + S.last_clause_tried + "len=" + bs->size());
+ ppGoals(bs);
+ }
+
+ inline bool Prog::isListCons(cstr name) { return "." == name || "[|]" == name || "list" == name; }
+ inline bool Prog::isOp(cstr name) { return "/" == name || "-" == name || "+" == name || "=" == name; }
+
+ string Prog::stats() const {
+ ostringstream s;
+ s // << heap_.capacity() << ' '
+ // << spines_top << " of " << spines.capacity() << ' '
+ // << trail.capacity() << ' '
+ // << unify_stack.capacity()
+ ;
+ return s.str();
+ }
+
+/**
+ * run - execute the logic program. "It also unpacks the actual answer term
+ * (by calling the method exportTerm) to a tree representation of a term,
+ * consisting of recursively embedded arrays hosting as leaves,
+ * an external representation of symbols, numbers and variables." [HHG doc]
+ */
+ void Prog::run(bool print_ans) {
+#define TR if(0)
+ const int MAX_OUTPUT_LINES = 5;
+ TR cout << "Prog::run(print_ans=" << print_ans << ")" << endl;
+#if 0
+ set_engine(this); // for static checkit, usable in other scopes(?)
+ checkit();
+#endif
+ int ctr = 0;
+ for (;; ctr++) {
+ Object A = ask();
+ TR cout << "Prog::run: r=ask()=" << A.toString() << endl;
+ if (A.type == Object::e_nullptr)
+ break;
+ if (ctr < MAX_OUTPUT_LINES && print_ans)
+ cout << "[" << ctr << "] " << "*** ANSWER=" << showTerm(A) << endl;
+ }
+ pp(cstr("TOTAL ANSWERS=") + ctr);
+if(indexing)
+ pp(cstr("n_matches=") + Engine::Ip->n_matches);
+ pp(cstr("n_alloced=") + CellList::alloced());
+#undef TR
+ }
+
+ void Prog::ppCode() const {
+ string t;
+
+ for (size_t i = 0; i < symTab.slist.size(); i++) {
+ if (i > 0) t += ", ";
+ t += symTab.slist[i] + "=" + i;
+ }
+
+ pp("\nSYMS:\n{" + t + "}");
+
+ pp((sym_tab&) symTab); // compiler was erroring out on trying to convert to string??
+
+ pp("\nCLAUSES:\n");
+
+ for (size_t i = 0; i < clauses.size(); i++) {
+ pp(cstr("[") + i + "]:" + showClause(clauses[i]));
+ }
+ pp("");
+ }
+
+ string Prog::showClause(const Clause &s) const {
+ string buf;
+
+ size_t l = s.skel_len;
+ buf += "\n";
+ buf += showCell(s.skel[0]);
+
+ if (l > 1) {
+ buf += " :- \n";
+ for (int i = 1; i < l; i++) {
+ cell e = s.skel[i];
+ buf += " ";
+ buf += showCell(e);
+ buf += "\n";
+ }
+ }
+ else {
+ buf += "\n";
+ }
+
+ buf += cstr("---base:[") + s.base + "] neck: " + s.neck + "-----\n";
+ buf += showCells(s.base, s.len); // TODO
+ buf += "\n";
+ buf += showCell(s.skel[0]);
+ buf += " :- [";
+ for (size_t i = 1; i < l; i++) {
+ cell e = s.skel[i];
+ buf += showCell(e);
+ if (i < l - 1)
+ buf += ", ";
+ }
+ buf += "]\n";
+
+ return buf;
+ }
+
+ string Prog::st0(const vector &args) {
+ string r;
+ if (!args.empty()) {
+ string name = args[0].toString();
+ if (args.size() == 3 && isOp(name)) {
+ r += "(";
+ r += maybeNull(args[0]);
+ r += " " + name + " ";
+ r += maybeNull(args[1]);
+ r += ")";
+ } else if (args.size() == 3 && isListCons(name)) {
+ r += '[';
+ r += maybeNull(args[1]);
+ Object tail = args[2];
+ for (;;) {
+ if ("[]" == tail.toString() || "nil" == tail.toString())
+ break;
+ if (tail.type != Object::e_vector) {
+ r += '|';
+ r += maybeNull(tail);
+ break;
+ }
+ const vector& list = tail.v;
+ if (!(list.size() == 3 && isListCons(list[0].toString()))) {
+ r += '|';
+ r += maybeNull(tail);
+ break;
+ } else {
+ r += ',';
+ r += maybeNull(list[1]);
+ tail = list[2];
+ }
+ }
+ r += ']';
+ } else if (args.size() == 2 && "$VAR" == name) {
+ r += "_" + args[1].toString();
+ } else {
+ string qname = maybeNull(args[0]);
+ r += qname;
+ r += "(";
+ for (size_t i = 1; i < args.size(); i++) {
+ r += maybeNull(args[i]);
+ if (i < args.size() - 1)
+ r += ",";
+ }
+ r += ")";
+ }
+ }
+ return r;
+ }
+
+ string Prog::maybeNull(const Object &O) {
+ if (O.type == Object::e_nullptr)
+ return "$null";
+ if (O.type == Object::e_vector)
+ return st0(O.v);
+ return O.toString();
+ }
+}
diff --git a/IP/cpp/iProlog/prog.h b/IP/cpp/iProlog/prog.h
new file mode 100644
index 00000000..e3511afe
--- /dev/null
+++ b/IP/cpp/iProlog/prog.h
@@ -0,0 +1,47 @@
+#pragma once
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+#include "Engine.h"
+#include "Object.h"
+
+using namespace std;
+
+namespace iProlog {
+ class Prog : Engine {
+ public:
+ void run(bool print_ans);
+ string stats() const;
+ void ppCode() const;
+ void ppc(const Spine &S);
+ string showClause(const Clause& s) const;
+
+ void ppGoals(CL_p bs);
+ void pp(const string s) const;
+ void pp(sym_tab &sym) const;
+ void ppTrail();
+ Prog(CellStack& heap_0,
+ vector& clauses_0,
+ sym_tab& sym_0,
+ index *Ip_0)
+ : Engine(heap_0, clauses_0, sym_0, Ip_0) {
+
+ if (indexing)
+ Ip = new index(heap_0, clauses_0);
+ }
+
+ string showTermCell(cell x) const;
+ string showTerm(Object O) const;
+ string show_index() const;
+
+ private:
+ static string maybeNull(const Object& O);
+ static inline bool isListCons(cstr name);
+ static inline bool isOp(cstr name);
+ static string st0(const vector& args);
+ string showCells(int base, int len) const;
+ };
+};
diff --git a/IP/cpp/iProlog/skeleton.h b/IP/cpp/iProlog/skeleton.h
new file mode 100644
index 00000000..b69dea57
--- /dev/null
+++ b/IP/cpp/iProlog/skeleton.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "defs.h"
+#include "cell.h"
+#include
+
+namespace iProlog {
+ using namespace std;
+
+#ifdef RAW_SKEL
+ // "Unlike _alloca, which doesn't require or permit a call
+ // to free to free the memory so allocated, _malloca requires
+ // the use of _freea to free memory." This can't necessarily
+ // work even as an inlined member function because it allocates
+ // within the existing C++ stack frame. alloca() may be
+ // preferable because of lower overhead, but is dangerous
+ // because it won't check stack bounds against C++ heap
+ // bounds and call malloc() if there's memory-corruption risk.
+ // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/malloca?view=msvc-170
+
+ typedef cell* clause_skel;
+
+# define alloc_skel(skel_len) (cell*)_malloca(skel_len * sizeof(cell));
+# define free_skel(s) _freea(s)
+# define skel_data(sk) sk
+# define init_skel(sk) sk = nullptr
+#else
+ typedef vector clause_skel;
+
+ #define alloc_skel(skel_len) vector(skel_len)
+ #define free_skel(s) s.clear()
+# define skel_data(sk) sk.data()
+# define init_skel(sk) sk.clear()
+#endif
+}
+
diff --git a/IP/cpp/iProlog/spine.cpp b/IP/cpp/iProlog/spine.cpp
new file mode 100644
index 00000000..7c738f6c
--- /dev/null
+++ b/IP/cpp/iProlog/spine.cpp
@@ -0,0 +1,91 @@
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+#include "spine.h"
+
+namespace iProlog {
+
+ using namespace std;
+
+ Spine *Spine::free_list = nullptr;
+
+ /**
+ * "Creates a spine - as a snapshot of some runtime elements."
+ *
+ * disaggregate hga_0 into reloced_head, reloced_body, and body_len instead
+ * of hg_len. Delete unfold.h. Maybe the relocations can even be done
+ * in parallel with building the CellList.
+ */
+ Spine* Spine::new_Spine(
+ Clause& C0,
+ cell b,
+ int base_0, // base
+ CL_p goals_0, // was gs[Java]; tail of G->goals in unfold()
+ int trail_top_0,
+ Unifiables &unifiables_0)
+ {
+#define TR if(0)
+ Spine* sp = Spine::alloc();
+
+ sp->base = base_0;
+ sp->trail_top = trail_top_0;
+ sp->last_clause_tried = 0;
+ sp->unifiables = unifiables_0;
+
+ for (int i = 0; i < MAXIND; ++i)
+ sp->index_vector[i] = cell::BAD;
+
+ sp->skel_len = (int)C0.skel_len;
+
+ CL_p acc = goals_0;
+ for (int i = sp->skel_len - 1; i > 0; i--)
+ acc = CellList::cons(C0.skel[size_t(i)].relocated_by(b), acc);
+ sp->the_goals = acc;
+ sp->head = C0.skel[0].relocated_by(b);
+
+ return sp;
+#undef TR
+ }
+
+ /**
+ * "Creates a specialized spine returning an answer (with no goals left to solve)." {Spine.java]
+ */
+ Spine *Spine::new_Spine(cell h, int tt) {
+ Spine* sp = Spine::alloc();
+ sp->head = h;
+ sp->base = 0;
+ sp->the_goals = nullptr;
+ sp->skel_len = 0;
+ sp->trail_top = tt;
+ sp->last_clause_tried = -1;
+ for (int i = 0; i < MAXIND; ++i)
+ sp->index_vector[i] = cell::BAD;
+ return sp;
+ }
+
+ string Spine::show() const {
+ string s = "Spine: {";
+ s += "\n head=" + to_string(head.as_int());
+ s += "\n base=" + to_string(base);
+ if (the_goals == nullptr)
+ s += "\n goals=";
+ else
+ s += "\n goals=" /* + goals.show()*/;
+ s += "\n trail_top=" + to_string(trail_top);
+ s += "\n last_clause_tried=" + to_string(last_clause_tried);
+ if (unifiables.empty())
+ s += "\n unifiables=";
+ else
+ s += "\n unifiables=" /* + unifiables.show()*/;
+ s += "\n}";
+ return s;
+ }
+
+ Spine::~Spine() {
+ abort();
+ }
+
+} // end namespace
diff --git a/IP/cpp/iProlog/spine.h b/IP/cpp/iProlog/spine.h
new file mode 100644
index 00000000..06809a48
--- /dev/null
+++ b/IP/cpp/iProlog/spine.h
@@ -0,0 +1,121 @@
+#pragma once
+/*
+ * iProlog/C++ [derived from Java version]
+ * License: Apache 2.0
+ * Copyright (c) 2017 Paul Tarau
+ */
+
+#include "defs.h"
+#include "cell.h"
+#include "CellList.h"
+#include "index.h"
+#include "clause.h"
+
+namespace iProlog {
+ using namespace std;
+
+ static Clause xp;
+
+ /**
+ * "Runtime representation of an immutable list of goals
+ * together with top of heap (base) and trail pointers
+ * and current clause tried out by head goal
+ * as well as registers associated to it". [iProlog Spine.java]
+ * "Registers" meaning deref'd cells for matching on the index.
+ *
+ * ("Note that (most of the) goal elements on this immutable list
+ * [of the goal stack] are shared among alternative branches" [HHG doc])
+ */
+ class Spine {
+ private:
+ static Spine* free_list;
+ Spine* next_free;
+ public:
+ cell head; // "head of the clause to which this corresponds" [Spine.java]
+ int base; // "base of the heap where the clause starts" [HHG doc]
+
+ CL_p the_goals; // goals - "with the top one ready to unfold" [Spine.java]
+ // "immutable list of the locations
+ // of the goal elements accumulated
+ // by unfolding clauses so far" [HHG doc]
+ int skel_len;
+
+ int trail_top; // "top of the trail when this was created" Spine.java]
+ // "as it was when this clause got unified" [HHG doc]
+
+ int last_clause_tried; // "index of the last clause [that]
+ // the top goal of [this] Spine
+ // has tried to match so far" [HHG doc]
+
+ t_index_vector index_vector; // "index elements" ("based on regs" [HHG] but no regs)
+ // "int[] regs: dereferenced goal registers" [HHG doc]
+ // [Comments in Engine.java suggest that this is regs]
+ // A note in Engine.java on makeIndexArgs(), which is called
+ // only in unfold(), says "xs contains dereferenced cells"
+ typedef vector Unifiables;
+ Unifiables unifiables; // "array of clauses known to be unifiable
+ // with top goal in goal stack" ("cs" in Spine.java)
+ // [This is not listed in the HHG description of Spine.]
+ // Initialized from unifiables, in Engine. If indexing
+ // is not activated, unifiables[i] == i.]
+
+ Spine() {
+ next_free = nullptr;
+ head = 0; // head of the clause to which this Spine corresponds
+ base = 0L; // top of the heap when this Spine was created
+ // "base of the heap where the clause starts" [HHG doc]
+ the_goals = nullptr;
+ skel_len = 0;
+ trail_top = 0; // "top of the trail when this Spine was created"
+ // "as it was when this clause got unified" [HHG doc]
+ last_clause_tried = -1; // "index of the last clause [that]
+ // the top goal of [this] Spine
+ // has tried to match so far" [HHG doc]
+ for (int i = 0; i < MAXIND; ++i)
+ index_vector[i] = cell::tag(cell::BAD, 0);
+ // index elements ("based on regs" [HHG] but no regs)
+ // "int[] regs: dereferenced goal registers" [HHG doc]
+ // Comments in Engine.java suggest that xs is regs
+ unifiables = vector(0);
+ }
+
+ /**
+ * "Creates a spine - as a snapshot of some runtime elements." [Spine.java]
+ */
+ static Spine* new_Spine(
+ Clause& C0,
+ cell b,
+ int base_0, // base
+ CL_p goals_0, // was gs/goal_stack [Java]
+ int trail_top_0,
+ Unifiables &unifiables_0); // was cs [Java]
+
+ /**
+ * "Creates a specialized spine returning an answer
+ * (with no goals left to solve)." [Spine.java]
+ */
+ static Spine *new_Spine(cell head, int trail_top);
+
+ inline bool hasGoals() { return the_goals != nullptr; }
+
+ ~Spine();
+
+ inline static Spine *alloc() {
+ if (free_list == nullptr)
+ return new Spine();
+ Spine* r = free_list;
+ free_list = r->next_free;
+ return r;
+ }
+
+ inline static void free(Spine *sp) {
+ CellList::collect_n(sp->the_goals, sp->skel_len-1);
+ sp->next_free = free_list;
+ free_list = sp;
+ }
+
+ string show() const;
+ };
+} // end namespace
+
+
diff --git a/IP/cpp/iProlog/sym_tab.h b/IP/cpp/iProlog/sym_tab.h
new file mode 100644
index 00000000..9033b5a9
--- /dev/null
+++ b/IP/cpp/iProlog/sym_tab.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "defs.h"
+#include
+
+namespace iProlog {
+
+ class sym_tab {
+ public:
+ unordered_map syms;
+ vector slist;
+
+ /**
+ * "Places an identifier in the symbol table."
+ */
+ int addSym(const string sym) {
+ try { return syms.at(sym); }
+ catch (const std::exception& e) {
+ std::ignore = e;
+ int I= (int) syms.size();
+ syms.insert(pair(sym, I));
+ slist.push_back(sym);
+ return I;
+ }
+ }
+
+ /**
+ * "Returns the symbol associated to an index
+ * in the symbol table."
+ */
+ string getSym(int w) const {
+ if (w < 0 || w >= slist.size()) {
+ cout << (cstr("BADSYMREF=") + w) << endl;
+ abort();
+ }
+ return slist[w];
+ }
+ };
+
+} // namespace
\ No newline at end of file
diff --git a/IP/cpp/iProlog/t b/IP/cpp/iProlog/t
new file mode 100644
index 00000000..4b66d6ef
--- /dev/null
+++ b/IP/cpp/iProlog/t
@@ -0,0 +1,350 @@
+diff --git a/IP/cpp/iProlog/Engine.h b/IP/cpp/iProlog/Engine.h
+index bb289ea..02c11eb 100644
+--- a/IP/cpp/iProlog/Engine.h
++++ b/IP/cpp/iProlog/Engine.h
+@@ -35,8 +35,8 @@ public:
+ vector clauses; // "Trimmed-down clauses ready to be quickly relocated
+ // to the heap" [Engine.java]
+ // [Not clear what "trimmed-down" means.]
+- vector clause_list; // if no indexing, contains [0..clauses.length-1]
+-
++ // vector clause_list; // if no indexing, contains [0..clauses.length-1]
++ Spine::Unifiables clause_list;
+ // "Symbol table - made of map (syms) + reverse map from
+ // ints to syms (slist)" [Engine.java]
+ sym_tab symTab;
+@@ -62,10 +62,21 @@ public:
+ if (clauses.size() == 0) {
+ throw logic_error(cstr("clauses: none"));
+ }
+-
++
+ trail.clear();
+- clause_list = toNums(clauses); // initially an array [0..clauses.length-1]
+- query = init(); /* initial spine built from query from which execution starts */
++ query = init(); /* initial spine built from query from which execution starts */
++
++ // clause_list = toNums(clauses); // initially an array [0..clauses.length-1
++
++ int l = (int)clauses.size();
++ Spine::Unifiables cls = query->get_space(l);
++ for (int i = 0; i < l; i++) {
++ ClauseIndex x(i);
++ cls[i] = x;
++ }
++
++ clause_list = cls;
++
+ base = heap_size(); // should be just after any code on heap
+ };
+
+@@ -157,7 +168,7 @@ protected:
+
+ Spine* unfold(Spine *G);
+
+- vector toNums(vector clauses);
++ // Spine::Unifiables toNums(vector clauses);
+
+ Clause getQuery();
+ Spine* init();
+diff --git a/IP/cpp/iProlog/config.h b/IP/cpp/iProlog/config.h
+index c380726..3a8a0ce 100644
+--- a/IP/cpp/iProlog/config.h
++++ b/IP/cpp/iProlog/config.h
+@@ -72,4 +72,6 @@ namespace iProlog {
+
+ #define RAW_SKEL
+
++// #define RAW_UNIFIABLES
++
+ }
+\ No newline at end of file
+diff --git a/IP/cpp/iProlog/engine.cpp b/IP/cpp/iProlog/engine.cpp
+index c181878..ee8dc1f 100644
+--- a/IP/cpp/iProlog/engine.cpp
++++ b/IP/cpp/iProlog/engine.cpp
+@@ -71,10 +71,10 @@ Spine* Engine::unfold(Spine *G) {
+
+ Ip->makeIndexArgs(heap, G, this_goal); // this goal is head(G->the_goals)
+
+- if (G->unifiables.size() > 0)
++ if (G->unifiables_size() > 0)
+ TR cout << "unfold: after makeIndexArgs with current G->unifiables[0]=" << G->unifiables[0].as_int() << endl;
+
+- size_t last = G->unifiables.size();
++ size_t last = G->unifiables_size();
+ TR cout << "unfold: last=" << to_string(last) << endl;
+
+ TR cout << "G->last_clause_tried=" << G->last_clause_tried << endl;
+@@ -205,18 +205,18 @@ bool Engine::unify_args(int w1, int w2) { // w1 & w1 already detagged in unify()
+ void Engine::clear() {
+ heap.setTop(-1);
+ }
+-
++#if 0
+ // was iota(clause_list.begin(), clause_list.end(), 0);
+- vector Engine::toNums(vector clauses) {
++ Spine::Unifiables Engine::toNums(vector clauses) {
+ int l = (int) clauses.size();
+- vector cls = vector(l);
++ Spine::Unifiables cls = Spine::get_space(l);
+ for (int i = 0; i < l; i++) {
+ ClauseIndex x(i);
+ cls[i] = x;
+ }
+ return cls;
+ }
+-
++#endif
+ /**
+ * Extracts a query - by convention of the form
+ * goal(Vars):-body to be executed by the engine
+@@ -251,7 +251,7 @@ Spine* Engine::answer(int trail_top) {
+ * top goal of this spine."
+ */
+ bool Engine::hasClauses(const Spine* S) const {
+- return S->last_clause_tried < S->unifiables.size();
++ return S->last_clause_tried < S->unifiables_size();
+ }
+
+ Object Engine::exportTerm(cell x) const {
+@@ -313,7 +313,7 @@ Object Engine::exportTerm(cell x) const {
+ * an external representation of symbols, numbers and variables." [HHG doc]
+ */
+ Object Engine::ask() {
+-#define TR if(0)
++#define TR if(1)
+ #if 0
+ set_engine(this); // for static checkit, usable in other scopes(?)
+ checkit();
+@@ -374,25 +374,32 @@ void Engine::popSpine() {
+ * returns null." [Engine.java]
+ */
+ Spine* Engine::yield() {
++#define TR if(1)
+ while (!spines.empty()) {
++ TR cout << "!spines.empty()" << endl;
+ Spine* G = spines.back(); // was "peek()" in Java
+
+ if (!hasClauses(G)) {
+ popSpine();
++ TR cout << "no clauses -- popSpine called" << endl;
+ continue;
+ }
+ Spine *C = unfold(G);
+ if (nullptr == C) {
++ TR cout << "no unfold result -- popSpine called" << endl;
+ popSpine(); // no matches
+ continue;
+ }
+ if (C->hasGoals()) {
++ TR cout << "C still has goals -- pushing C" << endl;
+ spines.push_back(C);
+ continue;
+ }
++ TR cout << "returning C" << endl;
+ return C; // answer
+ }
+ return nullptr;
++#undef TR
+ }
+
+ string Engine::showCell(cell w) const {
+diff --git a/IP/cpp/iProlog/index.cpp b/IP/cpp/iProlog/index.cpp
+index e00bd6d..b7aa28f 100644
+--- a/IP/cpp/iProlog/index.cpp
++++ b/IP/cpp/iProlog/index.cpp
+@@ -218,7 +218,7 @@ namespace iProlog {
+ return;
+
+ getIndexables(G->index_vector, heap, goal);
+- G->unifiables = matching_clauses_(G->index_vector);
++ matching_clauses_(G);
+ #undef TR
+ }
+
+@@ -356,8 +356,9 @@ namespace iProlog {
+ // If this is true, the mystery is why there's no loop break
+ // on zero.
+
+- vector index::matching_clauses_(t_index_vector& iv) {
++ void index::matching_clauses_(Spine *G) {
+ #define TR if(0)
++ t_index_vector iv = G->index_vector;
+ TR cout << "Entering matching_clauses, IV_LEN=" << to_string(IV_LEN) << endl;
+
+ cls_no_set_vec msp;
+@@ -406,7 +407,7 @@ namespace iProlog {
+
+ TR cout << " vms[0].m_size=" << to_string(vmsp[0]->size()) << endl;
+ new_end = intersect0_p(vmsp[0], msp, vmsp, new_end, push_count);
+- long cs_size = new_end - csp;
++ size_t cs_size = new_end - csp;
+
+ if (cs_size > n_to_alloc)
+ abort();
+@@ -417,18 +418,18 @@ namespace iProlog {
+ // cout << " after intersect0_p new_end - csp=" << cs_size << endl;
+
+ // is: clause numbers converted to indices
+- vector is; /*= cs.toArray() in Java, emulated here but
+- * with conversion to indices. Could
+- * probably be done on-the-fly in intersect0. */
+- is.reserve(cs_size);
++ /*= cs.toArray() in Java, emulated here but
++ * with conversion to indices. Could
++ * probably be done on-the-fly in intersect0. */
++ G->unifiables = G->get_space(cs_size);
+
+- TR cout << " (1) is.size()=" << is.size() << endl;
+- TR cout << " (1) is.capacity()=" << is.capacity() << endl;
++ TR cout << " (1) is.size()=" << G->unifiables_size() << endl;
++ TR cout << " (1) is.capacity()=" << G->unifiables_capacity() << endl;
+
+ for (int i = 0; i < cs_size; ++i)
+- is.emplace_back(to_clause_idx(csp[i]));
++ G->unifiables_pushback(to_clause_idx(csp[i]));
+
+- TR cout << " is.size()=" << to_string(is.size()) << endl;
++ TR cout << " G->unifiables_size()=" << to_string(G->unifiables_size()) << endl;
+
+ /* "Finally we sort the resulting set of clause numbers and
+ * hand it over to the main Prolog engine for unification
+@@ -436,21 +437,19 @@ namespace iProlog {
+ *
+ * I.e., respect standard Prolog clause ordering.
+ */
+- TR cout << " (2) is.size()=" << is.size() << endl;
+- TR cout << " (2) is.capacity()=" << is.capacity() << endl;
++ TR cout << " (2) is.size()=" << G->unifiables_size() << endl;
++ TR cout << " (2) is.capacity()=" << G->unifiables_capacity() << endl;
+ #ifndef RAW_QUICKSORT
+- if (is.size() > 1)
+- std::sort(is.begin(), is.end());
++ if (G->unifiables.size() > 1)
++ std::sort(G->unifiables.begin(), G->unifiables.end());
+ #endif
+ TR for (int i = 0; i < cs_size; ++i) {
+- cout << " is[" << i << "]=" << is[i].as_int() << endl;
++ cout << " is[" << i << "]=" << G->unifiables[i].as_int() << endl;
+ }
+
+ TR cout << " ==== matching_clauses: exiting" << endl << endl;
+
+ _freea((void*)csp);
+-
+- return is;
+ #undef TR
+ }
+
+diff --git a/IP/cpp/iProlog/index.h b/IP/cpp/iProlog/index.h
+index 7e8d953..55cd039 100644
+--- a/IP/cpp/iProlog/index.h
++++ b/IP/cpp/iProlog/index.h
+@@ -65,7 +65,8 @@ public:
+
+ void makeIndexArgs(CellStack &heap, Spine *G, cell goal);
+
+- vector matching_clauses_(t_index_vector& iv);
++ // vector matching_clauses_(t_index_vector& iv);
++ void matching_clauses_(Spine *G);
+
+ static inline ClauseNumber to_clause_no(ClauseIndex i) { ClauseNumber x(i.as_int()+1); return x; }
+
+diff --git a/IP/cpp/iProlog/prog.cpp b/IP/cpp/iProlog/prog.cpp
+index 90b778b..20da8e2 100644
+--- a/IP/cpp/iProlog/prog.cpp
++++ b/IP/cpp/iProlog/prog.cpp
+@@ -98,7 +98,7 @@ namespace iProlog {
+ * an external representation of symbols, numbers and variables." [HHG doc]
+ */
+ void Prog::run(bool print_ans) {
+-#define TR if(0)
++#define TR if(1)
+ const int MAX_OUTPUT_LINES = 5;
+ TR cout << "Prog::run(print_ans=" << print_ans << ")" << endl;
+ #if 0
+diff --git a/IP/cpp/iProlog/spine.cpp b/IP/cpp/iProlog/spine.cpp
+index ae961ca..56b755c 100644
+--- a/IP/cpp/iProlog/spine.cpp
++++ b/IP/cpp/iProlog/spine.cpp
+@@ -25,7 +25,7 @@ namespace iProlog {
+ int base_0, // base
+ CL_p goals_0, // was gs[Java]; tail of G->goals in unfold()
+ int trail_top_0,
+- vector &unifiables_0)
++ Unifiables &unifiables_0)
+ {
+ #define TR if(0)
+ Spine* sp = Spine::alloc();
+@@ -76,7 +76,7 @@ namespace iProlog {
+ s += "\n goals=" /* + goals.show()*/;
+ s += "\n trail_top=" + to_string(trail_top);
+ s += "\n last_clause_tried=" + to_string(last_clause_tried);
+- if (unifiables.empty())
++ if (unifiables_empty())
+ s += "\n unifiables=";
+ else
+ s += "\n unifiables=" /* + unifiables.show()*/;
+diff --git a/IP/cpp/iProlog/spine.h b/IP/cpp/iProlog/spine.h
+index 8e1a56d..12370a7 100644
+--- a/IP/cpp/iProlog/spine.h
++++ b/IP/cpp/iProlog/spine.h
+@@ -52,12 +52,38 @@ namespace iProlog {
+ // [Comments in Engine.java suggest that this is regs]
+ // A note in Engine.java on makeIndexArgs(), which is called
+ // only in unfold(), says "xs contains dereferenced cells"
+-
++#ifndef RAW_UNIFIABLES
+ vector unifiables; // "array of clauses known to be unifiable
+ // with top goal in goal stack" ("cs" in Spine.java)
+ // [This is not listed in the HHG description of Spine.]
+ // Initialized from unifiables, in Engine. If indexing
+ // is not activated, unifiables[i] == i.]
++ typedef vector Unifiables;
++ inline void unifiables_init() { } // rely on vector ctor
++ inline size_t unifiables_size() const { return unifiables.size(); }
++ inline bool unifiables_empty() const { return unifiables.empty(); }
++ inline Unifiables get_space(size_t n) {
++ vector is;
++ is.reserve(n);
++ return is;
++ }
++ inline void unifiables_pushback(ClauseIndex ci) { unifiables.push_back(ci); }
++ inline size_t unifiables_capacity() { return unifiables.capacity(); }
++#else
++ typedef ClauseIndex* Unifiables;
++ Unifiables unifiables;
++ size_t unifs_size = 0;
++ size_t unifs_cap = 0;
++ inline void unifiables_init() { abort(); /*stub*/ }
++ inline size_t unifiables_size() const { return unifs_size; }
++ inline bool unifiables_empty() const { return unifs_size == 0; }
++ inline Unifiables get_space(size_t n) {
++ unifs_cap = n;
++ return (ClauseIndex*) malloc(sizeof(ClauseIndex)*n);
++ }
++ inline void unifiables_pushback(ClauseIndex ci) { unifiables[unifs_size++] = ci; }
++ inline size_t unifiables_capacity() { return unifs_cap; }
++#endif
+
+ Spine() {
+ next_free = nullptr;
+@@ -76,7 +102,7 @@ namespace iProlog {
+ // index elements ("based on regs" [HHG] but no regs)
+ // "int[] regs: dereferenced goal registers" [HHG doc]
+ // Comments in Engine.java suggest that xs is regs
+- unifiables = vector(0);
++ unifiables = Spine::get_space(0);
+ }
+
+ /**
+@@ -88,7 +114,7 @@ namespace iProlog {
+ int base_0, // base
+ CL_p goals_0, // was gs/goal_stack [Java]
+ int trail_top_0,
+- vector &unifiables_0); // was cs [Java]
++ Unifiables &unifiables_0); // was cs [Java]
+
+ /**
+ * "Creates a specialized spine returning an answer
diff --git a/IP/cpp/iProlog/toks.cpp b/IP/cpp/iProlog/toks.cpp
new file mode 100644
index 00000000..2c0a59d6
--- /dev/null
+++ b/IP/cpp/iProlog/toks.cpp
@@ -0,0 +1,170 @@
+/*
+ * hhprolog: Hitchhiker Prolog
+ *
+ * Version: 1.0.0
+ * License: MIT
+ *
+ * Copyright (c) 2018,2019 Carlo Capelli
+ */
+
+#include
+#include
+#ifdef DUMP_TOKS
+#include
+#endif
+
+#include "cell.h" // just to get n_tag_bits?
+#include "toks.h"
+
+namespace iProlog {
+ Toks::Toks(string t, string s, int n) : t(t), s(s), n(n) {}
+
+// tokens as regex specification
+const string
+ SPACE = "\\s+",
+ ATOM = "[a-z]\\w*",
+ VAR = "[A-Z_]\\w*",
+ NUM = "-?\\d+",
+ DOT = "\\.";
+
+// atom keywords
+const string
+ IF = "if",
+ AND = "and",
+ HOLDS = "holds",
+ //NIL = "nil",
+ LISTS = "lists",
+ IS = "is"; // ?
+
+vector Toks::makeToks(string s) {
+ static string E = "("+SPACE+")|("+ATOM+")|("+VAR+")|("+NUM+")|("+DOT+")";
+ auto e = regex(E);
+ auto token = [](smatch r) {
+ // cout << "&r=" << (void*)&r << endl;
+ if (!r.empty()) {
+ auto tkAtom = [](string s) {
+ static const array kws = {IF, AND, HOLDS, /*NIL,*/ LISTS, IS};
+ auto p = find(kws.cbegin(), kws.cend(), s);
+ Toks r = { p == kws.cend() ? ATOM : s, s, 0 };
+ return r;
+ };
+ if (r[1].matched) return Toks{SPACE, r[0], 0};
+ if (r[2].matched) return tkAtom(r.str());
+ if (r[3].matched) return Toks{VAR, r[0], 0};
+ if (r[4].matched) return Toks{NUM, r[0], stoi(r[0])};
+ if (r[5].matched) return Toks{DOT, r[0], 0};
+ }
+ throw runtime_error("no match");
+ };
+ vector tokens;
+
+ sregex_iterator f(s.cbegin(), s.cend(), e), l = sregex_iterator();
+ while (f != l) {
+ auto r = token(*f++);
+ if (r.t != SPACE) {
+ tokens.push_back(r);
+ /*
+ cout << "r.s.c_str()=" << (void*) r.s.c_str()
+ << "tokens.back().s.c_str()" << (void*)(tokens.back().s.c_str()) << endl;
+ */
+ }
+ }
+
+ return tokens;
+}
+
+Tsss Toks::toSentences(string s) {
+ Tsss Wsss;
+ Tss Wss;
+ Ts Ws;
+ for (auto t : Toks::makeToks(s)) {
+ if (t.t == DOT) {
+ Wss.push_back(Ws);
+ Wsss.push_back(Wss);
+ Wss.clear();
+ Ws.clear();
+ continue;
+ }
+ if (t.t == IF) {
+ Wss.push_back(Ws);
+ Ws.clear();
+ continue;
+ }
+ if (t.t == AND) {
+ Wss.push_back(Ws);
+ Ws.clear();
+ continue;
+ }
+ if (t.t == HOLDS) {
+ Ws[0] = "h:" + Ws[0].substr(2);
+ continue;
+ }
+ if (t.t == LISTS) {
+ Ws[0] = "l:" + Ws[0].substr(2);
+ continue;
+ }
+ if (t.t == IS) {
+ Ws[0] = "f:" + Ws[0].substr(2);
+ continue;
+ }
+ if (t.t == VAR) {
+ Ws.push_back("v:" + t.s);
+ continue;
+ }
+ if (t.t == NUM) {
+ Ws.push_back((t.n < (1 << (cell::bitwidth - cell::n_tag_bits)) ? "n:" : "c:") + t.s);
+ continue;
+ }
+ if (t.t == ATOM) { // || t.t == NIL) {
+ Ws.push_back("c:" + t.s);
+ continue;
+ }
+ throw runtime_error("unknown token:" + t.t);
+ }
+#ifdef DUMP_TOKS
+ cout << "--- Wsss -----------------" << endl;
+ for (auto Wss : Wsss)
+ for (auto Ws : Wss)
+ for (auto s: Ws)
+ cout << s << endl;
+ cout << "--- returning Wsss -----------------" << endl;
+#endif
+ return Wsss;
+}
+
+Tss Toks::maybeExpand(Ts Ws) {
+ auto W = Ws[0];
+ if (W.size() < 2 || "l:" != W.substr(0, 2))
+ return Tss();
+ int l = int(Ws.size());
+ Tss Rss;
+ auto V = W.substr(2);
+ for (int i = 1; i < l; i++) {
+ string Vi = 1 == i ? V : V + "__" + (int) (i - 1);
+ string Vii = V + "__" + i;
+ Ts Rs = {
+ "h:" + Vi,
+ "c:list",
+ Ws[size_t(i)], i == l - 1 ?
+ "c:nil"
+ : "v:" + Vii
+ };
+ Rss.push_back(Rs);
+ }
+ return Rss;
+}
+
+Tss Toks::mapExpand(Tss Wss) {
+ Tss Rss;
+ for (auto Ws: Wss) {
+ auto Hss = maybeExpand(Ws);
+ if (Hss.empty())
+ Rss.push_back(Ws);
+ else
+ for (auto X: Hss)
+ Rss.push_back(X);
+ }
+ return Rss;
+}
+
+} // end namespace
diff --git a/IP/cpp/iProlog/toks.h b/IP/cpp/iProlog/toks.h
new file mode 100644
index 00000000..8b086dbc
--- /dev/null
+++ b/IP/cpp/iProlog/toks.h
@@ -0,0 +1,32 @@
+#pragma once
+/*
+ * hhprolog: Hitchhiker Prolog
+ *
+ * Version: 1.0.0
+ * License: MIT
+ *
+ * Copyright (c) 2018,2019 Carlo Capelli
+ */
+#include
+#include
+
+namespace iProlog {
+
+ using namespace std;
+
+ typedef vector Ts;
+ typedef vector Tss;
+ typedef vector Tsss;
+
+ class Toks {
+ private: const string t, s;
+ int n;
+ public:
+ Toks(string t, string s, int n); // t(t) : s(s) : n(n) {}
+ static Tsss toSentences(string s);
+ static Tss maybeExpand(Ts Ws);
+ static Tss mapExpand(Tss Wss);
+ static vector makeToks(string s);
+ static Tss vcreate(size_t l);
+ };
+}
diff --git a/IP/cpp/iProlog/x64/Release/iProlog.exe.recipe b/IP/cpp/iProlog/x64/Release/iProlog.exe.recipe
new file mode 100644
index 00000000..91665047
--- /dev/null
+++ b/IP/cpp/iProlog/x64/Release/iProlog.exe.recipe
@@ -0,0 +1,11 @@
+
+
+
+
+ C:\Users\Michael Turner\Documents\GitHub\iProlog\IP\cpp\x64\Release\iProlog.exe
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IP/cpp/iProlog/x64/Release/iProlog.vcxproj.FileListAbsolute.txt b/IP/cpp/iProlog/x64/Release/iProlog.vcxproj.FileListAbsolute.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/IP/cpp/iPrologC++.sln b/IP/cpp/iPrologC++.sln
new file mode 100644
index 00000000..e170e6d8
--- /dev/null
+++ b/IP/cpp/iPrologC++.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.7.34221.43
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iProlog", "iProlog\iProlog.vcxproj", "{1203CB19-DA21-43B9-9D0A-CA67E4EBE3AD}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1203CB19-DA21-43B9-9D0A-CA67E4EBE3AD}.Debug|x64.ActiveCfg = Release|x64
+ {1203CB19-DA21-43B9-9D0A-CA67E4EBE3AD}.Debug|x64.Build.0 = Release|x64
+ {1203CB19-DA21-43B9-9D0A-CA67E4EBE3AD}.Debug|x86.ActiveCfg = Debug|Win32
+ {1203CB19-DA21-43B9-9D0A-CA67E4EBE3AD}.Debug|x86.Build.0 = Debug|Win32
+ {1203CB19-DA21-43B9-9D0A-CA67E4EBE3AD}.Release|x64.ActiveCfg = Release|x64
+ {1203CB19-DA21-43B9-9D0A-CA67E4EBE3AD}.Release|x64.Build.0 = Release|x64
+ {1203CB19-DA21-43B9-9D0A-CA67E4EBE3AD}.Release|x86.ActiveCfg = Release|Win32
+ {1203CB19-DA21-43B9-9D0A-CA67E4EBE3AD}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {1F7F86CA-9BE0-4251-B6AF-880F42B063B3}
+ EndGlobalSection
+EndGlobal
diff --git a/IP/doc/explication b/IP/doc/explication
new file mode 100644
index 00000000..105ae78d
--- /dev/null
+++ b/IP/doc/explication
@@ -0,0 +1,22 @@
+add(s(X),Y,s(Z)):-add(X,Y,Z).
+
+ [5]a:4
+ [6]c:add // add
+ +-[7]r:10 // -> 1st arg: s(X)
+ | [8]v:8 // Y
+ +-|-[9]r:13
+ | |
+ | +>[10]a:2
+ | [11]c:s // s
+ | [12]v:12 // X
+ |
+ +-->[13]a:2
+ [14]c:s // s
+ [15]v:15 // Z
+
+ [16]a:4
+ [17]c:add // add
+ [18]u:12 // X
+ [19]u:8 // Y
+ [20]u:15 // Z
+
diff --git a/IP/go.sh b/IP/go.sh
index 28b5e6df..80cc20cd 100755
--- a/IP/go.sh
+++ b/IP/go.sh
@@ -1,12 +1,25 @@
#echo go.sh assumed in dir ./progs
-mkdir out
-mkdir out/production
-mkdir out/production/IP
+mkdir -p out
+mkdir -p out/production
+mkdir -p out/production/IP
export TARGET="out/production/IP"
-mkdir "$TARGET"
+mkdir -p "$TARGET"
rm -r -f $TARGET/iProlog
-rm -f progs/*.pl.nl
-javac -O -d "$TARGET" src/iProlog/*.java
-swipl -f pl2nl.pl -g "pl('$1'),halt"
-java -cp "$TARGET" iProlog.Main "progs/$1.pl"
-ls progs/*.pl
+# rm -f progs/*.pl.nl
+echo "starting java compilations"
+if javac -J-Duser.language=en \
+ -O -d "$TARGET" src/main/java/org/iprolog/*.java; then
+ echo "swipl starting pl2nl on $1.pl ...."
+ swipl -f pl2nl.pl -g "pl('$1'),halt"
+ echo "java starting on $1.pl.nl ...."
+ # java -cp "$TARGET" iProlog.Main "progs/$1.pl"
+ # java -cp "$TARGET/org/iprolog" org.iprolog.Main "progs/$1.pl"
+ java -Duser.language=en \
+ -ea \
+ -cp "$TARGET" org.iprolog.Main progs/$1.pl
+ echo -n "--Hit Enter to continue:";
+ read;
+else
+ echo -n "Compile failed--hit Enter to continue:";
+ read;
+fi
diff --git a/IP/main.cpp b/IP/main.cpp
new file mode 100644
index 00000000..e306a16b
--- /dev/null
+++ b/IP/main.cpp
@@ -0,0 +1,6 @@
+#include
+
+int main() {
+ std::cout << "Hello world" << std::endl;
+ return 0;
+}
diff --git a/IP/pl2nl.pl b/IP/pl2nl.pl
index e692fda4..0c5caaab 100644
--- a/IP/pl2nl.pl
+++ b/IP/pl2nl.pl
@@ -1,131 +1,268 @@
-c:-[pl2nl].
+c:-[pl_to_nl].
-go:-pl2nl('progs/perms.pl'),shell('cat progs/perms.pl.nl').
-go1:-pl2nl('progs/queens.pl'),shell('cat progs/queens.pl.nl').
-go2:-pl2nl('progs/sud4x.pl'),shell('cat progs/sud4x.pl.nl').
+% progs has sample code w/.pl suffix, arg to go.sh has no .pl, so add
+% these to get path to source code
+addprogpl(F,PL) :- concat_atom(['progs/',F,'.pl'],PL).
+% entry point:
+
pl(F):-
- concat_atom(['progs/',F,'.pl'],PL),
- pl2nl(PL),
+ addprogpl(F,PL), % PL <- proper source file path
+ pl_to_nl(PL), % make .nl source ("natural language assembly code")
% jpl_call('iProlog.Main',run,[PL],_R),
writeln(done).
-
-pl2nl(PL):-
- atom_concat(PL,'.nl',NL),
- pl2nl(PL,NL).
-
-pl2nl(PL,NL):-
- see(PL),
- tell(NL),
- repeat,
- clause2sent(EOF),
- EOF==yes,
- !,
- seen,
- told.
-
-read_with_names(T,T0):-
- read_term(T0,[variable_names(Es)]),
- copy_term(T0,T),
- maplist(call,Es).
-
-writes(if):-!,nl,write(if),nl,write(' ').
-writes(and):-!,write(and),nl,write(' ').
-writes((.)):-!,write((.)),nl.
-writes(W):-write(W),write(' ').
-
-clause2sent(EOF):-
- read_with_names(T,T0),
+pl_to_nl(PL):-
+ atom_concat(PL,'.nl',NL), % - add .nl for output
+ pl_to_nl(PL,NL). % translate .pl to .nl
+
+
+% pl_to_nl - translate contents of PL.pl to equational English-like
+% "sentence" form, writing to file named PL.pl.nl (=NL)
+
+pl_to_nl(PL,NL):-
+ see(PL), % Open PL for reading, make it current input
+ tell(NL), % Open NL for writing, make it current output
+ repeat, % for infinite choice points
+ clause_to_sentence(EOF), % send translation of clause to sentence to NL
+ EOF==yes, % if EOF hit instead of clause
+ !, % cut
+ seen, % Close PL. New input stream becomes user_input
+ told. % Close NL
+
+% ------------------------------------------
+% Pretty good explanation here:
+%
+
+read_with_names(Term,T0):-
+ write(user_error, '%---- read_with_names: '),nl(user_error),
+
+ read_term(T0,[variable_names(Es)]), % Read term from .pl, unify it with T0.
+ % "Unify Es with list of Name = Var,
+ % where Name is an atom describing
+ % the variable name and Var is a variable
+ % that shares with the corresponding
+ % variable in T0."???
+ % The variables appear in read-order.
+
+% https://stackoverflow.com/questions/7947910/converting-terms-to-atoms-preserving-variable-names-in-yap-prolog/7948525#7948525
+%
+% ?- read_term(Term,[variable_names(Eqs)]).
+% |: X+3*Y+X.
+% Eqs = ['X'=_A,'Y'=_B], % mapping of var-as-atom to gen'ed vars in Term:
+% Term = _A+3*_B+_A
+
+ write(user_error,' T0 = '),write(user_error,T0),nl(user_error),
+ write(user_error,' Es = '),write(user_error,Es),nl(user_error),
+
+ copy_term(T0,Term), % "Create a version of T0 with renamed
+ % (fresh) variables and unify it to Term.
+ % Attributed variables have their
+ % attributes copied"
+
+ write(user_error,'After copy_term(T0,Term) Term = '),
+ write(user_error,Term),nl(user_error),
+
+ maplist(call,Es). % Try to unify all Es?
+ % ????????????????????
+ % ????????????????????
+
+
+write_sym( if ) :- !, nl, write( if ), nl, write(' ').
+write_sym( and ) :- !, write( and ), nl, write(' ').
+write_sym( (.) ) :- !, write( (.) ), nl.
+write_sym( W ) :- write( W ), write(' ').
+
+% clause_to_sentence - translate a clause to a "sentence"
+%
+clause_to_sentence(EOF):-
+ read_with_names(Term,T0), % get some T0 from input,
+ % Term is copy with renamed variables,
+ % T0 unified to Term
(
- T==end_of_file->EOF=yes
- ;
+ Term==end_of_file->EOF=yes % like a possible result from read/1
+ ; % ;/2 -- like ||, and with -> above
+ % "transparent to cuts"
EOF=no,
- cls2nat(T,Ns),
- T=T0,
- clean_up_nats(Ns,Xs),
- maplist(writes,Xs),nl
+ clause_to_nats(Term,Nats), % Nats <- "naturalized" Term
+ Term=T0, % unify T0 to Term
+ clean_up_nats(Nats,Xs), %
+ maplist(write_sym,Xs),nl
).
-clean_up_nats(Ns,Xs):- clean_up_nats(Ns,Xs,0,_).
-
-clean_up_nats([],[])-->[].
-clean_up_nats([N|Ns],[X|Xs])-->
- clean_up_nat(N,X),
- clean_up_nats(Ns,Xs).
-
-clean_up_nat(N,X,K1,K2):-var(N),!,succ(K1,K2),atom_concat('_',K1,X),N=X.
-clean_up_nat(('[|]'),X,K,K):-!,X=list.
-clean_up_nat(([]),X,K,K):-!,X=nil.
-clean_up_nat(X,X,K,K).
-
-
-
-cls2nat(C,Ns):-
- cls2eqs(C,_,Es),
- eqss2nat(Es,Ns).
-
-
-cls2eqs(C,Xs,Es):-
- (C=(H:-Bs)->true;C=H,Bs=true),
- cls2list(H,Bs,Ts),
- maplist(term2eqs,Xs,Ts,Es).
-
-cls2list(H,Bs,Cs):-
- body2list((H,Bs),Cs).
-
-body2list(B,R):-var(B),!,R=[B].
-body2list((B,Cs),[B|Bs]):-!,body2list(Cs,Bs).
-body2list(true,[]):-!.
-body2list(C,[C]).
-
-
-eqss2nat(Xs,Ns):-eqss2nat(Xs,Ns,[]).
-
-eqss2nat([H])-->!,pred2nat(H),[(.)].
-eqss2nat([H|Bs])-->pred2nat(H),[(if)],body2nat(Bs),[(.)].
-
-body2nat([])--> !,[].
-body2nat([B])-->!,pred2nat(B).
-body2nat([B|Bs])-->pred2nat(B),[and],body2nat(Bs).
-
-
-pred2nat([_=P|Ts])-->{P=..Xs,trim_call(Xs,Ys)},eq2words(Ys),eqs2nat(Ts).
-
-
-trim_call([call|Xs],R):-!,R=Xs.
-trim_call(Xs,Xs).
-
-eqs2nat([])-->!,[].
-eqs2nat([X=T|Es])-->[and],{T=..Xs},[X],holds_lists_eqs(Xs),eqs2nat(Es).
-
-holds_lists_eqs([lists|Xs])-->!,[lists],eq2words(Xs).
-holds_lists_eqs(Xs)-->[holds],eq2words(Xs).
-
-eq2words([])-->[].
-eq2words([X|Xs])-->[X],eq2words(Xs).
-
+% Not sure what the apparent two final implicit params in DCG rules are
+% about.
+%
+clean_up_nats(Nats,Xs):- clean_up_nats(Nats,Xs,0,_),
+ write(user_error,'---- clean_up_nats:'), nl(user_error),
+ write(user_error,' Nats='),
+ write(user_error, Nats), nl(user_error),
+ write(user_error,' Xs='),
+ write(user_error, Xs), nl(user_error).
+
+% Use of "-->" Definite Clause Grammar (DCG) operator
+% https://stackoverflow.com/questions/32579266/what-does-the-operator-in-prolog-do
+
+clean_up_nats( [], [] ) --> [].
+clean_up_nats( [N|Nats], [X|Xs] ) --> clean_up_nat(N,X), clean_up_nats(Nats,Xs).
+
+% atom_concat('_',K1,X) -- generates the _0, _1, etc. vars
+
+clean_up_nat( Nat, X, K1, K2 ) :- var(Nat),
+ !, succ(K1,K2), atom_concat('_',K1,X), Nat=X.
+clean_up_nat( ('[|]'), X, K, K ) :- !, X=list.
+clean_up_nat( ([]), X, K, K ) :- !, X=nil.
+clean_up_nat( X, X, K, K ) .
+
+
+% clause_to_nats -
+
+clause_to_nats(Clause,Nats) :- clause_to_eqns(Clause,_,Es)
+ , eqnss_to_nat(Es,Nats)
+ .
+
+clause_to_eqns(Clause,Xs,Es) :- (Clause = (Head:-Bodies)->true ; Clause=Head,Bodies=true)
+ , clause_to_list(Head,Bodies,Terms)
+ , maplist(term_to_eqns,Xs,Terms,Es)
+ .
+
+clause_to_list(Head,Bodies,Cs) :- body_to_list((Head,Bodies),Cs).
+
+body_to_list( Body, R ) :- var(Body), !, R=[Body].
+body_to_list( (Body,Cs), [Body|Bodies] ) :- !, body_to_list(Cs,Bodies).
+body_to_list( true, [] ) :- !.
+body_to_list( C, [C] ) .
+
+
+eqnss_to_nat(Xs,Nats) :- eqnss_to_nat(Xs,Nats,[]).
+
+eqnss_to_nat( [Head] ) --> !, pred_to_nat(Head), [(.)].
+eqnss_to_nat( [Head|Bodies] ) --> pred_to_nat(Head)
+ , [(if)]
+ , body_to_nat(Bodies), [(.)]
+ .
+
+body_to_nat( [] ) --> !, [].
+body_to_nat( [Body] ) --> !, pred_to_nat(Body).
+body_to_nat( [Body|Bodies] ) --> pred_to_nat(Body), [and], body_to_nat(Bodies).
+
+
+pred_to_nat([_=P|Terms])--> {P=..Xs,trim_call(Xs,Ys)}
+ , eqn_to_words(Ys)
+ , eqns_to_nats(Terms)
+ , {write(user_error,"****** pred to nats **** ")}
+ , {nl(user_error)}
+ , {write(user_error," P=")}
+ , {write(user_error,P)}
+ , {nl(user_error)}
+ , {write(user_error," Xs=")}
+ , {write(user_error,Xs)}
+ , {nl(user_error)}
+ , {write(user_error," Ys=")}
+ , {write(user_error,Ys)}
+ , {nl(user_error)}
+ , {write(user_error," Terms=")}
+ , {write(user_error,Terms)}
+ , {nl(user_error)}
+ .
+
+trim_call( [call|Xs], R ) :- !, R=Xs.
+trim_call( Xs, Xs ) .
+
+eqns_to_nats( [] ) --> !, [].
+eqns_to_nats( [X=Term|Es] ) --> [and]
+ , {Term=..Xs}
+ , {write(user_error,"*** begin eqns to nats ***")}
+ , {write(user_error," Term=")}
+ , {write(user_error,Term)}
+ , {nl(user_error)}
+ , {write(user_error," Xs=")}
+ , {write(user_error,Xs)}
+ , {nl(user_error)}
+ , [X]
+ , holds_lists_eqns(Xs)
+ , {nl(user_error)}
+ , {write(user_error," X=")}
+ , {write(user_error,X)}
+ , {nl(user_error)}
+ , eqns_to_nats(Es)
+ , {write(user_error," Es=")}
+ , {write(user_error,Es)}
+ , {nl(user_error)}
+ , {write(user_error,"*** end eqns to nats ***")}
+ , {nl(user_error)}
+ .
+
+holds_lists_eqns( [lists|Xs] ) --> !, [lists], eqn_to_words(Xs).
+holds_lists_eqns( Xs ) --> [holds], eqn_to_words(Xs).
+
+eqn_to_words( [] ) --> [].
+eqn_to_words( [X|Xs] ) --> [X], eqn_to_words(Xs).
% terms to equations
-term2eqs(X,T,[X=T]):-var(T),!.
-term2eqs(X,T,[X=T]):-atomic(T),!.
-term2eqs(X,T,Es):-compound(T),term2eqs(X,T,Es,[]).
-
-term2eqs(X,T)-->{var(T)},!,{X=T}.
-term2eqs(X,T)-->{atomic(T)},!,{X=T}.
-term2eqs(X,Xs)-->{is_list(Xs)},!,{T=..[lists|Xs]},term2eqs(X,T).
-term2eqs(X,T)-->{compound(T),functor(T,F,N),functor(TT,F,N)},
- [X=TT],
- term2arg_eqs(0,N,TT,T).
-
-term2arg_eqs(N,N,_,_)-->!,[].
-term2arg_eqs(I,N,X,T)-->
- {I1 is I+1},
- {arg(I1,X,V)},
- {arg(I1,T,A)},
- term2eqs(V,A),
- term2arg_eqs(I1,N,X,T).
-
+term_to_eqns( X, Term, [X=Term] ) :- var(Term)
+ , write(user_error,'****** in var term_to_eqns')
+ , {nl(user_error)}
+ , write(user_error," X=")
+ , write(user_error, X)
+ , {nl(user_error)}
+ , write(user_error," Term=")
+ , write(user_error,Term)
+ , nl(user_error)
+ , !
+ .
+term_to_eqns( X, Term, [X=Term] ) :- atomic(Term)
+ , write(user_error,'****** in atomic term_to_eqns')
+ , nl(user_error)
+ , write(user_error," X=")
+ , write(user_error,X)
+ , nl(user_error)
+ , write(user_error," Term=")
+ , write(user_error,Term)
+ , nl(user_error)
+ , !
+ .
+term_to_eqns( X, Term, Es ) :- compound(Term)
+ , write(user_error,'****** begin compound term_to_eqns')
+ , term_to_eqns(X,Term,Es,[])
+ , nl(user_error)
+ , write(user_error," X=")
+ , write(user_error,X)
+ , nl(user_error)
+ , write(user_error," Term=")
+ , write(user_error,Term)
+ , nl(user_error)
+ , write(user_error,'****** end compound term_to_eqns')
+ , nl(user_error)
+ .
+
+term_to_eqns( X, Term ) --> {var(Term)}, !, {X=Term}.
+term_to_eqns( X, Term ) --> {atomic(Term)}, !, {X=Term}.
+term_to_eqns( X, Xs ) --> {is_list(Xs)}, !, {Term=..[lists|Xs]}, term_to_eqns(X,Term).
+term_to_eqns( X, Term ) --> {compound(Term)
+ , functor(Term,F,N)
+ , functor(TT,F,N)}
+ , [X=TT]
+ , term_to_arg_eqns(0,N,TT,Term)
+ .
+
+term_to_arg_eqns( N, N, _, _ ) --> !, [].
+term_to_arg_eqns( I, N, X, Term ) --> {I1 is I+1}
+ , {arg(I1,X,V)}
+ , {arg(I1,Term,A)}
+ , term_to_eqns(V,A)
+ , term_to_arg_eqns(I1,N,X,Term)
+ .
+
+go:-
+ pl_to_nl('progs/perms.pl'),
+ shell('cat progs/perms.pl.nl').
+go1:-
+ pl_to_nl('progs/queens.pl'),
+ shell('cat progs/queens.pl.nl').
+go2:-
+ pl_to_nl('progs/sud4x.pl'),
+ shell('cat progs/sud4x.pl.nl').
diff --git a/IP/pom.xml b/IP/pom.xml
new file mode 100644
index 00000000..65c82f9f
--- /dev/null
+++ b/IP/pom.xml
@@ -0,0 +1,79 @@
+
+ 4.0.0
+ org.iprolog
+ ptaraudactyl
+ 1.0-SNAPSHOT
+ ${project.groupId}:${project.artifactId}
+ Java library for Paul Tarau's iProlog engine
+ jar
+
+ 1.8
+ 1.8
+ UTF-8
+ 5.6.0
+ 3.0.0-M3
+ 3.0.0-M5
+ 3.0.0
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+ test
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+ false
+ false
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+
+
+
diff --git a/IP/progs/add.pl b/IP/progs/add.pl
index 68b4836a..bdd96e28 100644
--- a/IP/progs/add.pl
+++ b/IP/progs/add.pl
@@ -1,4 +1,18 @@
+% Unary arithmetic
+% X is the sum of zero and X
+
add(0,X,X).
+
+% if Z is the sum of X and Y
+% the successor of Z is the sum of the successor of X and Y
+
add(s(X),Y,s(Z)):-add(X,Y,Z).
- goal(R):-add(s(s(0)),s(s(0)),R).
\ No newline at end of file
+% gdebug(R):-the_sum_of(0,X,the_successor_of(0)).
+
+% R is the goal if
+% R is the sum of
+% the successor of the successor of zero
+% and
+% the successor of the successor of zero
+goal(R):-add(s(s(0)),s(s(0)),R).
diff --git a/IP/progs/big.pl b/IP/progs/big.pl
index efdea79b..e1903b40 100644
--- a/IP/progs/big.pl
+++ b/IP/progs/big.pl
@@ -1,31 +1,40 @@
-app([],Ys,Ys).
-app([X|Xs],Ys,[X|Zs]):-app(Xs,Ys,Zs).
+% if I append [] to something
+% this something is the same
+
+append([],Ys,Ys).
+
+% when I have Zs after I append Ys to Xs
+% there is one list X like this:
+% if I append Ys to [X|Xs]
+% I have [X|Zs]
+
+append([X|Xs],Ys,[X|Zs]):-append(Xs,Ys,Zs).
nrev([],[]).
-nrev([X|Xs],Zs):-nrev(Xs,Ys),app(Ys,[X],Zs).
-
-s(0,1).
-s(1,2).
-s(2,3).
-s(3,4).
-s(4,5).
-s(5,6).
-s(6,7).
-s(7,8).
-s(8,9).
-s(9,10).
-s(10,11).
-s(11,12).
-s(12,13).
-s(13,14).
-s(14,15).
-s(15,16).
-s(16,17).
-s(17,18).
+nrev([X|Xs],Zs):-nrev(Xs,Ys),append(Ys,[X],Zs).
+
+next_number_after(0,1).
+next_number_after(1,2).
+next_number_after(2,3).
+next_number_after(3,4).
+next_number_after(4,5).
+next_number_after(5,6).
+next_number_after(6,7).
+next_number_after(7,8).
+next_number_after(8,9).
+next_number_after(9,10).
+next_number_after(10,11).
+next_number_after(11,12).
+next_number_after(12,13).
+next_number_after(13,14).
+next_number_after(14,15).
+next_number_after(15,16).
+next_number_after(16,17).
+next_number_after(17,18).
dup(0,X,X).
-dup(N,X,R):-s(N1,N),app(X,X,XX),dup(N1,XX,R).
+dup(N,X,R):-next_number_after(N1,N),append(X,X,XX),dup(N1,XX,R).
-% goal([X,Y]):-dup(10,[a,b,c,d],R),nrev(R,[X,Y|_]).
+% goal([X,Y]):-dup(2,[a,b,c,d,e,f,g,h,i],R),nrev(R,[X,Y|_]).
goal([X,Y]):-dup(18,[a,b,c,d],[X,Y|_]).
diff --git a/IP/progs/combine.pl b/IP/progs/combine.pl
new file mode 100644
index 00000000..8ba4461c
--- /dev/null
+++ b/IP/progs/combine.pl
@@ -0,0 +1,17 @@
+member(X,[X|_]).
+member(X,[_|L]):-member(X,L).
+/*
+maplist(_,[]).
+maplist(P,[L1|L1s]) :- call(P,L1), maplist(P,L1s).
+*/
+maplist(_,[],[]).
+maplist(P,[L1|L1s],[L2|L2s]) :- call(P,L1,L2), maplist(P,L1s,L2s).
+/*
+maplist(_,[],[],[]).
+maplist(P,[L1|L1s],[L2|L2s],[L3|L3s]) :- call(P,L1,L2,L3), maplist(P,L1s,L2s,L3s).
+
+maplist(_,[],[],[],[]).
+maplist(P,[L1|L1s],[L2|L2s],[L3|L3s],[L4|L4s]) :- call(P,L1,L2,L3,L4), maplist(P,L1s,L2s,L3s,L4s).
+*/
+combine(Ls,Rs) :- maplist(member,Rs,Ls).
+goal(R) :- combine([[1,2],[a,b],[8,9],[x,y]],R).
diff --git a/IP/progs/lambdas.pl b/IP/progs/lambdas.pl
index c8ab7938..fa64f4c3 100644
--- a/IP/progs/lambdas.pl
+++ b/IP/progs/lambdas.pl
@@ -1,4 +1,5 @@
% generates all lambda terms of size 9
+% generates all lambda terms of size 3
genLambda(X,Vs,N,N):-memb(X,Vs).
genLambda(l(X,A),Vs,s(N1),N2):-genLambda(A,[X|Vs],N1,N2).
@@ -9,7 +10,8 @@
genClosedLambdaTerm(L,T):-genLambda(T,[],L,zero).
-nine(s(s(s(s(s(s(s(s(s(zero)))))))))).
+some(s(s(s(s(s(s(s(s(zero))))))))).
+% some(s(s(s(s(s(s(s(s(zero))))))))).
-goal(Lam):-nine(Size),genClosedLambdaTerm(Size,Lam).
+goal(Lam):-some(Size),genClosedLambdaTerm(Size,Lam).
diff --git a/IP/progs/memb.pl b/IP/progs/memb.pl
new file mode 100644
index 00000000..8fa8ecf0
--- /dev/null
+++ b/IP/progs/memb.pl
@@ -0,0 +1,4 @@
+memb(E,[E|_]).
+memb(E,[_|T]) :- memb(E,T).
+
+goal(E):-memb(E,[0,1,2,3,4]).
diff --git a/IP/progs/mini.pl b/IP/progs/mini.pl
new file mode 100644
index 00000000..46b6ac9e
--- /dev/null
+++ b/IP/progs/mini.pl
@@ -0,0 +1,3 @@
+a(1).
+a(2).
+goal(Y):-a(Y).
diff --git a/IP/progs/mperms.pl b/IP/progs/mperms.pl
index 3a88877d..1872c51a 100644
--- a/IP/progs/mperms.pl
+++ b/IP/progs/mperms.pl
@@ -7,6 +7,8 @@
cls([perm([],[])|Tail],Tail).
cls([perm([X|Xs],Zs),perm(Xs,Ys),sel(X,Zs,Ys)|Tail],Tail).
-input([1,2,3,4,5,6,7,8,9,10,11],[11,10,9,8,7,6,5,4,3,2,1]).
+% input([1,2,3,4,5,6,7,8,9,10,11],[11,10,9,8,7,6,5,4,3,2,1]).
+% input([1,2,3,4,5,6,7,8,9,10],[10,9,8,7,6,5,4,3,2,1]).
+input([1,2,3,4,5,6,7,8,9,10],[10,9,8,7,6,5,4,3,2,1]).
goal(Y):-input(X,Y),metaint([perm(X,Y),perm(Y,X)]).
diff --git a/IP/progs/neg_fail.pl b/IP/progs/neg_fail.pl
new file mode 100644
index 00000000..1d1e2623
--- /dev/null
+++ b/IP/progs/neg_fail.pl
@@ -0,0 +1,6 @@
+something(b).
+bad(a).
+
+goal(P) :-
+ not(something(P)),
+ bad(P).
diff --git a/IP/progs/neg_succ.pl b/IP/progs/neg_succ.pl
new file mode 100644
index 00000000..1d3dc786
--- /dev/null
+++ b/IP/progs/neg_succ.pl
@@ -0,0 +1,6 @@
+something(b).
+bad(a).
+
+goal(P) :-
+ bad(P),
+ not(something(P)).
diff --git a/IP/progs/not.pl b/IP/progs/not.pl
new file mode 100644
index 00000000..8eb8d3c8
--- /dev/null
+++ b/IP/progs/not.pl
@@ -0,0 +1 @@
+goal(X):-not(X).
diff --git a/IP/progs/nsm.pl b/IP/progs/nsm.pl
new file mode 100644
index 00000000..8b5ce806
--- /dev/null
+++ b/IP/progs/nsm.pl
@@ -0,0 +1,36 @@
+someone_(i_). % I am someone
+
+one_(i_). % There is only one me
+other_(i_) :- false. % There are no other mes.
+other_(someone_). % there are other people
+not_(there_is_(other_(i_))).
+
+not_(good_(someone_(X))) :- bad_(someone_(X)). % if someone is being bad, this person is not being good
+not_(bad_(someone_(X))) :- good_(someone_(X)). % if someone is being good, this person is not being bad
+
+
+can_(bad_,someone_). % There can be a bad person
+can_(good_,someone_). % There are good people
+can_(i_,say_(X)) :- not(X). % Grice - quantity; do not be redundant
+
+% if someone is good, someone else is bad
+
+% bad_(dummy) :- false. % just to make sure it's defined in CLI
+% bad_(someone_(other_)). % just to test that this can be satisfied
+
+bad_(i_). % test whether comb(bad_,i_) rejects if already bad; works
+% good_(i_). % test whether comb(bad_,i_) rejects if good; works
+% assert neither;
+
+good_(someone_) :- bad_(someone_(other_)).
+good_(dummy).
+bad_(dummy).
+
+
+comb(Modifier,Modified) :-
+ not(call(Modifier,Modified)), % not already asserted
+% call(Modifier,X), % others possible
+ other_(Modified),
+ call(can_(Modifier),X).
+
+goal(X) :- comb_(good_,someone_),someone_(X).
diff --git a/IP/progs/perms.pl b/IP/progs/perms.pl
index 0370b2d0..7473beb6 100644
--- a/IP/progs/perms.pl
+++ b/IP/progs/perms.pl
@@ -19,6 +19,7 @@
% goal(P):-eq(X,[1,2,3,4,5,6,7,8,9,10,11]),nrev(X,P),perm(X,P).
-input([1,2,3,4,5,6,7,8,9,10,11]).
+% input([1,2,3,4,5,6,7,8,9,10]).
+input([1,2]).
goal(Y):-input(X),nrev(X,Y),perm(X,Y),perm(Y,X).
diff --git a/IP/progs/queens.pl b/IP/progs/queens.pl
index 25867fc1..dc94b08a 100644
--- a/IP/progs/queens.pl
+++ b/IP/progs/queens.pl
@@ -1,14 +1,51 @@
-place_queen(I,[I|_],[I|_],[I|_]).
-place_queen(I,[_|Cs],[_|Us],[_|Ds]):-place_queen(I,Cs,Us,Ds).
+% Still haven't figured out how to translate this to English
+% There's no description of a chessboard or how the squares
+% relate in terms of how a queen can move
+% Q is represented by column ID [0,1,...]
+% No queen can be in the same column anyway, so the set of
+% distinct column IDs represents that implied constraint.
-place_queens([],_,_,_).
-place_queens([I|Is],Cs,Us,[_|Ds]):-
- place_queens(Is,Cs,[_|Us],Ds),
- place_queen(I,Cs,Us,Ds).
+% (This was called "place_queen".)
+% Guess: a queen doesn't fight herself
-gen_places([],[]).
-gen_places([_|Qs],[_|Ps]):-gen_places(Qs,Ps).
+doesnt_fight_in(
+ QueenColumn,
+ [QueenColumn|_],
+ [QueenColumn|_],
+ [QueenColumn|_] ).
-qs(Qs,Ps):-gen_places(Qs,Ps),place_queens(Qs,Ps,_,_).
+% if a queen doesn't fight along some cols,rows,diags squares
+% then it doesn't fight in ... ???
+
+doesnt_fight_in(Q,[_|Rows],[_|LeftDiags],[_|RightDiags]):-
+ doesnt_fight_in(Q,Rows,LeftDiags,RightDiags).
+
+% (This was called "place_queens".)
+
+dont_fight_on_these_lines([],_,_,_).
+dont_fight_on_these_lines(
+ [QueenColumn|Qs],
+ Rows,
+ LeftDiags,
+ [_|RightDiags]
+) :-
+ dont_fight_on_these_lines(Qs,Rows,[_|LeftDiags],RightDiags),
+ doesnt_fight_in(QueenColumn,Rows,LeftDiags,RightDiags).
+
+% (This was called "gen_places".)
+% (Looks like it is exhaustive.)
+% Loose constraint:
+% A queen in some column can be in a row
+% if other queens are in other columns and other rows
+
+can_be_in_these_places([],[]).
+can_be_in_these_places([_|OtherColumns],[_|OtherRows]):-
+ can_be_in_these_places(OtherColumns,OtherRows).
+
+qs(Columns,Rows):-
+ can_be_in_these_places(Columns,Rows),
+ dont_fight_on_these_lines(Columns,Rows,_,_).
+
+goal(Rows):-qs([0,1,2,3,4,5,6,7,8,9,10],Rows).
+% goal(Rows):-qs([0,1,3,4],Rows).
-goal(Ps):-qs([0,1,2,3,4,5,6,7,8,9,10,11],Ps).
diff --git a/IP/progs/queens.pl.nl b/IP/progs/queens.pl.nl
deleted file mode 100644
index 7b499515..00000000
--- a/IP/progs/queens.pl.nl
+++ /dev/null
@@ -1,40 +0,0 @@
-place_queen I _0 _1 _2 and
- _0 holds list I _3 and
- _1 holds list I _4 and
- _2 holds list I _5 .
-
-place_queen I _0 _1 _2 and
- _0 holds list _3 Cs and
- _1 holds list _4 Us and
- _2 holds list _5 Ds
-if
- place_queen I Cs Us Ds .
-
-place_queens nil _0 _1 _2 .
-
-place_queens _0 Cs Us _1 and
- _0 holds list I Is and
- _1 holds list _2 Ds
-if
- place_queens Is Cs _3 Ds and
- _3 holds list _4 Us and
- place_queen I Cs Us Ds .
-
-gen_places nil nil .
-
-gen_places _0 _1 and
- _0 holds list _2 Qs and
- _1 holds list _3 Ps
-if
- gen_places Qs Ps .
-
-qs Qs Ps
-if
- gen_places Qs Ps and
- place_queens Qs Ps _0 _1 .
-
-goal Ps
-if
- qs _0 Ps and
- _0 lists 0 1 2 3 4 5 6 7 8 9 10 11 .
-
diff --git a/IP/progs/queens1.pl b/IP/progs/queens1.pl
new file mode 100644
index 00000000..a49c81b9
--- /dev/null
+++ b/IP/progs/queens1.pl
@@ -0,0 +1,19 @@
+% Q represented by row (or col) number
+
+place_queen(Q,[Q|_],[Q|_],[Q|_]).
+place_queen(Q,[_|Cols],[_|Rows],[_|Diags]):-place_queen(Q,Cols,Rows,Diags).
+
+there_are_places_where_these_queens_dont_fight([],_,_,_).
+there_are_places_where_these_queens_dont_fight([Q|Qs],Cols,Rows,[_|Diags]):-
+ there_are_places_where_these_queens_dont_fight(Qs,Cols,[_|Rows],Diags),
+ place_queen(Q,Cols,Rows,Diags).
+
+these_queens_can_be_in_these_places([],[]).
+these_queens_can_be_in_these_places([_|Qs],[_|Ps]):-
+ these_queens_can_be_in_these_rows(Qs,Ps).
+
+qs(Qs,Ps):-
+ these_queens_can_be_in_these_places(Qs,Ps),
+ there_are_places_where_these_queens_dont_fight(Qs,Ps,_,_).
+
+goal(Ps):-qs([0,1,2,3,4,5,6,7,8,9,10,11],Ps).
diff --git a/IP/progs/sumDif.pl b/IP/progs/sumDif.pl
new file mode 100644
index 00000000..594495b5
--- /dev/null
+++ b/IP/progs/sumDif.pl
@@ -0,0 +1,3 @@
+sumDif([X,+|OpenList],Hole) :- integer(X),sumDif(OpenList,Hole).
+sumDif([X|Hole],Hole) :- integer(X).
+
diff --git a/IP/progs/t.pl b/IP/progs/t.pl
new file mode 100644
index 00000000..0d89803f
--- /dev/null
+++ b/IP/progs/t.pl
@@ -0,0 +1,2 @@
+good(you).
+goal(P) :- good(P).
diff --git a/IP/src/iProlog/Clause.java b/IP/src/iProlog/Clause.java
deleted file mode 100644
index b61d1950..00000000
--- a/IP/src/iProlog/Clause.java
+++ /dev/null
@@ -1,20 +0,0 @@
-
-package iProlog;
-/**
- * representation of a clause
- */
-class Clause {
- Clause(final int len, final int[] hgs, final int base, final int neck, final int[] xs) {
- this.hgs = hgs; // head+goals pointing to cells in cs
- this.base = base; // heap where this starts
- this.len = len; // length of heap slice
- this.neck = neck; // first after the end of the head
- this.xs = xs; // indexables in head
- }
-
- final int len;
- final int[] hgs;
- final int base;
- final int neck;
- final int[] xs;
-}
diff --git a/IP/src/iProlog/Engine.java b/IP/src/iProlog/Engine.java
deleted file mode 100644
index 33307bc5..00000000
--- a/IP/src/iProlog/Engine.java
+++ /dev/null
@@ -1,1035 +0,0 @@
-package iProlog;
-import java.util.*;
-
-/**
- * Implements execution mechanism
- */
-class Engine {
-
- final static int MAXIND = 3; // number of index args
- final static int START_INDEX = 20;
- // switches off indexing for less then START_INDEX clauses e.g. <20
-
- /**
- * Builds a new engine from a natural-language style assembler.nl file
- */
- Engine(final String fname) {
- syms = new LinkedHashMap();
- slist = new ArrayList();
-
- makeHeap();
-
- trail = new IntStack();
- ustack = new IntStack();
-
- clauses = dload(fname);
-
- cls = toNums(clauses);
-
- query = init();
-
- vmaps = vcreate(MAXIND);
- imaps = index(clauses, vmaps);
- }
-
- /**
- * trimmed down clauses ready to be quickly relocated to the heap
- */
- final Clause[] clauses;
-
- final int[] cls;
- /** symbol table made of map + reverse map from ints to syms */
-
- final LinkedHashMap syms;
- final private ArrayList slist;
-
- /** runtime areas:
- *
- * the heap contains code for and clauses their their copies
- * created during execution
- *
- * the trail is an undo list for variable bindings
- * that facilitates retrying failed goals with alternative
- * matching clauses
- *
- * the unification stack ustack helps handling term unification non-recursively
- *
- * the spines stack contains abstractions of clauses and goals and performs the
- * functions of both a choice-point stack and goal stack
- *
- * imaps: contains indexes for up toMAXIND>0 arg positions (0 for pred symbol itself)
- *
- * vmaps: contains clause numbers for which vars occur in indexed arg positions
- */
-
- private int heap[];
- private int top;
- static int MINSIZE = 1 << 15; // power of 2
-
- final private IntStack trail;
- final private IntStack ustack;
- final private ObStack spines = new ObStack();
-
- Spine query;
-
- final IMap[] imaps;
- final IntMap[] vmaps;
-
- /**
- * tags of our heap cells - that can also be seen as
- * instruction codes in a compiled implementation
- */
- final private static int V = 0;
- final private static int U = 1;
- final private static int R = 2;
-
- final private static int C = 3;
- final private static int N = 4;
-
- final private static int A = 5;
-
- // G - ground?
-
- final private static int BAD = 7;
-
- /**
- * tags an integer value while fliping it into a negative
- * number to ensure that untagged cells are always negative and the tagged
- * ones are always positive - a simple way to ensure we do not mix them up
- * at runtime
- */
- final private static int tag(final int t, final int w) {
- return -((w << 3) + t);
- }
-
- /**
- * removes tag after flipping sign
- */
- final private static int detag(final int w) {
- return -w >> 3;
- }
-
- /**
- * extracts the tag of a cell
- */
- final private static int tagOf(final int w) {
- return -w & 7;
- }
-
- /**
- * places an identifier in the symbol table
- */
- final private int addSym(final String sym) {
- Integer I = syms.get(sym);
- if (null == I) {
- final int i = syms.size();
- I = new Integer(i);
- syms.put(sym, I);
- slist.add(sym);
- }
- return I.intValue();
- }
-
- /**
- * returns the symbol associated to an integer index
- * in the symbol table
- */
- final private String getSym(final int w) {
- if (w < 0 || w >= slist.size())
- return "BADSYMREF=" + w;
- return slist.get(w);
- }
-
- private final void makeHeap() {
- makeHeap(MINSIZE);
- }
-
- private final void makeHeap(final int size) {
- heap = new int[size];
- clear();
- }
-
- private final int getTop() {
- return top;
- }
-
- private final int setTop(final int top) {
- return this.top = top;
- }
-
- private final void clear() {
- //for (int i = 0; i <= top; i++)
- //heap[i] = 0;
- top = -1;
- }
-
- /**
- * Pushes an element - top is incremented frirst than the
- * element is assigned. This means top point to the last assigned
- * element - which can be returned with peek().
- */
- private final void push(final int i) {
- heap[++top] = i;
- }
-
- final int size() {
- return top + 1;
- }
-
- /**
- * dynamic array operation: doubles when full
- */
- private final void expand() {
- final int l = heap.length;
- final int[] newstack = new int[l << 1];
-
- System.arraycopy(heap, 0, newstack, 0, l);
- heap = newstack;
- }
-
- private void ensureSize(final int more) {
- if (1 + top + more >= heap.length) {
- expand();
- }
- }
-
- /**
- * expands a "Xs lists .." statements to "Xs holds" statements
- */
-
- private final static ArrayList maybeExpand(final ArrayList Ws) {
- final String W = Ws.get(0);
- if (W.length() < 2 || !"l:".equals(W.substring(0, 2)))
- return null;
-
- final int l = Ws.size();
- final ArrayList Rss = new ArrayList();
- final String V = W.substring(2);
- for (int i = 1; i < l; i++) {
- final String[] Rs = new String[4];
- final String Vi = 1 == i ? V : V + "__" + (i - 1);
- final String Vii = V + "__" + i;
- Rs[0] = "h:" + Vi;
- Rs[1] = "c:list";
- Rs[2] = Ws.get(i);
- Rs[3] = i == l - 1 ? "c:nil" : "v:" + Vii;
- Rss.add(Rs);
- }
- return Rss;
-
- }
-
- /**
- * expands, if needed, "lists" statements in sequence of statements
- */
- private final static ArrayList mapExpand(final ArrayList> Wss) {
- final ArrayList Rss = new ArrayList();
- for (final ArrayList Ws : Wss) {
-
- final ArrayList Hss = maybeExpand(Ws);
-
- if (null == Hss) {
- final String[] ws = new String[Ws.size()];
- for (int i = 0; i < ws.length; i++) {
- ws[i] = Ws.get(i);
- }
- Rss.add(ws);
- } else {
- for (final String[] X : Hss) {
- Rss.add(X);
- }
- }
- }
- return Rss;
- }
-
- /**
- * loads a program from a .nl file of
- * "natural language" equivalents of Prolog/HiLog statements
- */
- Clause[] dload(final String s) {
- final boolean fromFile = true;
- final ArrayList>> Wsss = Toks.toSentences(s, fromFile);
-
- final ArrayList Cs = new ArrayList();
-
- for (final ArrayList> Wss : Wsss) {
- // clause starts here
-
- final LinkedHashMap | | | | | | | | |