<< Back to previous view

[LOGIC-158] membero documentation does not describe arguments Created: 30/May/14  Updated: 30/May/14

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Phillip Lord Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

(doc clojure.core.logic/membero)
-------------------------
clojure.core.logic/membero
A relation where l is a collection, such that l contains x.

Membero documentation is rather unhelpful because it doesn't
describe the order of the arguments (i.e. which is l and which
is x). appendo and permuteo are similarly affected, so it appears
to be a weakness in defne






[LOGIC-156] Two consecutive calls to run* return different results Created: 22/Feb/14  Updated: 05/Mar/14

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mauro Lopes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: bug

Attachments: File core.clj     File core.clj    

 Description   

Calling run* twice with the same input may return different results for no apparent reason.
See attachment.



 Comments   
Comment by David Nolen [ 05/Mar/14 9:19 AM ]

The problematic code incorrectly uses finite domain operations on fresh variables that have not be assigned domains. Can we get an updated version of the problematic code that demonstrate the issue after the corrections?

Comment by Mauro Lopes [ 05/Mar/14 9:25 PM ]

Sure. I have just added a new code version with finite domains assigned to each variable that is involved in an fd operation. The problem persists.





[LOGIC-154] walk* of an empty set overflows the stack Created: 31/Dec/13  Updated: 31/Dec/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Kevin Downey Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

I noticed this issue when asserting a relation with an empty set in it in the new pldb stuff, the minimal case I have is (walk* empty-s #{})






[LOGIC-146] run macro should take * in addition to n Created: 26/Nov/13  Updated: 26/Nov/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None





[LOGIC-144] Extending cljs.core.logic with all of the functionality from clojure.core.logic Created: 19/Nov/13  Updated: 04/Dec/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Adrian Medina Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Repo I've been working from is here: https://github.com/aamedina/cljs.core.logic

I've followed the code layout in clojure.core.logic pretty much verbatim, compensating for ClojureScript necessities, moved macros into sister .clj files. I have no hard opinions about the best way to organize the macros for each file, the way I do it is just the way I've found easiest, because then you don't have to namespace qualify every syntax quoted form that references a function in the sister .cljs file.

Additionally I ported all of the clojure.core.logic tests with clojurescript.test.



 Comments   
Comment by Adrian Medina [ 27/Nov/13 10:43 PM ]

So I've updated the repo to reflect a more faithful adherence to the existing cljs.core.logic code conventions and organization. I'd appreciate any insight into how to potentially optimize the code to run more on par with the JVM.

Comment by David Nolen [ 04/Dec/13 1:09 AM ]

What kinds of performance problems are you observing? Do you have a particular benchmark that you're running?

Comment by Adrian Medina [ 04/Dec/13 12:18 PM ]

Appendo and membero are particularly slow.

(dotimes [i ie5] (run* [q] (== q true))) takes around ~4500ms, which is about 10x slower than the JVM.

After profiling, I've discovered the problem - pretty standard recursion optimizations are necessary, it's re-walking (by reunifying) lvars recursively as the code is executed. E.g.,
for appendo, (run 5 [q] (fresh [x y] (appendo x y q))) will expand into something like..

((_0) (_0 . _1) (_0 _1 . _2) ... ))

and it's slow because every new list is actually re-walking every previously unified lvar again.

I'll have more time to investigate this over the weekend, but I think it might be as simple as not generating unique lvars by default, since then the identical? for unification check should catch any attempted walk.





[LOGIC-142] Unified map values are returned as LVar rather than the unified value in ClojureScript Created: 29/Sep/13  Updated: 29/Sep/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Darrick Wiebe Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None
Environment:

ClojureScript



 Description   

This works correctly in core.logic for clojure:

(run 1 [q]
     (fresh [v]
            (== v 1)
            (== {:x v} q)))

In ClojureScript, I get this though:

({:x <lvar:v_4>})





[LOGIC-140] compile time occurs check for pattern matching Created: 25/Jun/13  Updated: 25/Jun/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Claire Alvis correctly pointed out that it's easy to make a mistake when you have many clauses and accidentally use parameter name in a match for that parameter - we could easily do a compile time occurs check for these cases.






