diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d511905c1647a1e311e8b20d5930a37a9c2531cd
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/entity/Base.java b/entity/Base.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2cc1244d861288c7fa5f835c59a73fc55a6eecf
--- /dev/null
+++ b/entity/Base.java
@@ -0,0 +1,122 @@
+
+package nars.entity;
+
+import nars.io.Symbols;
+import nars.main.Parameters;
+
+/**
+ * Each Sentence has a list of serial numbers of a constant length.
+ * The input sentence gets a unique number.
+ * The derived sentences inherits from its parents.
+ */
+public class Base implements Cloneable {            // make it part of TruthValue?
+    private static final int MAXLENGTH = Parameters.MAXMUM_LABEL_RECORD_LENGTH;
+    private static final String opener = Symbols.Base_opener;
+    private static final String closer = Symbols.Base_closer;
+    private static final String separator = Symbols.Base_separator;
+    private static final String separator0 = Symbols.Base_separator0;
+    
+    // for each base
+    private int length;
+    private long list[] = new long[MAXLENGTH];
+    
+    // sequential number, for the whole system
+    private static long current = 0;
+    
+    // generate a new number --- serial number or generation time?
+    // called in StringParser
+    public Base() {
+        current++;
+        length = 1;
+        list[0] = current;
+    }
+    
+    // the first one is no shorter than the second
+    private Base(Base first, Base second) {
+        int i1, i2, j;
+        i1 = i2 = j = 0;
+        while (i2 < second.length() && j < MAXLENGTH) {
+            list[j] = first.list(i1);
+            i1++;
+            j++;
+            list[j] = second.list(i2);
+            i2++;
+            j++;
+        }
+        while (i1 < first.length() && j < MAXLENGTH) {
+            list[j] = first.list(i1);
+            i1++;
+            j++;
+        }
+        length = j;
+    }
+    
+    // try to merge two Bases, return null if have overlap
+    public static Base make(Base first, Base second) {
+        for (int i = 0; i < first.length(); i++)
+            for (int j = 0; j < second.length(); j++)
+                if (first.list(i) == second.list(j))
+                    return null;
+        if (first.length() > second.length())
+            return new Base(first, second);
+        else
+            return new Base(second, first);
+    }
+    
+    // Initialize the machenism
+    // called in Center
+    public static void init() {
+        current = 0;
+    }
+    
+    public int length() {
+        return length;
+    }
+    
+    public long list(int i) {
+        return list[i];
+    }
+
+    // to check for subset
+    public boolean include(Base s) {
+        boolean result = false;
+        if (length >= s.length())
+            for (int i = 0; i < s.length(); i++) {
+                result = false;
+                for (int j = 0; j < length; j++)
+                    if (list[j] == s.list(i)) {
+                        result = true;
+                        break;
+                    }
+                if (result == false)
+                    break;
+            }
+        return result;
+    }
+    
+    public boolean equals(Object that) {
+        return ((that instanceof Base) && this.include((Base) that) && ((Base) that).include(this));
+    }
+    
+    // called for update
+    public long latest() {
+        long v = list[0];
+        for (int i = 1; i < length(); i++) 
+            if (list[i] > v)
+                v = list[i];
+        return v;
+    }
+    
+    // for display
+    public String toString() {
+        String buf = new String(opener + length + separator0);
+        for (int i = 0; i < length; i++) {
+            buf = buf.concat(Long.toString(list[i]));
+            if (i < (length - 1))
+                buf = buf.concat(separator);
+            else
+                buf = buf.concat(closer);
+        }
+        return buf;
+    }
+}
diff --git a/entity/BudgetValue.java b/entity/BudgetValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..df395086e82513503a619f0a0b0389fe3145a532
--- /dev/null
+++ b/entity/BudgetValue.java
@@ -0,0 +1,120 @@
+
+package nars.entity;
+
+import nars.inference.*;
+import nars.io.*;
+
+/**
+ * A triple of priority (current), durability (decay), and quality (long-term average).
+ */
+public class BudgetValue implements Cloneable {
+    public static final char mark = Symbols.BUDGET_VALUE_MARK;       // default
+    public static final char separator = Symbols.VALUE_SEPARATOR;    // default
+    protected ShortFloat priority;      // short-term, first-order
+    protected ShortFloat durability;    // short-term, second-order
+    protected ShortFloat quality;       // long-term
+    
+    /* ----- constructors ----- */
+    
+    public BudgetValue() {
+        priority = new ShortFloat(0.01f);
+        durability = new ShortFloat(0.01f);
+        quality = new ShortFloat(0.01f);
+    }
+
+    public BudgetValue(float p, float d, float q) {
+        priority = new ShortFloat(p);
+        durability = new ShortFloat(d);
+        quality = new ShortFloat(q);
+    }
+
+    public BudgetValue(BudgetValue v) { // clone, not copy
+        priority = new ShortFloat(v.getPriority());
+        durability = new ShortFloat(v.getDurability());
+        quality = new ShortFloat(v.getQuality());
+    }
+
+    public Object clone() {
+        return new BudgetValue(this.getPriority(), this.getDurability(), this.getQuality());
+    }
+
+    /* ----- priority ----- */
+
+    public float getPriority() {
+        return priority.getValue();
+    }
+
+    public void setPriority(float v) {
+        priority.setValue(v);
+    }
+    
+    public void incPriority(float v) {
+        priority.setValue(UtilityFunctions.or(priority.getValue(), v));
+    }
+
+    public void decPriority(float v) {
+        priority.setValue(UtilityFunctions.and(priority.getValue(), v));
+    }
+
+    /* ----- durability ----- */
+
+    public float getDurability() {
+        return durability.getValue();
+    }
+    
+    public void setDurability(float v) {
+        durability.setValue(v);
+    }
+    
+    public void incDurability(float v) {
+        durability.setValue(UtilityFunctions.or(durability.getValue(), v));
+    }
+
+    public void decDurability(float v) {
+        durability.setValue(UtilityFunctions.and(durability.getValue(), v));
+    }
+
+    /* ----- quality ----- */
+
+    public float getQuality() {
+        return quality.getValue();
+    }
+    
+    public void setQuality(float v) {
+        quality.setValue(v);
+    }
+        
+    public void incQuality(float v) {
+        quality.setValue(UtilityFunctions.or(quality.getValue(), v));
+    }
+
+    public void decQuality(float v) {
+        quality.setValue(UtilityFunctions.and(quality.getValue(), v));
+    }
+
+    /* ----- utility ----- */
+
+    public void merge(BudgetValue that) {
+        BudgetFunctions.merge(this, that);
+    }
+
+    // called from Memory only
+    public float singleValue() {
+//        return (priority.getValue() + quality.getValue()) * durability.getValue() / 2.0f;
+        return UtilityFunctions.aveAri(priority.getValue(), durability.getValue(), quality.getValue());
+    }
+    
+    public boolean aboveThreshold() {
+        return (singleValue() > 0.001); // to be revised to depend on how busy the system is
+    }
+    
+    // full output
+    public String toString() {
+        return mark + priority.toString() + separator + durability.toString() + separator + quality.toString() + mark;
+    }
+
+    // short output
+    public String toString2() {
+        return mark + priority.toString2() + separator + durability.toString2() + separator + quality.toString2() + mark;
+    }
+}
diff --git a/entity/Concept.java b/entity/Concept.java
new file mode 100644
index 0000000000000000000000000000000000000000..bad863586f8f844131680d5ed75512ba109e8963
--- /dev/null
+++ b/entity/Concept.java
@@ -0,0 +1,331 @@
+
+package nars.entity;
+
+import java.util.*;
+import java.io.*;
+
+import nars.inference.*;
+import nars.language.*;
+import nars.io.*;
+import nars.gui.*;
+import nars.main.*;
+import nars.operation.*;
+import nars.storage.*;
+
+/**
+ * A concept contains information directly related to a term, including directly and indirectly related tasks and beliefs.
+ * <p>
+ * To make sure the space will be released, no other will refer to a concept, except in a concept bag.
+ */
+public final class Concept extends Item {
+    /* Constant term as the unique ID of the concept */
+    private Term term;
+    /* Direct Task lists (they may never be used for a Term that is not used as a Statement) */
+    private ArrayList<Judgment> directBeliefs;      // Judgments with the same content
+    private ArrayList<Goal> directGoals;            // Goals that can be directly achieved
+    private Question directQuestion;                // Question that can be directly answered
+    private boolean revisible = true;               // truth value of judgments can be revised
+    /* Link bags for indirect processing (always used) */
+    private ArrayList<TermLink> linkTemplates;  // templates of TermLink, only in Concepts for CompoundTerms
+    private TaskLinkBag taskLinks;
+    private TermLinkBag termLinks;
+    /* Display related fields */
+    private boolean showing;            // whether this concept has an active window
+    private ConceptWindow window;       // (direct) content display window
+    
+    /* ---------- constructor ---------- */
+    
+    /**
+     * Constructor
+     * @param tm A constant term corresponding to the concept
+     */
+    public Concept(Term tm) {
+        super();
+        term = tm;
+        key = tm.toString();
+        directBeliefs = new ArrayList<Judgment>();
+        directGoals = new ArrayList<Goal>();
+        directQuestion = null;
+        taskLinks = new TaskLinkBag();
+        termLinks = new TermLinkBag();
+        if (tm instanceof CompoundTerm) {
+            linkTemplates = ((CompoundTerm) tm).prepareComponentLinks();
+            checkRevisibility();
+        }
+        showing = false;
+        window = null;
+    }
+    
+    private void checkRevisibility() {
+        revisible = !(term instanceof Tense);                                   // no tense
+        if (revisible)
+            revisible = ((key.indexOf("()") < 0) && (key.indexOf("(#")) < 0);   // no dependent variable
+    }
+    
+    /* ---------- add direct information as Tasks ---------- */
+    
+    /**
+     * New task to be directly processed in a constant time, called from Memory only
+     * @param task The task to be processed
+     */
+    public void directProcess(Task task) {
+        Memory.currentTask = task;
+        Sentence sentence = task.getSentence();
+        if (sentence instanceof Question)
+            processQuestion(task);
+        else if (sentence instanceof Goal)
+            processGoal(task);
+        else
+            processJudgment(task);
+        Memory.activateConcept(this, task.getBudget());
+        if (showing)
+            window.post(displayContent());
+    }
+    
+    /**
+     * New question to be directly answered by existing beliefs
+     * @param task The task to be processed
+     */
+    private void processQuestion(Task task) {
+        if (directQuestion == null)
+            directQuestion = (Question) task.getSentence();         // remember it
+        for (int i = 0; i < directBeliefs.size(); i++) {
+            Judgment judg = directBeliefs.get(i);
+            MatchingRules.trySolution(directQuestion, judg, task);    // look for better answer
+        }
+    }
+    
+    /**
+     * New judgment
+     * @param task The task to be processed
+     */
+    private void processJudgment(Task task) {
+        Judgment judg = (Judgment) task.getSentence();
+        if (revisible)
+            reviseTable(task, directBeliefs);
+        else
+            updateTable(task);
+        if (task.getPriority() > 0) {               // if still valuable --- necessary???
+            if (directQuestion != null)
+                MatchingRules.trySolution(directQuestion, judg, task);
+            for (int i = 0; i < directGoals.size(); i++) {
+                Goal goal = directGoals.get(i);
+                MatchingRules.trySolution(goal, judg, task);
+            }
+            addToTable(judg, directBeliefs, Parameters.MAXMUM_BELIEF_LENGTH);
+        }
+    }
+    
+    /**
+     * New goal
+     * @param task The task to be processed
+     */
+    private void processGoal(Task task) {
+        Goal goal = (Goal) task.getSentence();
+        if (revisible)
+            reviseTable(task, directGoals);
+        else
+            updateTable(task);
+        for (int i = 0; i < directBeliefs.size(); i++) {
+            Judgment judg = directBeliefs.get(i);
+            MatchingRules.trySolution(goal, judg, task);
+        }
+        if (task.getPriority() > 0) {              // if still valuable
+            addToTable(goal, directGoals, Parameters.MAXMUM_GOALS_LENGTH);         // with the feedbacks
+        }
+        decisionMaking(task);
+    }
+    
+    private void decisionMaking(Task task) {    // add plausibility
+        Goal goal = (Goal) task.getSentence();
+        float desire = 2 * goal.getTruth().getExpectation() - 1;
+        float quality = (desire < 0) ? 0 : desire;
+        task.setQuality(quality);
+    }
+    
+    // revise previous beliefs or goals
+    private void reviseTable(Task task, ArrayList table) {
+        Judgment belief;
+        for (int i = 0; i < table.size(); i++) {    // call select()
+            belief = (Judgment) table.get(i);
+            if (belief.noOverlapping((Judgment) task.getSentence()))
+                MatchingRules.revision(task, belief, false);
+        }
+    }
+    
+    // to be rewritten
+    private void updateTable(Task task) {
+//        Judgment belief;
+//        for (int i = 0; i < directBeliefs.size(); i++) {    // call select()
+//            belief = directBeliefs.get(i);
+//            if (((Judgment) task.getSentence()).getBase().latest() > belief.getBase().latest())
+//                MatchingRules.update(task, belief);
+//        }
+    }
+    
+    // add the Task as a new direct Belief or Goal, remove redundant ones
+    // table sorted by rank
+    private void addToTable(Judgment newJudgment, ArrayList table, int capacity) {
+        float rank1 = BudgetFunctions.rankBelief(newJudgment);    // for the new belief
+        Base base1 = newJudgment.getBase();
+        Judgment judgment2;
+        float rank2;
+        int i;
+        for (i = 0; i < table.size(); i++) {        // go through everyone
+            judgment2 = (Judgment) table.get(i);
+            rank2 = BudgetFunctions.rankBelief(judgment2); // previous belief
+            if (rank1 >= rank2) {
+                if (newJudgment.equivalentTo(judgment2))
+                    return;
+                table.add(i, newJudgment);
+                break;
+            }
+        }
+        if (table.size() == capacity)
+            table.remove(capacity - 1);
+        else if (i == table.size())
+            table.add(newJudgment);
+    }
+
+    // return a piece of Belief to be used with the task
+    // get the first qualified one
+    public Judgment getBelief(Task task) {
+        Sentence sentence = task.getSentence();
+        Judgment belief;
+        for (int i = 0; i < directBeliefs.size(); i++) {
+            belief = directBeliefs.get(i);
+            if ((sentence instanceof Question) || belief.noOverlapping((Judgment) sentence)) {
+                Record.append(" * Selected Belief: " + belief + "\n");
+                return belief;
+            }
+        }
+        return null;
+    }
+    
+    /* ---------- insert relational information as Links ---------- */
+    
+    public ArrayList<TermLink> getTermLinks() {
+        return linkTemplates;
+    }
+    
+    // insert TaskLink into the task base, called from Memory only
+    public void insertTaskLink(TaskLink taskLink) {
+        BudgetValue budget = taskLink.getBudget();
+        taskLinks.putIn(taskLink);
+        Memory.activateConcept(this, budget);       // activate the concept
+        if (term instanceof CompoundTerm)
+            buildTermLinks(budget);
+    }
+    
+    private void buildTermLinks(BudgetValue budget) {
+        Term t;
+        Concept c;
+        TermLink cLink1, cLink2;
+        BudgetValue subBudget = BudgetFunctions.distributeAmongLinks(budget, linkTemplates.size());
+        if (!subBudget.aboveThreshold())
+            return;
+        for(TermLink link : linkTemplates) {
+            t = link.getTarget();
+            c = Memory.getConcept(t);
+            cLink1 = new TermLink(t, link, subBudget);
+            insertTermLink(cLink1);   // this link to that
+            cLink2 = new TermLink(term, link, subBudget);
+            c.insertTermLink(cLink2);   // that link to this
+            if (t instanceof CompoundTerm)
+                c.buildTermLinks(subBudget);
+        }
+    }
+    
+    // insert TermLink into the Belief base, called from Memory only
+    public void insertTermLink(TermLink cLink) {
+        termLinks.putIn(cLink);
+        Memory.activateConcept(this, cLink.getBudget());
+    }
+    
+    /* ---------- main loop ---------- */
+    
+    // a single step of syllogism within a concept
+    public void fire() {
+        TaskLink tLink = (TaskLink) taskLinks.takeOut();
+        if (tLink == null)
+            return;
+        Memory.currentTaskLink = tLink;
+        Memory.currentBeliefLink = null;
+        if (NARS.isStandAlone())
+            Record.append(" * Selected TaskLink: " + tLink + "\n");
+        Task task = tLink.getTargetTask();
+        Record.append(" * Selected Task: " + task + "\n");
+        Memory.currentTask = task;
+        if ((tLink.getType() == TermLink.TRANSFORM) && !task.isStructual()) {
+            RuleTables.transformTask(task, tLink);      // inference from a TaskLink and the Task --- for Product and Image
+            return; // cannot be used otherwise
+        }
+        TermLink bLink = (TermLink) termLinks.takeOut(tLink);  // to avoid repeated syllogism
+        if (bLink != null) {
+            if (NARS.isStandAlone())
+                Record.append(" * Selected BeliefLink: " + bLink + "\n");
+            Memory.currentBeliefLink = bLink;
+            RuleTables.reason(tLink, bLink);
+            termLinks.putBack(bLink);
+        }
+        taskLinks.putBack(tLink);
+    }
+    
+    /* ---------- utility ---------- */
+    
+    public Term getTerm() {     // called from Memory only
+        return term;
+    }
+    
+    public String toString() {  // called from concept bag
+        if (NARS.isStandAlone())
+            return (super.toString2() + " " + key);
+        else
+            return key;
+    }
+    
+    public float getQuality() {         // re-calculate-and-set? consider syntactic complexity?
+        return UtilityFunctions.and(taskLinks.averagePriority(), termLinks.averagePriority());
+    }
+    
+    /* ---------- display ---------- */
+    
+    // to display contents and links, called from ConceptWindow only
+    public void startPlay() {
+        window = new ConceptWindow(this);
+        showing = true;
+        window.post(displayContent());
+        taskLinks.startPlay("Task Links in " + term);
+        termLinks.startPlay("Term Links in " + term);
+    }
+    
+    // called from ConceptWindow only
+    public void play() {
+        showing = true;
+        window.post(displayContent());
+    }
+    
+    // called from ConceptWindow only
+    public void stop() {
+        showing = false;
+    }
+    
+    // display direct belief, questions, and goals
+    private String displayContent() {
+        StringBuffer buffer = new StringBuffer();
+        if (directBeliefs.size() > 0) {
+            buffer.append("  Beliefs:\n");
+            for (int i = 0; i < directBeliefs.size(); i++)
+                buffer.append(directBeliefs.get(i) + "\n");
+        }
+        if (directGoals.size() > 0) {
+            buffer.append("\n  Goals:\n");
+            for (int i = 0; i < directGoals.size(); i++)
+                buffer.append(directGoals.get(i) + "\n");
+        }
+        if (directQuestion != null)
+            buffer.append("\n  Question:\n" + directQuestion + "\n");
+        return buffer.toString();
+    }
+}
+
diff --git a/entity/Goal.java b/entity/Goal.java
new file mode 100644
index 0000000000000000000000000000000000000000..1527e044226777125a1e811808ebd2bb29de9858
--- /dev/null
+++ b/entity/Goal.java
@@ -0,0 +1,14 @@
+
+package nars.entity;
+
+import nars.language.Term;
+
+/**
+ * A Goal is an event to be realized, and may conain query variables
+ */
+public class Goal extends Judgment {
+    public Goal(Term term, char punc, TruthValue t, Base b) {
+        super(term, punc, t, b);
+    }
+}
+
diff --git a/entity/Item.java b/entity/Item.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e1453e418aacfa0bbc08c439423064d0425b339
--- /dev/null
+++ b/entity/Item.java
@@ -0,0 +1,53 @@
+
+package nars.entity;
+
+/**
+ * An item is an object that can be put into a Bag,
+ * and it participates in the resource competation of the system.
+ */
+public abstract class Item extends BudgetValue implements Cloneable {
+    /**
+     * The key of the Item, unique in a Bag
+     */
+    protected String key;   // uniquely define an Item in a bag
+    
+    protected Item() {}
+    
+    protected Item(BudgetValue v) {
+        super(v);
+    }
+            
+    /**
+     * Get the current key
+     * @return Current key value
+     */
+    public String getKey() {
+        return key;
+    }
+    
+    /**
+     * Set a new key value
+     * @param k New key value
+     */
+    public void setKey(String k) {
+        key = k;
+    }
+
+    /**
+     * Get current BudgetValue
+     * @return Current BudgetValue
+     */
+    public BudgetValue getBudget() {
+        return this;
+    }
+
+    /**
+     * Set new BudgetValue
+     * @param v new BudgetValue
+     */
+    public void setBudget(BudgetValue v) {      // is this necessary?
+        setPriority(v.getPriority());
+        setDurability(v.getDurability());
+        setQuality(v.getQuality());
+    }
+}
diff --git a/entity/Judgment.java b/entity/Judgment.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb54c68089382ea37868c8d51126094fa06de878
--- /dev/null
+++ b/entity/Judgment.java
@@ -0,0 +1,61 @@
+
+package nars.entity;
+
+import nars.language.Term;
+import nars.main.*;
+
+/**
+ * A Judgment is an piece of new knowledge to be absorbed.
+ */
+public class Judgment extends Sentence {
+    
+    public Judgment(Term term, char punc, TruthValue t, Base b) {
+        content = term;
+        punctuation = punc;
+        truth = t;
+        base = b;
+    }
+  
+    public TruthValue getTruth() {
+        return truth;
+    }
+
+    public float getFrequency() {
+        return truth.getFrequency();
+    }
+
+    public float getConfidence() {
+        return truth.getConfidence();
+    }
+
+    public Base getBase() {
+        return base;
+    }
+
+    boolean equivalentTo(Judgment judgment2) {
+        return (truth.equals(judgment2.getTruth()) && base.equals(judgment2.getBase())); // may have different key
+    }
+
+    public float getExpectationDifference(Judgment that) {
+        return getTruth().getExpDifAbs(that.getTruth());
+    }
+    
+    public float solutionQuality(Sentence sentence) {
+        Term problem = sentence.getContent(); 
+        if (sentence instanceof Goal) 
+            return truth.getExpectation();
+        else if (problem.isConstant())          // "yes/no" question
+            return truth.getConfidence();                                 // by confidence
+        else                                                            // "what" question or goal
+            return truth.getExpectation() / content.getComplexity();      // by likelihood/simplicity, to be refined
+    }
+    
+    public boolean noOverlapping(Judgment judgment) {
+        Base b = Base.make(base, judgment.getBase());
+        if (b == null)
+            return false;
+        Memory.currentBase = b;
+        return true;
+    }
+}
+
diff --git a/entity/Question.java b/entity/Question.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4c020940c6aac04a1dd72563f252ea5030f75c9
--- /dev/null
+++ b/entity/Question.java
@@ -0,0 +1,16 @@
+
+package nars.entity;
+
+import nars.language.Term;
+
+/**
+ * A Question is a sentence without a truth value needs evaluation, and may conain query variables
+ */
+public class Question extends Sentence {
+
+    public Question(Term term, char punc) {
+        content = term;
+        punctuation = punc;
+    }
+}
+
diff --git a/entity/Sentence.java b/entity/Sentence.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2cde5f832e412c917ee74d9c7f213a52a5d5106
--- /dev/null
+++ b/entity/Sentence.java
@@ -0,0 +1,134 @@
+
+package nars.entity;
+
+import nars.io.Symbols;
+import nars.language.*;
+import nars.main.*;
+
+/**
+ * A Sentence contains a Statement, a TruthValue, and a Base list.
+ *<p>
+ * It is used as the premises and conclusions of all inference rules.
+ */
+public abstract class Sentence {    
+    protected Term content;
+    protected char punctuation;
+    protected TruthValue truth = null;
+    protected Base base = null;
+    protected boolean input = false;            // whether it is an input sentence
+    protected Judgment bestSolution = null;       // for Question and Goal
+    
+    /**
+     * Make a Sentence from an input String. Called by StringParser.
+     * @param term The content of the sentence
+     * @param punc The puncuation (and therefore, type) of the sentence
+     * @param truth The truth value of the sentence, if it is a Judgment (or Goal)
+     * @param base The base of the truth value (for Judgment or Goal)
+     * @return the Sentence generated from the arguments
+     */
+    public static Sentence make(Term term, char punc, TruthValue truth, Base base) {
+        if (term instanceof CompoundTerm)
+            ((CompoundTerm) term).renameVariables();
+        switch (punc) {
+            case Symbols.JUDGMENT_MARK:
+                return new Judgment(term, punc, truth, base);
+            case Symbols.GOAL_MARK:
+                return new Goal(term, punc, truth, base);
+            case Symbols.QUESTION_MARK:
+                return new Question(term, punc);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Make a derived Sentence. Called by Memory.
+     * @param term The content of the sentence
+     * @param oldS A sample sentence providing the type of the new sentence
+     * @param truth The truth value of the sentence, if it is a Judgment (or Goal)
+     * @param base The base of the truth value (for Judgment or Goal)
+     * @return the Sentence generated from the arguments
+     */
+    public static Sentence make(Sentence oldS, Term term, TruthValue truth, Base base) {
+        if (term instanceof CompoundTerm)
+            ((CompoundTerm) term).renameVariables();
+        if (oldS instanceof Question)
+            return new Question(term, Symbols.QUESTION_MARK);
+        if (oldS instanceof Goal)
+            return new Goal(term, Symbols.GOAL_MARK, truth, base);
+        return new Judgment(term, Symbols.JUDGMENT_MARK, truth, base);
+    }
+    
+    public Term getContent() {
+        return content;
+    }
+
+    public Term cloneContent() {
+        return (Term) content.clone();
+    }
+
+    public TruthValue getTruth() {
+        return null;
+    }
+
+    public Base getBase() {
+        return null;
+    }
+    
+    // distinguish Judgment from Goal
+    public boolean isJudgment() {
+        return (punctuation == Symbols.JUDGMENT_MARK);
+    }
+
+    public boolean isInput() {
+        return input;
+    }
+    
+    public void setInput() {
+        input = true;
+    }
+
+    public Judgment getBestSolution() {
+        return bestSolution;
+    }
+    
+    public void setBestSolution(Judgment judg) {
+        bestSolution = judg;
+        if (input)
+            Memory.report(judg, false);        // report answer to input question
+    }
+    
+    // display a sentence
+    public String toString() {
+        StringBuffer s = new StringBuffer();
+        s.append(content.getName());
+        s.append(punctuation + " ");
+        if (truth != null) {
+            s.append(truth.toString()); 
+            if (NARS.isStandAlone())
+                s.append(base.toString());
+        }
+        if (NARS.isStandAlone()) {
+            if (bestSolution != null)
+                s.append("BestSolution: " + bestSolution);
+        }
+        return s.toString();
+    }
+    
+    // display a sentence in compact form (2 digits)
+    public String toString2() {
+        StringBuffer s = new StringBuffer();
+        s.append(content.getName());
+        s.append(punctuation + " ");
+        if (truth != null) {
+            s.append(truth.toString2()); 
+            if (NARS.isStandAlone())
+                s.append(base.toString());
+        }
+        if (NARS.isStandAlone()) {
+            if (bestSolution != null)
+                s.append("BestSolution: " + bestSolution);
+        }
+        return s.toString();
+    }
+}
diff --git a/entity/ShortFloat.java b/entity/ShortFloat.java
new file mode 100644
index 0000000000000000000000000000000000000000..076f1d54c05d2be6100da2b748d8c40e95bf8c67
--- /dev/null
+++ b/entity/ShortFloat.java
@@ -0,0 +1,58 @@
+
+package nars.entity;
+
+/**
+ * A float value in [0, 1], with 4 digits accuracy.
+ */
+public class ShortFloat implements Cloneable {
+    
+    // the values are saved as short integers (-32768 to 32767, only 0 to 10000 used),
+    // but used as float
+    private short value;
+    
+    public ShortFloat(float v) {
+        setValue(v);
+    }
+    
+    // access value
+    public float getValue() {
+        return (float) (value * 0.0001);
+    }
+
+    public short getShortValue() {
+        return value;
+    }
+    
+    // set new value, rounded, with validity checking
+    public void setValue(float v) {
+        if ((v < 0) || (v > 1))
+            System.out.println("!!! Wrong value: " + v);
+        else
+            value = (short) (v * 10000.0 + 0.5);
+    }
+    
+    public boolean equals(Object that) {
+        return ((that instanceof ShortFloat) && (value == ((ShortFloat) that).getShortValue()));
+    }
+    
+    // full output
+    public String toString() {
+        if (value == 10000)
+            return "1.0000";
+        else {
+            String s = String.valueOf(value);
+            while (s.length() < 4)
+                s = "0" + s;
+            return "0." + s;
+        }
+    }
+    
+    // output with 2 digits, rounded
+    public String toString2() {
+        String s = toString();
+        if (s.length() > 4)
+            return s.substring(0, 4);
+        else
+            return s;
+    }
+}
diff --git a/entity/Task.java b/entity/Task.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bd9ee5c753369f6a9f31e9e142602e615705f26
--- /dev/null
+++ b/entity/Task.java
@@ -0,0 +1,61 @@
+
+package nars.entity;
+
+import nars.main.NARS;
+import nars.operation.Operator;
+import nars.language.*;
+
+/**
+ * A task to be processed.
+ */
+public class Task extends Item {
+    private Sentence sentence;
+    protected boolean structual = false;        // whether it is based on a structual rule
+           
+    public Task(Sentence s, BudgetValue b) {
+        super(b);
+        sentence = s;
+        key = sentence.toString();
+    }
+    
+    public Sentence getSentence() {
+        return sentence;
+    }
+
+    public Term getContent() {
+        return sentence.getContent();
+    }
+
+    public boolean isStructual() {
+        return structual;
+    }
+    
+    public void setStructual() {
+        structual = true;
+    }
+
+    public void merge(Item that) {
+        ((BudgetValue) this).merge(that.getBudget());
+        structual = (structual || ((Task) that).isStructual());
+    }
+
+    public String toString() {
+        StringBuffer s = new StringBuffer();
+        if (NARS.isStandAlone())
+            s.append(super.toString());
+        s.append(sentence);
+        return s.toString();
+    }
+
+    public String toString2() {
+        StringBuffer s = new StringBuffer();
+        if (NARS.isStandAlone())
+            s.append(super.toString2());
+        if (sentence instanceof Question)
+            s.append(sentence);
+        else
+            s.append(((Judgment) sentence).toString2());
+        return s.toString();
+    }
+}
+
diff --git a/entity/TaskLink.java b/entity/TaskLink.java
new file mode 100644
index 0000000000000000000000000000000000000000..1381b8397ad7755ea92b5dde9c743dea6680a66e
--- /dev/null
+++ b/entity/TaskLink.java
@@ -0,0 +1,67 @@
+
+package nars.entity;
+
+import java.util.*;
+import nars.language.Term;
+import nars.main.Parameters;
+
+/**
+ * Reference to a Task.
+ * <p>
+ * The reason to separate a Task and a TaskLink is that a Task can be linked from multiple Concepts, with different BudgetValue.
+ */
+public class TaskLink extends TermLink {
+    private static final int RECORD_LENGTH = Parameters.TASK_INFERENCE_RECORD_LENGTH;
+    private Task targetTask;        // now "target" means a term !!!
+    private ArrayList<String> record; // remember the CompositionLinks that has been used recently
+    
+    public TaskLink(Task t, TermLink template, BudgetValue v) {
+        super(v);
+        if (template == null) {
+            type = TermLink.SELF;
+            index = null;
+        } else {
+            type = template.getType();
+            index = template.getIndices();            
+        }
+        targetTask = t;
+        record = new ArrayList<String>(Parameters.TASK_INFERENCE_RECORD_LENGTH);
+        setKey();
+        key += t.getKey();
+    }
+    
+    public Task getTargetTask() {
+        return targetTask;
+    }
+    
+    public ArrayList<String> getRecord() {
+        return record;
+    }
+    
+    public void merge(Item that) {
+        ((BudgetValue) this).merge(that.getBudget());
+        ArrayList<String> v = ((TaskLink) that).getRecord();
+        for (int i = 0; i < v.size(); i++)
+            if (record.size() <= RECORD_LENGTH)
+                record.add(v.get(i));
+    }
+
+    // To check whether a TaskLink can use a TermLink
+    // return false if they intereacted recently
+    // called in CompositionBag only
+    // move into Task ?
+    public boolean novel(TermLink bLink) {
+        Term bTerm = bLink.getTarget();
+        if (bTerm.equals(targetTask.getSentence().getContent()))
+            return false;
+        String key = bLink.getKey();
+        for (int i = 0; i < record.size(); i++)
+            if (key.equals((String) record.get(i)))
+                return false;
+        record.add(key);       // add knowledge reference to record
+//        if (record.size() > RECORD_LENGTH)         // keep a constant length --- allow repeatation
+//            record.remove(0);
+        return true;
+    }
+}
+
diff --git a/entity/TermLink.java b/entity/TermLink.java
new file mode 100644
index 0000000000000000000000000000000000000000..974e06c9143b9a69e6c062b93d050bb3a00534bd
--- /dev/null
+++ b/entity/TermLink.java
@@ -0,0 +1,145 @@
+
+package nars.entity;
+
+import nars.language.CompoundTerm;
+import nars.language.Term;
+import nars.io.Symbols;
+import nars.main.Memory;
+
+/**
+ * A link between a compound term and a component term
+ * <p>
+ * A TermLink links the current Term to a target Term, which is 
+ * either a component of, or compound made from, the current term.
+ * <p>
+ * Both terms are constant.
+ * <p>
+ * The index value(s) indicates the location of the component in the compound.
+ */
+public class TermLink extends Item {
+
+    public static final short SELF = 0;                 // TaskLink only
+    public static final short COMPONENT = 1;
+    public static final short COMPOUND = 2;
+    public static final short COMPONENT_STATEMENT = 3;
+    public static final short COMPOUND_STATEMENT = 4;
+    public static final short COMPONENT_CONDITION = 5;
+    public static final short COMPOUND_CONDITION = 6;
+    public static final short TRANSFORM = 7;            // TaskLink only
+        
+    private Term target;
+    protected short type;
+    protected short[] index;
+    
+    public TermLink() {}
+    
+    /**
+     * Simplest constructor, called in CompoundTerm and Implication
+     * @param t target Term
+     * @param p link type
+     * @param i component index in compound
+     */
+    public TermLink(Term t, short p, int i) {
+        target = t;
+        type = p;
+        index = new short[1];
+        index[0] = (short) i;
+        setKey();
+    }
+
+    public TermLink(Term t, short p, int i, int j) {
+        target = t;
+        type = p;
+        index = new short[2];
+        index[0] = (short) i;
+        index[1] = (short) j;
+        setKey();
+    }
+
+    public TermLink(Term t, short p, int i, int j, int k) {
+        target = t;
+        type = p;
+        index = new short[3];
+        index[0] = (short) i;
+        index[1] = (short) j;
+        index[2] = (short) k;
+        setKey();
+    }
+
+    protected TermLink(BudgetValue v) {
+        super(v);
+    }
+    
+    // the CompotionLink that is actually inserted
+    public TermLink(Term t, TermLink template, BudgetValue v) {
+        super(v);
+        target = t;
+        type = template.getType();
+        if (template.getTarget().equals(target)) 
+            type = reverse(type);
+        index = template.getIndices();
+        setKey();
+    }
+
+    protected void setKey() {
+        String at1, at2;
+        if (toComponent()) {
+            at1 = Symbols.LinkToComponent_at1;
+            at2 = Symbols.LinkToComponent_at2;
+        } else {
+            at1 = Symbols.LinkToCompound_at1;
+            at2 = Symbols.LinkToCompound_at2;            
+        }
+        String in = "T" + type;
+        if (index != null)
+            for (int i = 0; i < index.length; i++) {
+                in += "-" + (index[i]+1);
+            }
+        key = at1 + in + at2;
+        if (target != null)
+            key += target;
+    }
+    
+    public Term getTarget() {
+        return target;
+    }
+
+    public short getType() {
+        return type;
+    }
+    
+    public boolean toComponent() {
+        return ((type % 2) > 0);
+    }
+
+    public int indexLength() {
+        return (index == null)? 0 : index.length;
+    }
+    
+    public short[] getIndices() {
+        return index;
+    }
+    
+    public short getIndex(int i) {
+        if ((index != null) && (i < index.length))
+            return index[i];
+        else
+            return -1;
+    }
+    
+    protected short reverse(short i) {
+        if ((i % 2) == 0)
+            return (short) (i - 1);
+        else
+            return (short) (i + 1);
+    }
+    
+    public String toString() {
+        return (super.toString() + " " + key);
+    }
+
+    public String toString2() {
+        return (super.toString2() + " " + key);
+    }
+}
+
diff --git a/entity/TruthValue.java b/entity/TruthValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..e14320675159f514b3cfc06a94edd7aaccb8e69f
--- /dev/null
+++ b/entity/TruthValue.java
@@ -0,0 +1,61 @@
+
+package nars.entity;
+
+import nars.language.Term;
+import nars.io.*;
+
+/**
+ * Frequency and confidence.
+ */
+public class TruthValue { // implements Cloneable {
+    public static final char DELIMITER = Symbols.TRUTH_VALUE_MARK;       // default
+    public static final char SEPARATOR = Symbols.VALUE_SEPARATOR;    // default
+    protected ShortFloat frequency;
+    protected ShortFloat confidence;
+        
+    public TruthValue(float f, float c) {
+        frequency = new ShortFloat(f);
+        confidence = new ShortFloat(c);
+    }
+    
+    public TruthValue(TruthValue v) {
+        frequency = new ShortFloat(v.getFrequency());
+        confidence = new ShortFloat(v.getConfidence());
+    }
+        
+    public float getFrequency() {
+        return frequency.getValue();
+    }
+        
+    public float getConfidence() {
+        return confidence.getValue();
+    }
+        
+    public float getExpectation() {
+        return (float) (confidence.getValue() * (frequency.getValue() - 0.5) + 0.5);
+    }
+    
+    public float getExpDifAbs(float e) {
+        return Math.abs(e - getExpectation());
+    }
+    
+    public float getExpDifAbs(TruthValue t) {
+        return getExpDifAbs(t.getExpectation());
+    }
+    
+    public boolean equals(Object that) {
+        return ((that instanceof TruthValue) 
+                && (getFrequency() == ((TruthValue) that).getFrequency())
+                && (getConfidence() == ((TruthValue) that).getConfidence()));
+    }
+    
+    // full output
+    public String toString() {
+        return DELIMITER + frequency.toString() + SEPARATOR + confidence.toString() + DELIMITER;
+    }
+    
+    // short output
+    public String toString2() {
+        return DELIMITER + frequency.toString2() + SEPARATOR + confidence.toString2() + DELIMITER;
+    }
+}
diff --git a/entity/package.html b/entity/package.html
new file mode 100644
index 0000000000000000000000000000000000000000..ffbdf26feae18dffab1f0cb478624683dbc2e22d
--- /dev/null
+++ b/entity/package.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<body bgcolor="white">
+
+Data entities that are independently stored
+
+<h2>Package Specification</h2>
+
+<ul>
+<li>ShortFloats: BudgetValue (priority/durability/quality) and TruthValue (frequency/confidence)</li>
+<li>Base: serial numbers associated to TruthVallue</li>
+<li>Sentence: a Term, a TruthValue, and a Base. A Sentence can be a Judgment, a Goal, or a Question.</li>
+<li>Task: a Sentence to be processed.</li>
+<li>TermLink: built in pair between a compound term and a component term.</li>
+<li>TaskLink: special TermLink refering to a Task, whose Term equals or directly contains the current Term.</li>
+<li>Concept: labeled by a Term, contains a TaskLink bag and a TermLink bag for indirect tasks/beliefs, as well as beliefs/questions/goals directly on the Term.</li>
+<li>Item: Concept, Task, or TermLink</li>
+</ul>    
+
+</body>
+</html>
diff --git a/gui/BagWindow.java b/gui/BagWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef5bb04046d5d1f9f48ec5106f900fed1061a4d1
--- /dev/null
+++ b/gui/BagWindow.java
@@ -0,0 +1,132 @@
+
+package nars.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import nars.main.Parameters;
+import nars.storage.Bag;
+
+/**
+ * Window display the priority distribution of items within a given bag
+ */
+public class BagWindow extends NarsFrame implements ActionListener, AdjustmentListener {
+    /** The bag to be displayed */    
+    private Bag	bag;
+    /** The lowest level displayed */    
+    private int         showLevel;
+    
+    /* GUI components */    
+    private Button      playButton, stopButton, closeButton;
+    private TextArea	text;
+    private Label       valueLabel;
+    private Scrollbar   valueBar;
+    
+    /** The location of the display area, shifted according to the number of windows openned */    
+    private static int counter;
+    
+    /**
+     * Constructor
+     * @param b The bag to be displayed
+     * @param title The title of the window
+     */
+    public BagWindow(Bag b, String title) {
+        super(title);
+        bag = b;
+        showLevel = Parameters.BAG_THRESHOLD;
+        setBackground(MULTIPLE_WINDOW_COLOR);
+        GridBagLayout gridbag = new GridBagLayout();
+        GridBagConstraints c = new GridBagConstraints();
+        setLayout(gridbag);
+        
+        c.ipadx = 3;
+        c.ipady = 3;
+        c.insets = new Insets(5,5,5,5);
+        c.fill = GridBagConstraints.BOTH;
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.weightx = 1.0;
+        c.weighty = 1.0;
+        text = new TextArea("");
+        text.setBackground(DISPLAY_BACKGROUND_COLOR);
+        text.setEditable(false);
+        gridbag.setConstraints(text, c);
+        add(text);
+        
+        c.weighty = 0.0;
+        c.gridwidth = 1;
+        valueLabel = new Label(String.valueOf(showLevel), Label.RIGHT);
+        gridbag.setConstraints(valueLabel, c);
+        add(valueLabel);
+        
+        valueBar = new Scrollbar(Scrollbar.HORIZONTAL,showLevel,0,1,Parameters.BAG_LEVEL);
+        valueBar.addAdjustmentListener(this);
+        gridbag.setConstraints(valueBar, c);
+        add(valueBar);
+        
+        playButton = new Button("Play");
+        gridbag.setConstraints(playButton, c);
+        playButton.addActionListener(this);
+        add(playButton);
+        
+        stopButton = new Button("Stop");
+        gridbag.setConstraints(stopButton, c);
+        stopButton.addActionListener(this);
+        add(stopButton);
+        
+        closeButton = new Button("Close");
+        gridbag.setConstraints(closeButton, c);
+        closeButton.addActionListener(this);
+        add(closeButton);
+        
+        setBounds(400, 60 + counter * 20, 400, 270);
+        counter++;
+        setVisible(true);
+    }
+    
+    /**
+     * Post the bag content
+     * @param str The text
+     */
+    public void post(String str) {
+        text.setText(str);
+    }
+    
+    /**
+     * The lowest display level
+     * @return The level
+     */
+    public int showLevel() {
+        return showLevel;
+    }
+    
+    /**
+     * Handling button click
+     * @param e The ActionEvent
+     */
+    public void actionPerformed(ActionEvent e) {
+        Object source = e.getSource();
+        if (source == playButton) {
+            bag.play();
+        } else if (source == stopButton) {
+            bag.stop();
+        } else if (source == closeButton) {
+            bag.stop();
+            dispose();
+            counter--;
+        }
+    }
+
+    /**
+     * Handling scrollbar movement
+     * @param e The AdjustmentEvent
+     */
+    public void adjustmentValueChanged(AdjustmentEvent e) {
+        if (e.getSource() == valueBar) {
+            int v = ((Scrollbar) valueBar).getValue();
+            valueLabel.setText(String.valueOf(v));
+            valueBar.setValue(v);
+            showLevel = v;
+            bag.play();
+        }
+    }   
+}
diff --git a/gui/ConceptWindow.java b/gui/ConceptWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..d05b0deab1efe3b37e4cb95d40bbc1b7021c95a2
--- /dev/null
+++ b/gui/ConceptWindow.java
@@ -0,0 +1,88 @@
+
+package nars.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import nars.entity.Concept;
+
+/**
+ * Window displaying a Concept
+ */
+public class ConceptWindow extends NarsFrame implements ActionListener {
+    /* GUI components */
+    private Button      playButton, stopButton, closeButton;
+    private TextArea	text;
+    /** The concept to be displayed */
+    private Concept     concept;
+    
+    /**
+     * Constructor
+     * @param concept The concept to be displayed
+     */
+    public ConceptWindow(Concept concept) {
+        super(concept.getKey());
+        this.concept = concept;
+        setBackground(MULTIPLE_WINDOW_COLOR);
+        GridBagLayout gridbag = new GridBagLayout();
+        GridBagConstraints c = new GridBagConstraints();
+        setLayout(gridbag);
+        
+        c.ipadx = 3;
+        c.ipady = 3;
+        c.insets = new Insets(5,5,5,5);
+        c.fill = GridBagConstraints.BOTH;
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.weightx = 1.0;
+        c.weighty = 1.0;
+        text = new TextArea("");
+        text.setBackground(DISPLAY_BACKGROUND_COLOR);
+        text.setEditable(false);
+        gridbag.setConstraints(text, c);
+        add(text);
+        
+        c.weighty = 0.0;
+        c.gridwidth = 1;
+        playButton = new Button("Play");
+        gridbag.setConstraints(playButton, c);
+        playButton.addActionListener(this);
+        add(playButton);
+        
+        stopButton = new Button("Stop");
+        gridbag.setConstraints(stopButton, c);
+        stopButton.addActionListener(this);
+        add(stopButton);
+        
+        closeButton = new Button("Close");
+        gridbag.setConstraints(closeButton, c);
+        closeButton.addActionListener(this);
+        add(closeButton);
+        
+        setBounds(400, 60, 400, 270);
+        setVisible(true);
+    }
+    
+    /**
+     * Display the content of the concept
+     * @param str The text to be displayed
+     */
+    public void post(String str) {
+        text.setText(str);
+    }
+    
+    /**
+     * Handling button click
+     * @param e The ActionEvent
+     */
+    public void actionPerformed(ActionEvent e) {
+        Object s = e.getSource();
+        if (s == playButton) {
+            concept.play();
+        } else if (s == stopButton) {
+            concept.stop();
+        } else if (s == closeButton) {
+            concept.stop();
+            dispose();
+        }
+    }
+}
diff --git a/gui/InferenceWindow.java b/gui/InferenceWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..59bb656c6b8bae907f80e440112bc4a973a4871c
--- /dev/null
+++ b/gui/InferenceWindow.java
@@ -0,0 +1,88 @@
+
+package nars.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import nars.io.Record;
+
+/**
+ * Window displying inference log
+ */
+public class InferenceWindow extends NarsFrame implements ActionListener {
+    /* GUI components */    
+    private Button      playButton, stopButton, hideButton;
+    private TextArea	text;
+    
+    /**
+     * Constructor
+     */
+    public InferenceWindow() {
+        super("Inference log");
+        setBackground(SINGLE_WINDOW_COLOR);
+        GridBagLayout gridbag = new GridBagLayout();
+        GridBagConstraints c = new GridBagConstraints();
+        setLayout(gridbag);
+        
+        c.ipadx = 3;
+        c.ipady = 3;
+        c.insets = new Insets(5,5,5,5);
+        c.fill = GridBagConstraints.BOTH;
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.weightx = 1.0;
+        c.weighty = 1.0;
+        text = new TextArea("");
+        text.setBackground(DISPLAY_BACKGROUND_COLOR);
+        text.setEditable(false);
+        gridbag.setConstraints(text, c);
+        add(text);
+        
+        c.weighty = 0.0;
+        c.gridwidth = 1;
+        playButton = new Button("Play");
+        gridbag.setConstraints(playButton, c);
+        playButton.addActionListener(this);
+        add(playButton);
+        
+        stopButton = new Button("Stop");
+        gridbag.setConstraints(stopButton, c);
+        stopButton.addActionListener(this);
+        add(stopButton);
+        
+        hideButton = new Button("Hide");
+        gridbag.setConstraints(hideButton, c);
+        hideButton.addActionListener(this);
+        add(hideButton);
+        
+        setBounds(400, 200, 400, 400);
+    }
+    
+    /**
+     * Clear display
+     */
+    public void clear() {
+        text.setText("");
+    }
+        
+    /**
+     * Append a new line to display
+     */
+    public void append(String str) {
+        text.append(str);
+    }
+    
+    /**
+     * Handling button click
+     * @param e The ActionEvent
+     */
+    public void actionPerformed(ActionEvent e) {
+        Object s = e.getSource();
+        if (s == playButton) {
+            Record.play();
+        } else if (s == stopButton) {
+            Record.stop();
+        } else if (s == hideButton) {
+            Record.stop();
+            dispose();
+        }
+    }
+}
diff --git a/gui/InputWindow.java b/gui/InputWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..6966ebcf9d22efae8ebe6e62c941bfeeb4c2ca60
--- /dev/null
+++ b/gui/InputWindow.java
@@ -0,0 +1,112 @@
+
+package nars.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import nars.main.Memory;
+
+/**
+ * Input window, accepting user tasks
+ */
+public class InputWindow extends NarsFrame implements ActionListener {
+    /* GUI components */
+    Button      okButton, holdButton, clearButton, closeButton;
+    TextArea    inputText;
+    /** Whether the window is ready to accept new input */
+    boolean     ready;
+    
+    /**
+     * Constructor
+     */
+    public InputWindow() {
+        super("Input Window");
+        setBackground(SINGLE_WINDOW_COLOR);
+        GridBagLayout gridbag = new GridBagLayout();
+        GridBagConstraints c = new GridBagConstraints();
+        setLayout(gridbag);
+        c.ipadx = 3;
+        c.ipady = 3;
+        c.insets = new Insets(5,5,5,5);
+        c.fill = GridBagConstraints.BOTH;
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.weightx = 1.0;
+        c.weighty = 1.0;
+        inputText = new TextArea("");
+        gridbag.setConstraints(inputText, c);
+        add(inputText);
+        c.weighty = 0.0;
+        c.gridwidth = 1;
+        okButton = new Button("OK");
+        okButton.addActionListener(this);
+        gridbag.setConstraints(okButton, c);
+        add(okButton);
+        holdButton = new Button("Hold");
+        holdButton.addActionListener(this);
+        gridbag.setConstraints(holdButton, c);
+        add(holdButton);
+        clearButton = new Button("Clear");
+        clearButton.addActionListener(this);
+        gridbag.setConstraints(clearButton, c);
+        add(clearButton);
+        closeButton = new Button("Hide");
+        closeButton.addActionListener(this);
+        gridbag.setConstraints(closeButton, c);
+        add(closeButton);
+        setBounds(0, 40, 400, 210);
+        setVisible(true);
+    }
+    
+    /**
+     * Initialize the window
+     */
+    public void init() {
+        ready = false;
+        inputText.setText("");
+    }
+    
+    /**
+     * Handling button click
+     * @param e The ActionEvent
+     */
+    public void actionPerformed(ActionEvent e) {
+        Button b = (Button) e.getSource();
+        if (b == okButton)
+            ready = true;
+        else if (b == holdButton)
+            ready = false;
+        else if (b == clearButton)
+            inputText.setText("");
+        else if (b == closeButton)
+            setVisible(false);
+    }
+    
+    /**
+     * Get input lines, and send them to Memory
+     * @return Nember of running steps, if any
+     */
+    public long getInput() {
+        while (ready) {
+            String input = inputText.getText();
+            int EOL = input.indexOf('\n');
+            if (EOL < 0)
+                EOL = input.length();
+            String line = input.substring(0, EOL).trim();
+            if (input.length() > EOL)
+                inputText.setText(input.substring(EOL+1));
+            else
+                inputText.setText("");
+            if (line.length() == 0) // pause
+                ready = false;
+            else {
+                try {
+                    Long i = new Long(line);
+                    return i.longValue();
+                } catch (NumberFormatException e) {
+                    Memory.inputTask(line);
+                }
+            }
+        }
+        return 0;
+    }
+}
diff --git a/gui/MainWindow.java b/gui/MainWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b0775cad2c56332ebd7a18480894ef80045b228
--- /dev/null
+++ b/gui/MainWindow.java
@@ -0,0 +1,224 @@
+
+package nars.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import nars.io.Record;
+import nars.main.*;
+
+/**
+ * Main window of NARS GUI
+ */
+public class MainWindow	extends NarsFrame implements ActionListener {
+    /* GUI components */
+    private MenuBar	menuBar;
+    private TextArea    ioText;
+    private Button      stopButton, walkButton, runButton, exitButton;
+    private TextField   timerText;
+    private Label       timerLabel;
+    private long        timer;
+    
+    /* Some sub-windows with unique instantiation */
+    public static TermWindow conceptWin;
+    public static ParameterWindow forgetTW, forgetBW, forgetCW, silentW;
+    private MessageDialog messageDia;
+    
+    /**
+     * Constructor
+     */    
+    public MainWindow() {
+        super("NARS");
+        setBackground(MAIN_WINDOW_COLOR);
+        MenuBar menuBar = new MenuBar();
+        
+        Menu m = new Menu("File");
+        m.add(new MenuItem("Record Inference"));
+        m.addSeparator();
+        m.add(new MenuItem("Record Conversation"));
+        m.add(new MenuItem("Read Conversation"));
+        m.addActionListener(this);
+        menuBar.add(m);
+        
+        m = new Menu("Memory");
+        m.add(new MenuItem("Initialize"));
+        m.addSeparator();
+        m.add(new MenuItem("Load"));
+        m.add(new MenuItem("Save"));
+        m.addActionListener(this);
+        menuBar.add(m);
+        
+        m = new Menu("View");
+        m.add(new MenuItem("Concepts"));
+        m.add(new MenuItem("Task Buffer"));
+        m.add(new MenuItem("Inference Log"));
+        m.add(new MenuItem("Concept Content"));
+        m.add(new MenuItem("Input Window"));
+        m.addActionListener(this);
+        menuBar.add(m);
+        
+        m = new Menu("Parameter");
+        m.add(new MenuItem("Concept Forgetting Rate"));
+        m.add(new MenuItem("Task Forgetting Rate"));
+        m.add(new MenuItem("Belief Forgetting Rate"));
+        m.addSeparator();
+        m.add(new MenuItem("Report Silence Level"));
+        m.addActionListener(this);
+        menuBar.add(m);
+        
+        m = new Menu("Help");
+        m.add(new MenuItem("Related Information"));
+        m.add(new MenuItem("About NARS"));
+        m.addActionListener(this);
+        menuBar.add(m);
+        
+        setMenuBar(menuBar);
+        
+        GridBagLayout gridbag = new GridBagLayout();
+        GridBagConstraints c = new GridBagConstraints();
+        setLayout(gridbag);
+        
+        c.ipadx = 3;
+        c.ipady = 3;
+        c.insets = new Insets(5,5,5,5);
+        c.fill = GridBagConstraints.BOTH;
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.weightx = 1.0;
+        c.weighty = 1.0;
+        ioText = new TextArea("");
+        ioText.setBackground(DISPLAY_BACKGROUND_COLOR);
+        ioText.setEditable(false);
+        gridbag.setConstraints(ioText, c);
+        add(ioText);
+        
+        c.weightx = 0.0;
+        c.weighty = 0.0;
+        c.gridwidth = 1;
+        runButton = new Button(" Run ");
+        gridbag.setConstraints(runButton, c);
+        runButton.addActionListener(this);
+        add(runButton);
+        walkButton = new Button(" Walk ");
+        gridbag.setConstraints(walkButton, c);
+        walkButton.addActionListener(this);
+        add(walkButton);
+        stopButton = new Button(" Stop ");
+        gridbag.setConstraints(stopButton, c);
+        stopButton.addActionListener(this);
+        add(stopButton);
+        timerLabel = new Label("Clock:", Label.RIGHT);
+        timerLabel.setBackground(MAIN_WINDOW_COLOR);
+        gridbag.setConstraints(timerLabel, c);
+        add(timerLabel);
+        
+        c.weightx = 1.0;
+        timerText = new TextField("");
+        timerText.setBackground(DISPLAY_BACKGROUND_COLOR);
+        timerText.setEditable(false);
+        gridbag.setConstraints(timerText, c);
+        add(timerText);
+        
+        c.weightx = 0.0;
+        exitButton = new Button(" Exit ");
+        gridbag.setConstraints(exitButton, c);
+        exitButton.addActionListener(this);
+        add(exitButton);
+        
+        setBounds(0, 250, 400, 350);
+        setVisible(true);
+        
+        conceptWin = new TermWindow();
+        forgetTW = new ParameterWindow("Task Forgetting Rate", Parameters.TASK_DEFAULT_FORGETTING_CYCLE);
+        forgetBW = new ParameterWindow("Belief Forgetting Rate", Parameters.BELIEF_DEFAULT_FORGETTING_CYCLE);
+        forgetCW = new ParameterWindow("Concept Forgetting Rate", Parameters.CONCEPT_DEFAULT_FORGETTING_CYCLE);
+        silentW = new ParameterWindow("Report Silence Level", Parameters.SILENT_LEVEL);
+        
+        initTimer();
+    }
+    
+    /**
+     * Initialize the system for a new run
+     */    
+    private void init() {
+        initTimer();
+        ioText.setText("");
+        Center.reset();
+        Record.init();
+    }
+    
+    /**
+     * Reset timer and its display
+     */    
+    private void initTimer() {
+        timer = 0;
+        timerText.setText(timer + "");
+    }
+    
+    /**
+     * Update timer and its display
+     */    
+    public void tickTimer() {
+        timer++;
+        timerText.setText(timer + "");
+    }
+    
+    /**
+     * Add new line to the display, plus the time interval
+     * @param line The text to be displayed
+     */    
+    public void post(String line) {
+        String outText = new String(timer + "\n " + line);
+        ioText.append(outText);
+        initTimer();
+    }
+    
+    /**
+     * Handling button click
+     * @param e The ActionEvent
+     */    
+    public void actionPerformed(ActionEvent e) {
+        Object obj = e.getSource();
+        if (obj instanceof Button) {
+            if (obj == runButton)
+                Center.setStoper(-1);
+            else if (obj == stopButton)
+                Center.setStoper(0);
+            else if (obj == walkButton)
+                Center.setStoper(1);
+            else if (obj == exitButton) {
+                setVisible(false);
+                System.exit(0);
+            }
+        } else if (obj instanceof MenuItem) {
+            String label = e.getActionCommand();
+            if (label.equals("Initialize")) {
+                init();
+            } else if (label.equals("Concepts")) {
+                Memory.conceptsStartPlay("Active Concepts");
+            } else if (label.equals("Task Buffer")) {
+                Memory.taskBufferStartPlay("Task Buffer");
+            } else if (label.equals("Inference Log")) {
+                Record.show();
+                Record.play();
+            } else if (label.equals("Concept Content") && NARS.isStandAlone()) {
+                conceptWin.setVisible(true);
+            } else if (label.equals("Input Window")) {
+                Center.showInputWindow();
+            } else if (label.equals("Task Forgetting Rate")) {
+                forgetTW.setVisible(true);
+            } else if (label.equals("Belief Forgetting Rate")) {
+                forgetBW.setVisible(true);
+            } else if (label.equals("Concept Forgetting Rate")) {
+                forgetCW.setVisible(true);
+            } else if (label.equals("Report Silence Level")) {
+                silentW.setVisible(true);
+            } else if (label.equals("Related Information")) {
+                messageDia = new MessageDialog(this, NARS.WEBSITE);
+            } else if (label.equals("About NARS")) {
+                messageDia = new MessageDialog(this, NARS.INFO);
+            } else {
+                messageDia = new MessageDialog(this, UNAVAILABLE);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/gui/MessageDialog.java b/gui/MessageDialog.java
new file mode 100644
index 0000000000000000000000000000000000000000..945ea0d6b8fe9cce5079bd2ea1faa06bb444c8a0
--- /dev/null
+++ b/gui/MessageDialog.java
@@ -0,0 +1,48 @@
+
+package nars.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * Pop-up message for the user
+ */
+public class MessageDialog extends Dialog implements ActionListener {
+    protected Button button;
+    protected TextArea text;
+    
+    /**
+     * Constructor
+     * @param parent The parent Frame
+     * @param message The text to be displayed
+     */    
+    public MessageDialog(Frame parent, String message) {
+        super(parent, "Message", false);
+        setLayout(new BorderLayout(5, 5));
+        setBackground(NarsFrame.SINGLE_WINDOW_COLOR);
+        setFont(NarsFrame.NarsFont);
+        text = new TextArea(message);
+        text.setBackground(NarsFrame.DISPLAY_BACKGROUND_COLOR);
+        this.add("Center", text);
+        button = new Button(" OK ");
+        button.addActionListener(this);
+        Panel p = new Panel();
+        p.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
+        p.add(button);
+        this.add("South", p);
+        setModal(true);
+        setBounds(200, 250, 400, 180);
+        setVisible(true);
+    }
+    
+    /**
+     * Handling button click
+     * @param e The ActionEvent
+     */    
+    public void actionPerformed(ActionEvent e) {
+        if (e.getSource() == button) {
+            this.setVisible(false);
+            this.dispose();
+        }
+    }
+}
\ No newline at end of file
diff --git a/gui/NarsFrame.java b/gui/NarsFrame.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0d1cdcb04ca15aa07c8d844a7a30b5d1a4cc6fb
--- /dev/null
+++ b/gui/NarsFrame.java
@@ -0,0 +1,35 @@
+
+package nars.gui;
+
+import java.awt.*;
+
+/**
+ * Specify shared properties of NARS windows
+ */
+public abstract class NarsFrame extends Frame {
+    /** Color for the background of the main window */
+    static final Color MAIN_WINDOW_COLOR = new Color(120, 120, 255);
+    /** Color for the background of the windows with unique instantiation */
+    static final Color SINGLE_WINDOW_COLOR = new Color(180, 100, 230);
+    /** Color for the background of the windows with multiple instantiations */
+    static final Color MULTIPLE_WINDOW_COLOR = new Color(100, 220, 100);
+    /** Color for the background of the text components that are read-only */
+    static final Color DISPLAY_BACKGROUND_COLOR = new Color(200, 230, 220);
+    /** Font for NARS GUI */
+    static final Font NarsFont = new Font("Helvetica", Font.PLAIN, 11);
+    /** Message for unimplemented functions */
+    static final String UNAVAILABLE = "\n Not implemented in this demo applet.";
+    
+    /** Default constructor */
+    NarsFrame() {
+        super();
+    }
+    /**
+     * Constructor with title and font setting
+     * @param title The title displayed by the window
+     */    
+    NarsFrame(String title) {
+        super(" " + title);
+        setFont(NarsFont);
+    }
+}
diff --git a/gui/ParameterWindow.java b/gui/ParameterWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ab384a08fd6c7df603b119a4be48fb2b8ed1035
--- /dev/null
+++ b/gui/ParameterWindow.java
@@ -0,0 +1,98 @@
+
+package nars.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * Window displaying a system parameter that can be adjusted in run time
+ */
+public class ParameterWindow extends NarsFrame implements ActionListener, AdjustmentListener {
+    /* GUI components */
+    private Label       valueLabel;
+    private Button      hideButton, undoButton, defaultButton;
+    private Scrollbar   valueBar;
+    /* parameter values */
+    private int defaultValue;
+    private int previousValue;
+    private int currentValue;
+    
+    /**
+     * Constructor
+     * @param title Parameter name
+     * @param dft The default value of the parameter
+     */
+    ParameterWindow(String title, int dft) {
+        super(title);
+        defaultValue = dft;
+        previousValue = dft;
+        currentValue = dft;
+        setLayout(new GridLayout(3,3,8,4));
+        setBackground(SINGLE_WINDOW_COLOR);
+        Label sp1 = new Label("");
+        sp1.setBackground(SINGLE_WINDOW_COLOR);
+        add(sp1);
+        valueLabel = new Label(String.valueOf(dft), Label.CENTER);
+        valueLabel.setBackground(SINGLE_WINDOW_COLOR);
+        add(valueLabel);
+        Label sp2 = new Label("");
+        sp2.setBackground(SINGLE_WINDOW_COLOR);
+        add(sp2);
+        add(new Label("0", Label.RIGHT));
+        valueBar = new Scrollbar(Scrollbar.HORIZONTAL,dft,0,0,101);
+        valueBar.addAdjustmentListener(this);
+        add(valueBar);
+        add(new Label("100", Label.LEFT));
+        undoButton = new Button("Undo");
+        undoButton.addActionListener(this);
+        add(undoButton);
+        defaultButton = new Button("Default");
+        defaultButton.addActionListener(this);
+        add(defaultButton);
+        hideButton = new Button("Hide");
+        hideButton.addActionListener(this);
+        add(hideButton);
+        this.setBounds(300, 300, 250, 120);
+    }
+    
+    /**
+     * Get the value of the parameter
+     * @return The current value
+     */
+    public int value() {
+        return currentValue;
+    }
+    
+    /**
+     * Handling button click
+     * @param e The ActionEvent
+     */
+    public void actionPerformed(ActionEvent e) {
+        Object s = e.getSource();
+        if (s == defaultButton) {
+            currentValue = defaultValue;
+            valueBar.setValue(currentValue);
+            valueLabel.setText(String.valueOf(currentValue));
+        } else if (s == undoButton) {
+            currentValue = previousValue;
+            valueBar.setValue(currentValue);
+            valueLabel.setText(String.valueOf(currentValue));
+        } else if (s == hideButton) {
+            previousValue = currentValue;
+            setVisible(false);
+        }
+    }
+    
+    /**
+     * Handling scrollbar movement
+     * @param e The AdjustmentEvent
+     */
+    public void adjustmentValueChanged(AdjustmentEvent e) {
+        if (e.getSource() == valueBar) {
+            int v = ((Scrollbar) valueBar).getValue();
+            valueLabel.setText(String.valueOf(v));
+            valueBar.setValue(v);
+            currentValue = v;
+        }
+    }
+}
diff --git a/gui/TermWindow.java b/gui/TermWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..671a9c1971df99955b087345d41185002a8b04ee
--- /dev/null
+++ b/gui/TermWindow.java
@@ -0,0 +1,74 @@
+package nars.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import nars.entity.Concept;
+import nars.main.Memory;
+
+/**
+ * Window accept a Term, then display its Concept
+ */
+public class TermWindow extends NarsFrame implements ActionListener {
+    /* GUI components */    
+    private Label       termLabel;
+    private TextField   termField;
+    private Button      playButton, hideButton;
+    private TextArea	text;
+    
+    /**
+     * Constructor
+     */
+    TermWindow() {
+        super("Term Window");
+        setBackground(SINGLE_WINDOW_COLOR);
+        GridBagLayout gridbag = new GridBagLayout();
+        GridBagConstraints c = new GridBagConstraints();
+        setLayout(gridbag);
+        
+        c.ipadx = 3;
+        c.ipady = 3;
+        c.insets = new Insets(5,5,5,5);
+        c.fill = GridBagConstraints.BOTH;
+        c.gridwidth = 1;
+        c.weightx = 0.0;
+        c.weighty = 0.0;
+        termLabel = new Label("Term:", Label.RIGHT);
+        termLabel.setBackground(SINGLE_WINDOW_COLOR);
+        gridbag.setConstraints(termLabel, c);
+        add(termLabel);
+        
+        c.weightx = 1.0;
+        termField = new TextField("");
+        gridbag.setConstraints(termField, c);
+        add(termField);
+        
+        c.weightx = 0.0;
+        playButton = new Button("Show");
+        playButton.addActionListener(this);
+        gridbag.setConstraints(playButton, c);
+        add(playButton);
+        
+        hideButton = new Button("Hide");
+        hideButton.addActionListener(this);
+        gridbag.setConstraints(hideButton, c);
+        add(hideButton);
+        
+        setBounds(400, 0, 400, 100);
+    }
+    
+    /**
+     * Handling button click
+     * @param e The ActionEvent
+     */
+    public void actionPerformed(ActionEvent e) {
+        Button b = (Button) e.getSource();
+        if (b == playButton) {
+            String name = termField.getText().trim();
+            Concept concept = Memory.nameToConcept(name);
+            if (concept != null)
+                concept.startPlay();
+        } else if (b == hideButton)
+            setVisible(false);
+    }
+}
diff --git a/gui/package.html b/gui/package.html
new file mode 100644
index 0000000000000000000000000000000000000000..4eebd6edebac16938a9f4c78ec47223b45add903
--- /dev/null
+++ b/gui/package.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<body bgcolor="white">
+
+Graphical user interface of the system
+
+<h2>Package Specification</h2>
+
+All windows extend <tt>NarsFrame</tt>, with <tt>MessageDialog</tt> as the only exception.
+
+</body>
+</html>
diff --git a/inference/BudgetFunctions.java b/inference/BudgetFunctions.java
new file mode 100644
index 0000000000000000000000000000000000000000..3399adf5eb99b811bb392abf3550cfe1f5bb06ac
--- /dev/null
+++ b/inference/BudgetFunctions.java
@@ -0,0 +1,213 @@
+
+package nars.inference;
+
+import nars.entity.*;
+import nars.language.Term;
+import nars.main.*;
+
+/**
+ * Budget functions controlling the resources allocation
+ */
+public final class BudgetFunctions extends UtilityFunctions {    
+    
+    /* ----------------------- Belief evaluation ----------------------- */
+    
+    /**
+     * Determine the quality of a judgment by its truth value
+     * <p>
+     * Mainly decided by confidence, though binary judgment is also preferred
+     * @param t The truth value of a judgment
+     * @return The quality of the judgment, according to truth value only
+     */
+    public static float truthToQuality(TruthValue t) {
+        float freq = t.getFrequency();
+        float conf = t.getConfidence();
+        return and(conf, Math.abs(freq - 0.5f) + freq * 0.5f);
+    }
+
+    /**
+     * Determine the rank of a judgment by its confidence and originality (base length)
+     * <p>
+     * @param judg The judgment to be ranked
+     * @return The rank of the judgment, according to truth value only
+     */
+    public static float rankBelief(Judgment judg) {
+//        TruthValue truth = judg.getTruth();
+//        float quality = truthToQuality(truth);
+        float confidence = judg.getConfidence();
+        float originality = 1.0f / (judg.getBase().length() + 1);
+        return or(confidence, originality);
+    }
+    
+    /* ----- Functions used both in direct and indirect processing of tasks ----- */
+
+    /**
+     * Evaluate the quality of a belief as a solution to a problem, then reward the belief and de-prioritize the problem
+     * @param problem The problem (question or goal) to be solved
+     * @param solution The belief as solution
+     * @param task The task to be immediatedly processed, or null for continued process
+     * @return The budget for the new task which is the belief activated, if necessary
+     */
+    static BudgetValue solutionEval(Sentence problem, Judgment solution, Task task) {
+        BudgetValue budget = null;
+        boolean feedbackToLinks = false;
+        if (task == null) {                 // called in continued processing
+            task = Memory.currentTask;
+            feedbackToLinks = true;
+        }
+        boolean judgmentTask = task.getSentence().isJudgment();
+        float quality;                      // the quality of the solution
+        if (problem instanceof Question) 
+            quality = solution.solutionQuality((Question) problem);
+        else // problem is goal
+            quality = solution.getTruth().getExpectation();
+        if (judgmentTask)
+            task.incPriority(quality);
+        else {
+            task.setPriority(Math.min(1 - quality, task.getPriority()));
+            budget = new BudgetValue(quality, task.getDurability(), truthToQuality(solution.getTruth()));
+        }
+        if (feedbackToLinks) {
+            TaskLink tLink = Memory.currentTaskLink;
+            tLink.setPriority(Math.min(1 - quality, tLink.getPriority()));
+            TermLink bLink = Memory.currentBeliefLink;
+            bLink.incPriority(quality);
+        }
+        return budget;
+    }
+
+    /**
+     * Evaluate the quality of a revision, then de-prioritize the premises
+     * @param tTruth The truth value of the judgment in the task
+     * @param bTruth The truth value of the belief
+     * @param truth The truth value of the conclusion of revision
+     * @param task The task to be immediatedly or continuely processed
+     * @return The budget for the new task 
+     */
+    static BudgetValue revise(TruthValue tTruth, TruthValue bTruth, TruthValue truth, Task task, boolean feedbackToLinks) {
+        float difT = truth.getExpDifAbs(tTruth);
+        task.decPriority(1 - difT);
+        task.decDurability(1 - difT);
+        if (feedbackToLinks) {
+            TaskLink tLink = Memory.currentTaskLink;
+            tLink.decPriority(1 - difT);
+            tLink.decDurability(1 - difT);
+            TermLink bLink = Memory.currentBeliefLink;
+            float difB = truth.getExpDifAbs(bTruth);
+            bLink.decPriority(1 - difB);            
+            bLink.decDurability(1 - difB);
+        }
+        float dif = truth.getConfidence() - Math.max(tTruth.getConfidence(), bTruth.getConfidence());
+        float priority = or(dif, task.getPriority());
+        float durability = or(dif, task.getDurability());
+        float quality = truthToQuality(truth);
+        return new BudgetValue(priority, durability, quality);
+    }
+    
+    /* ----------------------- Links ----------------------- */
+    
+    public static BudgetValue distributeAmongLinks(BudgetValue b, int n) {
+        float priority = (float) (b.getPriority() / Math.sqrt(n));
+        return new BudgetValue(priority, b.getDurability(), b.getQuality());
+    }
+
+    /* ----------------------- Concept ----------------------- */
+
+    /**
+     * Activate a concept by an incoming item (Task, TaskLink, or TermLink)
+     * @param concept The concept
+     * @param budget The budget for the new item 
+     */
+    public static void activate(Concept concept, BudgetValue budget) {
+        float quality = aveAri(concept.getQuality(), budget.getPriority());
+        float oldPri = concept.getPriority();
+        float priority = or(oldPri, quality);
+        float durability = aveAri(concept.getDurability(), budget.getDurability(), oldPri / priority);
+        concept.setPriority(priority);
+        concept.setDurability(durability);
+        concept.setQuality(quality);
+    }
+
+    /* ---------------- Bag functions, on all Items ------------------- */
+    
+    /**
+     * Decrease Priority after an item is used, called in Bag
+     * <p>
+     * After a constant time, p should become d*p.  Since in this period, the item is accessed c*p times, 
+     * each time p-q should multiple d^(1/(c*p)). 
+     * The intuitive meaning of the parameter "forgetRate" is: after this number of times of access, 
+     * priority 1 will become d, it is a system parameter adjustable in run time.
+     *
+     * @param budget The previous budget value
+     * @param forgetRate The budget for the new item
+     */
+    public static void forget(BudgetValue budget, float forgetRate, float relativeThreshold) {
+        double quality = budget.getQuality() * relativeThreshold;      // re-scaled quality
+        double p = budget.getPriority() - quality;                     // priority above quality
+        if (p > 0)
+            quality += p * Math.pow(budget.getDurability(), 1.0 / (forgetRate * p));    // priority Durability
+        budget.setPriority((float) quality);
+    }
+    
+    /**
+     * Merge an item into another one in a bag, when the two are identical except in budget values
+     * @param baseValue The budget value to be modified
+     * @param adjustValue The budget doing the adjusting
+     */
+    public static void merge(BudgetValue baseValue, BudgetValue adjustValue) {
+        baseValue.incPriority(adjustValue.getPriority());
+        baseValue.setDurability(Math.max(baseValue.getDurability(), adjustValue.getDurability()));
+        baseValue.setQuality(Math.max(baseValue.getQuality(), adjustValue.getQuality()));
+    }
+
+    /* ----- Task derivation in MatchingRules and SyllogisticRules ----- */
+
+    /**
+     * Forward inference result and adjustment
+     * @param truth The truth value of the conclusion
+     * @return The budget value of the conclusion
+     */
+    static BudgetValue forward(TruthValue truth) {
+        return budgetInference(truthToQuality(truth), 1);
+    }
+    
+    // backward inference result and adjustment
+    public static BudgetValue backward(TruthValue truth) {
+        return budgetInference(truthToQuality(truth), 1);
+    }
+    
+    public static BudgetValue backwardWeak(TruthValue truth) {
+        return budgetInference(w2c(1) * truthToQuality(truth), 1);
+    }
+
+    /* ----- Task derivation in CompositionalRules and StructuralRules ----- */
+
+    // forward inference with CompoundTerm conclusion and adjustment
+    public static BudgetValue compoundForward(TruthValue truth, Term content) {
+        return budgetInference(truthToQuality(truth), content.getComplexity());
+    }
+    
+    public static BudgetValue compoundBackward(Term content) {
+        return budgetInference(1, content.getComplexity());
+    }
+
+    public static BudgetValue compoundBackwardWeak(Term content) {
+        return budgetInference(w2c(1), content.getComplexity());
+    }
+
+    /* ----- common function for all inference ----- */
+    
+    private static BudgetValue budgetInference(float qual, int complexity) {
+        TaskLink tLink = Memory.currentTaskLink;
+        TermLink bLink = Memory.currentBeliefLink;
+        float priority = tLink.getPriority();
+        float durability = tLink.getDurability();
+        float quality = (float) (qual / Math.sqrt(complexity));
+        if (bLink != null) {
+            priority = aveAri(priority, bLink.getPriority());
+            durability = aveAri(durability, bLink.getDurability());
+            bLink.incPriority(quality);
+        }
+        return new BudgetValue(and(priority, quality), and(durability, quality), quality);
+    }    
+}
diff --git a/inference/CompositionalRules.java b/inference/CompositionalRules.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea937faabbe859d455fc0ed2f68ee944334476c6
--- /dev/null
+++ b/inference/CompositionalRules.java
@@ -0,0 +1,342 @@
+
+package nars.inference;
+
+import nars.entity.*;
+import nars.language.*;
+import nars.io.*;
+import nars.main.*;
+
+/**
+ * Compound term composition and decomposition rules, with two premises.
+ * <p>
+ * Forward inference only, except the last rule (abdDepOuter) can also be used backward.
+ */
+public final class CompositionalRules {
+    
+    /* -------------------- intersections and differences -------------------- */
+    
+    /**
+     * {<S ==> M>, <P ==> M>} |- {<(S|P) ==> M>, <(S&P) ==> M>, <(S-P) ==> M>, <(P-S) ==> M>}
+     * 
+     * @param sentence The first premise
+     * @param belief The second premise
+     * @param index The location of the shared term
+     */
+    static void composeCompound(Sentence sentence, Judgment belief, int index) {
+        if (!sentence.isJudgment())
+            return;                             // forward only
+        Statement content1 = (Statement) sentence.getContent();
+        Statement content2 = (Statement) belief.getContent();
+        if (content1.getClass() != content2.getClass())
+            return;
+        if (content1.getTemporalOrder() != content2.getTemporalOrder())
+            return;
+        Term component1, component2;
+        component1 = content1.componentAt(1 - index);
+        component2 = content2.componentAt(1 - index);
+        Term component = content1.componentAt(index);
+        if ((component1 instanceof CompoundTerm) && ((CompoundTerm) component1).containAllComponents(component2)) {
+            decomposeCompound((CompoundTerm) component1, component2, component, index, true);
+            return;
+        } else if ((component2 instanceof CompoundTerm) && ((CompoundTerm) component2).containAllComponents(component1)) {
+            decomposeCompound((CompoundTerm) component2, component1, component, index, false);
+            return;
+        }
+        Term t1 = null;
+        Term t2 = null;
+        Term t3 = null;
+        Term t4 = null;
+        TruthValue v1 = sentence.getTruth();
+        TruthValue v2 = belief.getTruth();
+        if (index == 0) {
+            if (content1 instanceof Inheritance) {
+                t1 = IntersectionInt.make(component1, component2);
+                t2 = IntersectionExt.make(component1, component2);
+                t3 = DifferenceExt.make(component1, component2);
+                t4 = DifferenceExt.make(component2, component1);
+            } else if (content1 instanceof Implication) {
+                t1 = Disjunction.make(component1, component2);
+                t2 = Conjunction.make(component1, component2);
+                t3 = Conjunction.make(component1, Negation.make(component2));
+                t4 = Conjunction.make(component2, Negation.make(component1));
+            }
+            processComposed(content1, component, t1, TruthFunctions.union(v1, v2));
+            processComposed(content1, component, t2, TruthFunctions.intersection(v1, v2));
+            processComposed(content1, component, t3, TruthFunctions.difference(v1, v2));
+            processComposed(content1, component, t4, TruthFunctions.difference(v2, v1));
+            if (content1.isConstant())
+                introVarDepOuter(content1, content2, index);
+        } else {
+            if (content1 instanceof Inheritance) {
+                t1 = IntersectionExt.make(component1, component2);
+                t2 = IntersectionInt.make(component1, component2);
+                t3 = DifferenceInt.make(component1, component2);
+                t4 = DifferenceInt.make(component2, component1);
+            } else if (content1 instanceof Implication) {
+                t1 = Conjunction.make(component1, component2);
+                t2 = Disjunction.make(component1, component2);
+                t3 = Disjunction.make(component1, Negation.make(component2));
+                t4 = Disjunction.make(component2, Negation.make(component1));
+            }
+            processComposed(content1, t1, component, TruthFunctions.union(v1, v2));
+            processComposed(content1, t2, component, TruthFunctions.intersection(v1, v2));
+            processComposed(content1, t3, component, TruthFunctions.difference(v1, v2));
+            processComposed(content1, t4, component, TruthFunctions.difference(v2, v1));
+            if (content1.isConstant())
+                introVarDepOuter(content1, content2, index);
+        }
+    }
+    
+    /**
+     * Finish composing compound term
+     * @param statement Type of the content
+     * @param subject Subject of content
+     * @param predicate Predicate of content
+     * @param truth TruthValue of the content
+     */
+    private static void processComposed(Statement statement, Term subject, Term predicate, TruthValue truth) {
+        if ((subject == null) || (predicate == null)) 
+            return;
+        Term content = Statement.make(statement, subject, predicate);
+        if ((content == null) || content.equals(statement) || content.equals(Memory.currentBelief.getContent()))
+            return;
+        BudgetValue budget = BudgetFunctions.compoundForward(truth, content);
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+    
+    /**
+     * {<(S|P) ==> M>, <P ==> M>} |- <S ==> M>
+     * @param compound The compound term to be decomposed
+     * @param component The part of the compound to be removed
+     * @param term1 The other term in the content
+     * @param index The location of the shared term: 0 for subject, 1 for predicate
+     * @param compoundTask Whether the compound comes from the task
+     */
+    private static void decomposeCompound(CompoundTerm compound, Term component, Term term1, int index, boolean compoundTask) {
+        Term term2 = CompoundTerm.reduceComponents(compound, component);
+        if (term2 == null)
+            return;
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        Judgment belief = Memory.currentBelief;
+        Statement oldContent = (Statement) task.getContent();
+        TruthValue v1, v2;
+        if (compoundTask) {
+            v1 = sentence.getTruth();
+            v2 = belief.getTruth();
+        } else {
+            v1 = belief.getTruth();
+            v2 = sentence.getTruth();
+        }
+        TruthValue truth = null;
+        Term content;
+        if (index == 0) {
+            content = Statement.make(oldContent, term1, term2);
+            if (content == null)
+                return;
+            if (oldContent instanceof Inheritance) {
+                if (compound instanceof IntersectionExt) {
+                    truth = TruthFunctions.reduceConjunction(v1, v2);
+                } else if (compound instanceof IntersectionInt) {
+                    truth = TruthFunctions.reduceDisjunction(v1, v2);
+                } else if ((compound instanceof SetInt) && (component instanceof SetInt)) {
+                    truth = TruthFunctions.reduceConjunction(v1, v2);
+                } else if ((compound instanceof SetExt) && (component instanceof SetExt)) {
+                    truth = TruthFunctions.reduceDisjunction(v1, v2);
+                } else if (compound instanceof DifferenceExt) {
+                    if (compound.componentAt(0).equals(component)) {
+                        truth = TruthFunctions.reduceDisjunction(v2, v1);
+                    } else {
+                        truth = TruthFunctions.reduceConjunctionNeg(v1, v2);
+                    }
+                }
+            } else if (oldContent instanceof Implication) {
+                if (compound instanceof Conjunction) {
+                    truth = TruthFunctions.reduceConjunction(v1, v2);
+                } else if (compound instanceof Disjunction) {
+                    truth = TruthFunctions.reduceDisjunction(v1, v2);
+                }
+            }
+        } else {
+            content = Statement.make(oldContent, term2, term1);
+            if (content == null)
+                return;
+            if (oldContent instanceof Inheritance) {
+                if (compound instanceof IntersectionInt) {
+                    truth = TruthFunctions.reduceConjunction(v1, v2);
+                } else if (compound instanceof IntersectionExt) {
+                    truth = TruthFunctions.reduceDisjunction(v1, v2);
+                } else if ((compound instanceof SetExt) && (component instanceof SetExt)) {
+                    truth = TruthFunctions.reduceConjunction(v1, v2);
+                } else if ((compound instanceof SetInt) && (component instanceof SetInt)) {
+                    truth = TruthFunctions.reduceDisjunction(v1, v2);
+                } else if (compound instanceof DifferenceInt) {
+                    if (compound.componentAt(1).equals(component)) {
+                        truth = TruthFunctions.reduceDisjunction(v2, v1);
+                    } else {
+                        truth = TruthFunctions.reduceConjunctionNeg(v1, v2);
+                    }
+                }
+            } else if (oldContent instanceof Implication) {
+                if (compound instanceof Disjunction) {
+                    truth = TruthFunctions.reduceConjunction(v1, v2);
+                } else if (compound instanceof Conjunction) {
+                    truth = TruthFunctions.reduceDisjunction(v1, v2);
+                }
+            }
+        }
+        BudgetValue budget = BudgetFunctions.compoundForward(truth, content);
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+        
+    /**
+     * {(||, S, P), P} |- S
+     * @param compound The compound term to be decomposed
+     * @param component The part of the compound to be removed
+     * @param compoundTask Whether the compound comes from the task
+     */
+    static void decomposeStatement(CompoundTerm compound, Term component, boolean compoundTask) {
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        if (!sentence.isJudgment())
+            return;
+        Judgment belief = Memory.currentBelief;
+        Term content = CompoundTerm.reduceComponents(compound, component);
+        if (content == null)
+            return;
+        TruthValue v1, v2;
+        if (compoundTask) {
+            v1 = sentence.getTruth();
+            v2 = belief.getTruth();
+        } else {
+            v1 = belief.getTruth();
+            v2 = sentence.getTruth();
+        }        
+        TruthValue truth = null;
+        if (compound instanceof Conjunction) {
+            if (sentence instanceof Goal) {
+                if (compoundTask)
+                    truth = TruthFunctions.reduceDisjunction(v1, v2);
+                else
+                    return;
+            } else if (sentence instanceof Judgment)
+                truth = TruthFunctions.reduceConjunction(v1, v2);
+        } else if (compound instanceof Disjunction) {
+            if (sentence instanceof Goal) {
+                if (compoundTask)
+                    truth = TruthFunctions.reduceConjunction(v1, v2);
+                else
+                    return;
+            } else if (sentence instanceof Judgment)
+                truth = TruthFunctions.reduceDisjunction(v1, v2);
+        } else
+            return;
+        BudgetValue budget = BudgetFunctions.compoundForward(truth, content);
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+    
+    /* ---------------- dependent variable and conjunction ---------------- */
+    
+    /**
+     * {<M --> S>, <M --> P>} |- (&&, <#x() --> S>, <#x() --> P>>
+     * @param premise1 The first premise <M --> P>
+     * @param premise2 The second premise <M --> P>
+     * @param index The location of the shared term: 0 for subject, 1 for predicate
+     */
+    private static Conjunction introVarDep(Statement premise1, Statement premise2, int index) {
+        Statement state1, state2;
+        Variable v1 = new Variable(Symbols.VARIABLE_TAG + "0()");
+        Variable v2 = new Variable(Symbols.VARIABLE_TAG + "0()");
+        if (index == 0) {
+            state1 = Statement.make(premise1, v1, premise1.getPredicate());
+            state2 = Statement.make(premise2, v2, premise2.getPredicate());
+        } else {
+            state1 = Statement.make(premise1, premise1.getSubject(), v1);
+            state2 = Statement.make(premise2, premise2.getSubject(), v2);
+        }
+        Conjunction content = (Conjunction) Conjunction.make(state1, state2);
+        return content;
+    }
+
+    /**
+     * Introduce a dependent variable in an outer-layer conjunction
+     * @param premise1 The first premise <M --> S>
+     * @param premise2 The second premise <M --> P>
+     * @param index The location of the shared term: 0 for subject, 1 for predicate
+     */
+    private static void introVarDepOuter(Statement premise1, Statement premise2, int index) {
+        Term content = introVarDep(premise1, premise2, index);
+        TruthValue v1 = Memory.currentTask.getSentence().getTruth();
+        TruthValue v2 = Memory.currentBelief.getTruth();
+        TruthValue truth = TruthFunctions.intersection(v1, v2);
+        BudgetValue budget = BudgetFunctions.compoundForward(truth, content);
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+    
+    /**
+     * Introduce a dependent variable in an inner-layer conjunction
+     * @param compound The compound containing the first premise
+     * @param component The first premise <M --> S>
+     * @param premise The second premise <M --> P>
+     */
+    static void introVarDepInner(CompoundTerm compound, Term component, Term premise) {
+        if (!(component instanceof Statement) || !(component.getClass() == premise.getClass()))
+            return;
+        Statement premise1 = (Statement) premise;
+        Statement premise2 = (Statement) component;
+        int index;
+        if (premise1.getSubject().equals(premise2.getSubject()))
+            index = 0;
+        else if (premise1.getPredicate().equals(premise2.getPredicate()))
+            index = 1;
+        else
+            return;
+        Term innerContent = introVarDep(premise1, premise2, index);
+        if (innerContent == null)
+            return;
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        Judgment belief = Memory.currentBelief;
+        Term content = task.getContent();
+        if (compound instanceof Implication)
+            content = Statement.make((Statement) content, compound.componentAt(0), innerContent);
+        else if (compound instanceof Conjunction)
+            content = CompoundTerm.replaceComponent(compound, component, innerContent);
+        TruthValue truth = null;
+        if (sentence instanceof Goal)
+            truth = TruthFunctions.intersection(belief.getTruth(), sentence.getTruth());    // to be revised
+        else if (sentence instanceof Judgment)
+            truth = TruthFunctions.intersection(belief.getTruth(), sentence.getTruth());
+        else
+            return; // don't do it for questions
+        BudgetValue budget = BudgetFunctions.compoundForward(truth, content);
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+
+    /**
+     * {(&&, <#x() --> S>, <#x() --> P>>, <M --> P>} |- <M --> S>
+     * @param compound The compound term to be decomposed
+     * @param component The part of the compound to be removed
+     * @param compoundTask Whether the compound comes from the task
+     */
+    static void abdVarDepOuter(CompoundTerm compound, Term component, boolean compoundTask) {
+        Term content = CompoundTerm.reduceComponents(compound, component);
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        Judgment belief = Memory.currentBelief;
+        TruthValue v1 = sentence.getTruth();
+        TruthValue v2 = belief.getTruth();
+        TruthValue truth = null;
+        BudgetValue budget;
+        if (sentence instanceof Question)
+            budget = (compoundTask ? BudgetFunctions.backward(v2) : BudgetFunctions.backwardWeak(v2));
+        else {
+            if (sentence instanceof Goal)
+                truth = (compoundTask ? TruthFunctions.desireStrong(v1, v2) : TruthFunctions.desireWeak(v1, v2));
+            else
+                truth = (compoundTask ? TruthFunctions.existAnalogy(v1, v2) : TruthFunctions.existAnalogy(v2, v1));
+            budget = BudgetFunctions.compoundForward(truth, content);
+        }
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+}
diff --git a/inference/MatchingRules.java b/inference/MatchingRules.java
new file mode 100644
index 0000000000000000000000000000000000000000..287ffba0d8d6613852ebcfdb9bcdaeae8dce76fa
--- /dev/null
+++ b/inference/MatchingRules.java
@@ -0,0 +1,177 @@
+
+package nars.inference;
+
+import nars.entity.*;
+import nars.language.*;
+import nars.io.*;
+import nars.main.Memory;
+
+/**
+ * Directly process a task by a belief, with only two Terms in both
+ */
+public final class MatchingRules {
+    
+    /* -------------------- same contents -------------------- */
+    
+    // the task and belief match each other
+    // forward inference only
+    // called only for Figure 0 of syllogism, in ThreeTermRules
+    public static void match(Task task, Judgment belief) {
+        Sentence sentence = task.getSentence();
+        if (sentence.isJudgment())
+            revision(task, belief, true);
+        else
+            trySolution(sentence, belief, null);
+    }
+    
+    // to be rewritten
+    public static void update(Task newBelief, Judgment oldBelief) {
+//        Base label = Base.make(newBelief.getBase(), oldBelief.getBase());
+//        if ((label == null) || (label.length() == 0))
+//            return;
+//        Task updatedNewBelief = (Task) newBelief.clone();
+//        Task updatedOldBelief = (Task) oldBelief.clone();
+//        Term content = oldBelief.getContent();
+//        Term toPast = Past.make(content);
+//        updatedOldBelief.setContent(toPast);
+//        updatedNewBelief.setBase(label);
+//        TruthValue truth = TruthFunctions.revision(newBelief.getTruth(), oldBelief.getTruth());
+//        float confidence = truth.getConfidence();
+//        updatedNewBelief.setConfidence(confidence);
+//        BudgetValue usage = BudgetFunctions.update(newBelief, oldBelief);
+//        updatedNewBelief.setBudget(usage);
+//        Memory.derivedTask(updatedNewBelief);
+//        Memory.derivedTask(updatedOldBelief);
+    }
+    
+    // called from Concept (direct) and match (indirect)
+    public static void revision(Task task, Judgment belief, boolean feedbackToLinks) {
+        Judgment judgment = (Judgment) task.getSentence();
+        TruthValue tTruth = judgment.getTruth();
+        TruthValue bTruth = belief.getTruth();
+        TruthValue truth = TruthFunctions.revision(tTruth, bTruth);
+        BudgetValue budget = BudgetFunctions.revise(tTruth, bTruth, truth, task, feedbackToLinks);
+        Term content = judgment.getContent();
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+
+    /**
+     * Check if a Judgment provide a better answer to a Question
+     * @param task The task to be processed
+     */
+    public static void trySolution(Sentence problem, Judgment belief, Task task) {
+        Judgment oldBest = problem.getBestSolution();
+        if (betterSolution(belief, oldBest, problem)) {
+            problem.setBestSolution(belief);
+            BudgetValue budget = BudgetFunctions.solutionEval(problem, belief, task);
+            if (budget != null)
+                Memory.activatedTask(budget, belief, problem.isInput());
+        }
+    }
+    
+    // s1 is a better answer to q than s2 is
+    private static boolean betterSolution(Judgment newSol, Judgment oldSol, Sentence problem) {
+        if (oldSol == null)
+            return true;
+        else
+            return (newSol.solutionQuality(problem) > oldSol.solutionQuality(problem));
+    }
+
+    /* -------------------- same terms, difference relations -------------------- */
+    
+    // the task and belief match each other reversely
+    // forward inference only
+    // called only for Figure 5 of syllogism
+    public static void matchReverse() {
+        Task task = Memory.currentTask;
+        Judgment belief = Memory.currentBelief;
+        if (task.getContent().getTemporalOrder() != CompoundTerm.temporalReverse(belief.getContent().getTemporalOrder()))
+            return;
+        Sentence sentence = task.getSentence();
+        if (sentence.isJudgment())
+            inferToSym((Judgment) sentence, belief);
+        else
+            conversion();
+    }
+    
+    // Inheritance matches Similarity
+    // forward inference only
+    // called from ThreeTermRules only
+    public static void matchAsymSym(Sentence asym, Sentence sym, int figure) { // (Task task, Sentence belief, int order, boolean inhSim) {
+        CompoundTerm.TemporalOrder order1 = asym.getContent().getTemporalOrder();
+        CompoundTerm.TemporalOrder order2 = sym.getContent().getTemporalOrder();
+        CompoundTerm.TemporalOrder order = CompoundTerm.temporalInferenceWithFigure(order1, order2, figure);
+        if (order == CompoundTerm.TemporalOrder.UNSURE)
+            return;
+        if (Memory.currentTask.getSentence().isJudgment())
+            inferToAsym((Judgment) asym, (Judgment) sym, order);
+        else {
+            convertRelation();
+        }
+    }
+
+    /* -------------------- two-premise inference rules -------------------- */
+    
+    /**
+     * Produce Similarity/Equivalence from a pire of reversed Inheritance/Implication
+     * @param judgment1 The first premise
+     * @param judgment2 The second premise
+     */
+    private static void inferToSym(Judgment judgment1, Judgment judgment2) {
+        Statement s1 = (Statement) judgment1.getContent();
+        Statement s2 = (Statement) judgment2.getContent();
+        Term t1 = s1.getSubject();
+        Term t2 = s1.getPredicate();
+        Term content;
+        if (s1 instanceof Inheritance)
+            content = Similarity.make(t1, t2);
+        else if (s1 instanceof ImplicationAfter)
+            content = EquivalenceAfter.make(t1, t2);
+        else if (s1 instanceof ImplicationBefore)
+            content = EquivalenceAfter.make(t2, t1);
+        else
+            content = Equivalence.make(t1, t2);
+        TruthValue value1 = judgment1.getTruth();
+        TruthValue value2 = judgment2.getTruth();
+        TruthValue truth = TruthFunctions.intersection(value1, value2);
+        BudgetValue budget = BudgetFunctions.forward(truth);
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+    
+    /**
+     * Produce an Inheritance/Implication from a Similarity/Equivalence and a reversed Inheritance/Implication
+     * @param asym The asymmetric premise
+     * @param sym The symmetric premise
+     */
+    private static void inferToAsym(Judgment asym, Judgment sym, CompoundTerm.TemporalOrder order) {
+        Statement statement = (Statement) asym.getContent();
+        Term sub = statement.getPredicate();
+        Term pre = statement.getSubject();
+        Statement content = Statement.make(statement, sub, pre, order);
+        TruthValue truth = TruthFunctions.reduceConjunction(sym.getTruth(), asym.getTruth());
+        BudgetValue budget = BudgetFunctions.forward(truth);
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+
+    /* -------------------- one-premise inference rules -------------------- */
+    
+    /**
+     * Produce an Inheritance/Implication from a reversed Inheritance/Implication
+     */
+    private static void conversion() {
+        TruthValue truth = TruthFunctions.conversion(Memory.currentBelief.getTruth());
+        BudgetValue budget = BudgetFunctions.forward(truth);
+        Memory.singlePremiseTask(truth, budget);
+    }
+    
+    // switch between Inheritance/Implication and Similarity/Equivalence
+    private static void convertRelation() {
+        TruthValue truth = Memory.currentBelief.getTruth();
+        if (((Statement) Memory.currentTask.getContent()).isCommutative())
+            truth = TruthFunctions.implied(truth);
+        else
+            truth = TruthFunctions.implying(truth);
+        BudgetValue budget = BudgetFunctions.forward(truth);
+        Memory.singlePremiseTask(truth, budget);
+    }
+}
diff --git a/inference/RuleTables.java b/inference/RuleTables.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ed0824187abbdf79d3f4cabfeace8221104a692
--- /dev/null
+++ b/inference/RuleTables.java
@@ -0,0 +1,401 @@
+
+package nars.inference;
+
+import nars.entity.*;
+import nars.language.*;
+import nars.main.Memory;
+
+/**
+ * Table of inference rules, indexed by the CompositionLinks for the task and the belief.
+ * Used for indirective processing of a task
+ */
+public final class RuleTables {
+    
+    /* ----- inferences with two composition links ----- */
+    
+    public static void reason(TaskLink tLink, TermLink bLink) {
+        Task task = Memory.currentTask;
+        Term taskTerm = (Term) task.getContent().clone();         // cloning for substitution
+        Term beliefTerm = (Term) bLink.getTarget().clone();       // cloning for substitution
+        Concept beliefConcept = Memory.termToConcept(beliefTerm);
+        Judgment belief = null;
+        if (beliefConcept != null)
+            belief = beliefConcept.getBelief(task);
+        Memory.currentBelief = belief;  // may be null
+        if ((belief != null) && (Variable.findSubstitute(Variable.VarType.QUERY, taskTerm, beliefTerm) != null))
+            MatchingRules.match(task, belief);
+        short tIndex = tLink.getIndex(0);
+        short bIndex = bLink.getIndex(0);
+        switch(tLink.getType()) {
+            case TermLink.SELF:
+                switch(bLink.getType()) {
+                    case TermLink.COMPONENT:
+                        compoundAndSelf((CompoundTerm) taskTerm, beliefTerm, true);
+                        break;
+                    case TermLink.COMPOUND:
+                        compoundAndSelf((CompoundTerm) beliefTerm, taskTerm, false);
+                        break;
+                    case TermLink.COMPONENT_STATEMENT:  // detachment
+                        if (belief != null)
+                            SyllogisticRules.detachment((Statement) taskTerm, null, true, bIndex);
+                        break;
+                    case TermLink.COMPOUND_STATEMENT:   // detachment
+                        if (belief != null)
+                            SyllogisticRules.detachment((Statement) beliefTerm, null, false, bIndex); //task, beliefTerm);
+                        break;
+                    case TermLink.COMPONENT_CONDITION:
+                        if (belief != null)
+                            SyllogisticRules.conditionalDedInd((Implication) taskTerm, bIndex, beliefTerm, tIndex);
+                        break;
+                    case TermLink.COMPOUND_CONDITION:
+                        if (belief != null)
+                            SyllogisticRules.conditionalDedInd((Implication) beliefTerm, bIndex, taskTerm, tIndex);
+                        break;
+                    default:
+                }
+                break;
+            case TermLink.COMPOUND:
+                switch(bLink.getType()) {
+                    case TermLink.COMPOUND:
+                        compoundAndCompound((CompoundTerm) taskTerm, tIndex, (CompoundTerm) beliefTerm, bIndex);
+                        break;
+                    case TermLink.COMPOUND_STATEMENT:
+                        compoundAndStatement((CompoundTerm) taskTerm, tIndex, (Statement) beliefTerm, bIndex, beliefTerm);
+                        break;
+                    default:
+                }
+                break;
+            case TermLink.COMPOUND_STATEMENT:
+                switch(bLink.getType()) {
+                    case TermLink.COMPONENT:
+                        componentAndStatement((CompoundTerm) Memory.currentTerm, bIndex, (Statement) taskTerm, tIndex);
+                        break;
+                    case TermLink.COMPOUND:
+                        compoundAndStatement((CompoundTerm) beliefTerm, bIndex, (Statement) taskTerm, tIndex, beliefTerm);
+                        break;
+                    case TermLink.COMPOUND_STATEMENT:
+                        if (belief != null)
+                            syllogisms(tLink, bLink, taskTerm, beliefTerm);
+                        break;
+                    case TermLink.COMPOUND_CONDITION:
+                        if (belief != null)
+                            conditionalDedIndWithVar((Implication) beliefTerm, bIndex, (Statement) taskTerm, tIndex);
+                        break;
+                    default:
+                }
+                break;
+            case TermLink.COMPOUND_CONDITION:
+                switch(bLink.getType()) {
+                    case TermLink.COMPOUND_STATEMENT:
+                        if (belief != null)
+                            conditionalDedIndWithVar((Implication) taskTerm, tIndex, (Statement) beliefTerm, bIndex);
+                        break;
+                    default:
+                }
+                break;
+            default:
+                // to be revised to cover all types
+        }
+    }
+    
+    /* ----- syllogistic inferences ----- */
+    
+    /**
+     * Meta-table of syllogistic rules, indexed by the content classes of the sentence and the belief
+     */
+    private static void syllogisms(TaskLink tLink, TermLink bLink, Term taskTerm, Term beliefTerm) {
+        Sentence sentence = Memory.currentTask.getSentence();
+        Judgment belief = Memory.currentBelief;
+        int figure;
+        if (taskTerm instanceof Inheritance) {
+            if (beliefTerm instanceof Inheritance) {
+                figure = indexToFigure(tLink, bLink);
+                asymmetricAsymmetric(sentence, belief, figure);
+            } else if (beliefTerm instanceof Similarity) {
+                figure = indexToFigure(tLink, bLink);
+                asymmetricSymmetric(sentence, belief, figure);
+            } else
+                detachmentWithVar((Statement) beliefTerm, false, bLink.getIndex(0), (Statement) taskTerm, belief);
+        } else if (taskTerm instanceof Similarity) {
+            if (beliefTerm instanceof Inheritance) {
+                figure = indexToFigure(bLink, tLink);
+                asymmetricSymmetric(belief, sentence, figure);
+            } else if (beliefTerm instanceof Similarity) {
+                figure = indexToFigure(bLink, tLink);
+                symmetricSymmetric(belief, sentence, figure);
+            }
+        } else if (taskTerm instanceof Implication) {
+            if (beliefTerm instanceof Implication) {
+                figure = indexToFigure(tLink, bLink);
+                asymmetricAsymmetric(sentence, belief, figure);
+            } else if (beliefTerm instanceof Equivalence) {
+                figure = indexToFigure(tLink, bLink);
+                asymmetricSymmetric(sentence, belief, figure);
+            } else if (beliefTerm instanceof Inheritance)
+                detachmentWithVar((Statement) taskTerm, true, tLink.getIndex(0), (Statement) beliefTerm, belief);
+            // or intro 2nd ind var
+        } else if (taskTerm instanceof Equivalence) {
+            if (beliefTerm instanceof Implication) {
+                figure = indexToFigure(bLink, tLink);
+                asymmetricSymmetric(belief, sentence, figure);
+            } else if (beliefTerm instanceof Equivalence) {
+                figure = indexToFigure(bLink, tLink);
+                symmetricSymmetric(belief, sentence, figure);
+            } else if (beliefTerm instanceof Inheritance)
+                detachmentWithVar((Statement) taskTerm, true, tLink.getIndex(0), (Statement) beliefTerm, belief);
+        }
+    }
+    
+    private static int indexToFigure(TermLink link1, TermLink link2) {
+        return (link1.getIndex(0) + 1) * 10 + (link2.getIndex(0) + 1);    // valid value: 11, 12, 21, 22
+    }
+    
+    /**
+     * Syllogistic rules whose both premises are on the same asymmetric relation
+     */
+    private static void asymmetricAsymmetric(Sentence sentence, Judgment belief, int figure) {
+        Statement s1 = (Statement) sentence.cloneContent();
+        Statement s2 = (Statement) belief.cloneContent();
+        Term t1, t2;
+        switch (figure) {
+            case 11:    // induction
+                if (Variable.unify(Variable.VarType.INDEPENDENT, s1.getSubject(), s2.getSubject(), s1, s2)) {
+                    t1 = s2.getPredicate();
+                    t2 = s1.getPredicate();
+                    SyllogisticRules.abdIndCom(t1, t2, sentence, belief, figure);
+                    CompositionalRules.composeCompound(sentence, belief, 0);
+                }
+                break;
+            case 12:    // deduction
+                if (Variable.unify(Variable.VarType.INDEPENDENT, s1.getSubject(), s2.getPredicate(), s1, s2)) {
+                    t1 = s2.getSubject();
+                    t2 = s1.getPredicate();
+                    if (Variable.unify(Variable.VarType.QUERY, t1, t2, s1, s2))
+                        MatchingRules.matchReverse();
+                    else
+                        SyllogisticRules.dedExe(t1, t2, sentence, belief);
+                }
+                break;
+            case 21:    // exemplification
+                if (Variable.unify(Variable.VarType.INDEPENDENT, s1.getPredicate(), s2.getSubject(), s1, s2)) {
+                    t1 = s1.getSubject();
+                    t2 = s2.getPredicate();
+                    if (Variable.unify(Variable.VarType.QUERY, t1, t2, s1, s2))
+                        MatchingRules.matchReverse();
+                    else
+                        SyllogisticRules.dedExe(t1, t2, sentence, belief);
+                }
+                break;
+            case 22:    // abduction
+                if (Variable.unify(Variable.VarType.INDEPENDENT, s1.getPredicate(), s2.getPredicate(), s1, s2)) {
+                    t1 = s1.getSubject();
+                    t2 = s2.getSubject();
+                    if (! SyllogisticRules.conditionalAbd(t1, t2, s1, s2)) {         // if conditional abduction, skip the following
+                        SyllogisticRules.abdIndCom(t1, t2, sentence, belief, figure);
+                        CompositionalRules.composeCompound(sentence, belief, 1);
+                    }
+                }
+                break;
+            default:
+        }
+    }
+    
+    /**
+     * Syllogistic rules whose first premise is on an asymmetric relation, and the second on a symmetric relation
+     */
+    private static void asymmetricSymmetric(Sentence asym, Sentence sym, int figure) {
+        Statement asymSt = (Statement) asym.cloneContent();
+        Statement symSt = (Statement) sym.cloneContent();
+        Term t1, t2;
+        switch (figure) {
+            case 11:
+                if (Variable.unify(Variable.VarType.INDEPENDENT, asymSt.getSubject(), symSt.getSubject(), asymSt, symSt)) {
+                    t1 = asymSt.getPredicate();
+                    t2 = symSt.getPredicate();
+                    if (Variable.unify(Variable.VarType.QUERY, t1, t2, asymSt, symSt))
+                        MatchingRules.matchAsymSym(asym, sym, figure); //task, belief, order1 - order2, false);
+                    else
+                        SyllogisticRules.analogy(t2, t1, asym, sym, figure);
+                }
+                break;
+            case 12:
+                if (Variable.unify(Variable.VarType.INDEPENDENT, asymSt.getSubject(), symSt.getPredicate(), asymSt, symSt)) {
+                    t1 = asymSt.getPredicate();
+                    t2 = symSt.getSubject();
+                    if (Variable.unify(Variable.VarType.QUERY, t1, t2, asymSt, symSt))
+                        MatchingRules.matchAsymSym(asym, sym, figure); //task, belief, order1 - order2, false);
+                    else
+                        SyllogisticRules.analogy(t2, t1, asym, sym, figure);
+                }
+                break;
+            case 21:
+                if (Variable.unify(Variable.VarType.INDEPENDENT, asymSt.getPredicate(), symSt.getSubject(), asymSt, symSt)) {
+                    t1 = asymSt.getSubject();
+                    t2 = symSt.getPredicate();
+                    if (Variable.unify(Variable.VarType.QUERY, t1, t2, asymSt, symSt))
+                        MatchingRules.matchAsymSym(asym, sym, figure); //task, belief, order1 - order2, false);
+                    else
+                        SyllogisticRules.analogy(t1, t2, asym, sym, figure);
+                }
+                break;
+            case 22:
+                if (Variable.unify(Variable.VarType.INDEPENDENT, asymSt.getPredicate(), symSt.getPredicate(), asymSt, symSt)) {
+                    t1 = asymSt.getSubject();
+                    t2 = symSt.getSubject();
+                    if (Variable.unify(Variable.VarType.QUERY, t1, t2, asymSt, symSt))
+                        MatchingRules.matchAsymSym(asym, sym, figure); //task, belief, order1 - order2, false);
+                    else
+                        SyllogisticRules.analogy(t1, t2, asym, sym, figure);
+                }
+                break;
+        }
+    }
+    
+    /**
+     * Syllogistic rules whose both premises are on the same symmetric relation
+     */
+    private static void symmetricSymmetric(Judgment belief, Sentence sentence, int figure) {
+        Statement s1 = (Statement) belief.cloneContent();
+        Statement s2 = (Statement) sentence.cloneContent();
+        switch (figure) {
+            case 11:
+                if (Variable.unify(Variable.VarType.INDEPENDENT, s1.getSubject(), s2.getSubject(), s1, s2))
+                    SyllogisticRules.resemblance(s1.getPredicate(), s2.getPredicate(), belief, sentence, figure);
+                break;
+            case 12:
+                if (Variable.unify(Variable.VarType.INDEPENDENT, s1.getSubject(), s2.getPredicate(), s1, s2))
+                    SyllogisticRules.resemblance(s1.getPredicate(), s2.getSubject(), belief, sentence, figure);
+                break;
+            case 21:
+                if (Variable.unify(Variable.VarType.INDEPENDENT, s1.getPredicate(), s2.getSubject(), s1, s2))
+                    SyllogisticRules.resemblance(s1.getSubject(), s2.getPredicate(), belief, sentence, figure);
+                break;
+            case 22:
+                if (Variable.unify(Variable.VarType.INDEPENDENT, s1.getPredicate(), s2.getPredicate(), s1, s2))
+                    SyllogisticRules.resemblance(s1.getSubject(), s2.getSubject(), belief, sentence, figure);
+                break;
+        }
+    }
+    
+    /* ----- conditional inferences ----- */
+    
+    private static void detachmentWithVar(Statement statement, boolean compoundTask, int index, CompoundTerm compound, Sentence belief) {
+        Term component = statement.componentAt(index);
+        Term inh = (compound instanceof Tense) ? compound.componentAt(0) : compound;
+        if ((component instanceof Inheritance) && (belief != null)) {
+            if (component.isConstant())
+                SyllogisticRules.detachment(statement, compound, compoundTask, index);
+            else if (Variable.unify(Variable.VarType.INDEPENDENT, component, inh, statement, compound))
+                SyllogisticRules.detachment(statement, compound, compoundTask, index);
+            else if ((statement instanceof Implication) && (Memory.currentTask.getSentence().isJudgment())) {
+                SyllogisticRules.introVarIndInner(statement, statement.getPredicate(), inh);    // tense???
+                CompositionalRules.introVarDepInner(statement, statement.getPredicate(), inh);  // tense???
+            }
+        }
+    }
+    
+    private static void conditionalDedIndWithVar(Implication conditional, short index, Statement statement, short side) {
+        CompoundTerm condition = (CompoundTerm) conditional.getSubject();
+        Term component = condition.componentAt(index);
+        Term component2 = null;
+        if (statement instanceof Inheritance)
+            component2 = statement;
+        else if (statement instanceof Implication)
+            component2 = statement.componentAt(side);
+        if ((component2 != null) && Variable.unify(Variable.VarType.INDEPENDENT, component, component2, conditional, statement))
+            SyllogisticRules.conditionalDedInd(conditional, index, statement, -1);
+    }
+    
+    /* ----- structural inferences ----- */
+    
+    private static void compoundAndSelf(CompoundTerm compound, Term component, boolean compoundTask) {
+        if ((compound instanceof Conjunction) || (compound instanceof Disjunction)) {
+            if (Memory.currentBelief != null)
+                CompositionalRules.decomposeStatement(compound, component, compoundTask);
+            else if (compound.containComponent(component))
+                StructuralRules.structuralCompound(compound, component, compoundTask);
+        } else if ((compound instanceof Negation) && !Memory.currentTask.isStructual()) {
+            if (compoundTask)
+                StructuralRules.transformNegation(((Negation) compound).componentAt(0));
+            else
+                StructuralRules.transformNegation(compound);
+        }
+    }
+    
+    private static void compoundAndCompound(CompoundTerm taskTerm, int tIndex, CompoundTerm beliefTerm, int bIndex) {
+        if (taskTerm.getClass() == beliefTerm.getClass()) {
+            if (taskTerm.size() > beliefTerm.size())
+                compoundAndSelf(taskTerm, beliefTerm, true);
+            else if (taskTerm.size() < beliefTerm.size())
+                compoundAndSelf(beliefTerm, taskTerm, false);
+        }
+    }
+    
+    private static void compoundAndStatement(CompoundTerm compound, short index, Statement statement, short side, Term beliefTerm) {
+        Term component = compound.componentAt(index);
+        Task task = Memory.currentTask;
+        if (component.getClass() ==  statement.getClass()) {
+            if ((compound instanceof Conjunction) && (Memory.currentBelief != null)) {
+                if (Variable.unify(Variable.VarType.DEPENDENT, component, statement, compound, statement))
+                    CompositionalRules.abdVarDepOuter(compound, component, statement.equals(beliefTerm));
+                else if (task.getSentence().isJudgment()) {
+                    SyllogisticRules.introVarIndInner(compound, component, statement);
+                    CompositionalRules.introVarDepInner(compound, component, statement);
+                }
+            }
+        } else {
+            if (compound instanceof Tense) {
+                if (component instanceof Inheritance)
+                    detachmentWithVar(statement, statement.equals(beliefTerm), side, compound, Memory.currentBelief);
+                else {
+                    Sentence belief = Memory.currentBelief;
+                    if (belief != null)
+                        SyllogisticRules.detachment(statement, compound, statement.equals(beliefTerm), side);
+                }
+            } else if (!task.isStructual() && task.getSentence().isJudgment()) {
+                if (statement instanceof Inheritance) {
+                    StructuralRules.structuralCompose1(compound, index, statement);
+                    if (!(compound instanceof SetExt) && !(compound instanceof SetInt))
+                        StructuralRules.structuralCompose2(compound, index, statement, side);    // {A --> B, A @ (A&C)} |- (A&C) --> (B&C)
+                } else if (statement instanceof Similarity)
+                    StructuralRules.structuralCompose2(compound, index, statement, side);    // {A <-> B, A @ (A&C)} |- (A&C) <-> (B&C)
+            }
+        }
+    }
+    
+    private static void componentAndStatement(CompoundTerm compound, short index, Statement statement, short side) {
+        if (!Memory.currentTask.isStructual()) {
+            if (statement instanceof Inheritance) {
+                StructuralRules.structuralDecompose1(compound, index, statement, side);
+                if (!(compound instanceof SetExt) && !(compound instanceof SetInt))
+                    StructuralRules.structuralDecompose2(statement);   // {(C-B) --> (C-A), A @ (C-A)} |- A --> B
+                else
+                    StructuralRules.transformSetRelation(compound, statement, side);
+            } else if (statement instanceof Similarity) {
+                StructuralRules.structuralDecompose2(statement);   // {(C-B) --> (C-A), A @ (C-A)} |- A --> B
+                if ((compound instanceof SetExt) || (compound instanceof SetInt))
+                    StructuralRules.transformSetRelation(compound, statement, side);
+            } else if ((statement instanceof Implication) && (compound instanceof Negation))
+                StructuralRules.contraposition(statement);
+        }
+    }
+    
+    /* ----- inference with one composition link ----- */
+    
+    public static void transformTask(Task task,  TaskLink tLink) {          // move to StructuralRules???
+        CompoundTerm content = (CompoundTerm) task.getContent().clone();
+        short[] indices = tLink.getIndices();
+        Term inh = null;
+        if (indices.length == 2) {
+            inh = content;
+        } else if (indices.length == 3) {
+            if ((content instanceof Implication) && (content.componentAt(0) instanceof Conjunction))
+                inh = ((CompoundTerm) content.componentAt(0)).componentAt(indices[0]);
+            else
+                inh = content.componentAt(indices[0]);
+        }
+        if (inh instanceof Inheritance)
+            StructuralRules.transformProductImage((Inheritance)inh, content, indices, task);
+    }
+    
+}
diff --git a/inference/StructuralRules.java b/inference/StructuralRules.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc6a3efc48a99cbd336a7a58a221e1839f8edb20
--- /dev/null
+++ b/inference/StructuralRules.java
@@ -0,0 +1,400 @@
+
+package nars.inference;
+
+import java.util.*;
+
+import nars.entity.*;
+import nars.language.*;
+import nars.io.*;
+import nars.main.*;
+
+/**
+ * Forward inference rules involving compound terms.
+ * Input premises are one sentence and one BeliefLink.
+ */
+public final class StructuralRules {
+    
+    /* -------------------- transform between compounds and components -------------------- */
+    
+    /**
+     * {<A --> B>} |- <(A&C) --> (B&C)>
+     * @param compound The compound term
+     * @param index The location of the indicated term in the compound
+     * @param statement The premise
+     * @param side The location of the indicated term in the premise
+     */
+    static void structuralCompose2(CompoundTerm compound, short index, Statement statement, short side) {
+        Term sub = statement.getSubject();
+        Term pred = statement.getPredicate();
+        ArrayList<Term> components = compound.cloneComponents();
+        if (((side == 0) && components.contains(pred)) || ((side == 1) && components.contains(sub)))
+            return;     // both terms in compound
+        if (side == 0) {
+            sub = compound;
+            components.set(index, pred);
+            pred = CompoundTerm.make(compound, components);
+        } else {
+            components.set(index, sub);
+            sub = CompoundTerm.make(compound, components);
+            pred = compound;
+        }
+        if ((sub == null) || (pred == null))
+            return;
+        Term content;
+        if (switchOrder(compound, index))
+            content = Statement.make(statement, pred, sub);     // {A --> B, A @ (C-A)} |- (C-B) --> (C-A)
+        else
+            content = Statement.make(statement, sub, pred);     // the other cases
+        if (content == null)
+            return;
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        TruthValue truth = sentence.getTruth();
+        BudgetValue budget;
+        if (sentence instanceof Question) {
+            budget = BudgetFunctions.compoundBackwardWeak(content);
+        } else {
+            if (compound.size() > 1) {
+                if (sentence.isJudgment())
+                    truth = TruthFunctions.implying(truth);
+                else // Goal
+                    truth = TruthFunctions.implied(truth);
+            }
+            budget = BudgetFunctions.compoundForward(truth, content);
+        }
+        Memory.singlePremiseTask(budget, content, truth);
+    }
+
+    /**
+     * {<(A&C) --> (B&C)>} |- <A --> B>
+     * @param statement The premise
+     */
+    static void structuralDecompose2(Statement statement) {
+        Term subj = statement.getSubject();
+        Term pred = statement.getPredicate();
+        if (subj.getClass() != pred.getClass())
+            return;
+        CompoundTerm sub = (CompoundTerm) subj;
+        CompoundTerm pre = (CompoundTerm) pred;
+        if (sub.size() != pre.size())
+            return;
+        int index = -1;
+        Term t1, t2;
+        for (int i = 0; i < sub.size(); i++) {
+            t1 = sub.componentAt(i);
+            t2 = pre.componentAt(i);
+            if (!t1.equals(t2)) {
+                if (index < 0)
+                    index = i;
+                else
+                    return;
+            }
+        }
+        t1 = sub.componentAt(index);
+        t2 = pre.componentAt(index);
+        Term content;
+        if (switchOrder(sub, (short) index))
+            content = Statement.make(statement, t2, t1);
+        else
+            content = Statement.make(statement, t1, t2);
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        TruthValue truth = sentence.getTruth();
+        BudgetValue budget;
+        if (sentence instanceof Question) {
+            budget = BudgetFunctions.compoundBackward(content);
+        } else {
+            if (sub.size() > 1) {
+                if (sentence.isJudgment())
+                    truth = TruthFunctions.implied(truth);
+                else // Goal
+                    truth = TruthFunctions.implying(truth);
+            }
+            budget = BudgetFunctions.compoundForward(truth, content);
+        }
+        Memory.singlePremiseTask(budget, content, truth);
+    }
+    
+    private static boolean switchOrder(CompoundTerm compound, short index) {
+        return ((((compound instanceof DifferenceExt) || (compound instanceof DifferenceInt)) && (index == 1)) ||
+                ((compound instanceof ImageExt) && (index != ((ImageExt) compound).getRelationIndex())) ||
+                ((compound instanceof ImageInt) && (index != ((ImageInt) compound).getRelationIndex())));
+    }
+    
+    /**
+     * {<A --> B>} |- <A --> (B&C)>
+     * @param compound The compound term
+     * @param index The location of the indicated term in the compound
+     * @param statement The premise
+     */
+    static void structuralCompose1(CompoundTerm compound, short index, Statement statement) {
+        if (!Memory.currentTask.getSentence().isJudgment())
+            return;
+        Term component = compound.componentAt(index);
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        TruthValue truth = sentence.getTruth();
+        Term subj = statement.getSubject();
+        Term pred = statement.getPredicate();
+        if (component.equals(subj)) {
+            if (compound instanceof IntersectionExt)                        // {A --> B, A @ (A&C)} |- (A&C) --> B
+                structuralStatement(compound, pred, TruthFunctions.implying(truth));
+            else if (compound instanceof IntersectionInt)                   // {A --> B, A @ (A|C)} |- (A|C) --> B
+                structuralStatement(compound, pred, TruthFunctions.implied(truth));
+            else if ((compound instanceof DifferenceExt) && (index == 0))   // {A --> B, A @ (A-C)} |- (A-C) --> B
+                structuralStatement(compound, pred, TruthFunctions.implying(truth));
+            else if (compound instanceof DifferenceInt)
+                if (index == 0)                                             // {A --> B, A @ (A~C)} |- (A~C) --> B
+                    structuralStatement(compound, pred, TruthFunctions.implied(truth));
+                else                                                        // {A --> B, A @ (C~A)} |- (C~A) --> B
+                    structuralStatement(compound, pred, TruthFunctions.negImply(truth));
+        } else if (component.equals(pred)) {
+            if (compound instanceof IntersectionExt)                        // {B --> A, A @ (A&C)} |- B --> (A&C)
+                structuralStatement(subj, compound, TruthFunctions.implied(truth));
+            else if (compound instanceof IntersectionInt)                   // {B --> A, A @ (A|C)} |- B --> (A|C)
+                structuralStatement(subj, compound, TruthFunctions.implying(truth));
+            else if (compound instanceof DifferenceExt)
+                if (index == 0)                                             // {B --> A, A @ (A-C)} |- B --> (A-C)
+                    structuralStatement(subj, compound, TruthFunctions.implied(truth));
+                else                                                        // {B --> A, A @ (C-A)} |- B --> (C-A)
+                    structuralStatement(subj, compound, TruthFunctions.negImply(truth));
+            else if ((compound instanceof DifferenceInt) && (index == 0))   // {B --> A, A @ (A~C)} |- B --> (A~C)
+                structuralStatement(subj, compound, TruthFunctions.implying(truth));
+        }
+    }
+    
+    /**
+     * {<(A&C) --> B>} |- <A --> B>
+     * @param compound The compound term
+     * @param index The location of the indicated term in the compound
+     * @param statement The premise
+     * @param side The location of the indicated term in the premise
+     */
+    static void structuralDecompose1(CompoundTerm compound, short index, Statement statement, short side) {
+        if (!Memory.currentTask.getSentence().isJudgment())
+            return;
+        Term component = compound.componentAt(index);
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        TruthValue truth = sentence.getTruth();
+        Term subj = statement.getSubject();
+        Term pred = statement.getPredicate();
+        if (compound.equals(subj)) {
+            if (compound instanceof IntersectionExt)                        // {(A&C) --> B, A @ (A&C)} |- A --> B
+                structuralStatement(component, pred, TruthFunctions.implied(truth));
+            else if (compound instanceof IntersectionInt)                   // {(A|C) --> B, A @ (A|C)} |- A --> B
+                structuralStatement(component, pred, TruthFunctions.implying(truth));
+            else if ((compound instanceof DifferenceExt) && (index == 0))   // {(A-C) --> B, A @ (A-C)} |- A --> B
+                structuralStatement(component, pred, TruthFunctions.implied(truth));
+            else if (compound instanceof DifferenceInt)
+                if (index == 0)                                             // {(A~C) --> B, A @ (A~C)} |- A --> B
+                    structuralStatement(component, pred, TruthFunctions.implying(truth));
+                else                                                        // {(C~A) --> B, A @ (C~A)} |- A --> B
+                    structuralStatement(component, pred, TruthFunctions.negImply(truth));
+        } else if (compound.equals(pred)) {
+            if (compound instanceof IntersectionExt)                        // {B --> (A&C), A @ (A&C)} |- B --> A
+                structuralStatement(subj, component, TruthFunctions.implying(truth));
+            else if (compound instanceof IntersectionInt)                   // {B --> (A&C), A @ (A&C)} |- B --> A
+                structuralStatement(subj, component, TruthFunctions.implied(truth));
+            else if (compound instanceof DifferenceExt)
+                if (index == 0)                                             // {B --> (A-C), A @ (A-C)} |- B --> A
+                    structuralStatement(subj, component, TruthFunctions.implying(truth));
+                else                                                        // {B --> (C-A), A @ (C-A)} |- B --> A
+                    structuralStatement(subj, component, TruthFunctions.negImply(truth));
+            else if ((compound instanceof DifferenceInt) && (index == 0))   // {B --> (A~C), A @ (A~C)} |- B --> A
+                structuralStatement(subj, component, TruthFunctions.implied(truth));
+        }
+    }
+    
+    /**
+     * Common final operations of the above two methods
+     * @param subject The subject of the new task
+     * @param predicate The predicate of the new task
+     * @param truth The truth value of the new task
+     */
+    private static void structuralStatement(Term subject, Term predicate, TruthValue truth) { // Inheritance only?
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        Term content = Statement.make((Statement) task.getContent(), subject, predicate);
+        if (content == null)
+            return;
+        BudgetValue budget = BudgetFunctions.compoundForward(truth, content);
+//        if (sentence instanceof Question)
+//            budget = BudgetFunctions.compoundBackward(content);
+//        else
+//            budget = BudgetFunctions.compoundForward(truth, content);
+        Memory.singlePremiseTask(budget, content, truth);
+    }
+    
+    /* -------------------- set transform -------------------- */
+    
+    /**
+     * {<S --> {P}>} |- <S <-> {P}>
+     * @param compound The set compound
+     * @param statement The premise
+     * @param side The location of the indicated term in the premise
+     */
+    static void transformSetRelation(CompoundTerm compound, Statement statement, short side) {
+        if (compound.size() > 1)
+            return;
+        if (statement instanceof Inheritance)
+            if (((compound instanceof SetExt) && (side == 0)) || ((compound instanceof SetInt) && (side == 1)))
+                return;
+        Term sub = statement.getSubject();
+        Term pre = statement.getPredicate();
+        Term content;
+        if (statement instanceof Inheritance) {
+            content = Similarity.make(sub, pre);
+        } else {
+            if (((compound instanceof SetExt) && (side == 0)) || ((compound instanceof SetInt) && (side == 1)))
+                content = Inheritance.make(pre, sub);
+            else
+                content = Inheritance.make(sub, pre);
+        }
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        TruthValue truth = sentence.getTruth();
+        BudgetValue budget;
+        if (sentence instanceof Question)
+            budget = BudgetFunctions.compoundBackward(content);
+        else
+            budget = BudgetFunctions.compoundForward(truth, content);
+        Memory.singlePremiseTask(budget, content, truth);
+    }
+    
+    /* -------------------- products and images transform -------------------- */
+    
+    static void transformProductImage(Inheritance inh, CompoundTerm oldContent, short[] indices, Task task) {
+        Term subject = null;
+        Term predicate = null;
+        short index = indices[indices.length - 1];
+        short side = indices[indices.length - 2];
+        CompoundTerm comp = (CompoundTerm) inh.componentAt(side);
+        if (comp instanceof Product) {
+            if (side == 0) {           // Product as subject
+                subject = comp.componentAt(index);
+                predicate = ImageExt.make((Product) comp, inh.getPredicate(), index);
+            } else {
+                subject = ImageInt.make((Product) comp, inh.getSubject(), index);
+                predicate = comp.componentAt(index);
+            }
+        } else if ((comp instanceof ImageExt) && (side == 1)) {   // ImageExt as predicate
+            if (index == ((ImageExt) comp).getRelationIndex()) {
+                subject = Product.make(comp, inh.getSubject(), index);
+                predicate = comp.componentAt(index);
+            } else {
+                subject = comp.componentAt(index);
+                predicate = ImageExt.make((ImageExt) comp, inh.getSubject(), index);
+            }
+        } else if ((comp instanceof ImageInt) && (side == 0)) {   // ImageInt as subject
+            if (index == ((ImageInt) comp).getRelationIndex()) {
+                subject = comp.componentAt(index);
+                predicate = Product.make(comp, inh.getPredicate(), index);
+            } else {
+                subject = ImageInt.make((ImageInt) comp, inh.getPredicate(), index);
+                predicate = comp.componentAt(index);
+            }
+        } else //        if ((subject == null) || (predicate == null))
+            return;
+        Inheritance newInh = Inheritance.make(subject, predicate);
+        Term content = null;
+        if (indices.length == 2)
+            content = newInh;
+        else {
+            ArrayList<Term> componentList;
+            if ((oldContent instanceof Implication) && (oldContent.componentAt(0) instanceof Conjunction)) {
+                componentList = ((CompoundTerm) oldContent.componentAt(0)).cloneComponents();
+                componentList.set(indices[0], newInh);
+                Term newCond = Conjunction.make(componentList);
+                content = Implication.make(newCond, ((Statement) oldContent).getPredicate());
+            } else
+                componentList = oldContent.cloneComponents();
+            componentList.set(indices[0], newInh);
+            if (oldContent instanceof Conjunction)
+                content = Conjunction.make(componentList);
+            else if ((oldContent instanceof Implication) || (oldContent instanceof Equivalence))
+                content = Statement.make((Statement) oldContent, componentList.get(0), componentList.get(1));
+        }
+        if (content == null)
+            return;
+        Sentence sentence = task.getSentence();
+        TruthValue truth = sentence.getTruth();
+        BudgetValue budget;
+        if (sentence instanceof Question)
+            budget = BudgetFunctions.compoundBackward(content);
+        else
+            budget = BudgetFunctions.compoundForward(truth, content);
+        Memory.singlePremiseTask(budget, content, truth);
+    }
+    
+    /* --------------- Disjunction and Conjunction transform --------------- */
+    
+    /**
+     * {(&&, A, B)} |- A
+     * @param compound The premise
+     * @param component The recognized component in the premise
+     * @param compoundTask Whether the compound comes from the task
+     */
+    static void structuralCompound(CompoundTerm compound, Term component, boolean compoundTask) {
+        Term content = (compoundTask ? component : compound);
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        TruthValue truth = sentence.getTruth();
+        BudgetValue budget;
+        if (sentence instanceof Question) {
+            budget = BudgetFunctions.compoundBackward(content);
+        } else {
+            if ((sentence.isJudgment()) == (compoundTask == (compound instanceof Conjunction)))
+                truth = TruthFunctions.implying(truth);
+            else
+                truth = TruthFunctions.implied(truth);
+            budget = BudgetFunctions.compoundForward(sentence.getTruth(), content);
+        }
+        Memory.singlePremiseTask(budget, content, truth);
+    }
+    
+    /* --------------- Negation related rules --------------- */
+    
+    /**
+     * {A} |- (--, A)
+     * @param content The premise
+     */
+    static void transformNegation(Term content) {
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        TruthValue truth = sentence.getTruth();
+        if (sentence instanceof Judgment)
+            truth = TruthFunctions.negation(truth);
+        BudgetValue budget;
+        if (sentence instanceof Question)
+            budget = BudgetFunctions.compoundBackward(content);
+        else
+            budget = BudgetFunctions.compoundForward(truth, content);
+        Memory.singlePremiseTask(budget, content, truth);
+    }
+    
+    /**
+     * {<A ==> B>} |- <(--B) ==> (--A)>
+     * @param statement The premise
+     */
+    static void contraposition(Statement statement) {
+        Term subj = statement.getSubject();
+        Term pred = statement.getPredicate();
+        Term content = Statement.make(statement, Negation.make(pred), Negation.make(subj), CompoundTerm.temporalReverse(statement.getTemporalOrder()));
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        TruthValue truth = sentence.getTruth();
+        BudgetValue budget;
+        if (sentence instanceof Question) {
+            if (content instanceof Implication)
+                budget = BudgetFunctions.compoundBackwardWeak(content);
+            else
+                budget = BudgetFunctions.compoundBackward(content);
+        } else {
+            if (content instanceof Implication)
+                truth = TruthFunctions.contraposition(truth);
+            budget = BudgetFunctions.compoundForward(truth, content);
+        }
+        Memory.singlePremiseTask(budget, content, truth);
+    }
+}
diff --git a/inference/SyllogisticRules.java b/inference/SyllogisticRules.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e5d1dd0967e64b04ecef82af2bd4085f1b159cc
--- /dev/null
+++ b/inference/SyllogisticRules.java
@@ -0,0 +1,483 @@
+
+package nars.inference;
+
+import java.util.*;
+
+import nars.entity.*;
+import nars.language.*;
+import nars.io.*;
+import nars.main.Memory;
+
+/**
+ * Syllogisms: Inference rules based on the transitivity of the relation.
+ */
+public final class SyllogisticRules {
+    
+    /* --------------- rules used in both first-order inference and higher-order inference --------------- */
+    
+    /**
+     * {<S ==> M>, <M ==> P>} |- {<S ==> P>, <P ==> S>}
+     * @param term1 Subject of the first new task
+     * @param term2 Predicate of the first new task
+     * @param sentence The first premise
+     * @param belief The second premise
+     */
+    static void dedExe(Term term1, Term term2, Sentence sentence, Judgment belief) {
+        if (Statement.invalidStatement(term1, term2))
+            return;
+        CompoundTerm.TemporalOrder order1 = sentence.getContent().getTemporalOrder();
+        CompoundTerm.TemporalOrder order2 = belief.getContent().getTemporalOrder();
+        CompoundTerm.TemporalOrder order = CompoundTerm.temporalInference(order1, order2);
+        if (order == CompoundTerm.TemporalOrder.UNSURE)
+            return;
+        Statement content1 = Statement.make((Statement) sentence.getContent(), term1, term2, order);
+        Statement content2 = Statement.make((Statement) sentence.getContent(), term2, term1, CompoundTerm.temporalReverse(order));
+        TruthValue value1 = sentence.getTruth();
+        TruthValue value2 = belief.getTruth();
+        TruthValue truth1 = null;
+        TruthValue truth2 = null;
+        BudgetValue budget1, budget2;
+        Task task = Memory.currentTask;
+        if (sentence instanceof Question) {
+            budget1 = BudgetFunctions.backwardWeak(value2);
+            budget2 = BudgetFunctions.backwardWeak(value2);
+        } else {
+            if (sentence instanceof Goal) {
+                truth1 = TruthFunctions.desireWeak(value1, value2);
+                truth2 = TruthFunctions.desireWeak(value1, value2);
+            } else {
+                truth1 = TruthFunctions.deduction(value1, value2);
+                truth2 = TruthFunctions.exemplification(value1, value2);
+            }
+            budget1 = BudgetFunctions.forward(truth1);
+            budget2 = BudgetFunctions.forward(truth2);
+        }
+        Memory.doublePremiseTask(budget1, content1, truth1);
+        Memory.doublePremiseTask(budget2, content2, truth2);
+    }
+    
+    /**
+     * {<M ==> S>, <M ==> P>} |- {<S ==> P>, <P ==> S>, <S <=> P>}
+     * @param term1 Subject of the first new task
+     * @param term2 Predicate of the first new task
+     * @param sentence The first premise
+     * @param belief The second premise
+     * @param figure Locations of the shared term in premises
+     */
+    static void abdIndCom(Term term1, Term term2, Sentence sentence, Judgment belief, int figure) {
+        if (Statement.invalidStatement(term1, term2))
+            return;
+        Statement st1 = (Statement) sentence.getContent();
+        Statement st2 = (Statement) belief.getContent();
+        CompoundTerm.TemporalOrder order1 = st1.getTemporalOrder();
+        CompoundTerm.TemporalOrder order2 = st2.getTemporalOrder();
+        CompoundTerm.TemporalOrder order;
+        if (figure == 11)
+            order = CompoundTerm.temporalInference(order1, CompoundTerm.temporalReverse(order2));
+        else
+            order = CompoundTerm.temporalInference(CompoundTerm.temporalReverse(order1), order2);
+        if (order == CompoundTerm.TemporalOrder.UNSURE)
+            return;
+        Statement statement1 = Statement.make(st1, term1, term2, order);
+        Statement statement2 = Statement.make(st1, term2, term1, CompoundTerm.temporalReverse(order));
+        Statement statement3 = Statement.makeSym(st1, term1, term2, order);
+        TruthValue truth1 = null;
+        TruthValue truth2 = null;
+        TruthValue truth3 = null;
+        BudgetValue budget1, budget2, budget3;
+        TruthValue value1 = sentence.getTruth();
+        TruthValue value2 = belief.getTruth();
+        if (sentence instanceof Question) {
+            budget1 = BudgetFunctions.backward(value2);
+            budget2 = BudgetFunctions.backwardWeak(value2);
+            budget3 = BudgetFunctions.backward(value2);
+        } else {
+            if (sentence instanceof Goal) {
+                truth1 = TruthFunctions.desireStrong(value1, value2);
+                truth2 = TruthFunctions.desireWeak(value2, value1);
+                truth3 = TruthFunctions.desireStrong(value1, value2);
+            } else {
+                truth1 = TruthFunctions.abduction(value1, value2);
+                truth2 = TruthFunctions.abduction(value2, value1);
+                truth3 = TruthFunctions.comparison(value1, value2);
+            }
+            budget1 = BudgetFunctions.forward(truth1);
+            budget2 = BudgetFunctions.forward(truth2);
+            budget3 = BudgetFunctions.forward(truth3);
+        }
+        Memory.doublePremiseTask(budget1, statement1, truth1);
+        Memory.doublePremiseTask(budget2, statement2, truth2);
+        Memory.doublePremiseTask(budget3, statement3, truth3);
+        if (statement1.isConstant()) {
+            Memory.doublePremiseTask(budget1, introVarInd(statement1, st2, st1, figure), truth1);
+            Memory.doublePremiseTask(budget2, introVarInd(statement2, st1, st2, figure), truth2);
+            Memory.doublePremiseTask(budget3, introVarInd(statement3, st1, st2, figure), truth3);
+        }
+    }
+    
+    /**
+     * {<S ==> P>, <M <=> P>} |- <S ==> P>
+     * @param term1 Subject of the new task
+     * @param term2 Predicate of the new task
+     * @param asym The asymmetric premise
+     * @param sym The symmetric premise
+     * @param figure Locations of the shared term in premises
+     */
+    static void analogy(Term term1, Term term2, Sentence asym, Sentence sym, int figure) {
+        if (Statement.invalidStatement(term1, term2))
+            return;
+        Statement asymSt = (Statement) asym.getContent();
+        Statement symSt = (Statement) sym.getContent();
+        CompoundTerm.TemporalOrder order1 = asymSt.getTemporalOrder();
+        CompoundTerm.TemporalOrder order2 = symSt.getTemporalOrder();
+        CompoundTerm.TemporalOrder order;
+        switch (figure) {
+            case 11:
+            case 12:
+                order = CompoundTerm.temporalInferenceWithFigure(order2, order1, figure);
+                break;
+            case 21:
+            case 22:
+                order = CompoundTerm.temporalInferenceWithFigure(order1, order2, figure);
+                break;
+            default:
+                return;
+        }
+        if (order == CompoundTerm.TemporalOrder.UNSURE)
+            return;
+        Term content = Statement.make(asymSt, term1, term2, order);
+        TruthValue truth = null;
+        BudgetValue budget;
+        Sentence sentence = Memory.currentTask.getSentence();
+        CompoundTerm taskTerm = (CompoundTerm) sentence.getContent();
+        if (sentence instanceof Question) {
+            if (taskTerm.isCommutative())
+                budget = BudgetFunctions.backwardWeak(asym.getTruth());
+            else
+                budget = BudgetFunctions.backward(sym.getTruth());
+        } else {
+            if (sentence instanceof Goal) {
+                if (taskTerm.isCommutative())
+                    truth = TruthFunctions.desireWeak(asym.getTruth(), sym.getTruth());
+                else
+                    truth = TruthFunctions.desireStrong(asym.getTruth(), sym.getTruth());
+            } else
+                truth = TruthFunctions.analogy(asym.getTruth(), sym.getTruth());
+            budget = BudgetFunctions.forward(truth);
+        }
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+    
+    /**
+     * {<S <=> M>, <M <=> P>} |- <S <=> P>
+     * @param term1 Subject of the new task
+     * @param term2 Predicate of the new task
+     * @param belief The first premise
+     * @param sentence The second premise
+     * @param figure Locations of the shared term in premises
+     */
+    static void resemblance(Term term1, Term term2, Judgment belief, Sentence sentence, int figure) {
+        if (Statement.invalidStatement(term1, term2))
+            return;
+        Statement st1 = (Statement) belief.getContent();
+        Statement st2 = (Statement) sentence.getContent();
+        CompoundTerm.TemporalOrder order1 = st1.getTemporalOrder();
+        CompoundTerm.TemporalOrder order2 = st2.getTemporalOrder();
+        CompoundTerm.TemporalOrder order = CompoundTerm.temporalInferenceWithFigure(order1, order2, figure);
+        if (order == CompoundTerm.TemporalOrder.UNSURE)
+            return;
+        Term statement = Statement.make(st1, term1, term2, order);
+        TruthValue truth = null;
+        BudgetValue budget;
+        Task task = Memory.currentTask;
+        if (sentence instanceof Question) {
+            budget = BudgetFunctions.backward(belief.getTruth());
+        } else {
+            if (sentence instanceof Goal)
+                truth = TruthFunctions.desireStrong(sentence.getTruth(), belief.getTruth());
+            else
+                truth = TruthFunctions.resemblance(belief.getTruth(), sentence.getTruth());
+            budget = BudgetFunctions.forward(truth);
+        }
+        Memory.doublePremiseTask(budget, statement, truth);
+    }
+    
+    /* --------------- rules used only in conditional inference --------------- */
+    
+    /**
+     * {<S ==> P>, S} |- P
+     * @param statement The premise statement
+     * @param compound The compound containing the shared component, can be null
+     * @param compoundTask Whether the component comes in the task
+     * @param side The location of the shared term in the statement
+     */
+    static void detachment(Statement statement, CompoundTerm compound, boolean compoundTask, int side) {
+        if (!(statement instanceof Implication) && !(statement instanceof Equivalence)) // necessary?
+            return;
+        Sentence sentence = Memory.currentTask.getSentence();
+        Judgment belief = Memory.currentBelief;
+        TruthValue value1, value2;
+        if (compoundTask) {
+            value2 = sentence.getTruth();
+            value1 = belief.getTruth();
+        } else {
+            value1 = sentence.getTruth();
+            value2 = belief.getTruth();
+        }
+        TruthValue truth = null;
+        BudgetValue budget;
+        if (sentence instanceof Question) {
+            if (statement instanceof Equivalence)
+                budget = BudgetFunctions.backward(belief.getTruth());
+            else if (side == 0)
+                budget = BudgetFunctions.backwardWeak(belief.getTruth());
+            else
+                budget = BudgetFunctions.backward(belief.getTruth());
+        } else {
+            if (sentence instanceof Goal) {
+                if (statement instanceof Equivalence)
+                    truth = TruthFunctions.desireStrong(value1, value2);
+                else if (side == 0)
+                    truth = TruthFunctions.desireInd(value1, value2);
+                else
+                    truth = TruthFunctions.desireDed(value1, value2);
+            } else {
+                if (statement instanceof Equivalence)
+                    truth = TruthFunctions.analogy(value1, value2);
+                else if (side == 0)
+                    truth = TruthFunctions.deduction(value1, value2);
+                else
+                    truth = TruthFunctions.abduction(value1, value2);
+            }
+            budget = BudgetFunctions.forward(truth);
+        }
+        Term content = statement.componentAt(1 - side);
+        if (!content.isConstant())
+            return;
+        if ((compound != null) && (compound instanceof Tense)){
+            CompoundTerm.TemporalOrder order1 = compound.getTemporalOrder();
+            CompoundTerm.TemporalOrder order2 = statement.getTemporalOrder();
+            CompoundTerm.TemporalOrder order;
+            if (side == 0)
+                order = CompoundTerm.temporalInference(order1, order2);
+            else
+                order = CompoundTerm.temporalInference(order1, CompoundTerm.temporalReverse(order2));
+            if (order == CompoundTerm.TemporalOrder.UNSURE)
+//                order = order1;
+                return;
+            content = Tense.make(content, order);
+        }
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+    
+    /**
+     * {<(&&, S1, S2) ==> P>, <M ==> S1>} |- <(&&, M, S2) ==> P>
+     * @param premise1 The conditional premise
+     * @param index The location of the shared term in the condition of premise1
+     * @param premise2 The premise which, or part of which, appears in the condition of premise1
+     * @param side The location of the shared term in premise2: 0 for subject, 1 for predicate, -1 for the whole term
+     */
+    static void conditionalDedInd(Implication premise1, short index, Term premise2, int side) {
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        Judgment belief = Memory.currentBelief;
+        Conjunction oldCondition = (Conjunction) premise1.getSubject();
+        CompoundTerm.TemporalOrder order1 = premise1.getTemporalOrder();
+        CompoundTerm.TemporalOrder order2 = premise2.getTemporalOrder();
+        boolean deduction = (side != 0);
+        if (!(order1 == order2))                                    // to be refined to cover tense
+            if (!(deduction && ((side == -1) || (index == 0))))     // don't replace middle conditions
+                return;
+        Term newComponent = null;
+        if (side == 0)
+            newComponent = ((Statement) premise2).getPredicate();
+        else if (side == 1)
+            newComponent = ((Statement) premise2).getSubject();
+        Term newCondition = CompoundTerm.replaceComponent(oldCondition, index, newComponent);
+        Term content = Statement.make(premise1, newCondition, premise1.getPredicate(), order1);
+        if (content == null)
+            return;
+        TruthValue value1 = sentence.getTruth();
+        TruthValue value2 = belief.getTruth();
+        TruthValue truth = null;
+        BudgetValue budget;
+        boolean conditionalTask = (Variable.findSubstitute(Variable.VarType.INDEPENDENT, premise2, belief.getContent()) != null);
+        if (sentence instanceof Question) {
+            budget = BudgetFunctions.backwardWeak(value2);
+        } else {
+            if (sentence instanceof Goal) {
+                if (conditionalTask)
+                    truth = TruthFunctions.desireWeak(value1, value2);
+                else if (deduction)
+                    truth = TruthFunctions.desireInd(value1, value2);
+                else
+                    truth = TruthFunctions.desireDed(value1, value2);
+                budget = BudgetFunctions.forward(truth);
+            } else {
+                if (deduction)
+                    truth = TruthFunctions.deduction(value1, value2);
+                else if (conditionalTask)
+                    truth = TruthFunctions.induction(value2, value1);
+                else
+                    truth = TruthFunctions.induction(value1, value2);
+            }
+            budget = BudgetFunctions.forward(truth);
+        }
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+    
+    /**
+     * {<(&&, S1, S2) ==> P>, <(&&, S1, S3) ==> P>} |- <S2 ==> S3>
+     * @param cond1 The condition of the first premise
+     * @param cond2 The condition of the second premise
+     * @param st1 The first premise
+     * @param st2 The second premise
+     * @return Whether there are derived tasks
+     */
+    static boolean conditionalAbd(Term cond1, Term cond2, Statement st1, Statement st2) {
+        if (!(st1 instanceof Implication) || !(st2 instanceof Implication))
+            return false;
+        if (!(cond1 instanceof Conjunction) && !(cond2 instanceof Conjunction))
+            return false;
+        CompoundTerm.TemporalOrder order1 = st1.getTemporalOrder();
+        CompoundTerm.TemporalOrder order2 = st2.getTemporalOrder();
+        if (order1 != CompoundTerm.temporalReverse(order2))
+            return false;
+        Term term1 = null;
+        Term term2 = null;
+        if (cond1 instanceof Conjunction) {
+            term1 = CompoundTerm.reduceComponents((Conjunction) cond1, cond2);
+        }
+        if (cond2 instanceof Conjunction) {
+            term2 = CompoundTerm.reduceComponents((Conjunction) cond2, cond1);
+        }
+        if ((term1 == null) && (term2 == null))
+            return false;
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        Judgment belief = Memory.currentBelief;
+        TruthValue value1 = sentence.getTruth();
+        TruthValue value2 = belief.getTruth();
+        boolean keepOrder = (Variable.findSubstitute(Variable.VarType.INDEPENDENT, st1, task.getContent()) != null);
+        Term content;
+        TruthValue truth = null;
+        BudgetValue budget;
+        if (term1 != null) {
+            if (term2 != null)
+                content = Statement.make(st2, term2, term1, order2);
+            else
+                content = term1;
+            if (sentence instanceof Question) {
+                budget = BudgetFunctions.backwardWeak(value2);
+            } else {
+                if (sentence instanceof Goal) {
+                    if (keepOrder)
+                        truth = TruthFunctions.desireDed(value1, value2);
+                    else
+                        truth = TruthFunctions.desireInd(value1, value2);
+                } else
+                    truth = TruthFunctions.abduction(value2, value1);
+                budget = BudgetFunctions.forward(truth);
+            }
+            Memory.doublePremiseTask(budget, content, truth);
+        }
+        if (term2 != null) {
+            if (term1 != null)
+                content = Statement.make(st1, term1, term2, order1);
+            else
+                content = term2;
+            if (sentence instanceof Question) {
+                budget = BudgetFunctions.backwardWeak(value2);
+            } else {
+                if (sentence instanceof Goal) {
+                    if (keepOrder)
+                        truth = TruthFunctions.desireDed(value1, value2);
+                    else
+                        truth = TruthFunctions.desireInd(value1, value2);
+                } else
+                    truth = TruthFunctions.abduction(value1, value2);
+                budget = BudgetFunctions.forward(truth);
+            }
+            Memory.doublePremiseTask(budget, content, truth);
+        }
+        return true;
+    }
+    
+    /* --------------- rules used for independent variable introduction --------------- */
+    
+    /**
+     * {<C ==> <M --> P>>, <M --> S>} |- <(&&,C,<#x --> S>) ==> <#x --> P>>
+     * @param compound The compound statement: Implication or Conjunction
+     * @param component The component to be used as a premise in internal induction
+     * @param premise The second premise, directly used in internal induction
+     */
+    static void introVarIndInner(CompoundTerm compound, Term component, Term premise) {
+        Task task = Memory.currentTask;
+        Sentence sentence = task.getSentence();
+        if (!sentence.isJudgment())
+            return;
+        if (!(component instanceof Statement))
+            return;
+        if (component.getClass() != premise.getClass())
+            return;
+        Variable v1 = new Variable(Symbols.VARIABLE_TAG + "0");
+        Variable v2 = new Variable(Symbols.VARIABLE_TAG + "0");
+        Statement premise1 = (Statement) premise;
+        Statement premise2 = (Statement) component;
+        CompoundTerm.TemporalOrder order1 = premise1.getTemporalOrder();
+        CompoundTerm.TemporalOrder order2 = premise2.getTemporalOrder();
+        if (order1 != CompoundTerm.temporalReverse(order2))
+            return;
+        int figure;
+        if (premise1.getSubject().equals(premise2.getSubject()))
+            figure = 11;
+        else if (premise1.getPredicate().equals(premise2.getPredicate()))
+            figure = 22;
+        else
+            return;
+        Statement innerContent = introVarInd(null, premise1, premise2, figure);
+        if (innerContent == null)
+            return;
+        Sentence belief = Memory.currentBelief;
+        Term content;
+        if (compound instanceof Implication) {
+            content = Statement.make((Statement) compound, compound.componentAt(0), innerContent);
+        } else if (compound instanceof Conjunction) {
+            content = CompoundTerm.replaceComponent(compound, component, innerContent);
+        } else
+            return;
+        TruthValue truth;
+        if (premise.equals(sentence.getContent()))
+            truth = TruthFunctions.abduction(sentence.getTruth(), belief.getTruth());
+        else
+            truth = TruthFunctions.abduction(belief.getTruth(), sentence.getTruth());
+        BudgetValue budget = BudgetFunctions.forward(truth);
+        Memory.doublePremiseTask(budget, content, truth);
+    }
+    
+    /**
+     * {<M --> S>, <M --> P>} |- <<#x --> S> ==> <#x --> P>>
+     * @param temp The template for the new task <S --> P>
+     * @param premise1 The first premise <M --> P>
+     * @param premise2 The second premise <M --> P>
+     * @param figure The figure indicating the location of the shared term
+     */
+    private static Statement introVarInd(Statement temp, Statement premise1, Statement premise2, int figure) {
+        Statement state1, state2;
+        Variable v1 = new Variable(Symbols.VARIABLE_TAG + "0");
+        Variable v2 = new Variable(Symbols.VARIABLE_TAG + "0");
+        if (figure == 11) {
+            state1 = Statement.make(premise1, v1, premise1.getPredicate());
+            state2 = Statement.make(premise2, v2, premise2.getPredicate());
+        } else {
+            state1 = Statement.make(premise1, premise1.getSubject(), v1);
+            state2 = Statement.make(premise2, premise2.getSubject(), v2);
+        }
+        Statement content;
+        if ((temp == null) || !temp.isCommutative())
+            content = Implication.make(state1, state2);
+        else
+            content = Equivalence.make(state1, state2);
+        return content;
+    }
+}
diff --git a/inference/TruthFunctions.java b/inference/TruthFunctions.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca520c74f26330fd1e59881cee42a7a92d541b60
--- /dev/null
+++ b/inference/TruthFunctions.java
@@ -0,0 +1,349 @@
+
+package nars.inference;
+
+import nars.entity.TruthValue;
+
+/**
+ * All truth value functions used in inference rules 
+ */
+public final class TruthFunctions extends UtilityFunctions {
+    
+    /* ----- Single Argument Functions, called in MatchingRules and StructuralRules ----- */
+
+    /**
+     * {A} |- (--A)
+     * @param v1 Truth value of the premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue negation(TruthValue v1) {
+        float f = 1 - v1.getFrequency();
+        float c = v1.getConfidence();
+        return new TruthValue(f, c);
+    }
+    
+    /**
+     * {<A ==> B>} |- <B ==> A>
+     * @param v1 Truth value of the premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue conversion(TruthValue v1) {
+        float f1 = v1.getFrequency();
+        float c1 = v1.getConfidence();
+        float w = and(f1, c1);
+        float c = w2c(w);
+        return new TruthValue(1, c);
+    }
+    
+    /**
+     * {<A ==> B>} |- <(--, B) ==> (--, A)>
+     * @param v1 Truth value of the premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue contraposition(TruthValue v1) {
+        float f1 = v1.getFrequency();
+        float c1 = v1.getConfidence();
+        float w = and(1-f1, c1);
+        float c = w2c(w);
+        return new TruthValue(0, c);
+    }
+    
+    /**
+     * {<A ==> B>, A} |- B
+     * @param v1 Truth value of the premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue implying(TruthValue v1) {
+        float f1 = v1.getFrequency();
+        float c1 = v1.getConfidence();
+        float c = and(f1, c1);
+        return new TruthValue(f1, c);
+    }
+
+    /**
+     * {<A ==> B>, B} |- A
+     * @param v1 Truth value of the premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue implied(TruthValue v1) {
+        float f1 = v1.getFrequency();
+        float c1 = v1.getConfidence();
+        float c = w2c(c1);
+        return new TruthValue(f1, c);
+    }
+    
+    /**
+     * {<A ==> (--, B)>, A} |- B
+     * @param v1 Truth value of the premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue negImply(TruthValue v1) {
+        return negation(implying(v1));
+    }
+    
+    /* -------------------- called in MatchingRules -------------------- */
+    
+    /**
+     * {<S ==> P>, <S ==> P>} |- <S ==> P>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue revision(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float w1 = c1 / (1 - c1);
+        float w2 = c2 / (1 - c2);
+        float f = (w1 * f1 + w2 * f2) / (w1 + w2);
+        float c = (w1 + w2) / (w1 + w2 + 1);
+        return new TruthValue(f, c);
+    }
+    
+    /* -------------------- called in SyllogisticRules -------------------- */
+    
+    /**
+     * {<S ==> M>, <M ==> P>} |- <S ==> P>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue deduction(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float f = and(f1, f2);
+        float c = and(c1, c2, f);
+        return new TruthValue(f, c);
+    }
+    
+    /**
+     * {<S ==> M>, <P ==> M>} |- <S ==> P>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue abduction(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float w = and(f2, c1, c2);
+        float c = w2c(w);
+        return new TruthValue(f1, c);
+    }
+    
+    /**
+     * {<M ==> S>, <M ==> P>} |- <S ==> P>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue induction(TruthValue v1, TruthValue v2) {
+        return abduction(v2, v1);
+    }
+    
+    /**
+     * {<M ==> S>, <P ==> M>} |- <S ==> P>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue exemplification(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float w = and(f1, f2, c1, c2);
+        float c = w2c(w);
+        return new TruthValue(1, c);
+    }
+    
+    /**
+     * {<M ==> S>, <M ==> P>} |- <S <=> P>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue comparison(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float f0 = or(f1, f2);
+        float f = (f0 == 0) ? 0 : (and(f1, f2) / f0) ;
+        float w = and(f0, c1, c2);
+        float c = w2c(w);
+        return new TruthValue(f, c);
+    }
+    
+    /**
+     * {<S ==> M>, <M <=> P>} |- <S ==> P>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue analogy(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float f = and(f1, f2);
+        float c0 = and(f2, c2);
+        float c = and(c1, c0, c0);
+        return new TruthValue(f, c);
+    }
+    
+    /**
+     * {<S <=> M>, <M <=> P>} |- <S <=> P>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue resemblance(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float f = and(f1, f2);
+        float c = and(c1, c2, or(f1, f2));
+        return new TruthValue(f, c);
+    }
+    
+    // -------------------- desire rules -------------------- //
+    
+    // strong backword inference on goals
+    // called in SyllogisticRules and HighOrderRules
+    static TruthValue desireStrong(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float f = and(f1, f2);
+        float c = and(c1, c2, f2);
+        return new TruthValue(f, c);
+    }
+    
+    // weak backword inference on goals
+    // called in SyllogisticRules and HighOrderRules
+    static TruthValue desireWeak(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float f = and(f1, f2);
+        float c = and(c1, c2, f2, w2c(1.0f));
+        return new TruthValue(f, c);
+    }
+    
+    // strong backword inference on goals
+    // called in HighOrderRules only
+    static TruthValue desireDed(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float f = and(f1, f2);
+        float c = and(f, c1, c2);
+        return new TruthValue(f, c);
+    }
+    
+    // called in HighOrderRules only
+    static TruthValue desireInd(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float w = and(f2, c1, c2);
+        float c = w2c(w);
+        return new TruthValue(f1, c);
+    }
+    
+    /* -------------------- called in CompositionalRules -------------------- */
+    
+    /**
+     * {<M --> S>, <M <-> P>} |- <M --> (S|P)>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue union(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float f = or(f1, f2);
+        float c = or(and(f1, c1), and(f2, c2)) + and(1 - f1, 1 - f2, c1, c2);
+        return new TruthValue(f, c);
+    }
+    
+    /**
+     * {<M --> S>, <M <-> P>} |- <M --> (S&P)>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue intersection(TruthValue v1, TruthValue v2) {
+        float f1 = v1.getFrequency();
+        float f2 = v2.getFrequency();
+        float c1 = v1.getConfidence();
+        float c2 = v2.getConfidence();
+        float f = and(f1, f2);
+        float c = or(and(1 - f1, c1), and(1 - f2, c2)) + and(f1, f2, c1, c2);
+        return new TruthValue(f, c);
+    }
+    
+    /**
+     * {<M --> S>, <M <-> P>} |- <M --> (S-P)>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue difference(TruthValue v1, TruthValue v2) {
+        TruthValue v0 = negation(v2);
+        return intersection(v1, v0);
+    }
+    
+    /**
+     * {(||, A, B), (--, B)} |- A
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue reduceDisjunction(TruthValue v1, TruthValue v2) {
+        TruthValue v0 = intersection(v1, negation(v2));
+        return implying(v0);
+    }
+    
+    /**
+     * {(--, (&&, A, B)), B} |- (--, A)
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue reduceConjunction(TruthValue v1, TruthValue v2) {
+        TruthValue v0 = intersection(negation(v1), v2);
+        return negation(implying(v0));
+    }
+    
+    /**
+     * {(--, (&&, A, (--, B))), (--, B)} |- (--, A)
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue reduceConjunctionNeg(TruthValue v1, TruthValue v2) {
+        return reduceConjunction(v1, negation(v2));
+    }
+    
+    /**
+     * {(&&, <#x() ==> M>, <#x() ==> P>), S ==> M} |- <S ==> P>
+     * @param v1 Truth value of the first premise
+     * @param v2 Truth value of the second premise
+     * @return Truth value of the conclusion
+     */
+    static TruthValue existAnalogy(TruthValue v1, TruthValue v2) {
+        return abduction(v1, v2);
+    }
+}
diff --git a/inference/UtilityFunctions.java b/inference/UtilityFunctions.java
new file mode 100644
index 0000000000000000000000000000000000000000..efa61e582569126b12eb5961b9a78aa9e22f7273
--- /dev/null
+++ b/inference/UtilityFunctions.java
@@ -0,0 +1,38 @@
+
+package nars.inference;
+
+import nars.main.Parameters;
+
+/**
+ * Common functions on real numbers in [0,1].
+ */
+public class UtilityFunctions {
+
+    // arithmetic average
+    public static float aveAri(float... arr) {
+        float sum = 0;
+        for(int i=0; i<arr.length; i++)
+            sum += arr[i];
+        return sum / arr.length;
+    }
+
+    public static float or(float... arr) {
+        float product = 1;
+        for(int i=0; i<arr.length; i++)
+            product *= (1 - arr[i]);
+        return 1 - product;
+    }
+    
+    public static float and(float... arr) {
+        float product = 1;
+        for(int i=0; i<arr.length; i++)
+            product *= arr[i];
+        return product;
+    }
+    
+    // weight to confidence
+    public static float w2c(float v) {
+        return v / (v + Parameters.NEAR_FUTURE);
+    }    
+}
+
diff --git a/inference/package.html b/inference/package.html
new file mode 100644
index 0000000000000000000000000000000000000000..95e3f329d6c7cde84007e0da90daa4e8706d645e
--- /dev/null
+++ b/inference/package.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<body bgcolor="white">
+
+The inference rules and control functions
+
+<h2>Package Specification</h2>
+
+Every task is processed in two stages:
+<ol>
+<li>Direct processing by matching, in the concept corresponding to the content, in one step. It happens when the task is inserted into memory.</li>
+<li>Indirect processing by reasoning, in related concepts and unlimited steps. It happens in each inference cycle.</li>
+</ol>
+The transmission from (1) to (2) corresponds to "decision making".
+<p>
+Inference between premises with no shared term can only happen in task buffer, between tasks based on common evidence.
+<p>
+In matching, the new task is compared with all existing direct Tasks in that Concept, to carry out:
+<ul>
+<li>revision: between judgments on non-overlapping evidence</li>
+<li>update: between judgments</li>
+<li>satisfy: between a Judgment and a Question/Goal</li>
+<li>merge: between items of the same type and base</li>
+</ul>
+<p>
+In reasoning, there are two basic types:
+<ol>
+<li>Between a Task and a TermLink (syntatic information)</li>
+<li>Between a Task and a Belief obtained by following a TermLink (semantic information).</li>
+</ol>
+In each case, there may be multiple applicable rules, which will be applied in parallel. For each rule, each conclusion is formed in three stages, to determine (1) the content (as a Term), (2) the truth-value or desire-value, and (3) the budget-value, in that order.
+<p>
+In the system, forward inference (the task is a Judgment) and backward inference (the task is a Question or Goal) are mostly isomorphic to each other, so that the inference rules produce clonclusions with the same content for different types of tasks. However, there are exceptions. For example, backward inference does not generate more complex terms.
+</body>
+</html>
diff --git a/io/Record.java b/io/Record.java
new file mode 100644
index 0000000000000000000000000000000000000000..73bc5bf480c35cb900de0ae2a6126edffa6b3e97
--- /dev/null
+++ b/io/Record.java
@@ -0,0 +1,41 @@
+
+package nars.io;
+
+import nars.gui.*;
+
+/**
+ * Inference log, different for (demo) applet and (working) application
+ */
+public class Record {    
+    /** the display window */
+    private static InferenceWindow window = new InferenceWindow();
+    /** whether to display */
+    private static boolean isReporting = false;
+    
+    /** initialize the window */
+    public static void init() {
+        window.clear();
+    }
+    
+    /** show the window */
+    public static void show() {
+        window.setVisible(true);
+    }
+    
+    /** begin the display */
+    public static void play() {
+        isReporting = true;
+    }
+    
+    /** stop the display */
+    public static void stop() {
+        isReporting = false;
+    }
+
+    /** add new text to display */
+    public static void append(String s) {
+        if (isReporting)
+            window.append(s);
+    }
+}
+
diff --git a/io/StringParser.java b/io/StringParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6a830fd8301cb40753e7a5359922ea7e65d6167
--- /dev/null
+++ b/io/StringParser.java
@@ -0,0 +1,380 @@
+
+package nars.io;
+
+import java.util.*;
+
+import nars.entity.*;
+import nars.inference.BudgetFunctions;
+import nars.language.*;
+import nars.main.*;
+
+/**
+ * Parse input String into Task.
+ */
+public abstract class StringParser extends Symbols {
+    /**
+     * All kinds of invalid input lines
+     */
+    private static class InvalidInputException extends Exception {
+        /**
+         * An invalid input line.
+         * @param s type of error
+         */
+        public InvalidInputException(String s) {
+            super(s);
+        }
+    }
+        
+    /**
+     * The only public (static) method of the class, called from Memory.inputTask.
+     * @param buffer the single-line input String
+     * @return an input Task, or null if the input line cannot be parsed into a Task
+     */
+    public static Task parseTask(StringBuffer buffer) {
+        try {
+            String budgetString = getBudgetString(buffer);
+            String truthString = getTruthString(buffer);
+            String str = buffer.toString().trim();
+            int last = str.length() - 1;
+            char punc = str.charAt(last);
+            TruthValue truth = parseTruth(truthString, punc);
+            BudgetValue budget = parseBudget(budgetString, punc, truth);
+            Term content = parseTerm(str.substring(0,last));
+            Base base = (punc == QUESTION_MARK) ? null : new Base();
+            Sentence sentence = Sentence.make(content, punc, truth, base);
+            if (sentence == null)
+                throw new InvalidInputException("invalid sentence");
+            sentence.setInput();
+            Task task = new Task(sentence, budget);
+            return task;
+        } catch (InvalidInputException e) {
+            System.out.println(" !!! INVALID INPUT: " + buffer + " --- " + e.getMessage());
+            return null;
+        }
+    }
+    
+    /* ---------- parse values ---------- */
+    
+    /**
+     * return the prefex of a task string that contains a BudgetValue
+     * @return a String containing a BudgetValue
+     * @param s the input in a StringBuffer
+     * @throws nars.io.StringParser.InvalidInputException if the input cannot be parsed into a BudgetValue
+     */
+    private static String getBudgetString(StringBuffer s) throws InvalidInputException {
+        if (s.charAt(0) != BUDGET_VALUE_MARK) // use default
+            return null;  // null values
+        int i = s.indexOf(BUDGET_VALUE_MARK + "", 1);    // looking for the end
+        if (i < 0) // no matching closer
+            throw new InvalidInputException("missing budget closer");
+        String budgetString = s.substring(1, i).trim();
+        if (budgetString.length() == 0) // empty usage
+            throw new InvalidInputException("empty budget");
+        s.delete(0, i+1);                 // remaining input to be processed outside
+        return budgetString;
+    }
+    
+    /**
+     * return the postfex of a task string that contains a TruthValue
+     * @return a String containing a TruthValue
+     * @param s the input in a StringBuffer
+     * @throws nars.io.StringParser.InvalidInputException if the input cannot be parsed into a TruthValue
+     */
+    private static String getTruthString(StringBuffer s) throws InvalidInputException {
+        int last = s.length()-1;
+        if (s.charAt(last) != TRUTH_VALUE_MARK)     // use default
+            return null;
+        int first = s.indexOf(TRUTH_VALUE_MARK + "");    // looking for the beginning
+        if (first == last) // no matching closer
+            throw new InvalidInputException("missing truth mark");
+        String truthString = s.substring(first+1, last).trim();
+        if (truthString.length() == 0) // empty usage
+            throw new InvalidInputException("empty truth");
+        s.delete(first, last+1);                 // remaining input to be processed outside
+        return truthString;
+    }
+    
+    /**
+     * parse the input String into a TruthValue (or DesireValue)
+     * @param s input String
+     * @param type Task type
+     * @throws nars.io.StringParser.InvalidInputException If the String cannot be parsed into a TruthValue
+     * @return the input TruthValue
+     */
+    private static TruthValue parseTruth(String s, char type) throws InvalidInputException {
+        if (type == QUESTION_MARK)
+            return null;
+        float frequency = 1.0f;
+        float confidence = Parameters.DEFAULT_JUDGMENT_CONFIDENCE;
+        if (s != null) {
+            int i = s.indexOf(VALUE_SEPARATOR);
+            if (i < 0)
+                frequency = Float.parseFloat(s);
+            else {
+                frequency = Float.parseFloat(s.substring(0,i));
+                confidence = Float.parseFloat(s.substring(i+1));
+            }
+        }
+        return new TruthValue(frequency, confidence);
+    }
+    
+    /**
+     * parse the input String into a BudgetValue
+     * 
+     * @param truth the TruthValue of the task
+     * @param s input String
+     * @param punctuation Task punctuation
+     * @return the input BudgetValue
+     * @throws nars.io.StringParser.InvalidInputException If the String cannot be parsed into a BudgetValue
+     */
+    private static BudgetValue parseBudget(String s, char punctuation, TruthValue truth) throws InvalidInputException {
+        float priority, durability;
+        switch (punctuation) {
+            case JUDGMENT_MARK:
+                priority = Parameters.DEFAULT_JUDGMENT_PRIORITY;
+                durability = Parameters.DEFAULT_JUDGMENT_DURABILITY;
+                break;
+            case GOAL_MARK:
+                priority = Parameters.DEFAULT_GOAL_PRIORITY;
+                durability = Parameters.DEFAULT_GOAL_DURABILITY;
+                break;
+            case QUESTION_MARK:
+                priority = Parameters.DEFAULT_QUESTION_PRIORITY;
+                durability = Parameters.DEFAULT_QUESTION_DURABILITY;
+                break;
+            default:
+                throw new InvalidInputException("unknown punctuation");
+        }
+        if (s != null) { // overrite default
+            int i = s.indexOf(VALUE_SEPARATOR);
+            if (i < 0) {        // default durability
+                priority = Float.parseFloat(s);
+            } else {
+                priority = Float.parseFloat(s.substring(0, i));
+                durability = Float.parseFloat(s.substring(i+1));
+            }
+        }
+        float quality = (punctuation == QUESTION_MARK) ? 1 : BudgetFunctions.truthToQuality(truth);
+        return new BudgetValue(priority, durability, quality);
+    }
+    
+    /* ---------- parse String into term ---------- */
+    
+    /**
+     * Top-level method that parse a Term in general, which may recursively call itself.
+     * <p>
+     * There are 5 valid cases:
+     * 1. (Op, A1, ..., An) is a common CompoundTerm (including CompoundStatement);
+     * 2. {A1, ..., An} is an SetExt;
+     * 3. [A1, ..., An] is an SetInt;
+     * 4. <T1 Re T2> is a Statement (including higher-order Statement);
+     * 5. otherwise it is a simple term.
+     * @param s0 the String to be parsed
+     * @throws nars.io.StringParser.InvalidInputException the String cannot be parsed into a Term
+     * @return the Term generated from the String
+     */
+    private static Term parseTerm(String s0) throws InvalidInputException {
+        String s = s0.trim();
+        if (s.length() == 0)
+            throw new InvalidInputException("missing content");
+        Term t = Memory.nameToListedTerm(s);    // existing constant or operator
+        if (t != null)
+            return t;                           // existing Term
+        int index = s.length()-1;
+        char first = s.charAt(0);
+        char last = s.charAt(index);
+        switch (first) {
+            case COMPOUND_TERM_OPENER:
+                if (last == COMPOUND_TERM_CLOSER)
+                    return parseCompoundTerm(s.substring(1, index));
+                else
+                    throw new InvalidInputException("missing CompoundTerm closer");
+            case SET_EXT_OPENER:
+                if (last == SET_EXT_CLOSER)
+                    return SetExt.make(parseArguments(s.substring(1, index) + ARGUMENT_SEPARATOR));
+                else
+                    throw new InvalidInputException("missing ExtensionSet closer");
+            case SET_INT_OPENER:
+                if (last == SET_INT_CLOSER)
+                    return SetInt.make(parseArguments(s.substring(1, index) + ARGUMENT_SEPARATOR));
+                else
+                    throw new InvalidInputException("missing IntensionSet closer");
+            case STATEMENT_OPENER:
+                if (last == STATEMENT_CLOSER)
+                    return parseStatement(s.substring(1, index));
+                else
+                    throw new InvalidInputException("missing Statement closer");
+            default:
+                return parseSimpleTerm(s);
+        }
+    }
+    
+    /**
+     * Parse a Term that has no internal structure.
+     * <p>
+     * The Term can be a constant or a variable.
+     * @param s0 the String to be parsed
+     * @throws nars.io.StringParser.InvalidInputException the String cannot be parsed into a Term
+     * @return the Term generated from the String
+     */
+    private static Term parseSimpleTerm(String s0) throws InvalidInputException {
+        String s = s0.trim();
+        if (s.length() == 0)
+            throw new InvalidInputException("missing term");
+        if (s.contains(" "))                // invalid characters in a name
+            throw new InvalidInputException("invalid term");
+        Term term;
+        char prefix = s.charAt(0);
+        if ((prefix == VARIABLE_TAG) || (prefix == QUERY_VARIABLE_TAG)) {
+            term = new Variable(s);         // the only place to directly call this constructor
+        } else
+            term = new Term(s);             // the only place to directly call this constructor
+        return term;
+    }
+    
+    /**
+     * Parse a String to create a Statement.
+     * @return the Statement generated from the String
+     * @param s0 The input String to be parsed
+     * @throws nars.io.StringParser.InvalidInputException the String cannot be parsed into a Term
+     */
+    private static Statement parseStatement(String s0) throws InvalidInputException {
+        String s = s0.trim();
+        int i = topRelation(s);
+        if (i < 0)
+            throw new InvalidInputException("invalid statement");
+        String relation = s.substring(i, i+3);
+        Term subject = parseTerm(s.substring(0, i));
+        Term predicate = parseTerm(s.substring(i+3));
+        Statement t = Statement.make(relation, subject, predicate);
+        if (t == null)
+            throw new InvalidInputException("invalid statement");
+        return t;
+    }
+    
+    /**
+     * Parse a String to create a CompoundTerm.
+     * @return the Term generated from the String
+     * @param s0 The String to be parsed
+     * @throws nars.io.StringParser.InvalidInputException the String cannot be parsed into a Term
+     */
+    private static Term parseCompoundTerm(String s0) throws InvalidInputException {
+        String s = s0.trim();
+        int firstSeparator = s.indexOf(ARGUMENT_SEPARATOR);
+        String op = s.substring(0, firstSeparator).trim();
+        if (!CompoundTerm.isOperator(op))
+            throw new InvalidInputException("unknown operator: " + op);
+        ArrayList<Term> arg = parseArguments(s.substring(firstSeparator+1) + ARGUMENT_SEPARATOR);
+        Term t = CompoundTerm.make(op, arg);
+        if (t == null)
+            throw new InvalidInputException("invalid compound term");
+        return t;
+    }
+    
+    /**
+     * Parse a String into the argument list of a CompoundTerm.
+     * @return the arguments in an ArrayList
+     * @param s0 The String to be parsed
+     * @throws nars.io.StringParser.InvalidInputException the String cannot be parsed into an argument list
+     */
+    private static ArrayList<Term> parseArguments(String s0) throws InvalidInputException {
+        String s = s0.trim();
+        ArrayList<Term> list = new ArrayList<Term>();
+        int start = 0;
+        int end = 0;
+        Term t;
+        while (end < s.length()-1) {
+            end = nextSeparator(s, start);
+            t = parseTerm(s.substring(start, end));     // recursive call
+            list.add(t);
+            start = end+1;
+        }
+        if (list.isEmpty())
+            throw new InvalidInputException("null argument");
+        return list;
+    }
+    
+    /* ---------- locate top-level substring ---------- */
+    
+    /**
+     * Locate the first top-level separator in a CompoundTerm
+     * @return the index of the next seperator in a String
+     * @param s The String to be parsed
+     * @param first The starting index
+     */
+    private static int nextSeparator(String s, int first) {
+        int levelCounter = 0;
+        int i = first;
+        while (i < s.length()-1) {
+            if (isOpener(s, i))
+                levelCounter++;
+            else if (isCloser(s, i))
+                levelCounter--;
+            else if (s.charAt(i) == ARGUMENT_SEPARATOR)
+                if (levelCounter == 0)
+                    break;
+            i++;
+        }
+        return i;
+    }
+    
+    /**
+     * locate the top-level relation in a statement
+     * @return the index of the top-level relation
+     * @param s The String to be parsed
+     */
+    private static int topRelation(String s) {      // need efficiency improvement
+        int levelCounter = 0;
+        int i = 0;
+        while (i < s.length()-3) {    // don't need to check the last 3 characters
+            if ((levelCounter == 0) && (Statement.isRelation(s.substring(i, i+3))))
+                return i;
+            if (isOpener(s, i))
+                levelCounter++;
+            else if (isCloser(s, i))
+                levelCounter--;
+            i++;
+        }
+        return -1;
+    }
+        
+    /* ---------- recognize symbols ---------- */
+        
+    /**
+     * check CompoundTerm opener symbol
+     * @return if the given String is an opener symbol
+     * @param s The String to be checked
+     * @param i The starting index
+     */
+    private static boolean isOpener(String s, int i) {
+        char c = s.charAt(i);
+        boolean b = (c == COMPOUND_TERM_OPENER) ||
+                (c == SET_EXT_OPENER) ||
+                (c == SET_INT_OPENER) ||
+                (c == STATEMENT_OPENER);
+        if (!b)
+            return false;
+        if (i+3 <= s.length() && Statement.isRelation(s.substring(i, i+3)))
+            return false;
+        return true;
+    }
+    
+    /**
+     * check CompoundTerm closer symbol
+     * @return if the given String is a closer symbol
+     * @param s The String to be checked
+     * @param i The starting index
+     */
+    private static boolean isCloser(String s, int i) {
+        char c = s.charAt(i);
+        boolean b = (c == COMPOUND_TERM_CLOSER) ||
+                (c == SET_EXT_CLOSER) ||
+                (c == SET_INT_CLOSER) ||
+                (c == STATEMENT_CLOSER);
+        if (!b)
+            return false;
+        if (i >= 2 && Statement.isRelation(s.substring(i-2, i+1)))
+            return false;
+        return true;
+    }
+}
diff --git a/io/Symbols.java b/io/Symbols.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7fdbfee2828f52b9372a2b9efaf6cb30afc7ebe
--- /dev/null
+++ b/io/Symbols.java
@@ -0,0 +1,88 @@
+
+package nars.io;
+
+import java.awt.*;
+
+/**
+ * The ASCII symbols used in I/O.
+ */
+public class Symbols {
+
+    // sentence delimitors
+    public static final char JUDGMENT_MARK = '.';
+    public static final char QUESTION_MARK = '?';
+    public static final char GOAL_MARK = '!';
+
+    // numerical value delimitors
+    // must be different from the Term opener/closer pairs
+    public static final char BUDGET_VALUE_MARK = '$';
+    public static final char TRUTH_VALUE_MARK = '%';
+    public static final char VALUE_SEPARATOR = ';';
+
+    // special Term name prefix
+    public static final char VARIABLE_TAG = '#';
+    public static final char QUERY_VARIABLE_TAG = '?';
+    public static final char OPERATOR_TAG = '^';
+
+    // CompountTerm delimitors
+    // must use 4 different pairs, see CompoundTerm/fromString.
+    public static final char COMPOUND_TERM_OPENER = '(';
+    public static final char COMPOUND_TERM_CLOSER = ')';
+    public static final char STATEMENT_OPENER = '<';
+    public static final char STATEMENT_CLOSER = '>';
+    public static final char SET_EXT_OPENER = '{';
+    public static final char SET_EXT_CLOSER = '}';
+    public static final char SET_INT_OPENER = '[';
+    public static final char SET_INT_CLOSER = ']';
+    
+    public static final char ARGUMENT_SEPARATOR = ',';
+    public static final char IMAGE_PLACE_HOLDER = '_';
+
+    // CompountTerm operators
+    // length = 1
+    public static final String INTERSECTION_EXT_OPERATOR = "&";
+    public static final String INTERSECTION_INT_OPERATOR = "|";
+    public static final String DIFFERENCE_EXT_OPERATOR = "-";
+    public static final String DIFFERENCE_INT_OPERATOR = "~";
+    public static final String PRODUCT_OPERATOR = "*";
+    public static final String IMAGE_EXT_OPERATOR = "/";
+    public static final String IMAGE_INT_OPERATOR = "\\";
+
+    // CompoundStatement operators
+    // length = 2
+    public static final String NEGATION_OPERATOR = "--";
+    public static final String DISJUNCTION_OPERATOR = "||";
+    public static final String CONJUNCTION_OPERATOR = "&&";
+    public static final String SEQUENCE_OPERATOR = "&/";
+    public static final String PARALLEL_OPERATOR = "&|";
+    public static final String FUTURE_OPERATOR = "/>";
+    public static final String PRESENT_OPERATOR = "|>";
+    public static final String PAST_OPERATOR = "\\>";
+
+    // built-in relations
+    // length = 3
+    public static final String INHERITANCE_RELATION = "-->";
+    public static final String SIMILARITY_RELATION = "<->";
+    public static final String INSTANCE_RELATION = "{--";
+    public static final String PROPERTY_RELATION = "--]";
+    public static final String INSTANCE_PROPERTY_RELATION = "{-]";
+    public static final String IMPLICATION_RELATION = "==>";
+    public static final String EQUIVALENCE_RELATION = "<=>";
+    public static final String IMPLICATION_AFTER_RELATION = "=/>";
+    public static final String IMPLICATION_BEFORE_RELATION = "=\\>";
+    public static final String IMPLICATION_WHEN_RELATION = "=|>";
+    public static final String EQUIVALENCE_AFTER_RELATION = "</>";
+    public static final String EQUIVALENCE_WHEN_RELATION = "<|>";
+
+    // Base, display only
+    public static final String Base_opener = " {";
+    public static final String Base_closer = "} ";
+    public static final String Base_separator = ";";
+    public static final String Base_separator0 = ": ";
+    
+    // TermLink type, display only
+    public static final String LinkToComponent_at1 = " @(";
+    public static final String LinkToComponent_at2 = ") _ ";
+    public static final String LinkToCompound_at1 = " _ @(";
+    public static final String LinkToCompound_at2 = ") ";
+}
diff --git a/io/package.html b/io/package.html
new file mode 100644
index 0000000000000000000000000000000000000000..21f94732368a6d27c10d021d3bc56fb743f35194
--- /dev/null
+++ b/io/package.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<body bgcolor="white">
+
+Input/output management
+
+<h2>Package Specification</h2>
+
+In the future, the file-related code for log and load/save will be here.
+
+</body>
+</html>
diff --git a/language/CompoundTerm.java b/language/CompoundTerm.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab376054acb489313e049d7a392ded4d6e5d2aa7
--- /dev/null
+++ b/language/CompoundTerm.java
@@ -0,0 +1,618 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.entity.TermLink;
+import nars.inference.BudgetFunctions;
+import nars.io.Symbols;
+import nars.main.Memory;
+
+/**
+ * A Term with internal structure
+ * <p>
+ * Formed by a term operator with one or more component Terms.
+ * <p>
+ * This abstract class contains default methods for all CompoundTerms.
+ */
+public abstract class CompoundTerm extends Term {
+    
+    /**
+     * list of (direct) components
+     */
+    protected ArrayList<Term> components;
+    
+    /**
+     * list of open variables in the compound
+     */
+    protected ArrayList<Variable> openVariables;    // always necessary?
+    
+    /**
+     * list of closed variables in the compound
+     */
+    protected ArrayList<Variable> closedVariables;    // remove???
+    
+    /**
+     * syntactic complexity of the compound, which is the sum of those of its components plus 1
+     */
+    protected short complexity;
+    
+    
+    // abstract methods
+    public abstract Object clone();
+    public abstract String operator();
+    
+    /* ----- object builders, called from subclasses ----- */
+    
+    /**
+     * default constructor
+     */
+    public CompoundTerm() {}
+    
+    /**
+     * constructor called from subclasses constructors to clone the fields
+     */
+    protected CompoundTerm(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        name = n;
+        components = cs;
+        openVariables = open;
+        closedVariables = closed;
+        complexity = i;
+    }
+    
+    /**
+     * constructor called from subclasses constructors to initialize the fields
+     */
+    protected CompoundTerm(String n, ArrayList<Term> arg) {
+        components = arg;
+        calcComplexity();
+        markVariables();    // set in components, not in this
+        name = makeName();
+    }
+    
+    /**
+     * the complexity of the term is the sum of those of the components plus 1
+     */
+    private void calcComplexity() {
+        Term t;
+        complexity = 1;
+        for (int i = 0; i < components.size(); i++) {
+            t = components.get(i);
+            complexity += t.getComplexity();
+        }
+    }
+    
+    /**
+     * check CompoundTerm operator symbol
+     * @return if the given String is an operator symbol
+     * @param s The String to be checked
+     */
+    public static boolean isOperator(String s) {
+        if (s.length() == 1)
+            return (s.equals(Symbols.INTERSECTION_EXT_OPERATOR) ||
+                    s.equals(Symbols.INTERSECTION_INT_OPERATOR) ||
+                    s.equals(Symbols.DIFFERENCE_EXT_OPERATOR) ||
+                    s.equals(Symbols.DIFFERENCE_INT_OPERATOR) ||
+                    s.equals(Symbols.PRODUCT_OPERATOR) ||
+                    s.equals(Symbols.IMAGE_EXT_OPERATOR) ||
+                    s.equals(Symbols.IMAGE_INT_OPERATOR));
+        if (s.length() == 2)
+            return (s.equals(Symbols.NEGATION_OPERATOR) ||
+                    s.equals(Symbols.DISJUNCTION_OPERATOR) ||
+                    s.equals(Symbols.CONJUNCTION_OPERATOR) ||
+                    s.equals(Symbols.SEQUENCE_OPERATOR) ||
+                    s.equals(Symbols.PARALLEL_OPERATOR) ||
+                    s.equals(Symbols.PAST_OPERATOR) ||
+                    s.equals(Symbols.PRESENT_OPERATOR) ||
+                    s.equals(Symbols.FUTURE_OPERATOR));
+        return isBuiltInOperator(s); // s.length() > 2
+    }
+        
+    public static Term make(String op, ArrayList arg) {
+        if (op.length() == 1) {
+            if (op.equals(Symbols.INTERSECTION_EXT_OPERATOR))
+                return IntersectionExt.make(arg);
+            if (op.equals(Symbols.INTERSECTION_INT_OPERATOR))
+                return IntersectionInt.make(arg);
+            if (op.equals(Symbols.DIFFERENCE_EXT_OPERATOR))
+                return DifferenceExt.make(arg);
+            if (op.equals(Symbols.DIFFERENCE_INT_OPERATOR))
+                return DifferenceInt.make(arg);
+            if (op.equals(Symbols.PRODUCT_OPERATOR))
+                return Product.make(arg);
+            if (op.equals(Symbols.IMAGE_EXT_OPERATOR))
+                return ImageExt.make(arg);
+            if (op.equals(Symbols.IMAGE_INT_OPERATOR))
+                return ImageInt.make(arg);
+        }
+        if (op.length() == 2) {
+            if (op.equals(Symbols.NEGATION_OPERATOR))
+                return Negation.make(arg);
+            if (op.equals(Symbols.DISJUNCTION_OPERATOR))
+                return Disjunction.make(arg);
+            if (op.equals(Symbols.CONJUNCTION_OPERATOR))
+                return Conjunction.make(arg);
+            if (op.equals(Symbols.SEQUENCE_OPERATOR))
+                return ConjunctionSequence.make(arg);
+            if (op.equals(Symbols.PARALLEL_OPERATOR))
+                return ConjunctionSequence.make(arg);
+            if (op.equals(Symbols.FUTURE_OPERATOR))
+                return TenseFuture.make(arg);
+            if (op.equals(Symbols.PRESENT_OPERATOR))
+                return TensePresent.make(arg);
+            if (op.equals(Symbols.PAST_OPERATOR))
+                return TensePast.make(arg);
+        }
+        if (isBuiltInOperator(op)) {
+            // t = Operator.make(op, arg);
+            Term sub = Product.make(arg);
+            Term pre = Memory.nameToOperator(op);
+            return Inheritance.make(sub, pre);
+        }
+        return null;
+    }
+
+    /**
+     * check built-in operator name
+     * @return if the given String is an operator name
+     * @param s The String to be checked
+     */
+    private static boolean isBuiltInOperator(String s) {
+        return (s.charAt(0) == Symbols.OPERATOR_TAG) && (Memory.nameToOperator(s) != null);
+    }
+    
+    public static Term make(CompoundTerm compound, ArrayList<Term> components) {
+        if (compound instanceof SetExt)
+            return SetExt.make(components);
+        if (compound instanceof SetInt)
+            return SetInt.make(components);
+        if (compound instanceof IntersectionExt)
+            return IntersectionExt.make(components);
+        if (compound instanceof IntersectionInt)
+            return IntersectionInt.make(components);
+        if (compound instanceof DifferenceExt)
+            return DifferenceExt.make(components);
+        if (compound instanceof DifferenceInt)
+            return DifferenceInt.make(components);
+        if (compound instanceof Product)
+            return Product.make(components);
+        if (compound instanceof ImageExt)
+            return ImageExt.make(components, ((ImageExt) compound).getRelationIndex());
+        if (compound instanceof ImageInt)
+            return ImageInt.make(components, ((ImageInt) compound).getRelationIndex());
+        if (compound instanceof Disjunction)
+            return Disjunction.make(components);
+        if (compound instanceof ConjunctionSequence)
+            return ConjunctionSequence.make(components);
+        if (compound instanceof ConjunctionParallel)
+            return ConjunctionParallel.make(components);
+        if (compound instanceof Conjunction)
+            return Conjunction.make(components);
+        return null;
+    }
+    
+    /* ----- utilities for name ----- */
+    
+    /**
+     * default method to make the name of the current term from existing fields
+     * @return the name of the term
+     */
+    protected String makeName() {
+        return makeCompoundName(operator(), components);
+    }
+    
+    /**
+     * default method to make the name of a compound term from given fields
+     * @param op the term operator
+     * @param arg the list of components
+     * @return the name of the term
+     */
+    protected static String makeCompoundName(String op, ArrayList<Term> arg) {
+        StringBuffer name = new StringBuffer();
+        name.append(Symbols.COMPOUND_TERM_OPENER);      // also show closed variables???
+        name.append(op);
+        for (int i = 0; i < arg.size(); i++) {
+            name.append(Symbols.ARGUMENT_SEPARATOR);
+            name.append(arg.get(i).getName());
+        }
+        name.append(Symbols.COMPOUND_TERM_CLOSER);
+        return name.toString();
+    }
+    
+    /**
+     * make the name of an ExtensionSet or IntensionSet
+     * @param opener the set opener
+     * @param closer the set closer
+     * @param arg the list of components
+     * @return the name of the term
+     */
+    protected static String makeSetName(char opener, ArrayList<Term> arg, char closer) {
+        StringBuffer name = new StringBuffer();
+        name.append(opener);
+        name.append(arg.get(0).toString());
+        for (int i = 1; i < arg.size(); i++) {
+            name.append(Symbols.ARGUMENT_SEPARATOR);
+            name.append(arg.get(i).getName());
+        }
+        name.append(closer);
+        return name.toString();
+    }
+    
+    /**
+     * default method to make the name of an image term from given fields
+     * @param op the term operator
+     * @param arg the list of components
+     * @param relationIndex the location of the place holder
+     * @return the name of the term
+     */
+    protected static String makeImageName(String op, ArrayList<Term> arg, int relationIndex) {
+        StringBuffer name = new StringBuffer();
+        name.append(Symbols.COMPOUND_TERM_OPENER);
+        name.append(op);
+        name.append(Symbols.ARGUMENT_SEPARATOR);
+        name.append(arg.get(relationIndex).getName());
+        for (int i = 0; i < arg.size(); i++) {
+            name.append(Symbols.ARGUMENT_SEPARATOR);
+            if (i == relationIndex)
+                name.append(Symbols.IMAGE_PLACE_HOLDER);
+            else
+                name.append(arg.get(i).getName());
+        }
+        name.append(Symbols.COMPOUND_TERM_CLOSER);
+        return name.toString();
+    }
+    
+    /**
+     * skip all variable names to produce stable sorting order among components, not for display
+     * @return the constant part of the term name
+     */
+    public String getConstantName() {
+        StringBuffer s = new StringBuffer();
+        s.append(Symbols.COMPOUND_TERM_OPENER);
+        s.append(operator());
+        s.append(Symbols.ARGUMENT_SEPARATOR);
+        for (int i = 0; i < components.size(); i++) {
+            s.append(components.get(i).getConstantName());
+            s.append(Symbols.ARGUMENT_SEPARATOR);
+        }
+        s.append(Symbols.COMPOUND_TERM_CLOSER);
+        return s.toString();
+    }
+    
+    /* ----- utilities for other fields ----- */
+    
+    /**
+     * report the term's syntactic complexity
+     * @return the comlexity value
+     */
+    public int getComplexity() {
+        return complexity;
+    }
+    
+    /**
+     * check if the term contains free variable
+     * @return if the term is a constant
+     */
+    public boolean isConstant() {
+        return (openVariables == null);
+    }
+    
+    /**
+     * check if the order of the components matters
+     * <p>
+     * commutative CompoundTerms: Sets, Intersections;
+     * communative Statements: Similarity, Equivalence;
+     * communative CompoundStatements: Disjunction, Conjunction
+     * @return if the term is a communitive
+     */
+    public boolean isCommutative() {
+        return false;
+    }
+        
+    /**
+     * build a component list from two terms
+     * @param t1 the first component
+     * @param t2 the second component
+     * @return the component list
+     */
+    protected static ArrayList<Term> argumentsToList(Term t1, Term t2) {
+        ArrayList<Term> list = new ArrayList<Term>(2);
+        list.add(t1);
+        list.add(t2);
+        return list;
+    }
+    
+    /* ----- extend Collection methods to component list ----- */
+    
+    /**
+     * get the number of components
+     * @return the size of the component list
+     */
+    public int size() {
+        return components.size();
+    }
+    
+    /**
+     * get a component by index
+     * @param i index of the component
+     * @return the component
+     */
+    public Term componentAt(int i) {
+        return components.get(i);
+    }
+    
+    public ArrayList<Term> getComponents() {
+        return components;
+    }
+    
+    public ArrayList<Term> cloneComponents() {
+        return (ArrayList<Term>) cloneList(components);
+    }
+    
+    /**
+     * deep clone an array list
+     * @return an identical and separate copy of the list
+     */
+    public static ArrayList cloneList(ArrayList original) {
+        if (original == null)
+            return null;
+        ArrayList arr = new ArrayList(original.size());
+        for (int i = 0; i < original.size(); i++)
+            arr.add(((Term) original.get(i)).clone());
+        return arr;
+    }
+    
+    public boolean containComponent(Term t) {
+        return components.contains(t);
+    }
+    
+    public boolean containAllComponents(Term t) {
+        if (getClass() == t.getClass())
+            return components.containsAll(((CompoundTerm) t).getComponents());
+        else
+            return components.contains(t);
+    }
+    
+    /* ----- clone/modify methods ----- */
+    
+    public static Term reduceComponents(CompoundTerm t1, Term t2) {
+        boolean done;
+        ArrayList<Term> list = t1.cloneComponents();
+        if (t1.getClass() == t2.getClass())
+            done = list.removeAll(((CompoundTerm) t2).getComponents());
+        else
+            done = list.remove(t2);
+        return (done ? make(t1, list) : null);
+    }
+
+    public static Term replaceComponent(CompoundTerm compound, int index, Term t) {
+        ArrayList<Term> list = compound.cloneComponents();
+        if (t == null)
+            list.remove(index);
+        else if (compound.getClass() != t.getClass())
+            list.set(index, t);
+        else {
+            ArrayList<Term> list2 = ((CompoundTerm)t).cloneComponents();
+            for (int i = 0; i < list2.size(); i++)
+                list.add(index + i, list2.get(i));
+        }
+        if (compound.isCommutative())
+            Collections.sort(list);
+        return make(compound, list);
+    }
+
+    public static Term replaceComponent(CompoundTerm compound, Term oldComponent, Term newComponent) {
+        ArrayList<Term> list = compound.cloneComponents();
+        int index = list.indexOf(oldComponent);
+        list.remove(index);
+        if (compound.getClass() != newComponent.getClass())
+            list.add(index, newComponent);
+        else {
+            ArrayList<Term> list2 = ((CompoundTerm)newComponent).cloneComponents();
+            for (int i = 0; i < list2.size(); i++)
+                list.add(index + i, list2.get(i));
+        }
+        if (compound.isCommutative())
+            Collections.sort(list);
+        return make(compound, list);
+    }
+   
+    /* ----- variable-related utilities ----- */
+    
+    /**
+     * get the OpenVariables list
+     * @return the open variables list
+     */
+    public ArrayList<Variable> getOpenVariables() {
+        return openVariables;
+    }
+    
+    /**
+     * register open and closed variables in a CompoundTerm
+     * <p>
+     * an open variable only appears in one components, while a closed variable appears in multiple components
+     */
+    private void markVariables() {  // not recursive
+        openVariables = new ArrayList<Variable>();
+        ArrayList<Variable> closedVariables = new ArrayList<Variable>();  // local variable
+        ArrayList<Variable> list;
+        for (Term term : components) {
+            if ((term instanceof Variable) && (((Variable) term).getType() != Variable.VarType.ANONYMOUS)) {
+                openVariables.add((Variable) term);
+            } else if (term instanceof CompoundTerm) {
+                list = ((CompoundTerm) term).getOpenVariables();
+                if (list != null) {
+                    for (Variable var : list) {
+                        if (var.getType() == Variable.VarType.QUERY)
+                            openVariables.add(var);
+                        else {
+                            int i = openVariables.indexOf(var);
+                            if (i >= 0) {           // assume a (independent/dependent) variable appears exactly twice
+                                var.setScope(this);
+                                openVariables.get(i).setScope(this);
+                                openVariables.remove(i);
+                                closedVariables.add(var);
+                            } else
+                                openVariables.add(var);
+                        }
+                    }
+                }
+            }
+        }
+        if (openVariables.size() == 0) // {
+            openVariables = null;
+    }
+    
+    // only called from Sentence
+    public void renameVariables() {
+        renameVariables(new HashMap<String, String>());
+    }
+    
+    /**
+     * Recursively rename variables by their appearing order in the CompoundTerm
+     * <p>
+     * Since each occurance of a variable is processed exactly ones, there will be no confusion between new names and old names.
+     * @param varMap the mapping built so far
+     */
+    protected void renameVariables(HashMap<String, String> varMap) {
+        String oldName, newName;
+        for (Term t : components) {
+            if ((t instanceof Variable) && (((Variable) t).getType() != Variable.VarType.ANONYMOUS)) {
+                oldName = ((Variable) t).getSimpleName();
+                newName = varMap.get(oldName);
+                if (newName == null) {
+                    newName = makeVarName(varMap.size(), (Variable) t);
+                    varMap.put(oldName, newName);
+                }
+                ((Variable) t).setName(newName);
+//            } else if ((t instanceof CompoundTerm) && !(t.isConstant())) {
+            } else if (t instanceof CompoundTerm) {
+                ((CompoundTerm) t).renameVariables(varMap);
+            }
+        }
+        name = makeName();     // type-specific
+    }
+    
+    /**
+     * sequentially generate new variable names
+     * @param size the current size of the variable list
+     * @param v the variable to be renamed
+     * @return a new variable name
+     */
+    private String makeVarName(int size, Variable v) {
+        StringBuffer s = new StringBuffer();
+        Variable.VarType type = v.getType();
+        if (type == Variable.VarType.QUERY)
+            s.append(Symbols.QUERY_VARIABLE_TAG);
+        else
+            s.append(Symbols.VARIABLE_TAG);
+        s.append(size + 1);
+        return s.toString();
+    }
+    
+    public void substituteComponent(HashMap<String,Term> subs, boolean first) {
+        Term t1, t2;
+        String varName;
+        for (int i = 0; i < size(); i++) {
+            t1 = componentAt(i);
+            if (t1 instanceof Variable) {                  // simple Variable
+                varName = ((Variable) t1).getVarName(first);  // name of the variable used in the mapping
+                t2 = subs.get(varName);                       // new Term if exist
+                if (t2 != null) {
+                    components.set(i, t2);
+                }
+            } else if (t1 instanceof CompoundTerm) {              // compound
+                ((CompoundTerm) t1).substituteComponent(subs, first);   // apply substitute to a clone
+            }
+        }
+        markVariables();        // variable status may be changed, so need to re-do this
+        name = makeName();
+    }
+    
+    /* ----- link CompoundTerm and its components ----- */
+    
+    /**
+     * build CompositionalLinks to constant components and subcomponents
+     * <p>
+     * The compound type determines the link type; the component type determines whether to build the link.
+     */
+    public ArrayList<TermLink> prepareComponentLinks() {
+        ArrayList<TermLink> componentLinks = new ArrayList<TermLink>();
+        short type = (this instanceof Statement) ? TermLink.COMPOUND_STATEMENT : TermLink.COMPOUND;   // default
+        prepareComponentLinks(componentLinks, type, this);
+        return componentLinks;
+    }
+    
+    protected void prepareComponentLinks(ArrayList<TermLink> componentLinks, short type, CompoundTerm term) {
+        Term t1, t2, t3;
+        for (int i = 0; i < term.size(); i++) {     // first level components
+            t1 = term.componentAt(i);
+            if (t1.isConstant())                                            // first level
+                componentLinks.add(new TermLink(t1, type, i));
+            if ((this instanceof Implication) && (i == 0) && (t1 instanceof Conjunction)) {
+                ((CompoundTerm) t1).prepareComponentLinks(componentLinks, TermLink.COMPOUND_CONDITION, (CompoundTerm) t1);
+            } else if (t1 instanceof CompoundTerm) {
+                for (int j = 0; j < ((CompoundTerm) t1).size(); j++) {
+                    t2 = ((CompoundTerm) t1).componentAt(j);
+                    if (t2.isConstant()) {                                  // second level
+                        if ((t1 instanceof Product) || (t1 instanceof ImageExt) || (t1 instanceof ImageInt))
+                            componentLinks.add(new TermLink(t2, TermLink.TRANSFORM, i, j));
+                        else
+                            componentLinks.add(new TermLink(t2, type, i, j));
+                    }
+                    if ((t2 instanceof Product) || (t2 instanceof ImageExt) || (t2 instanceof ImageInt)) {
+                        for (int k = 0; k < ((CompoundTerm) t2).size(); k++) {
+                            t3 = ((CompoundTerm) t2).componentAt(k);
+                            if (t3.isConstant()) {                           // third level
+                                componentLinks.add(new TermLink(t3, TermLink.TRANSFORM, i, j, k));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    
+    /* ---------- temporal order among components ---------- */
+    
+    public enum TemporalOrder { BEFORE, WHEN, AFTER, NONE, UNSURE }
+    
+    public static TemporalOrder temporalInference(TemporalOrder t1, TemporalOrder t2) {
+        if ((t1 == TemporalOrder.UNSURE) || (t2 == TemporalOrder.UNSURE))
+            return TemporalOrder.UNSURE;
+        if (t1 == TemporalOrder.NONE)
+            return t2;
+        if (t2 == TemporalOrder.NONE)
+            return t1;
+        if (t1 == TemporalOrder.WHEN)
+            return t2;
+        if (t2 == TemporalOrder.WHEN)
+            return t1;
+        if (t1 == t2)
+            return t1;
+        return TemporalOrder.UNSURE;
+    }
+
+    public static TemporalOrder temporalReverse(TemporalOrder t1) {
+        if (t1 == TemporalOrder.BEFORE)
+            return TemporalOrder.AFTER;
+        if (t1 == TemporalOrder.AFTER)
+            return TemporalOrder.BEFORE;
+        return t1;
+    } 
+    
+    public static TemporalOrder temporalInferenceWithFigure(TemporalOrder order1, TemporalOrder order2, int figure) {
+        switch (figure) {
+        case 11:
+            return temporalInference(temporalReverse(order1), order2);
+        case 12:
+            return temporalInference(temporalReverse(order1), temporalReverse(order2));
+        case 21:
+            return temporalInference(order1, order2);
+        case 22:
+            return temporalInference(order1, temporalReverse(order2));
+        default:
+            return TemporalOrder.UNSURE;
+        }
+    }
+}
diff --git a/language/Conjunction.java b/language/Conjunction.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e8c717368ae4556ec41bd7ae7cf947a509c025d
--- /dev/null
+++ b/language/Conjunction.java
@@ -0,0 +1,112 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.inference.*;
+import nars.io.*;
+import nars.main.Memory;
+
+/**
+ * Conjunction of statements
+ */
+public class Conjunction extends CompoundTerm {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    protected Conjunction(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    protected Conjunction(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a Conjunction
+     */
+    public Object clone() {
+        return new Conjunction(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param term1 The first compoment
+     * @param term2 The second compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term term1, Term term2) {
+        TreeSet set;
+        if (term1 instanceof Conjunction) {
+            set = new TreeSet(((CompoundTerm) term1).cloneComponents());
+            if (term2 instanceof Conjunction)
+                set.addAll(((CompoundTerm) term2).cloneComponents());   // (&,(&,P,Q),(&,R,S)) = (&,P,Q,R,S)
+            else
+                set.add((Term) term2.clone());                          // (&,(&,P,Q),R) = (&,P,Q,R)
+        } else if (term2 instanceof Conjunction) {
+            set = new TreeSet(((CompoundTerm) term2).cloneComponents()); 
+            set.add((Term) term1.clone());                              // (&,R,(&,P,Q)) = (&,P,Q,R)
+        } else {
+            set = new TreeSet();
+            set.add(term1);         // valid solution???
+            set.add(term2);
+//            set.add((Term) term1.clone());
+//            set.add((Term) term2.clone());
+        }
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new compound from a list of components. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList the list of arguments
+     */
+    public static Term make(ArrayList<Term> argList) {
+        TreeSet<Term> set = new TreeSet<Term>(argList); // sort/merge arguments
+        return make(set);
+    }
+
+    /**
+     * Try to make a new compound from a set of components. Called by the public make methods.
+     * @param set a set of Term as compoments
+     * @return the Term generated from the arguments
+     */
+    public static Term make(TreeSet<Term> set) {
+        if (set.isEmpty())
+            return null;                         // special case: no component
+        if (set.size() == 1)
+            return set.first();                         // special case: single component
+        ArrayList<Term> argument = new ArrayList<Term>(set);
+        String name = makeCompoundName(Symbols.CONJUNCTION_OPERATOR, argument);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new Conjunction(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.CONJUNCTION_OPERATOR;
+    }
+    
+    /**
+     * Check if the compound is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return true;
+    }
+}
diff --git a/language/ConjunctionParallel.java b/language/ConjunctionParallel.java
new file mode 100644
index 0000000000000000000000000000000000000000..e96f65aac5513c62607633b554a9a17d223e7940
--- /dev/null
+++ b/language/ConjunctionParallel.java
@@ -0,0 +1,106 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.inference.*;
+import nars.io.*;
+import nars.main.Memory;
+
+/**
+ * A parallel conjunction of Statements.
+ */
+public class ConjunctionParallel extends Conjunction {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    protected ConjunctionParallel(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    protected ConjunctionParallel(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a Conjunction
+     */
+    public Object clone() {
+        return new ConjunctionParallel(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param term1 The first compoment
+     * @param term2 The second compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term term1, Term term2) {
+        TreeSet set;
+        if (term1 instanceof ConjunctionParallel) {
+            set = new TreeSet(((CompoundTerm) term1).cloneComponents());
+            if (term2 instanceof ConjunctionParallel)
+                set.addAll(((CompoundTerm) term2).cloneComponents());   // (&,(&,P,Q),(&,R,S)) = (&,P,Q,R,S)
+            else
+                set.add((Term) term2.clone());                          // (&,(&,P,Q),R) = (&,P,Q,R)
+        } else if (term2 instanceof ConjunctionParallel) {
+            set = new TreeSet(((CompoundTerm) term2).cloneComponents()); 
+            set.add((Term) term1.clone());                              // (&,R,(&,P,Q)) = (&,P,Q,R)
+        } else {
+            set = new TreeSet();
+            set.add(term1);         // valid solution???
+            set.add(term2);
+        }
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new compound from a list of components. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList the list of arguments
+     */
+    public static Term make(ArrayList<Term> argList) {
+        TreeSet<Term> set = new TreeSet<Term>(argList); // sort/merge arguments
+        return make(set);
+    }
+
+    /**
+     * Try to make a new compound from a set of components. Called by the public make methods.
+     * @param set a set of Term as compoments
+     * @return the Term generated from the arguments
+     */
+    public static Term make(TreeSet<Term> set) {
+        if (set.isEmpty())
+            return null;                         // special case: no component
+        if (set.size() == 1)
+            return set.first();                         // special case: single component
+        ArrayList<Term> argument = new ArrayList<Term>(set);
+        String name = makeCompoundName(Symbols.CONJUNCTION_OPERATOR, argument);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new ConjunctionParallel(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.PARALLEL_OPERATOR;
+    }
+
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.WHEN;
+    }
+}
diff --git a/language/ConjunctionSequence.java b/language/ConjunctionSequence.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1a856450f5ecd7dff4504a45e8b869b1773e598
--- /dev/null
+++ b/language/ConjunctionSequence.java
@@ -0,0 +1,76 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.inference.*;
+import nars.io.*;
+import nars.main.Memory;
+
+/**
+ * A sequential conjunction of Statements.
+ */
+public class ConjunctionSequence extends Conjunction {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private ConjunctionSequence(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private ConjunctionSequence(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a Conjunction
+     */
+    public Object clone() {
+        return new ConjunctionSequence(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new compound from a list of components. Called by StringParser.
+     * @param argument the list of arguments
+     * @return the Term generated from the arguments
+     */
+    public static Term make(ArrayList<Term> argument) {
+        if (argument.size() == 1)
+            return argument.get(0);
+        String name = makeCompoundName(Symbols.PRODUCT_OPERATOR, argument);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new ConjunctionSequence(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.SEQUENCE_OPERATOR;
+    }
+    
+    /**
+     * Check if the compound is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return false;
+    }
+
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.AFTER;
+    }
+}
diff --git a/language/DifferenceExt.java b/language/DifferenceExt.java
new file mode 100644
index 0000000000000000000000000000000000000000..b255d9988436e49b4eea62db92cd9eb02ed33ba2
--- /dev/null
+++ b/language/DifferenceExt.java
@@ -0,0 +1,84 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * A compound term whose extension is the difference of the extensions of its components
+ */
+public class DifferenceExt extends CompoundTerm {
+
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private DifferenceExt(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private DifferenceExt(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a DifferenceExt
+     */
+    public Object clone() {
+        return new DifferenceExt(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new DifferenceExt. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList The list of components
+     */     
+    public static Term make(ArrayList<Term> argList) {
+        if (argList.size() == 1)    // special case from CompoundTerm.reduceComponent
+            return argList.get(0);
+        if (argList.size() != 2)
+            return null;
+        String name = makeCompoundName(Symbols.DIFFERENCE_EXT_OPERATOR, argList);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new DifferenceExt(name, argList);
+    }
+
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param t1 The first compoment
+     * @param t2 The second compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term t1, Term t2) {
+        if (t1.equals(t2))
+            return null;
+        if ((t1 instanceof SetExt) && (t2 instanceof SetExt)) {
+            TreeSet set = new TreeSet(((CompoundTerm) t1).cloneComponents());
+            set.removeAll(((CompoundTerm) t2).cloneComponents());           // set difference
+            return SetExt.make(set);
+        }
+        ArrayList<Term> list = argumentsToList(t1, t2);
+        return make(list);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.DIFFERENCE_EXT_OPERATOR;
+    }
+}
diff --git a/language/DifferenceInt.java b/language/DifferenceInt.java
new file mode 100644
index 0000000000000000000000000000000000000000..1489b2b82b98b57ea1334c61ad85dbd53846fb52
--- /dev/null
+++ b/language/DifferenceInt.java
@@ -0,0 +1,86 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * A compound term whose extension is the difference of the intensions of its components
+ */
+public class DifferenceInt extends CompoundTerm {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private DifferenceInt(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private DifferenceInt(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a DifferenceInt
+     */
+    public Object clone() {
+        return new DifferenceInt(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new DifferenceExt. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList The list of components
+     */     
+    public static Term make(ArrayList<Term> argList) {
+        if (argList.size() == 1)    // special case from CompoundTerm.reduceComponent
+            return argList.get(0);
+        if (argList.size() != 2)
+            return null;
+        String name = makeCompoundName(Symbols.DIFFERENCE_INT_OPERATOR, argList);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new DifferenceInt(name, argList);
+    }
+
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param t1 The first compoment
+     * @param t2 The second compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term t1, Term t2) {
+        if (t1.equals(t2))
+            return null;
+        if ((t1 instanceof SetInt) && (t2 instanceof SetInt)) {
+            TreeSet set = new TreeSet(((CompoundTerm) t1).cloneComponents());
+            set.removeAll(((CompoundTerm) t2).cloneComponents());           // set difference
+            return SetInt.make(set);
+        }
+        ArrayList<Term> list = argumentsToList(t1, t2);
+        return make(list);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.DIFFERENCE_INT_OPERATOR;
+    }
+}
+
+
diff --git a/language/Disjunction.java b/language/Disjunction.java
new file mode 100644
index 0000000000000000000000000000000000000000..858082ed629726f2049fd9e6362bb848acc02efe
--- /dev/null
+++ b/language/Disjunction.java
@@ -0,0 +1,108 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.inference.*;
+import nars.io.*;
+import nars.main.Memory;
+
+/** 
+ * A disjunction of Statements.
+ */
+public class Disjunction extends CompoundTerm {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private Disjunction(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private Disjunction(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a Disjunction
+     */
+    public Object clone() {
+        return new Disjunction(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+
+    /**
+     * Try to make a new Disjunction from two components. Called by the inference rules.
+     * @param term1 The first compoment
+     * @param term2 The first compoment
+     * @return A Disjunction generated or a Term it reduced to
+     */
+    public static Term make(Term term1, Term term2) {
+        TreeSet set;
+        if (term1 instanceof Disjunction) {
+            set = new TreeSet(((CompoundTerm) term1).cloneComponents());
+            if (term2 instanceof Disjunction)
+                set.addAll(((CompoundTerm) term2).cloneComponents());   // (&,(&,P,Q),(&,R,S)) = (&,P,Q,R,S)
+            else
+                set.add((Term) term2.clone());                          // (&,(&,P,Q),R) = (&,P,Q,R)
+        } else if (term2 instanceof Disjunction) {
+            set = new TreeSet(((CompoundTerm) term2).cloneComponents()); 
+            set.add((Term) term1.clone());                              // (&,R,(&,P,Q)) = (&,P,Q,R)
+        } else {
+            set = new TreeSet();
+            set.add((Term) term1.clone());
+            set.add((Term) term2.clone());
+        }
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new IntersectionExt. Called by StringParser.
+     * @param argList a list of Term as compoments
+     * @return the Term generated from the arguments
+     */
+    public static Term make(ArrayList<Term> argList) {
+        TreeSet<Term> set = new TreeSet<Term>(argList); // sort/merge arguments
+        return make(set);
+    }
+
+    /**
+     * Try to make a new Disjunction from a set of components. Called by the public make methods.
+     * @param set a set of Term as compoments
+     * @return the Term generated from the arguments
+     */
+    public static Term make(TreeSet<Term> set) {
+        if (set.size() == 1)
+            return set.first();                         // special case: single component
+        ArrayList<Term> argument = new ArrayList<Term>(set);
+        String name = makeCompoundName(Symbols.DISJUNCTION_OPERATOR, argument);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new Disjunction(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.DISJUNCTION_OPERATOR;
+    }
+
+    /**
+     * Conjunction is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return true;
+    }    
+}
diff --git a/language/Equivalence.java b/language/Equivalence.java
new file mode 100644
index 0000000000000000000000000000000000000000..85b5ac160dfdd5be7d236c8ebc150f61c847b715
--- /dev/null
+++ b/language/Equivalence.java
@@ -0,0 +1,78 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * A Statement about an Equivalence relation.
+ */
+public class Equivalence extends Statement {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    protected Equivalence(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    protected Equivalence(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a Similarity
+     */
+    public Object clone() {
+        return new Equivalence(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+     
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or null
+     */
+    public static Equivalence make(Term subject, Term predicate) {
+        if (invalidStatement(subject, predicate))
+            return null;
+        if (subject.compareTo(predicate) > 0)
+            return make(predicate, subject);
+        String name = makeStatementName(subject, Symbols.EQUIVALENCE_RELATION, predicate);
+        Term t = Memory.nameToListedTerm(name);
+        if (t != null)
+            return (Equivalence) t;
+        ArrayList<Term> argument = argumentsToList(subject, predicate);
+        return new Equivalence(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.EQUIVALENCE_RELATION;
+    }
+    
+    /**
+     * Check if the compound is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return true;
+    }
+}
diff --git a/language/EquivalenceAfter.java b/language/EquivalenceAfter.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ebb5cf0d8a0cbab16080feb1e2216506917fbdf
--- /dev/null
+++ b/language/EquivalenceAfter.java
@@ -0,0 +1,79 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.main.Memory;
+
+/**
+ * Temporal Implication relation, predicate after subject.
+ */
+public class EquivalenceAfter extends Equivalence {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    public EquivalenceAfter(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private EquivalenceAfter(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a Similarity
+     */
+    public Object clone() {
+        return new EquivalenceAfter(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+     
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or null
+     */
+    public static EquivalenceAfter make(Term subject, Term predicate) {
+        if (invalidStatement(subject, predicate))
+            return null;
+        String name = makeStatementName(subject, Symbols.EQUIVALENCE_RELATION, predicate);
+        Term t = Memory.nameToListedTerm(name);
+        if (t != null)
+            return (EquivalenceAfter) t;
+        ArrayList<Term> argument = argumentsToList(subject, predicate);
+        return new EquivalenceAfter(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.EQUIVALENCE_AFTER_RELATION;
+    }
+    
+    /**
+     * Check if the compound is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return false;
+    }
+
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.AFTER;
+    }
+}
diff --git a/language/EquivalenceWhen.java b/language/EquivalenceWhen.java
new file mode 100644
index 0000000000000000000000000000000000000000..3017bd3b71f65d1023e70dfdc1c1c8df7ab0193a
--- /dev/null
+++ b/language/EquivalenceWhen.java
@@ -0,0 +1,74 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.main.Memory;
+
+/**
+ * Temporal Equivalence relation, concurrent.
+ *
+ */
+public class EquivalenceWhen extends Equivalence {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    public EquivalenceWhen(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private EquivalenceWhen(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a Similarity
+     */
+    public Object clone() {
+        return new EquivalenceWhen(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+     
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or null
+     */
+    public static EquivalenceWhen make(Term subject, Term predicate) {
+        if (invalidStatement(subject, predicate))
+            return null;
+        if (subject.compareTo(predicate) > 0)
+            return make(predicate, subject);
+        String name = makeStatementName(subject, Symbols.EQUIVALENCE_RELATION, predicate);
+        Term t = Memory.nameToListedTerm(name);
+        if (t != null)
+            return (EquivalenceWhen) t;
+        ArrayList<Term> argument = argumentsToList(subject, predicate);
+        return new EquivalenceWhen(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.EQUIVALENCE_WHEN_RELATION;
+    }
+
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.WHEN;
+    }
+}
diff --git a/language/ImageExt.java b/language/ImageExt.java
new file mode 100644
index 0000000000000000000000000000000000000000..ebef845e1271a59f3fd7c96416fa229d16b4a69f
--- /dev/null
+++ b/language/ImageExt.java
@@ -0,0 +1,151 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * An extension image.
+ * <p>
+ * B --> (/,P,A,_)) iff (*,A,B) --> P
+ * <p>
+ * Internally, it is actually (/,A,P)_1, with an index.
+ */
+public class ImageExt extends CompoundTerm {
+
+    /**
+     * The index of relation in the component list.
+     */
+    private short relationIndex;
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     * @param index The index of relation in the component list
+     */
+    private ImageExt(String n, ArrayList<Term> arg, short index) {
+        super(n, arg);
+        relationIndex = index;
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param complexity syntactic complexity of the compound
+     * @param n The name of the term
+     * @param index The index of relation in the component list
+     */
+    private ImageExt(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short complexity, short index) {
+        super(n, cs, open, closed, complexity);
+        relationIndex = index;
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into an ImageExt
+     */
+    public Object clone() {
+        return new ImageExt(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity, relationIndex);
+    }
+
+     /**
+     * Try to make a new ImageExt. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList The list of components
+     */
+    public static Term make(ArrayList<Term> argList) {
+        if (argList.size() < 3)
+            return null;
+        Term t;
+        Term relation = argList.get(0);
+        ArrayList<Term> argument = new ArrayList<Term>();
+        int index = 0;
+        for (int j = 1; j < argList.size(); j++) {
+            if (argList.get(j).getName().charAt(0) == Symbols.IMAGE_PLACE_HOLDER) {
+                index = j-1;
+                argument.add(relation);
+            } else
+                argument.add(argList.get(j));
+        }
+        return make(argument, (short) index);
+    }
+
+    /**
+     * Try to make an Image from a Product and a relation. Called by the inference rules.
+     * @param product The product
+     * @param relation The relation
+     * @param index The index of the place-holder
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Product product, Term relation, short index) {
+        if (relation instanceof Product) {
+            Product p2 = (Product) relation;
+            if ((product.size() == 2) && (p2.size() == 2)) {
+                if ((index == 0) && product.componentAt(1).equals(p2.componentAt(1))) // (/,_,(*,a,b),b) is reduced to a
+                    return p2.componentAt(0);  
+                if ((index == 1) && product.componentAt(0).equals(p2.componentAt(0))) // (/,(*,a,b),a,_) is reduced to b
+                    return p2.componentAt(1);  
+            }
+        }    
+        ArrayList<Term> argument = product.cloneComponents();
+        argument.set(index, relation);
+        return make(argument, index);
+    }
+        
+    /**
+     * Try to make an Image from an existing Image and a component. Called by the inference rules.
+     * @param oldImage The existing Image
+     * @param component The component to be added into the component list
+     * @param index The index of the place-holder in the new Image
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(ImageExt oldImage, Term component, short index) {
+        ArrayList<Term> argList = oldImage.cloneComponents();
+        int oldIndex = oldImage.getRelationIndex();
+        Term relation = argList.get(oldIndex);
+        argList.set(oldIndex, component);
+        argList.set(index, relation);
+        return make(argList, index);
+    }
+    
+    /**
+     * Try to make a new compound from a set of components. Called by the public make methods.
+     * @param argument The argument list
+     * @return the Term generated from the arguments
+     */
+    public static Term make(ArrayList<Term> argument, short index) {
+        String name = makeImageName(Symbols.IMAGE_EXT_OPERATOR, argument, index);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new ImageExt(name, argument, index);
+    }
+    
+    /**
+     * get the index of the relation in the component list
+     * @return the index of relation
+     */
+    public short getRelationIndex() {
+        return relationIndex;
+    }
+    
+    /**
+     * override the default in making the name of the current term from existing fields
+     * @return the name of the term
+     */
+    public String makeName() {
+        return makeImageName(Symbols.IMAGE_EXT_OPERATOR, components, relationIndex);
+    }
+
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.IMAGE_EXT_OPERATOR;
+    }
+}
diff --git a/language/ImageInt.java b/language/ImageInt.java
new file mode 100644
index 0000000000000000000000000000000000000000..313c830ac8dc2aeadcc37157a1a0444dc29e980b
--- /dev/null
+++ b/language/ImageInt.java
@@ -0,0 +1,151 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * An intension image.
+ * <p>
+ * (\,P,A,_)) --> B iff P --> (*,A,B)
+ * <p>
+ * Internally, it is actually (\,A,P)_1, with an index.
+ */
+public class ImageInt extends CompoundTerm {
+
+    /**
+     * The index of relation in the component list.
+     */
+    private short relationIndex;
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     * @param index The index of relation in the component list
+     */
+    private ImageInt(String n, ArrayList<Term> arg, short index) {
+        super(n, arg);
+        relationIndex = index;
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param complexity syntactic complexity of the compound
+     * @param n The name of the term
+     * @param index The index of relation in the component list
+     */
+    private ImageInt(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short complexity, short index) {
+        super(n, cs, open, closed, complexity);
+        relationIndex = index;
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into an ImageInt
+     */
+    public Object clone() {
+        return new ImageInt(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity, relationIndex);
+    }
+
+    /**
+     * Try to make a new ImageExt. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList The list of components
+     */
+    public static Term make(ArrayList<Term> argList) {
+        if (argList.size() < 3)
+            return null;
+        Term t;
+        Term relation = argList.get(0);
+        ArrayList<Term> argument = new ArrayList<Term>();
+        int index = 0;
+        for (int j = 1; j < argList.size(); j++) {
+            if (argList.get(j).getName().charAt(0) == Symbols.IMAGE_PLACE_HOLDER) {
+                index = j-1;
+                argument.add(relation);
+            } else
+                argument.add(argList.get(j));
+        }
+        return make(argument, (short) index);
+    }
+
+    /**
+     * Try to make an Image from a Product and a relation. Called by the inference rules.
+     * @param product The product
+     * @param relation The relation
+     * @param index The index of the place-holder
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Product product, Term relation, short index) {
+        if (relation instanceof Product) {
+            Product p2 = (Product) relation;
+            if ((product.size() == 2) && (p2.size() == 2)) {
+                if ((index == 0) && product.componentAt(1).equals(p2.componentAt(1))) // (\,_,(*,a,b),b) is reduced to a
+                    return p2.componentAt(0);  
+                if ((index == 1) && product.componentAt(0).equals(p2.componentAt(0))) // (\,(*,a,b),a,_) is reduced to b
+                    return p2.componentAt(1);  
+            }
+        }    
+        ArrayList<Term> argument = product.cloneComponents();
+        argument.set(index, relation);
+        return make(argument, index);
+    }
+        
+    /**
+     * Try to make an Image from an existing Image and a component. Called by the inference rules.
+     * @param oldImage The existing Image
+     * @param component The component to be added into the component list
+     * @param index The index of the place-holder in the new Image
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(ImageInt oldImage, Term component, short index) {
+        ArrayList<Term> argList = oldImage.cloneComponents();
+        int oldIndex = oldImage.getRelationIndex();
+        Term relation = argList.get(oldIndex);
+        argList.set(oldIndex, component);
+        argList.set(index, relation);
+        return make(argList, index);
+    }
+    
+    /**
+     * Try to make a new compound from a set of components. Called by the public make methods.
+     * @param argument The argument list
+     * @return the Term generated from the arguments
+     */
+    public static Term make(ArrayList<Term> argument, short index) {
+        String name = makeImageName(Symbols.IMAGE_INT_OPERATOR, argument, index);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new ImageInt(name, argument, index);
+    }
+    
+    /**
+     * get the index of the relation in the component list
+     * @return the index of relation
+     */
+    public short getRelationIndex() {
+        return relationIndex;
+    }
+    
+    /**
+     * override the default in making the name of the current term from existing fields
+     * @return the name of the term
+     */
+    public String makeName() {
+        return makeImageName(Symbols.IMAGE_INT_OPERATOR, components, relationIndex);
+    }
+
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.IMAGE_INT_OPERATOR;
+    }
+}
diff --git a/language/Implication.java b/language/Implication.java
new file mode 100644
index 0000000000000000000000000000000000000000..21569fc4bd1d3b47790ca69cd9faa42e6ee7d389
--- /dev/null
+++ b/language/Implication.java
@@ -0,0 +1,72 @@
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.main.Memory;
+
+/**
+ * A Statement about an Inheritance relation.
+ */
+public class Implication extends Statement {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    protected Implication(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    protected Implication(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new Implication(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+     
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Implication make(Term subject, Term predicate) {  // to be extended to check if subject is Conjunction
+        if (invalidStatement(subject, predicate))
+            return null;
+        String name = makeStatementName(subject, Symbols.IMPLICATION_RELATION, predicate);
+        Term t = Memory.nameToListedTerm(name);
+        if (t != null)
+            return (Implication) t;
+        if (predicate instanceof Implication) {
+            Term oldCondition = ((Implication) predicate).getSubject();
+            Term newCondition = Conjunction.make(subject, oldCondition);
+            return make(newCondition, ((Implication) predicate).getPredicate());
+        } else {
+            ArrayList<Term> argument = argumentsToList(subject, predicate);
+            return new Implication(name, argument);
+        }
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.IMPLICATION_RELATION;
+    }
+}
diff --git a/language/ImplicationAfter.java b/language/ImplicationAfter.java
new file mode 100644
index 0000000000000000000000000000000000000000..f9756f6d6553c36412e6ee590dbbfd34f5f05d3e
--- /dev/null
+++ b/language/ImplicationAfter.java
@@ -0,0 +1,78 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.main.Memory;
+
+/**
+ * Temporal Implication relation, predicate after subject.
+ */
+public class ImplicationAfter extends Implication {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private ImplicationAfter(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private ImplicationAfter(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new ImplicationAfter(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+     
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static ImplicationAfter make(Term subject, Term predicate) {  // to be extended to check if subject is Conjunction
+        if (invalidStatement(subject, predicate))
+            return null;
+        String name = makeStatementName(subject, Symbols.IMPLICATION_AFTER_RELATION, predicate);
+        Term t = Memory.nameToListedTerm(name);
+        if (t != null)
+            return (ImplicationAfter) t;
+        if (predicate instanceof ImplicationAfter) {
+            Term oldCondition = ((ImplicationAfter) predicate).getSubject();
+            Term newCondition = ConjunctionSequence.make(subject, oldCondition);
+            return make(newCondition, ((ImplicationAfter) predicate).getPredicate());
+        } else {
+            ArrayList<Term> argument = argumentsToList(subject, predicate);
+            return new ImplicationAfter(name, argument);
+        }
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.IMPLICATION_AFTER_RELATION;
+    }
+
+    // overwrite default
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.AFTER;
+    }
+}
diff --git a/language/ImplicationBefore.java b/language/ImplicationBefore.java
new file mode 100644
index 0000000000000000000000000000000000000000..5841133fe56ff3ebab8610a1cf42b012dd9704d3
--- /dev/null
+++ b/language/ImplicationBefore.java
@@ -0,0 +1,78 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.main.Memory;
+
+/**
+ * Temporal Implication relation, predicate before subject.
+ */
+public class ImplicationBefore extends Implication {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private ImplicationBefore(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private ImplicationBefore(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new ImplicationBefore(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+     
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static ImplicationBefore make(Term subject, Term predicate) {  // to be extended to check if subject is Conjunction
+        if (invalidStatement(subject, predicate))
+            return null;
+        String name = makeStatementName(subject, Symbols.IMPLICATION_BEFORE_RELATION, predicate);
+        Term t = Memory.nameToListedTerm(name);
+        if (t != null)
+            return (ImplicationBefore) t;
+        if (predicate instanceof ImplicationBefore) {
+            Term oldCondition = ((ImplicationBefore) predicate).getSubject();
+            Term newCondition = ConjunctionSequence.make(oldCondition, subject);
+            return make(newCondition, ((ImplicationBefore) predicate).getPredicate());
+        } else {
+            ArrayList<Term> argument = argumentsToList(subject, predicate);
+            return new ImplicationBefore(name, argument);
+        }
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.IMPLICATION_BEFORE_RELATION;
+    }
+
+    // overwrite default
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.BEFORE;
+    }
+}
diff --git a/language/ImplicationWhen.java b/language/ImplicationWhen.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b0ce8f2213ba5573940ab64fc6c1e30e4483ad1
--- /dev/null
+++ b/language/ImplicationWhen.java
@@ -0,0 +1,78 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.main.Memory;
+
+/**
+ * Temporal Implication relation, concurrent.
+ */
+
+public class ImplicationWhen extends Implication {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    protected ImplicationWhen(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    protected ImplicationWhen(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new ImplicationWhen(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+     
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static ImplicationWhen make(Term subject, Term predicate) {  // to be extended to check if subject is Conjunction
+        if (invalidStatement(subject, predicate))
+            return null;
+        String name = makeStatementName(subject, Symbols.IMPLICATION_RELATION, predicate);
+        Term t = Memory.nameToListedTerm(name);
+        if (t != null)
+            return (ImplicationWhen) t;
+        if (predicate instanceof ImplicationWhen) {
+            Term oldCondition = ((ImplicationWhen) predicate).getSubject();
+            Term newCondition = ConjunctionParallel.make(subject, oldCondition);
+            return make(newCondition, ((ImplicationWhen) predicate).getPredicate());
+        } else {
+            ArrayList<Term> argument = argumentsToList(subject, predicate);
+            return new ImplicationWhen(name, argument);
+        }
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.IMPLICATION_WHEN_RELATION;
+    }
+
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.WHEN;
+    }
+}
diff --git a/language/Inheritance.java b/language/Inheritance.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab8698b2338a9488d8bcbfc4ceb3f2605c2fca8a
--- /dev/null
+++ b/language/Inheritance.java
@@ -0,0 +1,69 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * A Statement about an Inheritance relation.
+ */
+public class Inheritance extends Statement {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private Inheritance(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private Inheritance(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new Inheritance(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+     
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or null
+     */
+    public static Inheritance make(Term subject, Term predicate) {
+        if (invalidStatement(subject, predicate))
+            return null;
+        String name = makeStatementName(subject, Symbols.INHERITANCE_RELATION, predicate);
+        Term t = Memory.nameToListedTerm(name);
+        if (t != null)
+            return (Inheritance) t;
+        ArrayList<Term> argument = argumentsToList(subject, predicate);
+        return new Inheritance(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.INHERITANCE_RELATION;
+    }
+}
+
diff --git a/language/Instance.java b/language/Instance.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d4b0a0422116152fbb5c2172f86ccf80cf5d404
--- /dev/null
+++ b/language/Instance.java
@@ -0,0 +1,21 @@
+
+package nars.language;
+
+/**
+ * A Statement about an Instance relation, which is used only in Narsese for I/O, 
+ * and translated into Inheritance for internal use.
+ */
+public abstract class Instance extends Statement {
+    
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * <p>
+     *  A {-- B becomes {A} --> B
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or null
+     */
+    public static Statement make(Term subject, Term predicate) {
+        return Inheritance.make(SetExt.make(subject), predicate);
+    }
+}
diff --git a/language/InstanceProperty.java b/language/InstanceProperty.java
new file mode 100644
index 0000000000000000000000000000000000000000..506315a9774094eac31569a7f2fe382b3abbc617
--- /dev/null
+++ b/language/InstanceProperty.java
@@ -0,0 +1,21 @@
+
+package nars.language;
+
+/**
+ * A Statement about an InstanceProperty relation, which is used only in Narsese for I/O, 
+ * and translated into Inheritance for internal use.
+ */
+public abstract class InstanceProperty extends Statement {
+    
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * <p>
+     *  A {-] B becomes {A} --> [B]
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or null
+     */
+    public static Statement make(Term subject, Term predicate) {
+        return Inheritance.make(SetExt.make(subject), SetInt.make(predicate));
+    }
+}
diff --git a/language/IntersectionExt.java b/language/IntersectionExt.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca5c4ce4c352ad11d7674b900e07f4e4fc1c5824
--- /dev/null
+++ b/language/IntersectionExt.java
@@ -0,0 +1,118 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * A compound term whose extension is the intersection of the extensions of its components
+ */
+public class IntersectionExt extends CompoundTerm {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private IntersectionExt(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private IntersectionExt(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a IntersectionExt
+     */
+    public Object clone() {
+        return new IntersectionExt(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param term1 The first compoment
+     * @param term2 The first compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term term1, Term term2) {
+        TreeSet set;
+        if ((term1 instanceof SetInt) && (term2 instanceof SetInt)) {
+            set = new TreeSet(((CompoundTerm) term1).cloneComponents());
+            set.addAll(((CompoundTerm) term2).cloneComponents());           // set union
+            return SetInt.make(set);
+        }
+        if ((term1 instanceof SetExt) && (term2 instanceof SetExt)) {
+            set = new TreeSet(((CompoundTerm) term1).cloneComponents());
+            set.retainAll(((CompoundTerm) term2).cloneComponents());        // set intersection
+            return SetExt.make(set);
+        }
+        if (term1 instanceof IntersectionExt) {
+            set = new TreeSet(((CompoundTerm) term1).cloneComponents());
+            if (term2 instanceof IntersectionExt)
+                set.addAll(((CompoundTerm) term2).cloneComponents());   // (&,(&,P,Q),(&,R,S)) = (&,P,Q,R,S)
+            else
+                set.add((Term) term2.clone());                          // (&,(&,P,Q),R) = (&,P,Q,R)
+        } else if (term2 instanceof IntersectionExt) {
+            set = new TreeSet(((CompoundTerm) term2).cloneComponents());
+            set.add((Term) term1.clone());                              // (&,R,(&,P,Q)) = (&,P,Q,R)
+        } else {
+            set = new TreeSet();
+            set.add((Term) term1.clone());
+            set.add((Term) term2.clone());
+        }
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new IntersectionExt. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList The list of components
+     */
+    public static Term make(ArrayList<Term> argList) {
+        TreeSet<Term> set = new TreeSet<Term>(argList); // sort/merge arguments
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new compound from a set of components. Called by the public make methods.
+     * @param set a set of Term as compoments
+     * @return the Term generated from the arguments
+     */
+    public static Term make(TreeSet<Term> set) {
+        if (set.size() == 1)
+            return set.first();                         // special case: single component
+        ArrayList<Term> argument = new ArrayList<Term>(set);
+        String name = makeCompoundName(Symbols.INTERSECTION_EXT_OPERATOR, argument);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new IntersectionExt(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.INTERSECTION_EXT_OPERATOR;
+    }
+    
+    /**
+     * Check if the compound is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return true;
+    }
+}
diff --git a/language/IntersectionInt.java b/language/IntersectionInt.java
new file mode 100644
index 0000000000000000000000000000000000000000..065b283f6d88f8a8506c5fde904dad6a372797c4
--- /dev/null
+++ b/language/IntersectionInt.java
@@ -0,0 +1,118 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * A compound term whose intension is the intersection of the extensions of its components
+ */
+public class IntersectionInt extends CompoundTerm {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private IntersectionInt(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private IntersectionInt(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a Conjunction
+     */
+    public Object clone() {
+        return new IntersectionInt(name, (ArrayList<Term>) cloneList(components), 
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param term1 The first compoment
+     * @param term2 The first compoment
+     * @return A compound generated or a term it reduced to
+     */    
+    public static Term make(Term term1, Term term2) {
+        TreeSet set;
+        if ((term1 instanceof SetExt) && (term2 instanceof SetExt)) {
+            set = new TreeSet(((CompoundTerm) term1).cloneComponents());
+            set.addAll(((CompoundTerm) term2).cloneComponents());           // set union
+            return SetExt.make(set);
+        }
+        if ((term1 instanceof SetInt) && (term2 instanceof SetInt)) {
+            set = new TreeSet(((CompoundTerm) term1).cloneComponents());
+            set.retainAll(((CompoundTerm) term2).cloneComponents());        // set intersection
+            return SetInt.make(set);
+        }
+        if (term1 instanceof IntersectionInt) {
+            set = new TreeSet(((CompoundTerm) term1).cloneComponents());
+            if (term2 instanceof IntersectionInt)
+                set.addAll(((CompoundTerm) term2).cloneComponents());   // (|,(|,P,Q),(|,R,S)) = (|,P,Q,R,S)
+            else
+                set.add((Term) term2.clone());                          // (|,(|,P,Q),R) = (|,P,Q,R)
+        } else if (term2 instanceof IntersectionInt) {
+            set = new TreeSet(((CompoundTerm) term2).cloneComponents());
+            set.add((Term) term1.clone());                              // (|,R,(|,P,Q)) = (|,P,Q,R)
+        } else {
+            set = new TreeSet();
+            set.add((Term) term1.clone());
+            set.add((Term) term2.clone());
+        }
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new IntersectionExt. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList The list of components
+     */
+    public static Term make(ArrayList<Term> argList) {
+        TreeSet<Term> set = new TreeSet<Term>(argList); // sort/merge arguments
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new compound from a set of components. Called by the public make methods.
+     * @param set a set of Term as compoments
+     * @return the Term generated from the arguments
+     */
+    public static Term make(TreeSet<Term> set) {
+        if (set.size() == 1)
+            return set.first();                         // special case: single component
+        ArrayList<Term> argument = new ArrayList<Term>(set);
+        String name = makeCompoundName(Symbols.INTERSECTION_INT_OPERATOR, argument);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new IntersectionInt(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.INTERSECTION_INT_OPERATOR;
+    }
+
+    /**
+     * Check if the compound is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return true;
+    }
+}
diff --git a/language/Negation.java b/language/Negation.java
new file mode 100644
index 0000000000000000000000000000000000000000..2119f3a250a1c26835558e59d18a0a6ff2abda8b
--- /dev/null
+++ b/language/Negation.java
@@ -0,0 +1,76 @@
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * A negation of a Statement.
+ */
+public class Negation extends CompoundTerm {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private Negation(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private Negation(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new Negation(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a Negation of one component. Called by the inference rules.
+     * @param t The compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term t) {
+        if (t instanceof Negation)
+            return (Term) ((CompoundTerm) t).cloneComponents().get(0);         // (--,(--,P)) = P
+        ArrayList<Term> argument = new ArrayList<Term>();
+        argument.add(t);
+        return make(argument);
+    }
+    
+    /**
+     * Try to make a new SetExt. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argument The list of components
+     */
+    public static Term make(ArrayList<Term> argument) {
+        if (argument.size() != 1)
+            return null;
+        String name = makeCompoundName(Symbols.NEGATION_OPERATOR, argument);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new Negation(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.NEGATION_OPERATOR;
+    }
+}
diff --git a/language/Product.java b/language/Product.java
new file mode 100644
index 0000000000000000000000000000000000000000..85d225681050bfb9f5509f2f452cc8bc84b969cd
--- /dev/null
+++ b/language/Product.java
@@ -0,0 +1,78 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * A Product is a sequence of terms.
+ */
+public class Product extends CompoundTerm {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private Product(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param complexity syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private Product(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short complexity) {
+        super(n, cs, open, closed, complexity);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into an ImageExt
+     */
+    public Object clone() {
+        return new Product(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+
+     /**
+     * Try to make a new compound. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argument The list of components
+     */
+    public static Term make(ArrayList<Term> argument) {
+        if (argument.size() < 2)
+            return null;
+        String name = makeCompoundName(Symbols.PRODUCT_OPERATOR, argument);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new Product(name, argument);
+    }
+        
+    /**
+     * Try to make a Product from an ImageExt/ImageInt and a component. Called by the inference rules.
+     * @param image The existing Image
+     * @param component The component to be added into the component list
+     * @param index The index of the place-holder in the new Image -- optional parameter
+     * @return A compound generated or a term it reduced to
+     */
+    // for both 
+    public static Term make(CompoundTerm image, Term component, int index) {
+        ArrayList<Term> argument = image.cloneComponents();
+        argument.set(index, component);
+        return make(argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.PRODUCT_OPERATOR;
+    }
+}
diff --git a/language/Property.java b/language/Property.java
new file mode 100644
index 0000000000000000000000000000000000000000..625582fba92fa864877ac4f44dc272c1bdc83720
--- /dev/null
+++ b/language/Property.java
@@ -0,0 +1,21 @@
+
+package nars.language;
+
+/**
+ * A Statement about a Property relation, which is used only in Narsese for I/O, 
+ * and translated into Inheritance for internal use.
+ */
+public abstract class Property extends Statement {
+    
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * <p>
+     *  A --] B becomes A --> [B]
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or null
+     */
+    public static Statement make(Term subject, Term predicate) {
+        return Inheritance.make(subject, SetInt.make(predicate));
+    }
+}
diff --git a/language/SetExt.java b/language/SetExt.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b018d64f53e9641d8fc5a81928c8c206d5b3d6d
--- /dev/null
+++ b/language/SetExt.java
@@ -0,0 +1,103 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * An extensionally defined set, which contains one or more instances defining the Term.
+ */
+public class SetExt extends CompoundTerm {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private SetExt(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private SetExt(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new SetExt(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new set from one component. Called by the inference rules.
+     * @param t The compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term t) {
+        TreeSet<Term> set = new TreeSet<Term>();
+        set.add(t);
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new SetExt. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList The list of components
+     */
+    public static Term make(ArrayList<Term> argList) {
+        TreeSet<Term> set = new TreeSet<Term>(argList); // sort/merge arguments
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new compound from a set of components. Called by the public make methods.
+     * @param set a set of Term as compoments
+     * @return the Term generated from the arguments
+     */
+    public static Term make(TreeSet<Term> set) {
+        if (set.isEmpty())
+            return null;
+        ArrayList<Term> argument = new ArrayList<Term>(set);
+        String name = makeSetName(Symbols.SET_EXT_OPENER, argument, Symbols.SET_EXT_CLOSER);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new SetExt(name, argument);
+    }
+
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return "" + Symbols.SET_EXT_OPENER;
+    }
+    
+    /**
+     * Check if the compound is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return true;
+    }
+
+    /**
+     * Make a String representation of the set, override the default.
+     * @return true for communitative
+     */
+    public String makeName() {
+        return makeSetName(Symbols.SET_EXT_OPENER, components, Symbols.SET_EXT_CLOSER);
+    }
+}
+
diff --git a/language/SetInt.java b/language/SetInt.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7292ad803414af871077def8d7a3d7f840fc6e4
--- /dev/null
+++ b/language/SetInt.java
@@ -0,0 +1,103 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * An intensionally defined set, which contains one or more instances defining the Term.
+ */
+public class SetInt extends CompoundTerm {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private SetInt(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private SetInt(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetInt
+     */
+    public Object clone() {
+        return new SetInt(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new set from one component. Called by the inference rules.
+     * @param t The compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term t) {
+        TreeSet<Term> set = new TreeSet<Term>();
+        set.add(t);
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new SetExt. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argList The list of components
+     */
+    public static Term make(ArrayList<Term> argList) {
+        TreeSet<Term> set = new TreeSet<Term>(argList); // sort/merge arguments
+        return make(set);
+    }
+    
+    /**
+     * Try to make a new compound from a set of components. Called by the public make methods.
+     * @param set a set of Term as compoments
+     * @return the Term generated from the arguments
+     */
+    public static Term make(TreeSet<Term> set) {
+        if (set.isEmpty())
+            return null;
+        ArrayList<Term> argument = new ArrayList<Term>(set);
+        String name = makeSetName(Symbols.SET_INT_OPENER, argument, Symbols.SET_INT_CLOSER);
+        Term t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new SetInt(name, argument);
+    }
+
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return "" + Symbols.SET_INT_OPENER;
+    }
+    
+    /**
+     * Check if the compound is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return true;
+    }
+
+    /**
+     * Make a String representation of the set, override the default.
+     * @return true for communitative
+     */
+    public String makeName() {
+        return makeSetName(Symbols.SET_INT_OPENER, components, Symbols.SET_INT_CLOSER);
+    }
+}
+
diff --git a/language/Similarity.java b/language/Similarity.java
new file mode 100644
index 0000000000000000000000000000000000000000..10c3b1eea5e4ce422ce7d752fd80a015cae85fb7
--- /dev/null
+++ b/language/Similarity.java
@@ -0,0 +1,78 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.entity.TermLink;
+import nars.main.Memory;
+
+/**
+ * A Statement about a Similarity relation.
+ */
+public class Similarity extends Statement {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private Similarity(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private Similarity(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a Similarity
+     */
+    public Object clone() {
+        return new Similarity(name, (ArrayList<Term>) cloneList(components), 
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+     
+    /**
+     * Try to make a new compound from two components. Called by the inference rules.
+     * @param subject The first compoment
+     * @param predicate The second compoment
+     * @return A compound generated or null
+     */
+    public static Similarity make(Term subject, Term predicate) {
+        if (invalidStatement(subject, predicate))
+            return null;
+        if (subject.compareTo(predicate) > 0)
+            return make(predicate, subject);
+        String name = makeStatementName(subject, Symbols.SIMILARITY_RELATION, predicate);
+        Term t = Memory.nameToListedTerm(name);
+        if (t != null)
+            return (Similarity) t;
+        ArrayList<Term> argument = argumentsToList(subject, predicate);
+        return new Similarity(name, argument);
+    }
+    
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.SIMILARITY_RELATION;
+    }
+    
+    /**
+     * Check if the compound is communitative.
+     * @return true for communitative
+     */
+    public boolean isCommutative() {
+        return true;
+    }
+}
diff --git a/language/Statement.java b/language/Statement.java
new file mode 100644
index 0000000000000000000000000000000000000000..560b45d9da14dca30fbaaa8317e645133e75d6e2
--- /dev/null
+++ b/language/Statement.java
@@ -0,0 +1,259 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.*;
+
+/**
+ * A statement is a compound term, consisting of a subject, a predicate,
+ * and a relation symbol in between. It can be of either first-order or higher-order.
+ */
+public abstract class Statement extends CompoundTerm {
+    
+    /**
+     * default constructor
+     */
+    protected Statement() {}
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    protected Statement(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    protected Statement(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * Make a Statement from String, called by StringParser
+     * @param relation The relation String
+     * @param subject The first component
+     * @param predicate The second component
+     * @return The Statement built
+     */
+    public static Statement make(String relation, Term subject, Term predicate) {
+        if (invalidStatement(subject, predicate))
+            return null;
+        if (relation.equals(Symbols.INHERITANCE_RELATION))
+            return Inheritance.make(subject, predicate);
+        if (relation.equals(Symbols.SIMILARITY_RELATION))
+            return Similarity.make(subject, predicate);
+        if (relation.equals(Symbols.INSTANCE_RELATION))
+            return Instance.make(subject, predicate);
+        if (relation.equals(Symbols.PROPERTY_RELATION))
+            return Property.make(subject, predicate);
+        if (relation.equals(Symbols.INSTANCE_PROPERTY_RELATION))
+            return InstanceProperty.make(subject, predicate);
+        if (relation.equals(Symbols.IMPLICATION_RELATION))
+            return Implication.make(subject, predicate);
+        if (relation.equals(Symbols.EQUIVALENCE_RELATION))
+            return Equivalence.make(subject, predicate);
+        if (relation.equals(Symbols.IMPLICATION_AFTER_RELATION))
+            return ImplicationAfter.make(subject, predicate);
+        if (relation.equals(Symbols.IMPLICATION_WHEN_RELATION))
+            return ImplicationWhen.make(subject, predicate);
+        if (relation.equals(Symbols.IMPLICATION_BEFORE_RELATION))
+            return ImplicationBefore.make(subject, predicate);
+        if (relation.equals(Symbols.EQUIVALENCE_AFTER_RELATION))
+            return EquivalenceAfter.make(subject, predicate);
+        if (relation.equals(Symbols.EQUIVALENCE_WHEN_RELATION))
+            return EquivalenceWhen.make(subject, predicate);
+        return null;
+    }
+    
+    /**
+     * Make a Statement from given components, called by the rules
+     * @return The Statement built
+     * @param sub The first component
+     * @param pred The second component
+     * @param statement A sample statement providing the class type
+     */
+    public static Statement make(Statement statement, Term sub, Term pred) {
+        if (statement instanceof Inheritance)
+            return Inheritance.make(sub, pred);
+        if (statement instanceof Similarity)
+            return Similarity.make(sub, pred);
+        if (statement instanceof ImplicationBefore)
+            return ImplicationBefore.make(sub, pred);
+        if (statement instanceof ImplicationWhen)
+            return ImplicationWhen.make(sub, pred);
+        if (statement instanceof ImplicationAfter)
+            return ImplicationAfter.make(sub, pred);
+        if (statement instanceof Implication)
+            return Implication.make(sub, pred);
+        if (statement instanceof EquivalenceWhen)
+            return EquivalenceWhen.make(sub, pred);
+        if (statement instanceof EquivalenceAfter)
+            return EquivalenceAfter.make(sub, pred);
+        if (statement instanceof Equivalence)
+            return Equivalence.make(sub, pred);
+        return null;
+    }
+    
+    /**
+     * Make a Statement from given components and temporal information, called by the rules
+     * @param statement A sample statement providing the class type
+     * @param sub The first component
+     * @param pred The second component
+     * @param order The temporal order of the statement
+     * @return The Statement built
+     */
+    public static Statement make(Statement statement, Term sub, Term pred, CompoundTerm.TemporalOrder order) {
+        if (order == CompoundTerm.TemporalOrder.UNSURE)
+            return null;
+        if (order == CompoundTerm.TemporalOrder.NONE)
+            return make(statement, sub, pred);
+        if (order == CompoundTerm.TemporalOrder.AFTER) {
+            if (statement instanceof Implication)
+                return ImplicationAfter.make(sub, pred);
+            if (statement instanceof Equivalence)
+                return EquivalenceAfter.make(sub, pred);
+            return null;
+        }
+        if (order == CompoundTerm.TemporalOrder.WHEN) {
+            if (statement instanceof Implication)
+                return ImplicationWhen.make(sub, pred);
+            if (statement instanceof Equivalence)
+                return EquivalenceWhen.make(sub, pred);
+            return null;
+        }
+        if (order == CompoundTerm.TemporalOrder.BEFORE) {
+            if (statement instanceof Implication)
+                return ImplicationBefore.make(sub, pred);
+            if (statement instanceof Equivalence)
+                return EquivalenceAfter.make(pred, sub);
+            return null;
+        }
+        return null;
+    }
+    
+    /**
+     * Make a symmetric Statement from given components and temporal information, called by the rules
+     * @param statement A sample asymmetric statement providing the class type
+     * @param sub The first component
+     * @param pred The second component
+     * @param order The temporal order of the statement
+     * @return The Statement built
+     */
+    public static Statement makeSym(Statement statement, Term sub, Term pred, CompoundTerm.TemporalOrder order) {
+        if (order == CompoundTerm.TemporalOrder.UNSURE)
+            return null;
+        if (order == CompoundTerm.TemporalOrder.NONE) {
+            if (statement instanceof Inheritance)
+                return Similarity.make(sub, pred);
+            if (statement instanceof Implication)
+                return Equivalence.make(sub, pred);
+            return null;
+        }
+        if (order == CompoundTerm.TemporalOrder.AFTER) {
+            if (statement instanceof Implication)
+                return EquivalenceAfter.make(sub, pred);
+            return null;
+        }
+        if (order == CompoundTerm.TemporalOrder.WHEN) {
+            if (statement instanceof Implication)
+                return EquivalenceWhen.make(sub, pred);
+            return null;
+        }
+        if (order == CompoundTerm.TemporalOrder.BEFORE) {
+            if (statement instanceof Implication)
+                return EquivalenceAfter.make(pred, sub);
+            return null;
+        }
+        return null;
+    }
+    
+    /**
+     * check Statement relation symbol
+     * @return if the given String is a relation symbol
+     * @param s0 The String to be checked
+     */
+    public static boolean isRelation(String s0) {
+        String s = s0.trim();
+        if (s.length() != 3)
+            return false;
+        return (s.equals(Symbols.INHERITANCE_RELATION) ||
+                s.equals(Symbols.SIMILARITY_RELATION) ||
+                s.equals(Symbols.INSTANCE_RELATION) ||
+                s.equals(Symbols.PROPERTY_RELATION) ||
+                s.equals(Symbols.INSTANCE_PROPERTY_RELATION) ||
+                s.equals(Symbols.IMPLICATION_RELATION) ||
+                s.equals(Symbols.EQUIVALENCE_RELATION) ||
+                s.equals(Symbols.IMPLICATION_AFTER_RELATION) ||
+                s.equals(Symbols.IMPLICATION_WHEN_RELATION) ||
+                s.equals(Symbols.IMPLICATION_BEFORE_RELATION) ||
+                s.equals(Symbols.EQUIVALENCE_WHEN_RELATION) ||
+                s.equals(Symbols.EQUIVALENCE_AFTER_RELATION) );
+    }
+    
+    /**
+     * override the default in making the name of the current term from existing fields
+     * @return the name of the term
+     */
+    protected String makeName() {
+        return makeStatementName(getSubject(), operator(), getPredicate());
+    }
+    
+    /**
+     * default method to make the name of an image term from given fields
+     * @param subject the first component
+     * @param predicate the second component
+     * @param relation the relation operator
+     * @return the name of the term
+     */
+    protected static String makeStatementName(Term subject, String relation, Term predicate) {
+        StringBuffer name = new StringBuffer();
+        name.append(Symbols.STATEMENT_OPENER);
+        name.append(subject.getName());
+        name.append(' ' + relation + ' ');
+        name.append(predicate.getName());
+        name.append(Symbols.STATEMENT_CLOSER);
+        return name.toString();
+    }
+    
+    /**
+     * check the validity of a potential Statement.
+     * <p>
+     * Minimum requirement: the two terms cannot be the same. To be strengthened.
+     * @param subject the first component
+     * @param predicate the second component
+     * @return Whether the Statement is invalid
+     */
+    public static boolean invalidStatement(Term subject, Term predicate) {
+        if (subject.equals(predicate))
+            return true;
+        if ((subject instanceof CompoundTerm) && ((CompoundTerm) subject).containComponent(predicate))
+            return true;
+        if ((predicate instanceof CompoundTerm) && ((CompoundTerm) predicate).containComponent(subject))
+            return true;
+        return false;
+    }
+    
+    /**
+     * Return the first component of the statement
+     * @return The first component
+     */
+    public Term getSubject() {
+        return components.get(0);
+    }
+        
+    /**
+     * Return the second component of the statement
+     * @return The second component
+     */
+    public Term getPredicate() {
+        return components.get(1);
+    }
+}
diff --git a/language/Tense.java b/language/Tense.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef385b7a08b6590f63a6f80675adef593f898a00
--- /dev/null
+++ b/language/Tense.java
@@ -0,0 +1,31 @@
+
+package nars.language;
+
+import java.util.*;
+
+/**
+ * Term with temporal relation with "now"
+ */
+public abstract class Tense extends CompoundTerm {
+    
+    protected Tense(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+
+    protected Tense(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+
+    public static Term make(Term content, CompoundTerm.TemporalOrder order) {
+        switch (order) {
+            case AFTER:
+                return TenseFuture.make(content);
+            case WHEN:
+                return TensePresent.make(content);
+            case BEFORE:
+                return TensePast.make(content);
+            default:
+                return content;
+        }
+    }
+}
diff --git a/language/TenseFuture.java b/language/TenseFuture.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3fbf3b7388d929d9539fa9731a447411bc679ce
--- /dev/null
+++ b/language/TenseFuture.java
@@ -0,0 +1,85 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.*;
+import nars.main.Memory;
+
+/**
+ * Future tense of a Statement.
+ */
+public class TenseFuture extends Tense {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private TenseFuture(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private TenseFuture(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new TenseFuture(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new compound. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argument The list of components
+     */
+    public static Term make(ArrayList<Term> argument) {
+        if (argument.size() != 1) 
+            return null;
+        Term t = argument.get(0);
+        if (t instanceof TenseFuture)
+            return t;         
+        if (t instanceof TensePast)
+            return null;         
+        if (t instanceof TensePresent)
+            t = ((CompoundTerm) t).componentAt(0);         
+        String name = makeCompoundName(Symbols.FUTURE_OPERATOR, argument);
+        t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new TenseFuture(name, argument);
+    }
+    
+    /**
+     * Try to make a compound of one component. Called by the inference rules.
+     * @param t The compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term t) {
+        ArrayList<Term> argument = new ArrayList<Term>();
+        argument.add(t);
+        return make(argument);
+    }
+
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.FUTURE_OPERATOR;
+    }
+
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.AFTER;
+    }
+}
\ No newline at end of file
diff --git a/language/TensePast.java b/language/TensePast.java
new file mode 100644
index 0000000000000000000000000000000000000000..135fdf3383ef70591230bfdc32b7e2efd5382830
--- /dev/null
+++ b/language/TensePast.java
@@ -0,0 +1,85 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.*;
+import nars.main.Memory;
+
+/**
+ * Past tense of a Statement.
+ */
+public class TensePast extends Tense {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private TensePast(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private TensePast(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new TensePast(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new compound. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argument The list of components
+     */
+    public static Term make(ArrayList<Term> argument) {
+        if (argument.size() != 1) 
+            return null;
+        Term t = argument.get(0);
+        if (t instanceof TensePast)
+            return t;         
+        if (t instanceof TenseFuture)
+            return null;         
+        if (t instanceof TensePresent)
+            t = ((CompoundTerm) t).componentAt(0);         
+        String name = makeCompoundName(Symbols.PAST_OPERATOR, argument);
+        t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new TensePast(name, argument);
+    }
+    
+    /**
+     * Try to make a compound of one component. Called by the inference rules.
+     * @param t The compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term t) {
+        ArrayList<Term> argument = new ArrayList<Term>();
+        argument.add(t);
+        return make(argument);
+    }
+
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.PAST_OPERATOR;
+    }
+
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.BEFORE;
+    }
+}
diff --git a/language/TensePresent.java b/language/TensePresent.java
new file mode 100644
index 0000000000000000000000000000000000000000..64dc86e802114f28726490b92df2edd194054b28
--- /dev/null
+++ b/language/TensePresent.java
@@ -0,0 +1,81 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.*;
+import nars.main.Memory;
+
+/**
+ * Present tense of a Statement.
+ */
+public class TensePresent extends Tense {
+    
+    /**
+     * constructor with partial values, called by make
+     * @param n The name of the term
+     * @param arg The component list of the term
+     */
+    private TensePresent(String n, ArrayList<Term> arg) {
+        super(n, arg);
+    }
+    
+    /**
+     * constructor with full values, called by clone
+     * @param cs component list
+     * @param open open variable list
+     * @param closed closed variable list
+     * @param i syntactic complexity of the compound
+     * @param n The name of the term
+     */
+    private TensePresent(String n, ArrayList<Term> cs, ArrayList<Variable> open, ArrayList<Variable> closed, short i) {
+        super(n, cs, open, closed, i);
+    }
+    
+    /**
+     * override the cloning methed in Object
+     * @return A new object, to be casted into a SetExt
+     */
+    public Object clone() {
+        return new TensePresent(name, (ArrayList<Term>) cloneList(components),
+                (ArrayList<Variable>) cloneList(openVariables), (ArrayList<Variable>) cloneList(closedVariables), complexity);
+    }
+    
+    /**
+     * Try to make a new compound. Called by StringParser.
+     * @return the Term generated from the arguments
+     * @param argument The list of components
+     */
+    public static Term make(ArrayList<Term> argument) {
+        if (argument.size() != 1) 
+            return null;
+        Term t = argument.get(0);
+        if ((t instanceof TensePresent) || (t instanceof TensePast) || (t instanceof TenseFuture))
+            return t;         
+        String name = makeCompoundName(Symbols.PRESENT_OPERATOR, argument);
+        t = Memory.nameToListedTerm(name);
+        return (t != null) ? t : new TensePresent(name, argument);
+    }
+    
+    /**
+     * Try to make a compound of one component. Called by the inference rules.
+     * @param t The compoment
+     * @return A compound generated or a term it reduced to
+     */
+    public static Term make(Term t) {
+        ArrayList<Term> argument = new ArrayList<Term>();
+        argument.add(t);
+        return make(argument);
+    }
+
+    /**
+     * get the operator of the term.
+     * @return the operator of the term
+     */
+    public String operator() {
+        return Symbols.PRESENT_OPERATOR;
+    }
+
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.WHEN;
+    }
+}
diff --git a/language/Term.java b/language/Term.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ab0a5f8893eea4fc107be8df8d5cd9f8b074f63
--- /dev/null
+++ b/language/Term.java
@@ -0,0 +1,108 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+import nars.main.Memory;
+import nars.inference.SyllogisticRules;
+
+/**
+ * Term is the basic component of Narsese, and the object of processing in NARS.
+ * <p>
+ * A Term may or may not have an associated Concept containing relations with other Terms. It
+ * is not linked in the Term, because a Concept may be forgot, while the Term exists.
+ */
+public class Term implements Cloneable, Comparable<Term> {
+    /**
+     * A Term is identified uniquely by its name, a sequence of characters in a given alphabet.
+     */
+    protected String name;              // name of the term, an ASCII string (can be changed to Unicode)
+    
+    /**
+     * Default constructor
+     */
+    protected Term() {}
+    
+    /**
+     * Constructor with a given name
+     * @param name A String as the name of the Term
+     */
+    public Term(String name) {
+        this.name = name;
+    }
+    
+    /**
+     * The same as getName, used in display.
+     * @return The name of the term as a String
+     */
+    public final String toString() {
+        return name;
+    }
+    
+    /**
+     * Reporting the name of the current Term.
+     * @return The name of the term as a String
+     */
+    public String getName() {
+        return name;
+    }
+    
+    /**
+     * Default, to be overrided in variable Terms.
+     * @return The name of the term as a String
+     */
+    public String getConstantName() {
+        return name;
+    }
+    
+    /**
+     * Make a new Term with the same name.
+     * @return The new Term
+     */
+    public Object clone() {
+        return new Term(name);
+    }
+    
+    /**
+     * Equal terms have identical name, though not necessarily the same reference.
+     * @return Whether the two Terms are equal
+     * @param that The Term to be compared with the current Term
+     */
+    public boolean equals(Object that) {
+        return (that instanceof Term) && getName().equals(((Term) that).getName());
+    }
+    
+    /**
+     * The default complexity, for constant automic Term, is 1.
+     * @return The conplexity of the term, an integer
+     */
+    public int getComplexity() {
+        return 1;
+    }
+        
+    /**
+     * Check the relative order of two Terms.
+     * @param that The Term to be compared with the current Term
+     * @return The same as compareTo as defined on Strings when the constant parts are compared
+     */
+    public final int compareTo(Term that) {
+        int i = this.getConstantName().compareTo(that.getConstantName());       // based on the constant part first
+        return (i != 0) ? i : this.getName().compareTo(that.getName());
+    }
+    
+    /**
+     * Check whether the current Term can name a Concept.
+     * @return a Term is constant by default
+     */
+    public boolean isConstant() {
+        return true;
+    }
+    
+    public CompoundTerm.TemporalOrder getTemporalOrder() {
+        return CompoundTerm.TemporalOrder.NONE;
+    }
+    
+    public final boolean containQueryVariable() {                           // to be revised
+        return (name.indexOf(Symbols.QUERY_VARIABLE_TAG) >= 0);
+    }
+}
diff --git a/language/Variable.java b/language/Variable.java
new file mode 100644
index 0000000000000000000000000000000000000000..c830665fe31423cd42885f9f8a70bc293cdf9930
--- /dev/null
+++ b/language/Variable.java
@@ -0,0 +1,213 @@
+
+package nars.language;
+
+import java.util.*;
+import nars.io.Symbols;
+
+/**
+ * A variable term.
+ */
+public class Variable extends Term {
+    public enum VarType { INDEPENDENT, DEPENDENT, ANONYMOUS, QUERY }
+    
+    private CompoundTerm scope;
+    private VarType type;
+    
+    public Variable() {
+        super();
+    }
+    
+    public Variable(String s) {
+        name = s;
+        scope = null;
+        char prefix = s.charAt(0);
+        if (prefix == Symbols.QUERY_VARIABLE_TAG)
+            type = VarType.QUERY;
+        else if (s.length() == 1)
+            type = VarType.ANONYMOUS;
+        else if (s.charAt(s.length()-1) == Symbols.COMPOUND_TERM_CLOSER) {
+            type = VarType.DEPENDENT;
+            int i = s.indexOf(Symbols.COMPOUND_TERM_OPENER);
+            name = s.substring(0, i);       // ignore the dependency list
+        } else
+            type = VarType.INDEPENDENT;
+    }
+
+    private Variable(String n, CompoundTerm s, VarType t) {
+        name = n;
+        scope = s;
+        type = t;
+    }
+    
+    /**
+     * Make a new Variable with the same name and type.
+     * @return The new Variable
+     */
+    public Object clone() {
+        return new Variable(name, scope, type);
+    }
+
+    // overwrite default, to be omitted in sorting
+    public String getConstantName() {
+        return ("" + Symbols.VARIABLE_TAG);
+    }
+    
+    /**
+     * Rename a variable temporally to distinguish it from variables in other Terms
+     * @param first Whether it is the first term
+     * @return The new name
+     */
+    public String getVarName(boolean first) {
+        if (first)
+            return Symbols.VARIABLE_TAG + "1" + name;
+        else
+            return Symbols.VARIABLE_TAG + "2" + name;
+    }
+    
+    public void setName(String n) {
+        name = n;
+    }
+    
+    public CompoundTerm getScope() {
+        return scope;
+    }
+    
+    public void setScope(CompoundTerm s) {
+        scope = s;
+    }
+    
+    public VarType getType() {
+        return type;
+    }
+    
+    public void setType(VarType t) {
+        type = t;
+    }
+
+    public boolean equals(Object that) {
+        return (that instanceof Variable) && name.equals(((Variable) that).getSimpleName());
+    }
+
+    public String getSimpleName() {
+        return name;
+    }
+
+    public String getName() {
+        if (type != VarType.DEPENDENT)
+            return name;
+        else {
+            StringBuffer buffer = new StringBuffer(name + "(");
+            if (scope != null) {
+                ArrayList<Variable> dependency = scope.getOpenVariables();
+                if (dependency != null) {
+                    for (Variable v : dependency)
+                        if (v.getType() == VarType.INDEPENDENT)
+                            buffer.append(v.toString());
+                }
+            }
+            buffer.append(")");
+            return buffer.toString();
+        }
+    }
+    
+    public boolean isConstant() {
+        return false;                // overridding default
+    }
+    
+    // move to RuleTable? the last two arguments must be clones
+    /**
+     * To unify two Terms, then apply the substitution to the two compounds
+     * @param type The type of Variable to be unified
+     * @param t1 The first Term to be unified
+     * @param t2 The second Term to be unified
+     * @param compound1 The first compound to be substituted
+     * @param compound2 The second compound to be substituted
+     * @return Whether a unification has been succeeded
+     */
+    public static boolean unify(VarType type, Term t1, Term t2, Term compound1, Term compound2) {
+        if (t1.isConstant() && t1.equals(t2))       // to constant Terms are unified if equals
+            return true;
+        if (!(compound1 instanceof CompoundTerm) || !(compound2 instanceof CompoundTerm))
+            return false;
+        HashMap<String,Term> substitute = findSubstitute(type, t1, t2, new HashMap<String,Term>()); // find substitution
+        if (substitute == null) // not unifiable
+            return false;
+        if (!substitute.isEmpty()) {
+            ((CompoundTerm) compound1).substituteComponent(substitute, true);   // apply the substitution to the first compound
+            ((CompoundTerm) compound2).substituteComponent(substitute, false);  // apply the substitution to the second compound
+        }
+        return true;
+    }
+
+
+    public static HashMap<String,Term> findSubstitute(VarType type, Term term1, Term term2) {
+        return findSubstitute(type, term1, term2, new HashMap<String,Term>());
+    }
+    
+    /**
+     * To find a substitution that can unify two Terms without changing them
+     * @param type The type of Variable to be substituted
+     * @param term1 The first Term to be unified
+     * @param term2 The second Term to be unified
+     * @param subs The substitution formed so far
+     * @return The substitution that unifies the two Terms
+     */
+    private static HashMap<String,Term> findSubstitute(VarType type, Term term1, Term term2, HashMap<String,Term> subs) {
+        Term oldValue, t1, t2;
+        if (term1.equals(term2))    // for constant, also shortcut for variable and compound
+            return subs;
+        if ((term1 instanceof Variable) && (((Variable) term1).getType() == type))  // the first Term is a unifiable Variable
+            return findSubstituteVar(type, (Variable) term1, term2, subs, true);
+        if ((term2 instanceof Variable) && (((Variable) term2).getType() == type))  // the second Term is a unifiable Variable
+            return findSubstituteVar(type, (Variable) term2, term1, subs, false);
+        if (term1 instanceof CompoundTerm) {
+            if (!term1.getClass().equals(term2.getClass()))   // two compounds must be of the same type to be unified
+                return null;
+            if (!(((CompoundTerm) term1).size() == ((CompoundTerm) term2).size())) // two compounds must be of the same size, too
+                return null;
+            for (int i = 0; i < ((CompoundTerm) term1).size(); i++) {   // recursively unify components
+                t1 = ((CompoundTerm) term1).componentAt(i);
+                t2 = ((CompoundTerm) term2).componentAt(i);
+                HashMap<String,Term> newSubs = findSubstitute(type, t1, t2, subs);
+                if (newSubs == null)            // fails in one component means no substitution
+                    return null;
+                subs.putAll(newSubs);       // put new mappings into the table
+            }
+            return subs;     // solve: <x,x,2> and <y,3,y>
+        }
+        return null;
+    }
+    
+    /**
+     * To find a substitution that can unify a Vriable and a Term
+     * @param type The type of Variable to be substituted
+     * @param var The Variable to be unified
+     * @param term The Term to be unified
+     * @param subs The substitution formed so far
+     * @param first If it is the first Term in unify
+     * @return The substitution that unifies the two Terms, as "name-term" pairs
+     */
+    private static HashMap<String,Term> findSubstituteVar(VarType type, Variable var, Term term, HashMap<String,Term> subs, boolean first) {
+        String name1 = var.getVarName(first);    // make a prefixed name for the avriable
+        Term oldTerm = subs.get(name1);          // check if a mapping for that name exist
+        if (oldTerm != null) {                  // processed variable
+            if (first)
+                return findSubstitute(type, oldTerm, term, subs);
+            else
+                return findSubstitute(type, term, oldTerm, subs);
+        } else {                                // novel variable
+            if (term instanceof Variable) {     // the other is also a variable
+                String name2 = ((Variable) term).getVarName(!first);
+                oldTerm = subs.get(name2);       
+                if (oldTerm != null) {          // if the other has a substitute
+                    if (first)
+                        return findSubstitute(type, var, oldTerm, subs);
+                    else
+                        return findSubstitute(type, oldTerm, var, subs);
+                }
+            }
+            subs.put(name1, term);  // check dependency for dependent variable!!!
+            return subs;
+        }
+    }
+}
diff --git a/language/package.html b/language/package.html
new file mode 100644
index 0000000000000000000000000000000000000000..4fb4628b371d496d1e8ff4076b32be12470040df
--- /dev/null
+++ b/language/package.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<body bgcolor="white">
+
+Terms in Narsese, a formal language
+
+<h2>Package Specification</h2>
+
+NARS 4.3 implements the following formal language, Narsese.
+<pre>
+           &lt;sentence&gt; ::= &lt;judgment&gt;
+                        | &lt;question&gt;
+                        | &lt;goal&gt;
+           &lt;judgment&gt; ::= &lt;statement&gt; &lt;truth-value&gt;
+           &lt;question&gt; ::= &lt;statement&gt;
+               &lt;goal&gt; ::= &lt;statement&gt; &lt;desire-value&gt;
+          &lt;statement&gt; ::= &lt;&lt;term&gt; &lt;relation&gt; &lt;term&gt;&gt;
+                        | &lt;compound-statement&gt;
+                        | &lt;term&gt;
+               &lt;term&gt; ::= &lt;word&gt;
+                        | &lt;variable&gt;
+                        | &lt;compound-term&gt;
+                        | &lt;statement&gt;
+           &lt;relation&gt; ::= -->
+                        | <->
+                        | {--
+                        | --]
+                        | {-]
+                        | ==>
+                        | <=>
+                        | =/>
+                        | =\>
+                        | &lt;/>
+ &lt;compound-statement&gt; ::= (-- &lt;statement&gt;)
+                        | (|| &lt;statement&gt; &lt;statement&gt;<sup>+</sup>)
+                        | (&& &lt;statement&gt; &lt;statement&gt;<sup>+</sup>)
+                        | (&/ &lt;statement&gt; &lt;statement&gt;<sup>+</sup>)
+                        | (/> &lt;statement&gt;)
+                        | (\> &lt;statement&gt;)
+      &lt;compound-term&gt; ::= {&lt;term&gt;<sup>+</sup>}
+                        | [&lt;term&gt;<sup>+</sup>]
+                        | (& &lt;term&gt; &lt;term&gt;<sup>+</sup>)
+                        | (| &lt;term&gt; &lt;term&gt;<sup>+</sup>)
+                        | (- &lt;term&gt; &lt;term&gt;)
+                        | (~ &lt;term&gt; &lt;term&gt;)
+                        | (* &lt;term&gt; &lt;term&gt;<sup>+</sup>)
+                        | (/ &lt;term&gt;<sup>+</sup> _ &lt;term&gt;<sup>*</sup>)
+                        | (\ &lt;term&gt;<sup>+</sup> _ &lt;term&gt;<sup>*</sup>)
+           &lt;variable&gt; ::= &lt;dependent-var&gt;
+                        | &lt;independent-var&gt;
+                        | &lt;query-var&gt;
+      &lt;dependent-var&gt; ::= #&lt;word&gt;(&lt;independent-var&gt;<sup>*</sup>)
+    &lt;independent-var&gt; ::= #&lt;word&gt;
+          &lt;query-var&gt; ::= ?&lt;word&gt;
+               &lt;word&gt; : string in an alphabet
+        &lt;truth-value&gt; : a pair of real numbers in [0, 1] x (0, 1)
+       &lt;desire-value&gt; : a pair of real numbers in [0, 1] x (0, 1)
+</pre>
+
+Major methods in the term classes:
+<ul>
+<li>constructors</li>
+<li>get and set</li>
+<li>clone, compare, and unify</li>
+<li>create and access corresponding concept</li>
+<li>structural operation in compound</li>
+<li>class-specific inference</li>
+</ul>
+
+</body>
+</html>
diff --git a/main/Center.java b/main/Center.java
new file mode 100644
index 0000000000000000000000000000000000000000..21eb6bcc462be250d45809a8c0876a13f31a813a
--- /dev/null
+++ b/main/Center.java
@@ -0,0 +1,91 @@
+
+package nars.main;
+
+import nars.entity.Base;
+import nars.io.*;
+import nars.gui.*;
+
+/**
+ * The control center of the system.
+ * <p>
+ * Create static main and inpit windows, reset memory, and manage system clock.
+ */
+public class Center {   // One Center per NARS, all members are static
+    /**
+     * The unique main window of the system.
+     */
+    public static MainWindow mainWindow;
+    /**
+     * The unique input window of the system.
+     */
+    public static InputWindow inputWindow;
+    /**
+     * System clock, relatively defined to guaranttee the repeatability of behaviors.
+     */
+    private static long clock;
+    /**
+     * Timer for fixed distance walking.
+     */
+    private static long stoper;
+    /**
+     * Running continously.
+     */
+    private static boolean running;
+    
+    /**
+     * Start the initial windows and memory. Called from NARS only.
+     */
+    public static void start() {
+        mainWindow = new MainWindow();
+        inputWindow = new InputWindow();
+        reset();
+    }
+    
+    /**
+     * Reset the system with an empty memory and reset clock. Called locally and from MainWindow.
+     */
+    public static void reset() {
+        stoper = 0;
+        clock = 0;
+        Base.init();
+        Memory.init();
+        running = false;
+    }
+    
+    /**
+     * Walk a fixed number of steps. Called from MainWindow only.
+     * @param i the number of steps of inference
+     */
+    public static void setStoper(long i) {
+        if (i < 0) {
+            running = true;
+            stoper = 0;
+        } else {
+            running = false;
+            stoper = i;
+        }
+    }
+    
+    /**
+     * A clock tick. Called from NARS only.
+     */
+    public static void tick() {
+        if (stoper == 0)
+            stoper = inputWindow.getInput();
+        if (running || (stoper > 0)) {
+            clock++;
+            Record.append(" --- " + clock + " ---\n");
+            mainWindow.tickTimer();
+            Memory.cycle();
+            if (stoper > 0)
+                stoper--;
+        }
+    }
+    
+    /**
+     * Redisplay the input window. Called from MainWindow only.
+     */
+    public static void showInputWindow() {
+        inputWindow.setVisible(true);
+    }
+}
diff --git a/main/Memory.java b/main/Memory.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a4c3c44983bf415adee112a9a9c8f9fa9061dce
--- /dev/null
+++ b/main/Memory.java
@@ -0,0 +1,406 @@
+
+package nars.main;
+
+import java.util.*;
+import nars.entity.*;
+import nars.language.*;
+import nars.operation.Operator;
+import nars.storage.*;
+import nars.io.*;
+import nars.inference.BudgetFunctions;
+
+/**
+ * The memory of the system.
+ */
+public class Memory {
+    
+    /* ---------- all members are static, with limited space ---------- */
+    
+    /**
+     * Concept bag. Containing all Concepts of the system.
+     */
+    private static ConceptBag concepts;
+    
+    /**
+     * Operators (built-in terms) table. Accessed by name.
+     */
+    private static HashMap<String, Operator> operators;
+    
+    // There is no global Term table, which may ask for unlimited space.
+    
+    /**
+     * List of inference newTasks, to be processed in the next working cycle.
+     */
+    private static ArrayList<Task> newTasks;
+    
+    /**
+     * New tasks to be processed in the near future.
+     */
+    private static TaskBag taskBuffer;
+    
+    /* ---------- global variables used to reduce method arguments ---------- */
+    
+    /**
+     * Shortcut to the selected Term.
+     */
+    public static Term currentTerm;
+    
+    /**
+     * Shortcut to the selected TaskLink.
+     */
+    public static TaskLink currentTaskLink;
+    
+    /**
+     * Shortcut to the selected Task.
+     */
+    public static Task currentTask;
+    
+    /**
+     * Shortcut to the selected TermLink.
+     */
+    public static TermLink currentBeliefLink;
+    
+    /**
+     * Shortcut to the selected belief (Sentence).
+     */
+    public static Judgment currentBelief;
+    
+    public static Base currentBase;
+    
+    /* ---------- initialization ---------- */
+    
+    /**
+     * Initialize a new memory by creating all members.
+     * <p>
+     * Called in Center.reset only
+     */
+    public static void init() {
+        concepts = new ConceptBag();         // initially empty
+        newTasks = new ArrayList<Task>();     // initially empty
+        taskBuffer = new TaskBag();       // initially empty
+        operators = Operator.setOperators(); // with operators created
+    }
+    
+    /* ---------- access utilities ---------- */
+    
+    /**
+     * Get a Term for a given name of a Concept or Operator, called in StringParser and the make methods of compound terms.
+     * @param name the name of a concept or operator
+     * @return a Term or null (if no Concept/Operator has this name)
+     */
+    public static Term nameToListedTerm(String name) {
+        Concept concept = concepts.get(name);
+        if (concept != null)
+            return concept.getTerm();           // Concept associated Term
+        return operators.get(name);
+    }
+    
+    /**
+     * Check if a string is an operator name, called in StringParser only.
+     * @param name the name of a possible operator
+     * @return the corresponding operator or null
+     */
+    public static Operator nameToOperator(String name) {
+        return operators.get(name);
+    }
+    
+    /**
+     * Get an existing Concept for a given name, called from Term and ConceptWindow.
+     * @param name the name of a concept
+     * @return a Concept or null
+     */
+    public static Concept nameToConcept(String name) {
+        return concepts.get(name);
+    }
+    
+    /**
+     * Get an existing Concept for a given Term.
+     * @param term The Term naming a concept
+     * @return a Concept or null
+     */
+    public static Concept termToConcept(Term term) {
+        return nameToConcept(term.getName());
+    }
+    
+    /**
+     * Get the Concept associated to a Term, or creat it.
+     * @param term indicating the concept
+     * @return an existing Concept, or a new one
+     */
+    public static Concept getConcept(Term term) {
+        String n = term.getName();
+        Concept concept = concepts.get(n);
+        if (concept == null)
+            concept = new Concept(term);        // the only place to make a new Concept
+        return concept;
+    }
+    
+    /**
+     * Adjust the activation level of a Concept or Operator, called in Concept only.
+     * @param c the concept to be adusted
+     * @param b the new BudgetValue
+     */
+    public static void activateConcept(Concept c, BudgetValue b) {
+        BudgetValue budget;
+        if (concepts.contains(c)) {     // listed Concept
+            concepts.pickOut(c.getKey());
+            BudgetFunctions.activate(c, b);
+            concepts.putBack(c);
+        } else {                        // new Concept
+            BudgetFunctions.activate(c, b);
+            concepts.putIn(c);
+        }
+    }
+    
+    /* ---------- new task entries ---------- */
+    
+    // There are three types of new tasks: (1) input, (2) derived, (3) activated
+    // They are all added into the newTasks list, to be processed in the next cycle.
+    // Some of them are reported and/or logged.
+    
+    /**
+     * Input task comes from the InputWindow.
+     * @param str the input line
+     */
+    public static void inputTask(String str) {
+        Task task = StringParser.parseTask(new StringBuffer(str));       // the only place to call StringParser
+        if (task != null) {
+            if (NARS.isStandAlone())
+                Record.append("!!! Input: " + task + "\n");   // append to inference record
+            if (task.aboveThreshold()) {                       // set a threshold?
+                report(task.getSentence(), true);             // report input
+                newTasks.add(task);       // wait to be processed in the next cycle
+                taskBuffer.refresh();           // refresh display
+            } else
+                Record.append("!!! Ignored: " + task + "\n");   // append to inference record
+        }
+    }
+    
+    /**
+     * Derived task comes from the inference rules.
+     * @param task the derived task
+     */
+    private static void derivedTask(Task task) {
+        Record.append("!!! Derived: " + task.toString() + "\n");
+        if (task.aboveThreshold()) {
+            float budget = task.getBudget().singleValue();
+            float minSilent = Center.mainWindow.silentW.value() / 100.0f;
+            if (budget > minSilent)
+                report(task.getSentence(), false);        // report significient derived Tasks
+            newTasks.add(task);
+            taskBuffer.refresh();
+        } else
+            Record.append("!!! Ignored: " + task + "\n");   // append to inference record
+    }
+    
+    /**
+     * Reporting executed task, called from Concept.directOperation.
+     * @param task the executed task
+     */
+    public static void executedTask(Task task) {   // called by the inference rules
+        Record.append("!!! Executed: " + task.toString() + "\n");
+        float budget = task.getBudget().singleValue();
+        float minSilent = Center.mainWindow.silentW.value() / 100.0f;
+        if (budget > minSilent)
+            report(task.getSentence(), false);
+    }
+    
+    /**
+     * Activated task comes from MatchingRules.
+     * @param budget The budget value of the new Task
+     * @param sentence The content of the new Task
+     * @param isInput Whether the question is input
+     */
+    public static void activatedTask(BudgetValue budget, Sentence sentence, boolean isInput) {
+        Task task = new Task(sentence, budget);
+        if (NARS.isStandAlone())
+            Record.append("!!! Activated: " + task.toString() + "\n");
+        newTasks.add(task);
+        taskBuffer.refresh();
+    }
+    
+    /* --------------- new task building --------------- */
+    
+    /**
+     * Shared final operations by all double-premise rules, called from the rules except StructuralRules
+     * @param budget The budget value of the new task
+     * @param content The content of the new task
+     * @param truth The truth value of the new task
+     */
+    public static void doublePremiseTask(BudgetValue budget, Term content, TruthValue truth) {
+        Sentence newSentence = Sentence.make(currentTask.getSentence(), content, truth, Memory.currentBase);
+        Task newTask = new Task(newSentence, budget);
+        derivedTask(newTask);
+    }
+    
+    /**
+     * Shared final operations by all single-premise rules, called in StructuralRules
+     * @param budget The budget value of the new task
+     * @param content The content of the new task
+     * @param truth The truth value of the new task
+     */
+    public static void singlePremiseTask(BudgetValue budget, Term content, TruthValue truth) {
+        Sentence sentence = currentTask.getSentence();
+        Sentence newSentence = Sentence.make(sentence, content, truth, sentence.getBase());
+        Task newTask = new Task(newSentence, budget);
+        newTask.setStructual();
+        derivedTask(newTask);
+    }
+
+    /**
+     * Shared final operations by all single-premise rules, called in MatchingRules
+     * @param budget The budget value of the new task
+     * @param truth The truth value of the new task
+     */
+    public static void singlePremiseTask(TruthValue truth, BudgetValue budget) {
+        Term content = Memory.currentTask.getContent();
+        Base base = Memory.currentBelief.getBase();
+        Sentence newJudgment = Sentence.make(content, Symbols.JUDGMENT_MARK, truth, base);
+        Task newTask = new Task(newJudgment, budget);
+        newTask.setStructual();
+        derivedTask(newTask);
+    }
+    
+    /* ---------- system working cycle ---------- */
+    
+    /**
+     * An atomic working cycle of the system. Called from Center only.
+     */
+    public static void cycle() {
+        processTask();      // tune relative frequency?
+        processConcept();   // use this order to check the new result
+    }
+    
+    /**
+     * Process the newTasks accumulated in the previous cycle, accept input ones
+     * and those that corresponding to existing concepts, plus one from the buffer.
+     */
+    private static void processTask() {
+        Task task;
+        int counter = newTasks.size();              // don't include new tasks produced in the current cycle
+        while (counter-- > 0) {                     // process the newTasks of the previous cycle
+            task = (Task) newTasks.remove(0);
+            if (task.getSentence().isInput() || (termToConcept(task.getContent()) != null))  // new input or existing concept
+                immediateProcess(task);                   // immediate process
+            else
+                taskBuffer.putIn(task);             // postponed process
+        }
+        task = (Task) taskBuffer.takeOut();         // select a task from taskBuffer
+        if (task != null)
+            immediateProcess(task);                       // immediate process
+    }
+    
+    /**
+     * Select a concept to fire.
+     */
+    private static void processConcept() {
+        Concept currentConcept = (Concept) concepts.takeOut();
+        if (currentConcept != null) {
+            currentTerm = currentConcept.getTerm();
+            Record.append(" * Selected Concept: " + currentTerm + "\n");
+            concepts.putBack(currentConcept);   // current Concept remains in the bag all the time
+            currentConcept.fire();              // a working cycle
+            taskBuffer.refresh();               // show new result
+        }
+    }
+    
+    /* ---------- task / belief insertion ---------- */
+    
+    /**
+     * Imediate processing of a new task
+     * @param task the task to be accepted
+     */
+    private static void immediateProcess(Task task) {
+        if (NARS.isStandAlone())
+            Record.append("!!! Accept: " + task.toString() + "\n");
+        Term content = task.getContent();
+        if (content.isConstant()) {                        // does not creat concept for Query?
+            Concept c = getConcept(content);
+            c.directProcess(task);
+        }
+        if (task.aboveThreshold())
+            continuedProcess(task, content);
+    }
+
+    /**
+     * Link to a new task from all relevant concepts for distributed processing.
+     * @param task The task to be linked
+     * @param content The content of the task
+     */
+    private static void continuedProcess(Task task, Term content) {
+        TaskLink tLink;
+        Concept c1 = null;                      // local Concept
+        BudgetValue budget = task.getBudget();
+        if (content.isConstant()) {
+            c1 = getConcept(content);
+            tLink = new TaskLink(task, null, budget);   // link type SELF
+            c1.insertTaskLink(tLink);
+        }
+        if (content instanceof CompoundTerm) {
+            Term component;                     // component term
+            Concept c2;                         // component concept
+            TermLink cLink1, cLink2;     // a pair of compound/component links
+            ArrayList<TermLink> cLinks;  // link list
+            cLinks = (c1 != null) ? c1.getTermLinks() : ((CompoundTerm) content).prepareComponentLinks();  // use saved
+            short[] indices;
+            BudgetValue subBudget = BudgetFunctions.distributeAmongLinks(budget, cLinks.size());
+            if (!subBudget.aboveThreshold())
+                return;
+            for (TermLink cLink0 : cLinks) {
+                component = cLink0.getTarget();
+                c2 = getConcept(component);
+                if (!(task.isStructual() && (cLink0.getType() == TermLink.TRANSFORM))) {
+                    tLink = new TaskLink(task, cLink0, subBudget);
+                    c2.insertTaskLink(tLink);               // component link to task
+                }
+            }
+        }
+    }
+    
+    /* ---------- display ---------- */
+    
+    /**
+     * Display active concepts, called from MainWindow.
+     * @param s the window title
+     */
+    public static void conceptsStartPlay(String s) {
+        concepts.startPlay(s);
+    }
+    
+    /**
+     * Display buffered tasks, called from MainWindow.
+     * @param s the window title
+     */
+    public static void taskBufferStartPlay(String s) {
+        taskBuffer.startPlay(s);
+    }
+    
+    /**
+     * Prepare buffered tasks for display, called from TaskBag.
+     * 
+     * @return the tasks as a String
+     */
+    public static String resultsToString() {
+        String s = " New Tasks: \n";
+        for (int i = 0; i < newTasks.size(); i++)
+            s += newTasks.get(i).toString() + "\n";
+        s += "\n Task Buffer: \n";
+        return s;
+    }
+    
+    /**
+     * Display selected task.
+     * @param sentence the sentence to be displayed
+     * @param input whether the task is input
+     */
+//    public static void report(Task task, boolean input) {
+    public static void report(Sentence sentence, boolean input) {
+        String s = " ";
+        if (input)
+            s += "IN: ";
+        else
+            s += "OUT: ";
+        s += sentence.toString2() + "\n";
+        Center.mainWindow.post(s);
+    }
+}
diff --git a/main/NARS.java b/main/NARS.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8e3489f7ea82e9d05686fcd066faab5ac7ef90f
--- /dev/null
+++ b/main/NARS.java
@@ -0,0 +1,101 @@
+package nars.main;
+
+import java.applet.*;
+
+/**
+ * The main class of the poject.
+ * <p>
+ * Define an application with full funcationality and a demonstration applet with limited functionality.
+ * <p>
+ * Manage the internal working thread. Communicate with Center only.
+ */
+public class NARS extends Applet implements Runnable {
+    /**
+     * The information about the author, version, and copyright of the project.
+     */
+    public static final String INFO =
+            "     Open-NARS     Version 1.0.0     January 2008  \n";
+    /**
+     * The project website.
+     */
+    public static final String WEBSITE =
+      " Open-NARS website:  http://code.google.com/p/open-nars/ \n" +
+      "      NARS website:  http://nars.wang.googlepages.com/ ";
+    /**
+     * Flag to distinguish the two running modes of the project.
+     */
+    private static boolean standAlone = false;  // application or applet
+    /**
+     * The internal working thread of the system.
+     */
+    Thread narsThread = null;
+    
+    /**
+     * The entry point of the standalone application.
+     * <p>
+     * Create an instance of the class, then run the init and start methods,
+     * shared by the application and the applet.
+     * @param args no arguments are used
+     */
+    public static void main(String args[]) {
+        standAlone = true;
+        NARS nars = new NARS();
+        nars.init();
+        nars.start();
+    }
+    
+    /**
+     * Initialize the system at the control center.
+     */
+    public void init() {
+        Center.start();
+    }
+       
+    /**
+     * start the thread if necessary, called when the page containing the applet first appears on the screen.
+     */
+    public void start() {
+        if (narsThread == null) {
+            narsThread = new Thread(this);
+            narsThread.start();
+        }
+    }
+    
+    /**
+     * The stop() method is called when the page containing the applet is no longer on the screen.
+     */
+    public void stop() {
+        narsThread = null;
+    }
+    
+    /**
+     * Repeatedly execute NARS working cycle. This method is called when the Runnable's thread is started.
+     */
+    public void run() {
+        Thread thisThread = Thread.currentThread();
+        while (narsThread == thisThread) {
+            try {
+                thisThread.sleep(10);
+            } catch (InterruptedException e){
+            }
+            Center.tick();
+//            repaint();      // necessary for the applet?
+        }
+    }
+    
+    /**
+     * Whether the project running as an application.
+     * @return true for application; false for applet.
+     */
+    public static boolean isStandAlone() {
+        return standAlone;
+    }
+    
+    /**
+     * Provide system information for the applet.
+     * @return The string containing the information about the applet.
+     */
+    public String getAppletInfo() {
+        return INFO;
+    }
+}
\ No newline at end of file
diff --git a/main/Parameters.java b/main/Parameters.java
new file mode 100644
index 0000000000000000000000000000000000000000..87fe60c669a629a4d542dcf83b28b4b67d20e6a8
--- /dev/null
+++ b/main/Parameters.java
@@ -0,0 +1,83 @@
+
+package nars.main;
+
+/**
+ * Collected system parameters. To be modified before compiling.
+ */
+public class Parameters {
+    
+    /* ---------- initial values of run-time adjustable parameters ---------- */
+
+    /** Concept decay rate, in [1, 99]. */
+    public static final int CONCEPT_DEFAULT_FORGETTING_CYCLE = 3;
+    /** TaskLink decay rate, in [1, 99]. */
+    public static final int TASK_DEFAULT_FORGETTING_CYCLE = 10;
+    /** CompositionLink decay rate, in [1, 99]. */
+    public static final int BELIEF_DEFAULT_FORGETTING_CYCLE = 50;
+       
+    /** Silent threshold for task reporting, in [0, 100]. */
+    public static final int SILENT_LEVEL = 100;
+
+    /* ---------- parameters fixed after compiling --- logical ---------- */
+
+    /** Horizon, the amount of evidence coming in the near future. */
+    public static final int NEAR_FUTURE = 1;    // or 2, can be float
+    
+    /** Default confidence of input judgment. */
+    public static final float DEFAULT_JUDGMENT_CONFIDENCE = (float) 0.9;  // ?
+
+    /* Default priority and durability of input judgment */
+    public static final float DEFAULT_JUDGMENT_PRIORITY = (float) 0.8;
+    public static final float DEFAULT_JUDGMENT_DURABILITY = (float) 0.8;
+    public static final float DEFAULT_GOAL_PRIORITY = (float) 0.9;
+    public static final float DEFAULT_GOAL_DURABILITY = (float) 0.7;
+    public static final float DEFAULT_QUESTION_PRIORITY = (float) 0.9;
+    public static final float DEFAULT_QUESTION_DURABILITY = (float) 0.7;
+    
+//    public static final float TASK_FOR_NEW_CONCEPT_DISCOUNT = (float) 0.2;  // ?
+    
+    // belief component priority rate
+    public static final float DEFAULT_COMPONENT_PRIORITY_RATE = (float) 0.7;
+    
+    /**
+     * Level granularity in Bag, two digits
+     */
+    public static final int BAG_LEVEL = 100;
+    /**
+     * Level separation in Bag, one digit, both for display (run-time adjustable) and management (fixed)
+     */
+    public static final int BAG_THRESHOLD = 10;         
+
+    /* ---------- parameters fixed after compiling --- time management ---------- */
+    
+    // time distribution
+    public static final int COLD_TASK_DELAY_STEPS = 10;                 // ?
+
+    // decay rate, within 100
+    public static final int NEW_TASK_DEFAULT_FORGETTING_CYCLE = 1;        // ?
+
+    // quality updating rate
+    public static final float DEFAULT_QUALITY_UPDATE_RATE = (float) 0.01; // percent of updating
+    
+    // maximum bLinks tried for each tLink (to avoid repeated inference)
+    public static final int MAX_TAKE_OUT_K_LINK = 10;
+    
+    // maximum belief tried for each task (to avoid overlapping evidence)
+    public static final int MAX_TAKE_OUT_BELIEF = 5;
+    
+    /* ---------- parameters fixed after compiling --- space management ---------- */
+
+    // bag size
+    public static final int CONCEPT_BAG_SIZE = 1000;         // vocabulary?
+    public static final int TASK_BUFFER_SIZE = 20;          // "7+-2"?
+    public static final int TASK_BAG_SIZE = 20;              // ?
+    public static final int BELIEF_BAG_SIZE = 100;        // ?
+    
+    // other storage size
+    public static final int MAXMUM_LABEL_RECORD_LENGTH = 16;    // should be pow(2,n)
+    public static final int TASK_INFERENCE_RECORD_LENGTH = 20;  // ?
+    public static final int MAXMUM_BELIEF_LENGTH = 8;      // duplicate links
+    public static final int MAXMUM_GOALS_LENGTH = 5;       // duplicate links
+    
+    public static final float LOAD_FACTOR = 0.5f;   // bag hashtable parameter
+}
diff --git a/main/package.html b/main/package.html
new file mode 100644
index 0000000000000000000000000000000000000000..e2da9b9a4a7d0ca465462dede2e9eb7d766be9f8
--- /dev/null
+++ b/main/package.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<body bgcolor="white">
+
+Top-level classes of the system
+
+<h2>Package Specification</h2>
+
+This package contains the top classes of the system.
+<p>
+<tt>NARS</tt> defines the application and applet. When NARS runs as an applet, it is for demonstration only, and certain functionalities are blocked.
+<p>
+<tt>Parameters</tt> collects all system parameters, which can be edited before compiling.
+<p>
+<tt>Memory</tt> maintains the work space of the system, including
+<ul>
+<li>a bag of concept,</li>
+<li>a look-up table of all built-in operators,</li>
+<li>a queue for new results to be processed in each cycle,</li>
+<li>a task buffer for new results to be processed in the future.</li>
+</ul>
+Each of the above occupies constant space.
+<p>
+Memory and all of its variables are static, so each is unique, and can be referred by name anywhere in the system.
+
+</body>
+</html>
diff --git a/operation/GoTo.java b/operation/GoTo.java
new file mode 100644
index 0000000000000000000000000000000000000000..231cba5dcbcbcd903a7d1a2220ff0004310c3f3c
--- /dev/null
+++ b/operation/GoTo.java
@@ -0,0 +1,20 @@
+
+package nars.operation;
+
+import java.io.*;
+import nars.entity.Task;
+
+/**
+ * A class used in testing only.
+ */
+public class GoTo extends Operator {
+    public GoTo(String name) {
+        super(name);
+    }
+    
+    public Object execute(Task task) {
+        System.out.println("EXECUTE in " + name + " " + task);
+        return null;
+    }
+}
+
diff --git a/operation/Open.java b/operation/Open.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a93350741f4fc25df0245778670025cd1a9015d
--- /dev/null
+++ b/operation/Open.java
@@ -0,0 +1,21 @@
+
+package nars.operation;
+
+import java.io.*;
+import nars.entity.Task;
+
+/**
+ * A class used in testing only.
+ */
+public class Open extends Operator {
+    public Open(String name) {
+        super(name);
+    }
+
+    public Object execute(Task task) {
+        System.out.println("EXECUTE in " + name + " " + task);
+        return null;
+    }
+}
+
+
diff --git a/operation/Operator.java b/operation/Operator.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3afacb2be1f919d21399698f7e39c59c76af8bf
--- /dev/null
+++ b/operation/Operator.java
@@ -0,0 +1,32 @@
+
+package nars.operation;
+
+import java.util.*;
+import java.io.*;
+import nars.language.Term;
+import nars.entity.Task;
+
+/**
+ * An individual operator that can be execute by the system.
+ * The only file to modify when adding a new operator into NARS
+ */
+public abstract class Operator extends Term {
+    public Operator(String name) {
+        super(name);
+    }
+    
+    // required method for every operation
+    public abstract Object execute(Task task);
+
+    // register the operators in the memory
+    // the only method to modify when adding a new operator into NARS
+    // an operator should contain at least two characters after "^""
+    public static HashMap<String, Operator> setOperators() {
+        HashMap<String, Operator> table = new HashMap<String, Operator>();
+        table.put("^go-to", new GoTo("^go-to"));
+        table.put("^pick", new Pick("^pick"));
+        table.put("^open", new Open("^open"));
+        return table;
+    }
+}
+
diff --git a/operation/Pick.java b/operation/Pick.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c3a3bedcb62ce9b83af349ea2a8fa738d4141a5
--- /dev/null
+++ b/operation/Pick.java
@@ -0,0 +1,19 @@
+
+package nars.operation;
+
+import java.io.*;
+import nars.entity.Task;
+
+/**
+ * A class used in testing only.
+ */
+public class Pick extends Operator {
+    public Pick(String name) {
+        super(name);
+    }
+
+    public Object execute(Task task) {
+        System.out.println("EXECUTE in " + name + " " + task);
+        return null;
+    }
+}
diff --git a/operation/package.html b/operation/package.html
new file mode 100644
index 0000000000000000000000000000000000000000..903b31203a9da4310d4412a2c4cb432c81c6dee0
--- /dev/null
+++ b/operation/package.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<body bgcolor="white">
+
+Built-in operators
+
+<h2>Package Specification</h2>
+
+The built-in operator classes are all subclasses of <tt>Operator</tt>, which extends <tt>Term</tt>.
+<p>
+Each built-in operator must implement a method <tt>execute</tt>, which takes a Task as argument.
+<p>
+All built-in operators are registered in class <tt>Operator</tt>.
+
+</body>
+</html>
diff --git a/overview.html b/overview.html
new file mode 100644
index 0000000000000000000000000000000000000000..8dbb004da0ae960b0adb15b5ded96c5728325eaf
--- /dev/null
+++ b/overview.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+    <head>NARS</head>
+    <body bgcolor="white">
+
+        NARS overview
+
+        <h2>Package Specification</h2>
+
+        The code for distribution is in <tt>NARS.jar</tt>, which works both as an application (full mode) and an applet 
+        (demo mode, with simplified display).
+
+    </body>
+</html>
diff --git a/storage/Bag.java b/storage/Bag.java
new file mode 100644
index 0000000000000000000000000000000000000000..6901acc55ea484092444df6454ecaf75fc9fb2f3
--- /dev/null
+++ b/storage/Bag.java
@@ -0,0 +1,230 @@
+
+package nars.storage;
+
+import java.util.*;
+
+import nars.gui.BagWindow;
+import nars.inference.BudgetFunctions;
+import nars.entity.Item;
+import nars.main.Parameters;
+
+/**
+ * A Bag is a storage with a constant capacity and maintains an internal priority
+ * distribution for retrieval.
+ * 
+ * Each entity in a bag must extend Item, which is a UsageValue with a key.
+ *
+ * A name table is used to merge duplicate items.
+ *
+ * The bag space is divided by a threshold, above which is mainly time management,
+ *   and below, space mamagement.
+ *   Differences: (1) level selection vs. item selection, (2) decay rate
+ */
+public abstract class Bag<Type extends Item> {
+    
+    protected static final int TOTAL_LEVEL = Parameters.BAG_LEVEL;          // priority levels
+    protected static final int THRESHOLD = Parameters.BAG_THRESHOLD;        // firing threshold
+    protected static final float RELATIVE_THRESHOLD = (float) THRESHOLD / (float) TOTAL_LEVEL;
+    protected static final float LOAD_FACTOR = Parameters.LOAD_FACTOR;       // hashtable parameter
+    protected static final Distributor DISTRIBUTOR = new Distributor(TOTAL_LEVEL); // only one instance
+
+    protected HashMap<String, Type> nameTable;    // from key to item
+    protected ArrayList<Type> itemTable[];        // array of lists of items, for items on different level
+       
+    protected int capacity;         // defined in different bags
+    protected int mass;             // current sum of occupied level
+    protected int levelIndex;       // index to get next level, kept in individual objects
+    protected int currentLevel;     // current take out level
+    protected int currentCounter;   // maximum number of items to be taken out at current level
+    
+    protected boolean showing;      // whether this bag has an active window
+    protected BagWindow window;     // display window
+    
+    // ---------- constructor ---------- //
+    
+    // called from subclasses
+    protected Bag() {
+        capacity = capacity();
+        levelIndex = capacity % TOTAL_LEVEL; // so that different bags start at different point
+        currentLevel = TOTAL_LEVEL - 1;
+        itemTable = new ArrayList[TOTAL_LEVEL];
+        nameTable = new HashMap<String, Type>((int) (capacity / LOAD_FACTOR), LOAD_FACTOR);
+        showing = false;
+    }
+    
+    // --- property methods --- //
+    
+    protected abstract int capacity();
+    
+    // the number of times for a decay factor to be fully applied
+    // it can be changed in run time by the user, so not a constant
+    protected abstract int forgetRate();
+    
+    // --- Bag property --- //
+    
+    // get the average priority of the bag --- can be removed???
+    public float averagePriority() {
+        if (nameTable.size() == 0)
+            return 0.01f;
+        float f = (float) mass / (nameTable.size() * TOTAL_LEVEL);
+        if (f > 1)
+            return 1.0f;
+        return f;
+    }
+    
+    // check if an item is in the bag
+    public boolean contains(Type it) {
+        return nameTable.containsValue(it);
+    }
+
+    public Type get(String key) {
+        return nameTable.get(key);
+    }
+    
+    // ---------- put/remove methods ---------- //
+    
+    // put a new Item into the Bag
+    public void putIn(Type newItem) {
+        String newKey = newItem.getKey();
+        Type oldItem = nameTable.put(newKey, newItem);
+        if (oldItem != null) {                  // merge duplications
+            outOfBase(oldItem);
+            newItem.merge(oldItem);
+        }
+        Type overflowItem = intoBase(newItem);  // put the (new or merged) item into itemTable
+        if (overflowItem != null) {             // remove overflow
+            String overflowKey = overflowItem.getKey();
+            nameTable.remove(overflowKey);
+        }
+    }
+    
+    // put an item back into the itemTable (it is already in the table)
+    public void putBack(Type oldItem) {
+        BudgetFunctions.forget(oldItem.getBudget(), forgetRate(), RELATIVE_THRESHOLD);
+        putIn(oldItem);
+    }
+    
+    // choose an item according to priority distribution
+    // and take it out of the itemTable
+    public Type takeOut() {     // default behavior: the Item will be putBack
+        if (mass == 0)                              // empty bag
+            return null;
+        if (emptyLevel(currentLevel) || (currentCounter == 0)) { // done with the current level
+            currentLevel = DISTRIBUTOR.pick(levelIndex);
+            levelIndex = DISTRIBUTOR.next(levelIndex);
+            while (emptyLevel(currentLevel)) {          // look for a non-empty level
+                currentLevel = DISTRIBUTOR.pick(levelIndex);
+                levelIndex = DISTRIBUTOR.next(levelIndex);
+            }
+            if (currentLevel < THRESHOLD)
+                currentCounter = 1;                             // for dormant levels, take one item
+            else
+                currentCounter = itemTable[currentLevel].size();     // for active levels, take all current items
+        }
+        Type selected = takeOutFirst(currentLevel); // take out the first item in the level
+        currentCounter--;
+        nameTable.remove(selected.getKey());    // 01-07-04
+        refresh();
+        return selected;
+    }
+    
+    // pick an item by key, then remove it from the bag
+    public Type pickOut(String key) {
+        Type picked = nameTable.get(key);
+        if (picked != null) {
+            outOfBase(picked);
+            nameTable.remove(key);    // 01-07-04
+        }
+        return picked;
+    }
+    
+    // ---------- private methods ---------- //
+    
+    // check whether a level is empty
+    private boolean emptyLevel(int n) {
+        if (itemTable[n] == null)
+            return true;
+        else
+            return itemTable[n].isEmpty();
+    }
+    
+    // decide the in level according to priority
+    private int getLevel(Type item) {
+        float fl = item.getPriority() * TOTAL_LEVEL;
+        int level = (int) Math.ceil(fl) - 1;
+        return (level < 0) ? 0 : level;     // cannot be -1
+    }
+    
+    // insert an item into the itemTable, and return the overflow
+    private Type intoBase(Type newItem) {
+        Type oldItem = null;
+        int inLevel = getLevel(newItem);
+        if (nameTable.size() > capacity) {      // the bag is full
+            int outLevel = 0;
+            while (emptyLevel(outLevel))
+                outLevel++;
+            if (outLevel > inLevel) {           // ignore the item and exit
+                return newItem;
+            } else {                            // remove an old item in the lowest non-empty level
+                oldItem = takeOutFirst(outLevel);
+            }
+        }
+        if (itemTable[inLevel] == null)
+            itemTable[inLevel] = new ArrayList();               // first time insert
+        itemTable[inLevel].add(newItem);          // FIFO
+        mass += (inLevel + 1);                          // increase total mass
+        refresh();                              // refresh the wondow
+        return oldItem;
+    }
+    
+    // take out the first or last Type in a level from the itemTable
+    private Type takeOutFirst(int level) {
+        Type selected = itemTable[level].get(0);
+        itemTable[level].remove(0);         // take the item out
+        mass -= (level + 1);                    // decrease total mass
+        refresh();                              // refresh the wondow
+        return selected;
+    }
+    
+    // remove an item from itemTable, then adjust mass
+    protected void outOfBase(Type oldItem) {
+        int level = getLevel(oldItem);
+        itemTable[level].remove(oldItem);
+        mass -= (level + 1);                    // decrease total mass
+        refresh();                              // refresh the wondow
+    }
+    
+    // ---------- display methods ---------- //
+    
+    public void startPlay(String title) {
+        window = new BagWindow(this, title);
+        showing = true;
+        window.post(toString());
+    }
+    
+    public void play() {
+        showing = true;
+        window.post(toString());
+    }
+    
+    public void refresh() {
+        if (showing)
+            window.post(toString());
+    }
+    
+    public void stop() {
+        showing = false;
+    }
+    
+    // list top levels in a string
+    public String toString() {
+        StringBuffer buf = new StringBuffer(" ");
+        for (int i = TOTAL_LEVEL; i >= window.showLevel(); i--)
+            if (!emptyLevel(i-1)) {
+            buf = buf.append("\n --- Level " + String.valueOf(i) + ":\n ");
+            for (int j = 0; j < itemTable[i-1].size(); j++)
+                buf = buf.append(itemTable[i-1].get(j) + "\n ");
+            }
+        return buf.toString();
+    }
+}
diff --git a/storage/ConceptBag.java b/storage/ConceptBag.java
new file mode 100644
index 0000000000000000000000000000000000000000..054941925b75b8d1a4b70a9ed75d902d0dcbfd96
--- /dev/null
+++ b/storage/ConceptBag.java
@@ -0,0 +1,21 @@
+
+package nars.storage;
+
+import nars.entity.Concept;
+import nars.main.Parameters;
+import nars.gui.MainWindow;
+
+/**
+ * Contains Concepts.
+ */
+public class ConceptBag extends Bag<Concept> {
+    
+    protected int capacity() {
+        return Parameters.CONCEPT_BAG_SIZE;
+    }
+    
+    // this is for active concept only
+    protected int forgetRate() {
+        return MainWindow.forgetCW.value();
+    }
+}
\ No newline at end of file
diff --git a/storage/Distributor.java b/storage/Distributor.java
new file mode 100644
index 0000000000000000000000000000000000000000..5837cd69526d1992df368dbf13b35da8e484d24a
--- /dev/null
+++ b/storage/Distributor.java
@@ -0,0 +1,56 @@
+
+package nars.storage;
+
+/**
+ * A pseudo-random number generator, used in Bag.
+ */
+class Distributor {
+    /**
+     * Random sequence of valid numbers
+     */
+    private int order[];
+    /**
+     * Capacity of the array
+     */
+    private int capacity;
+    
+    /**
+     * For any number N < range, there is N+1 copies of it in the array.
+     * 
+     * @param range Range of valid numbers
+     */
+    public Distributor(int range) {
+        int index, rank, time;
+        capacity = (range * (range + 1)) / 2;
+        order = new int[capacity];
+        for (index = 0; index < capacity; index++)
+            order[index] = -1;
+        for (rank = range; rank > 0; rank--)
+            for (time = 0; time < rank; time++) {
+                index = (((int) (capacity / rank)) + index) % capacity;
+                while (order[index] >= 0)
+                    index = (index + 1) % capacity;
+                order[index] = rank - 1;
+            }
+    }
+    
+    /**
+     * Get the next number according to the given index
+     * 
+     * @param index The current index
+     * @return the random value
+     */
+    public int pick(int index)	{
+        return order[index];
+    }
+    
+    /**
+     * Advance the index
+     * 
+     * @param index The current index
+     * @return the next index
+     */
+    public int next(int index) {
+        return (index + 1) % capacity;
+    }
+}
\ No newline at end of file
diff --git a/storage/TaskBag.java b/storage/TaskBag.java
new file mode 100644
index 0000000000000000000000000000000000000000..16854b3d33e9e0cfae454a39a4c310885bc9b8d8
--- /dev/null
+++ b/storage/TaskBag.java
@@ -0,0 +1,27 @@
+
+package nars.storage;
+
+import nars.entity.Task;
+import nars.gui.MainWindow;
+import nars.main.*;
+
+/**
+ * New tasks that contain new Term.
+ */
+public class TaskBag extends Bag<Task> {
+    public static final int defaultForgetRate = Parameters.NEW_TASK_DEFAULT_FORGETTING_CYCLE;
+    
+    protected int capacity() {
+        return Parameters.TASK_BUFFER_SIZE;
+    }
+    
+    protected int forgetRate() {
+        return MainWindow.forgetTW.value();
+    }
+    
+    // to include immediate tasks
+    public String toString() {
+        return Memory.resultsToString() + super.toString();
+    }
+}
+
diff --git a/storage/TaskLinkBag.java b/storage/TaskLinkBag.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a1522041fcc6b2652fef39ec89e6c27333eba9f
--- /dev/null
+++ b/storage/TaskLinkBag.java
@@ -0,0 +1,21 @@
+
+package nars.storage;
+
+import nars.entity.TaskLink;
+import nars.gui.*;
+import nars.main.Parameters;
+
+/**
+ * TaskLinkBag contains links to tasks.
+ */
+public class TaskLinkBag extends Bag<TaskLink> {
+
+    protected int capacity() {
+        return Parameters.TASK_BAG_SIZE;
+    }
+    
+    protected int forgetRate() {
+        return MainWindow.forgetTW.value();
+    }
+}
+
diff --git a/storage/TermLinkBag.java b/storage/TermLinkBag.java
new file mode 100644
index 0000000000000000000000000000000000000000..b902f894bbaa84c7c9e4814a14b4da6f400030bb
--- /dev/null
+++ b/storage/TermLinkBag.java
@@ -0,0 +1,37 @@
+
+package nars.storage;
+
+import nars.entity.*;
+import nars.gui.MainWindow;
+import nars.main.Parameters;
+
+/**
+ * Contains CompositionLinks to relevant Terms.
+ */
+public class TermLinkBag extends Bag<TermLink> {
+
+    private static final int maxTakeOut = Parameters.MAX_TAKE_OUT_K_LINK;
+    
+    protected int capacity() {
+        return Parameters.BELIEF_BAG_SIZE;
+    }
+    
+    protected int forgetRate() {
+        return MainWindow.forgetBW.value();
+    }
+    
+    // replace defualt to prevent repeated inference
+    public TermLink takeOut(TaskLink tLink) {
+        for (int i = 0; i < maxTakeOut; i++) {
+            TermLink bLink = takeOut();
+            if (bLink == null)
+                return null;
+            if (tLink.novel(bLink)) {
+                return bLink;
+            }
+            putBack(bLink);
+        }
+        return null;
+    }
+}
+
diff --git a/storage/package.html b/storage/package.html
new file mode 100644
index 0000000000000000000000000000000000000000..7e267931eef36c0e33f24628a0ee52fb7a433670
--- /dev/null
+++ b/storage/package.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<body bgcolor="white">
+
+Storage management
+
+<h2>Package Specification</h2>
+
+All items (Concept within Memory, TaskLinks and CompositionLinks within Concept) are put into Bags, which supports priority-based resources allocation.  Also, bag supports access by key (String).
+<p>
+A bag supports three major operations:
+<ul>
+<li>To take out an item by key.</li>
+<li>To take out an item probabilistically according to priority.</li>
+<li>To put an item into the bag.</li>
+</ul>
+All the operations take constant time to finish.
+<p>
+The "take out by priority" operation takes an item out probablistically, with the 
+probability proportional to the priority value.
+<p>
+The probability distribution is generated from a deterministic table.
+<p>
+All classes in package <tt>nars.storage</tt> extend <tt>Bag</tt>.
+<p>
+In NARS, the memory consists of a bag of concepts.  Each concept uniquely corresponds to a term, which uniquely corresponds to a String served as its name.  It is necessary to separate a term and the corresponding concept, because a concept may be deleted due to space competition, and a term is removed only when no other term is linked to it.  In the system, there may be multiple terms refer to the same concept, though the concept just refer to one of them.  NARS does not follow a "one term, one concept" policy and use a hash table in memory to maps names into terms, because the system needs to remove a concept without removing the term that naming it. 
+<p>
+Variable terms correspond to no concept, and their meaning is local to the "smallest" term that contains all occurences of the variable.
+<p>
+From name to term, call Term.nameToTerm(String).  From name to concept, call Concept.nameToConcept(String).   Both use the name as key to get the concept from the concept hashtable in memory. 
+<p>
+The main memory also contains buffers for new tasks. One buffer contains tasks to be processed immediately (to be finished in constant time), and the other, a bag, for the tasks to be processed later.
+
+</body>
+</html>