[LOGIC-136] Make benchmark suite as easy to run as `lein test` Created: 09/May/13  Updated: 09/May/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Austin Haas Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

`lein benchmark` (or some other non-lein-based incantation) prints a report listing the name of each benchmark and its timing.

Example:

$ lein benchmark
membero 2839
zebra 152738
$ lein benchmark comparison-report {:baseline "benchmark-5.9.2013-1"
                                    :runs 5
                                    :diffs-only true
                                    :threshold 25} ; only consider different if the delta is > 25 ms.                                     
membero +68ms
zebra -122ms
$ lein benchmark {:pretty true}
Thu May  9 11:21:41 PDT 2013

Linux mars 2.6.32-5-amd64 #1 SMP Fri Feb 15 15:39:52 UTC 2013 x86_64 GNU/Linux

java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)

membero     2839 ms
zebra     152738 ms

I haven't looked for any Clojure benchmarking libs, but ideally this would be a trivial script that automates the repetitive manual task of running benchmark tests. Unlike the test suite, we aren't looking for binary success or failure. Every run will generate unique results, so the script should accommodate a fuzzier comparison.

A "pretty" output that includes system info would be great for bug reports.



 Comments   
Comment by David Nolen [ 09/May/13 1:44 PM ]

Sounds like an excellent enhancement to me. Patch welcome for this.





[LOGIC-133] Add label goal Created: 29/Apr/13  Updated: 11/Jan/14

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

This goal should enumerate any fd vars found in a fresh/ground var bound to a sequence. This would avoid some unintuitive behavior that came up on the mailing list:

(defne weighted-listo [l w]
  ([() _] (fd/== w 0))
  ([[h . t] _]
    (fresh [n]
      (fd/in n (fd/interval 0 java.lang.Integer/MAX_VALUE))
      (fd/in h (fd/interval 1 java.lang.Integer/MAX_VALUE))
      (fd/+ h n w)
      (weighted-listo t n))))


 Comments   
Comment by Austin Haas [ 11/Jan/14 10:23 PM ]

Here is a link to the discussion on the mailing list: https://groups.google.com/forum/#!topic/minikanren/MgcvtkA6_EI





[LOGIC-129] matcha/matchu are not faithful to the semantics of conda/condu Created: 04/Apr/13  Updated: 09/Jun/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Austin Haas Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

The semantics of conda and condu give special importance to the head of each clause, but the pattern matching macros in core.logic that are built on top of them merge the body into the head. That means that every expression in a body position is considered when deciding which line is chosen, rather than just the expression in the head position. This is because the entire clause is wrapped in a fresh expression to bind the implicitly specified lvars that appear in the pattern.

To illustrate:

(matcha ['a]
    [['a] u#]
    [['a] s#])

;; expands to:

(conda
  [(fresh [] (== 'a 'a) u#)]
  [(fresh [] (== 'a 'a) s#)])

;; which has a different meaning than:

(conda
  [(== 'a 'a) u#]
  [(== 'a 'a) s#])

Ideally, we could devise a new system to marry the semantics of conda with pattern matching. At the very least, I think the offending macros should carry a warning in their docstring, stating this semantic difference.

I suspect this would also cause the "Third Commandment" warning to apply to the entire line, rather than just the head/question, but I have not investigated that issue.

Here's an example that demonstrates the difference in meaning:

;; This does not succeed, because we commit to the first line, 
;; since the question succeeds, but then fail in the body.

(run* [q]
  (conda
   [(== 'a 'a) u#]
   [(== 'a 'a) s#]))

;; => ()

;; This succeeds, because the whole line is used to determine which line to commit to, 
;; rather than just the pattern matching clause at the head. So when the first line fails,
;; the second line is tried.

(run* [q]
  (matcha ['a]
   [['a] u#]
   [['a] s#]))

;; => (_0)


 Comments   
Comment by Austin Haas [ 04/Apr/13 2:35 PM ]

For reference:

The Third Commandment

If prior to determining the question of a conda (or condu) line a variable is fresh, it must remain fresh in the question of that line.

From The Reasoned Schemer.

Comment by David Nolen [ 04/Apr/13 5:16 PM ]

Good catch I need to think about this one.

Comment by David Nolen [ 09/Jun/13 12:11 PM ]

The issue is that we rely on the `conde` macro which isn't very flexible. We should probably do something one step lower so can wrap each conde line in a `let` that constructs the logic vars and then ensure that all the head unifications are wrapped in one `fresh` so that the semantics of conda/u are preserved.





[LOGIC-128] add mod/rem/abs/min/max Created: 02/Apr/13  Updated: 02/Apr/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

SWI-Prolog's CLP(FD) module has this functionality http://www.swi-prolog.org/man/clpfd.html






[LOGIC-119] tie disequality Created: 12/Mar/13  Updated: 12/Mar/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Nada Amin Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   
(deftest test-tie-disequality
  (is (= (run* [q]
           (nom/fresh [a b]
             (!= (nom/tie a a) 'hello)))
        '(_0)))
  (is (= (run* [q]
           (nom/fresh [a b]
             (!= (nom/tie a a) (nom/tie b b))))
        ())))

Currently, the first causes an error, because IPersistentMap (which gets called because Tie is a record!) assumes that the the other term is also a record (that seems like a bug). If we revert the commit which makes Tie a record, this works.

The other one succeeds, when it should fail. This is regardless of whether Tie is a record or not.



 Comments   
Comment by Nada Amin [ 12/Mar/13 5:52 AM ]

quick fix in https://github.com/clojure/core.logic/commit/8af0f45f8d1cb515ec7a00e5acd751562a31bb37

for actually doing != modulo alpha equivalence requires the opposite of nom/hash.





[LOGIC-118] prep does not make lvar of ?x if ?x is in a vector Created: 11/Mar/13  Updated: 17/Mar/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Jonas Enlund Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: File LOGIC-118.diff    

 Description   

In latest master:

user=> (use 'clojure.core.logic.unifier)
user=> (prep '([?x]))
([<lvar:?x>]) ;; ok. ?x turned into an lvar
user=> (prep '([?x] . ?xs))
([?x] . <lvar:?xs>) ;; fail. ?x is not an lvar

The last expression should return ([<lvar:?x>] . <lvar:?xs>)



 Comments   
Comment by Jonas Enlund [ 12/Mar/13 12:11 AM ]

patch LOGIC-118.diff fixes this issue. I'm not sure if it does so correctly.

Comment by David Nolen [ 17/Mar/13 11:42 AM ]

It's seem to me that this should be corrected in prep*? It looks like the issue is that prep* only handles seqs and not collections in general.





[LOGIC-117] one-shot constraints with multiple rands may run more than once Created: 11/Mar/13  Updated: 19/Mar/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Nada Amin Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Here are two examples using fixc:

(run* [q]
  (fresh [x y]
    (fixc [x y]
      (fn [[x y] a _] (!= x 1))
      (fn [[x y] a] (= (walk a x) (walk a y)))
      '...)
    (== x y)))

(run* [q]
  (fresh [x y c d]
    (fixc [x y]
      (fn [[x y] a _] (!= x y))
      (fn [[x y] a] (or (not (lvar? (walk a x))) (not (lvar? (walk a y)))))
      '...)
    (== [x y] [[c] [d]])))

The constraint != is reified twice in each example, showing that the fixc constraint indeed ran twice.



 Comments   
Comment by David Nolen [ 17/Mar/13 7:27 PM ]

For the first example I see the following as a result on master:

((_0 :- (!= (_1 1))))

Is this what you're seeing? As x isn't even part of the answer I wonder if we should show these constraints?

Comment by Nada Amin [ 19/Mar/13 1:36 AM ]

I changed the reifier by setifying the set of constraints, hence you get only one result now. So my illustration is now out-of-date but the problem remains. It's a separate issue to filter out irrelevant constraints, and I agree it should be done (I try to do it for the nominal constraints).





[LOGIC-114] stack overflow with conda/u Created: 14/Feb/13  Updated: 17/Mar/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Austin Haas Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   
(ns test
  (:refer-clojure :exclude [==])
  (:require
   [clojure.core.logic :refer :all]))
 
(defn foo [in out]
  (matcha 
   [in out]
   ([('and a b . ()) ('and x y . ())] (foo a x) (foo b y))
   ([a ('bar ('baz a . ()) . ())])))
 
;; I get a stack overflow with the following, but if I remove one conjunct, then it will run.
 
(run 1 [q] (foo
            '(and p
                  (and p
                       (and p
                            (and p
                                 (and p
                                      (and p
                                           (and p
                                                (and p
                                                     (and p
                                                          (and p
                                                               (and p p)))))))))))
            q))


 Comments   
Comment by David Nolen [ 14/Feb/13 1:22 PM ]

It looks this issue still exists even if you swap the matcha with matche

Comment by Austin Haas [ 14/Feb/13 8:06 PM ]

I think the overflow is occurring during reification.

I was getting this error when returning a result from run, but now that I'm using the same value as the input to another goal there is no overflow.

If you replace q in the foo call with a fresh variable, it will not overflow.

Comment by David Nolen [ 17/Mar/13 7:30 PM ]

This works for me on master. Can you give me more specifics about your setup so I can try to recreate? I'm on OS X 10.8 running JDK 7 64bit.

Comment by Austin Haas [ 17/Mar/13 8:50 PM ]

I don't see the issue anymore, but I believe I was using Java 1.6 when I reported it and now I am using:

$ java -version
java version "1.7.0_15"
Java(TM) SE Runtime Environment (build 1.7.0_15-b03)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)

Comment by David Nolen [ 17/Mar/13 9:02 PM ]

OK, thanks for the quick response, I'll double check how things look under 1.6.





[LOGIC-99] StackOverflow for large `appendo` Created: 05/Jan/13  Updated: 07/Jan/13

Status: In Progress
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   
(def l (range 0 2000))
(run* [q] (appendo l l q))

Stacktrace

user=> (def l (range 0 2000))
#'user/l
user=> (run* [q] (appendo l l q))
StackOverflowError   clojure.core.logic.LVar (logic.clj:1307)
user=> (pst)
StackOverflowError 
    clojure.core.logic.LVar (logic.clj:1307)
    clojure.lang.KeywordLookupSite$1.get (KeywordLookupSite.java:45)
    clojure.core.logic.LVar (logic.clj:1325)
    clojure.lang.Util.equiv (Util.java:32)
    clojure.lang.PersistentHashMap$BitmapIndexedNode.find (PersistentHashMap.java:601)

    clojure.lang.PersistentHashMap$ArrayNode.find (PersistentHashMap.java:370)
    clojure.lang.PersistentHashMap$ArrayNode.find (PersistentHashMap.java:370)
    clojure.lang.PersistentHashMap.entryAt (PersistentHashMap.java:133)
    clojure.lang.RT.find (RT.java:720)
    clojure.core/find (core.clj:1432)
    clojure.core.logic.Substitutions (logic.clj:1134)
    clojure.core.logic/walk*/fn--2847 (logic.clj:1005)


 Comments   
Comment by David Nolen [ 05/Jan/13 2:44 PM ]

I cannot recreate on my machine but should try a large N & wait and see.





[LOGIC-89] Allow application again in pattern matches Created: 01/Jan/13  Updated: 07/Jan/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: 0.8.0


 Description   

Perhaps we can support simple function application in the following manner.

(defne substo [e new a out]
  (['(var ~a) _ _ new])
  (['(var ~y) _ _ '(var ~y)] (nom/hash a y))
  (['(app ~rator ~rand) _ _ '(app ~rator-res ~rand-res)]
     (substo rator new a rator-res)
     (substo rand new a rand-res))
  (['(lam ~(nom/tie c body)) _ _ '(lam ~(nom/tie c body-res))]
     (nom/hash c a) (nom/hash c new)
     (substo body new a body-res)))

If we have a seq in an unquote then we know we have an application. All function symbols are left alone, all arguments are considered to be fresh vars or locals.






[LOGIC-68] add Prolog meta-logical predicates bagof, setof, findall Created: 16/Nov/12  Updated: 13/Jun/13

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File 0001-LOGIC-68-Add-run-a-run-a-logic-functions-to-support-.patch    

 Description   

This can be done by annotating logic variables and embedding a run within a run by passing in the current substitution, running on it, and extracting out the reified values and unifying it back into the current substitution.



 Comments   
Comment by Aaron Brooks [ 16/Nov/12 1:23 PM ]

I'm working on understanding the answers given for this StackOverflow question:

http://stackoverflow.com/questions/7647758/prolog-findall-implementation

Comment by Aaron Brooks [ 16/Nov/12 10:02 PM ]

For discussion — Initial working patch supporting nested version of 'run-a/'run-a*. Binding symbols must match existing lvars with which vectors of all returned values will be unified.

Comment by Aaron Brooks [ 17/Nov/12 4:07 PM ]

Should we provide wrappers that emulate each of bagof, setof and findall?

I'm still not sold on the current names run-a/run-a*. "a" is really an internal implementation detail. Expect a revised patch with better names when I think of them (any ideas?).

Comment by David Nolen [ 17/Nov/12 8:32 PM ]

Yes please. Yeah I don't think the names of run-a/etc are critical - implementation details for now until we really understand the implications and it gets some use.

Comment by Aaron Brooks [ 21/Nov/12 10:51 AM ]

I'm considering simply making run/run* conditionalized on the first argument, using the nesting form if we are being called with a substitution map as the first argument.

My current understanding of bagof and findall makes me think they're not worth implementing beyond the nesting run functionality.

I'm still thinking about setof which is quite useful and will want help from the infrastructure to be fully efficient.

I'll submit a new patch after Thanksgiving.

Comment by David Nolen [ 21/Nov/12 10:57 AM ]

Excellent, thanks much.

Comment by Aaron Brooks [ 06/Dec/12 12:30 AM ]

I have not forgotten, I've just gotten swamped.

There's a small chance I'll get to this before Christmas, otherwise, it's after the new year.

Comment by Aaron Brooks [ 04/Feb/13 5:50 PM ]

After a car accident, travel to London and Morocco, getting caught up at work and getting caught up on the apparently very busy stream of core.logic activity (great work!), I'm back on this.

I found some bugs in my implementation after porting the patch forwards and realized these issues highlighted my sloppy understanding of some of the semantics I had created.

I'm fairly convinced now that we don't want to name this after run or run*. It's too much of a strain to try to make it mean the same thing in a nested context.

The current mechanism is still not quite a match for findall/bagof/setof, however, so I'm seeing what a good fit would be. I'll post meaningful thoughts for review as I have them.

Comment by David Nolen [ 13/Feb/13 1:50 PM ]

Glad to hear you're OK! No worries, will take a patch whenever you get around to one.

Comment by Aaron Brooks [ 12/Jun/13 9:03 PM ]

Having switched to Datomic which has bagof/setof type aggregation and subqueries (though the switch wasn't for those specifically) I don't know if or when I'll get back to this. Sorry!

Comment by David Nolen [ 13/Jun/13 1:36 AM ]

No worries! Glad Datomic is working out for you





[LOGIC-47] is macro needs to be improved Created: 30/Aug/12  Updated: 30/Aug/12

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

the is macro should work like the following:

(is x (- (+ a b) c))

All locals appearing in the right expression should be walked.






[LOGIC-38] Logic Threading Macro Created: 13/May/12  Updated: 13/May/12

Status: Open
Project: core.logic
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Jason Jackson Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File 0001-added-logic-threading-macro.patch     Text File 0001-added-logic-threading-macro.patch    

 Description   

This macro was somewhat useful when I was implementing static analysis for a compiler with core.logic.

(defmacro ==>> [expr-in & rel-forms]
"Thread the expr-in through rel-forms then unify with last rel-forms
(the 'out expression').

Example:
(==>> [[1]] (firsto) (firsto) x))
;; 'x' will become bound to value 1

This macro expands to:
(fresh [_A _B]
(firsto [[1]] _A)
(firsto _A _B)
(== _B q))

If you imagine that the 'return value' of firsto is its last parameter,
then it works just like clojure.core/-> as return value of each form is
first argument of the following form."



 Comments   
Comment by Jason Jackson [ 13/May/12 11:08 AM ]

There might be a better name, not sure.

Comment by Jason Jackson [ 13/May/12 11:18 AM ]

renamed ==>> to ==->





Generated at Sat Jul 26 12:13:30 CDT 2014 using JIRA 4.4#649-r158309.