001    /**
002     * Clifford.java
003     *
004     * This file is part of jclifford package and it's distributed under the terms of the MIT license.
005     *
006     * The MIT License :
007     * -----------------
008     * Copyright (c) 2002, 2003, 2004, 2005 Giorgio Vassallo, Pietro Brignola
009     *
010     * Permission is hereby granted, free of charge, to any person obtaining a
011     * copy of this software and associated documentation files (the "Software"),
012     * to deal in the Software without restriction, including without limitation
013     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
014     * and/or sell copies of the Software, and to permit persons to whom the
015     * Software is furnished to do so, subject to the following conditions:
016     * The above copyright notice and this permission notice shall be included in
017     * all copies or substantial portions of the Software.
018     *
019     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
020     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
021     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
022     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
023     * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
024     * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
025     * DEALINGS IN THE SOFTWARE.
026     */
027    
028    package jclifford;
029    
030    import java.util.Map;
031    import java.util.TreeMap;
032    import java.util.Iterator;
033    
034    /**
035     * <p>This class represents a Clifford element and all the operation in a signed space.</p>
036     * <p>Use this class for p + q <= 8. For p + q > 8 use CliffordBitSet or CliffordTreeSet instead.</p>
037     * @version <p>0.9</p>
038     * @author <p>Realized by <a href="mailto:vassallo@csai.unipa.it">Giorgio Vassallo</a>, <a href="mailto:pietro.brignola@libero.it">Pietro Brignola</a>, November 2002.</p>
039     * @see CliffordBitSet
040     * @see CliffordTreeSet
041     */
042    public class Clifford{
043    
044            /**
045             * Element's blade-value mappings.
046             */
047            protected TreeMap map;
048    
049            /**
050             * Algebra's Precision.
051             */
052            protected static double eps = 1e-12;
053    
054            /**
055             * Number of dimensions with positive square versors.
056             */
057            protected static int p = 0;
058    
059            /**
060             * Number of dimensions with negative square versors.
061             */
062            protected static int q = 0;
063    
064            /**
065             * Algebra's dimension.
066             */
067            protected static int dim = 0;
068            
069            /**
070             * Algebra's possible blades (2 ^ dim).
071             */
072            protected static int hdim = 1;
073    
074            /**
075             * Mask with bits 1 corresponding to the presence of dimensions (starting from l.s.b.).
076             *
077             * Es.: dim = p + q = 5, spacemask = [0...011111].
078             */
079            protected static int spaceMask = 0;
080    
081            /**
082             * Mask with p bits 0 and q bits 1 corresponding to negative square versors (starting from l.s.b.).
083             *
084             * Es.: p = 3, q = 2, signmask = [0...011000].
085             */
086            protected static int signMask = 0;
087    
088            /**
089             * Stores the grade (number of bits 1 in the binary rappresentation) of a blade.
090             */
091            protected static int[] gradeTable = {0};
092    
093            /**
094             * Stores the grade (number of bits 1 in the binary rappresentation) of a blade.
095             */
096            protected static boolean[][] signTable = {{false}};
097            
098            /**
099             * Algebra's pseudoscalar.
100             */
101            protected static Clifford pseudoScalar;
102    
103            /**
104             * Gets algebra's precision.
105             * @return algebra's precision.
106             */
107            public static double getEps()
108            {
109                    return eps;
110            }
111    
112            /**
113             * Sets algebra's precision.
114             * @throws IllegalArgumentException if the precision is negative.
115             * @param e positive precision.
116             */
117            public static void setEps(double e) throws IllegalArgumentException{
118                    //Positive precision required
119                    if(e <= 0)
120                            throw new IllegalArgumentException("Invalid precision: positive precision required.");
121                    eps = e;
122            }
123    
124            /**
125             * Gets algebra's dimension.
126             * @return algebra's dimension.
127             */
128            public static int getDim(){
129                    return dim;
130            }
131    
132            /**
133             * Gets algebra's possible blades.
134             * @return algebra's possible blades.
135             */
136            public static int getPossibleBlades(){
137                    return hdim;
138            }
139    
140            /**
141             * Counts the number of bits 1 in the binary rappresentation of a blade.
142             * @param bld the blade whose number of bits 1 in its binary rappresentation are to be counted.
143             * @return the number of bits 1 in the binary rappresentation of the specified blade.
144             */
145            private static int bitCount(int bld){
146                    int count = 0;
147                    while(bld != 0){
148                            count ++;
149                            bld &= bld - 1;
150                    }
151                    return count;
152            }
153    
154            /**
155             * Computes the sign of the product of two blades.
156             * @param bld1 the first blade of the product.
157             * @param bld2 the second blade of the product.
158             * @return true if the sign of the product of the specified blades is negative, false otherwise.
159             */
160            private static boolean getSign(int bld1,int bld2){
161                    int k,l,sign = 0;
162                    for(k = 1; k < hdim; k <<= 1){
163                            if((bld1 & k) != 0){
164                                    l = bld2 & (k -1);
165                                    if((gradeTable[l] & 1) != 0)
166                                            sign ^= 1;
167                            }
168                    }
169                    l = bld1 & bld2 & signMask;
170                    if((gradeTable[l] & 1) != 0)
171                            sign ^= 1;
172                    return sign != 0;
173            }
174    
175            /**
176             * Computes binomial coefficents.
177             * @param n the dimension.
178             * @param k the grade.
179             * @return binomial coefficent.
180             */
181            private static int bCoefficent(int n, int k){
182                    if((k == 0) || (k == n))
183                            return 1;
184                    if(k > (n / 2))
185                            k = n - k;
186                    double c = ((double) n) / k;
187                    n--;
188                    k--;
189                    while(k != 0){
190                            c *= n;
191                            c /= k;
192                            n--;
193                            k--;
194                    }
195                    return ((int) c);
196            }
197    
198            /**
199             * Initializes algebra's signature.
200             * @throws IllegalArgumentException if p or q are negative or their sum is greater than 8.
201             * @param pdim the number of dimensions with versors that square in 1.
202             * @param ndim the number of dimensions with versors that square in -1.
203             */
204            public static void init(int pdim, int ndim) throws IllegalArgumentException{
205                    //Checking for valid signature
206                    if((pdim < 0) || (ndim < 0) || ((pdim + ndim) > 8))
207                            throw new IllegalArgumentException("Invalid signature: use p >= 0, q >= 0, 0 <= p + q <= 8");
208                    //Initializing signature
209                    p = pdim;
210                    q = ndim;
211                    dim = p + q;
212                    hdim = 1 << dim;
213                    //Initializing spaceMask
214                    spaceMask = hdim - 1;
215                    //Initializing signMask
216                    signMask = ~((1 << p) - 1) & spaceMask;
217                    //Initializing gradeTable
218                    gradeTable = new int[hdim];
219                    for(int i = 0; i < hdim; i ++)
220                            gradeTable[i] = bitCount(i);
221                    //Initializing signTable
222                    signTable = new boolean[hdim][hdim];
223                    for(int i = 0; i < hdim; i++)
224                            for(int j = 0; j < hdim; j++)
225                                    signTable[i][j] = getSign(i,j);
226                    //Initialiting pseudoscalar
227                    pseudoScalar = new Clifford();
228                    pseudoScalar.map.put(new Blade(spaceMask),new Value(1.0));
229            }
230    
231            /**
232             * Sets meet operation subspace.
233             * @throws IllegalArgumentException if subspace is negative or outside algebra's space mask.
234             * @param subspace the meet operation subspace.
235             */
236            public static void setMeetSubSpace(int subspace) throws IllegalArgumentException{
237                    //Checking for valid subspace
238                    if(subspace < 0)
239                            throw new IllegalArgumentException("Blade cannot be negative.");
240                    if(subspace > spaceMask)
241                            throw new IllegalArgumentException("Invalid signature for this operation.");
242                    //Setting pseudoscalar
243                    pseudoScalar = new Clifford();
244                    pseudoScalar.map.put(new Blade(subspace),new Value(1.0));
245            }
246    
247            /**
248             * Creates and returns an element with no blade-value mappings.
249             * @throws RuntimeException if algebra's signature is not initialized.
250             */
251            public Clifford() throws RuntimeException{
252                    map = new TreeMap();
253            }
254            
255            /**
256             * Creates and returns an element with specified blade-value mappings.
257             * @throws RuntimeException if algebra's signature is not initialized.
258             * @throws IllegalArgumentException if the arrays have different lenght.
259             * @param blades the int array representing the specified blades.
260             * @param values the double array representing the corresponding values.
261             */
262            public Clifford(int blades[], double values[]) throws RuntimeException, IllegalArgumentException{
263                    //Checking for same length arrays
264                    if(blades.length != values.length)
265                            throw new IllegalArgumentException("Different length arrays.");
266                    //Creating element and adding mappings
267                    map = new TreeMap();
268                    for(int i = 0; i < blades.length; i ++)
269                            map.put(new Blade(blades[i]),new Value(values[i]));
270            }
271    
272            /**
273             * Gets the value of a blade.
274             * @throws IllegalArgumentException if blade is negative or outside algebra's space mask.
275             * @param blade the int representing the specified blade whose value is to be retrieved.
276             * @return the value of the specified blade, 0.0 if blade is not present.
277             */
278            public double get(int blade) throws IllegalArgumentException{
279                    if(blade < 0)
280                            throw new IllegalArgumentException("Blade cannot be negative.");
281                    if(blade > spaceMask)
282                            throw new IllegalArgumentException("Invalid signature for this operation.");
283                    Value val = (Value) map.get(new Blade(blade));
284                    return (val != null) ? val.value : 0.0;
285            }
286    
287            /**
288             * Gets the value of a blade.
289             * @throws IllegalArgumentException if blademask is invalid, blade is negative or outside algebra's space mask.
290             * @param blademask the binary mask representing the specified blade whose value is to be retrieved.
291             * @return the value of the specified blade, 0.0 if blade is not present.
292             */
293            public double get(String blademask) throws IllegalArgumentException{
294                    int blade;
295                    try{
296                            blade = Integer.parseInt(blademask,2);
297                    }catch(Exception e){
298                            throw new IllegalArgumentException("Invalid mask for this operation.");
299                    }
300                    if(blade < 0)
301                            throw new IllegalArgumentException("Blade cannot be negative.");
302                    if(blade > spaceMask)
303                            throw new IllegalArgumentException("Invalid signature for this operation.");
304                    Value val = (Value) map.get(new Blade(blade));
305                    return (val != null) ? val.value : 0.0;
306            }
307    
308            /**
309             * Puts a new blade-value mapping or updates existing.
310             * @throws IllegalArgumentException if blade is negative or outside algebra's space mask.
311             * @param blade the int representing the specified blade that is to be put.
312             * @param value the double representing the corresponding value of the specified blade.
313             */
314            public void put(int blade, double value) throws IllegalArgumentException{
315                    if(blade < 0)
316                            throw new IllegalArgumentException("Blade cannot be negative.");
317                    if(blade > spaceMask)
318                            throw new IllegalArgumentException("Invalid signature for this operation.");
319                    //Adding tresholded value
320                    map.put(new Blade(blade), new Value((Math.abs(value) >= eps) ? value : 0.0));
321            }
322    
323            /**
324             * Puts a new blade-value mapping or updates existing.
325             * @throws IllegalArgumentException if blademask is invalid, blade is negative or outside algebra's space mask.
326             * @param blademask the binary mask representing the specified blade that is to be put.
327             * @param value the double representing the corresponding value of the specified blade.
328             */
329            public void put(String blademask, double value) throws IllegalArgumentException{
330                    int blade;
331                    try{
332                            blade = Integer.parseInt(blademask,2);
333                    }catch(Exception e){
334                            throw new IllegalArgumentException("Invalid mask for this operation.");
335                    }
336                    if(blade < 0)
337                            throw new IllegalArgumentException("Blade cannot be negative.");
338                    if(blade > spaceMask)
339                            throw new IllegalArgumentException("Invalid signature for this operation.");
340                    //Adding tresholded value
341                    map.put(new Blade(blade), new Value((Math.abs(value) >= eps) ? value : 0.0));
342            }
343    
344            /**
345             * Removes blade-value mapping if existing.
346             * @throws IllegalArgumentException if blade is negative or outside algebra's space mask.
347             * @param blade the int representing the specified blade that is to be removed.
348             */
349            public void remove(int blade) throws IllegalArgumentException{
350                    if(blade < 0)
351                            throw new IllegalArgumentException("Blade cannot be negative.");
352                    if(blade > spaceMask)
353                            throw new IllegalArgumentException("Invalid signature for this operation.");
354                    map.remove(new Blade(blade));
355            }
356    
357            /**
358             * Returns a string representation of the element.
359             * @return the string representation of the element.
360             */
361            public String toString(){
362                    //String representation of the element
363                    String str = new String();
364                    //Temporary reference variables
365                    Map.Entry entry;
366                    Blade bld;
367                    Value val;
368                    //For all blade-value mappings
369                    Iterator it = map.entrySet().iterator();
370                    while(it.hasNext()) {
371                            //Getting blade and value
372                            entry = (Map.Entry) it.next();
373                            bld = (Blade) entry.getKey();
374                            val = (Value) entry.getValue();
375                            str = str + bld.toString() + "\t==>\t" + val.value + "\n";
376                    }
377                    str = str + "---------\n";
378                    //Returning the string
379                    return str;
380            }       
381    
382            /**
383             * Creates and returns an element deeply cloning this element.
384             */
385            public Object clone(){
386                    //Creating a new empty element
387                    Clifford newcl = new Clifford();
388                    //Temporary reference variables
389                    Map.Entry entry;
390                    Blade bld;
391                    Value val;
392                    //For all blade-value mappings
393                    Iterator it = map.entrySet().iterator();
394                    while(it.hasNext()) {
395                            //Getting blade and value
396                            entry = (Map.Entry) it.next();
397                            bld = (Blade) entry.getKey();
398                            val = (Value) entry.getValue();
399                            //Adding new blade-value mapping to newcl
400                            newcl.map.put(new Blade(bld.blade), new Value(val.value));
401                    }
402                    return newcl;
403            }
404    
405            /**
406             * Removes blades with values lower than eps.
407             */
408            public final void noZero(){
409                    //Temporary reference variables
410                    Map.Entry entry;
411                    Blade bld;
412                    Value val;
413                    //For all blade-value mappings
414                    Iterator it = map.entrySet().iterator();
415                    while(it.hasNext()) {
416                            entry = (Map.Entry) it.next();
417                            bld = (Blade) entry.getKey();
418                            val = (Value) entry.getValue();
419                            //Tresholding
420                            if(java.lang.Math.abs(val.value) < eps)
421                                    //Removing blade-value mappings
422                                    it.remove();
423                    }
424            }
425    
426            /**
427             * Computes the quad module of an element discarding signature.
428             * @return the quad module of the specified element discarding signature.
429             */
430            public final double uQuadMod(){
431                    //Temporary unsigned quad module
432                    double uqm = 0.0;
433                    //Temporary reference variables
434                    Map.Entry entry;
435                    Value val;
436                    //For all blade-value mappings
437                    Iterator it = map.entrySet().iterator();
438                    while(it.hasNext()) {
439                            //Getting value
440                            entry = (Map.Entry) it.next();
441                            val = (Value) entry.getValue();
442                            //Uptdating temporary module
443                            uqm += val.value * val.value;
444                    }
445                    return uqm;
446            }
447    
448            /**
449             * Computes the quad module of an element regarding signature.
450             * @return the quad module of the specified element regarding signature.
451             */
452            public final double sQuadMod(){
453                    //Temporary module
454                    double sqm = 0.0;
455                    //Temporary reference variables
456                    Map.Entry entry;
457                    Blade bld;
458                    Value val;
459                    //For all blade-value mappings
460                    Iterator it = map.entrySet().iterator();
461                    while(it.hasNext()) {
462                            //Getting blade and value
463                            entry = (Map.Entry) it.next();
464                            bld = (Blade) entry.getKey();
465                            val = (Value) entry.getValue();
466                            //Uptdating temporary module regarding versor square sign
467                            sqm += val.value * (signTable[bld.blade][bld.blade] ? -val.value : val.value);
468                    }
469                    return sqm;
470            }
471    
472            /**
473             * Normalizes this element respect the unsigned module.
474             */
475            public final void normalize(){
476                    //Unsigned module
477                    double um = Math.sqrt(uQuadMod());
478                    //Checking for null module
479                    if(um == 0.0)
480                            return;
481                    //Temporary reference variables
482                    Map.Entry entry;
483                    Blade bld;
484                    Value val;
485                    //For all blade-value mappings
486                    Iterator it = map.entrySet().iterator();
487                    while(it.hasNext()) {
488                            entry = (Map.Entry) it.next();
489                            bld = (Blade) entry.getKey();
490                            val = (Value) entry.getValue();
491                            //Updating value
492                            val.value /= um;
493                            //Tresholding
494                            if(java.lang.Math.abs(val.value) < eps)
495                                    //Removing blade-value mappings
496                                    it.remove();
497                    }
498            }
499    
500            /**
501             * Gets highest grade of this element.
502             * @return highest grade of this element.
503             */
504            public final int getMaxGrade(){//Not using entrySet().iterator
505                    //Temporary grade
506                    int maxgrade = 0;
507                    //Temporary reference variable
508                    Blade bld;
509                    //Defining an array of cl blades
510                    Object[] arrbld = map.keySet().toArray();
511                    //For all cl blades
512                    for(int x = 0; x < arrbld.length; x ++){
513                            //Getting blade
514                            bld = (Blade) arrbld[x];
515                            //Comparing blade grade with temporary grade
516                            if(gradeTable[bld.blade] > maxgrade)
517                                    maxgrade = gradeTable[bld.blade];
518                    }
519                    return maxgrade;
520            }
521    
522            /**
523             * Verifies if this element is a scalar.
524             * @return true if this element is a scalar, false otherwise.
525             */
526            public final boolean isScalar(){
527                    return (getMaxGrade() == 0) ? true : false;
528            }
529    
530            /**
531             * Verifies if this element is a vector.
532             * @return true if this element is a vector, false otherwise.
533             */
534            public final boolean isVector(){
535                    return ((get(0) == 0.0) && (getMaxGrade() == 1)) ? true : false;
536            }
537    
538            /**
539             * Compares two elements for equality.
540             * Two elements are considered equals if they have same blades and corresponding values differing less than 2*EPS.
541             * @param obj the second element that is to be compared.
542             * @return true if the two specified elements are equals, false otherwise.
543             */
544             public boolean equals(Object obj){
545                    //Casting to Clifford object
546                    Clifford cl = (Clifford) obj;
547                    //Verifyng if blades maps have the same size (number of blade-value mappings)
548                    if(map.size() != cl.map.size())
549                            return false;
550                    //Tollerance
551                    double tol = 2*eps;
552                    //Temporary reference variables
553                    Blade bld1, bld2;
554                    Value val1, val2;
555                    //Defining ordered arrays of blades and values of cl1 and cl2
556                    Object[] arrbld1 = map.keySet().toArray();
557                    Object[] arrbld2 = cl.map.keySet().toArray();
558                    Object[] arrval1 = map.values().toArray();
559                    Object[] arrval2 = cl.map.values().toArray();
560                    //For all blade-value mappings
561                    for(int x = 0; x < arrbld1.length; x ++){
562                            bld1 = (Blade) arrbld1[x];
563                            bld2 = (Blade) arrbld2[x];
564                            val1 = (Value) arrval1[x];
565                            val2 = (Value) arrval2[x];
566                            //Comparing blades
567                            if(bld1.blade != bld2.blade)
568                                    return false;
569                            //Comparing values
570                            if(java.lang.Math.abs(val1.value - val2.value) > tol)
571                                    return false;
572                    }
573                    //Elements are equals
574                    return true;
575             }
576    
577            /**
578             * Adds two elements.
579             * @param cl the second element of the sum.
580             * @return a new element from the sum of the two specified elements.
581             */
582            public final Clifford add(final Clifford cl){
583                    //Cloning this element
584                    Clifford newcl = (Clifford) clone();
585                    //Temporary reference variables
586                    Map.Entry entry;
587                    Blade bld;
588                    Value val, newval;
589                    //For all cl blade-value mappings
590                    Iterator it = cl.map.entrySet().iterator();
591                    while(it.hasNext()) {
592                            //Getting blade and value
593                            entry = (Map.Entry) it.next();
594                            bld = (Blade) entry.getKey();
595                            val = (Value) entry.getValue();
596                            //Searching bld in newcl
597                            newval = (Value) newcl.map.get(bld);
598                            //If newcl already contains bld
599                            if(newval != null)
600                                    //Updating corresponding value
601                                    newval.value += val.value;
602                            else
603                                    //Adding new blade-value mappings to newcl
604                                    newcl.map.put(new Blade(bld.blade),new Value(val.value));
605                    }
606                    //Returning tresholded element
607                    newcl.noZero();
608                    return newcl;
609            }
610    
611            /**
612             * Subtracts two elements.
613             * @param cl the second element of the difference.
614             * @return a new element from the difference of the two specified elements.
615             */
616            public final Clifford sub(final Clifford cl){
617                    //Cloning this element
618                    Clifford newcl = (Clifford) clone();
619                    //Temporary reference variables
620                    Map.Entry entry;
621                    Blade bld;
622                    Value val, newval;
623                    //For all cl blade-value mappings
624                    Iterator it = cl.map.entrySet().iterator();
625                    while(it.hasNext()) {
626                            //Getting blade and value
627                            entry = (Map.Entry) it.next();
628                            bld = (Blade) entry.getKey();
629                            val = (Value) entry.getValue();
630                            //Searching bld in newcl
631                            newval = (Value) newcl.map.get(bld);
632                            //If newcl already contains bld
633                            if(newval != null)
634                                    //Updating corresponding value
635                                    newval.value -= val.value;
636                            else
637                                    //Adding new blade-value mappings to newcl
638                                    newcl.map.put(new Blade(bld.blade),new Value(-val.value));
639                    }
640                    //Returning tresholded element
641                    newcl.noZero();
642                    return newcl;
643    
644            }
645    
646            /**
647             * Computes the grade involution of this element.
648             * @return a new element from the grade involution of the specified element.
649             */
650            public final Clifford gradeInv(){
651                    //Creating a new empty element
652                    Clifford newcl = new Clifford();
653                    //Resulting value
654                    double result;
655                    //Temporary reference variables
656                    Map.Entry entry;
657                    Blade bld;
658                    Value val;
659                    //For all blade-value mappings
660                    Iterator it = map.entrySet().iterator();
661                    while(it.hasNext()) {
662                            //Getting blade and value
663                            entry = (Map.Entry) it.next();
664                            bld = (Blade) entry.getKey();
665                            val = (Value) entry.getValue();
666                            //Computing resulting value regarding parity of the grade (number of inversions)
667                            result = ((gradeTable[bld.blade] & 1) != 0) ? -val.value : val.value;
668                            //Adding new blade-value mapping to newcl
669                            newcl.map.put(new Blade(bld.blade), new Value(result));
670                    }
671                    return newcl;
672            }
673    
674            /**
675             * Computes the reverse of this element.
676             * @return a new element from the reversion of this element.
677             */
678            public final Clifford rev(){
679                    //Creating a new empty element
680                    Clifford newcl = new Clifford();
681                    //Resulting value
682                    double result;
683                    //Temporary reference variables
684                    Map.Entry entry;
685                    Blade bld;
686                    Value val;
687                    //For all blade-value mappings
688                    Iterator it = map.entrySet().iterator();
689                    while(it.hasNext()) {
690                            //Getting blade and value
691                            entry = (Map.Entry) it.next();
692                            bld = (Blade) entry.getKey();
693                            val = (Value) entry.getValue();
694       /*
695                            Computing resulting value regarding the sign: (-1)^((r(r-1))/2)
696                            Sign only depends upon the odd-ness or even-ness of the number of transpositions
697                            required to get things back in order. This obeys a simple recurrance relationship.
698                            Let T(n) be the number of transpositions required to revert an n-form.
699                            Then, T(n+1) = T(n) + n - 1 because it will require T(n) transpositions to reorder
700                            the first n subscripts and n-1 transpositions to get the n+1-th subscript from one
701                            end of the list to the other.
702                            So odd-ness or even-ness of T(n+4) is the same as that of T(n), because:
703                            T(n+4) = T(n+3) + n + 2 = T(n+2) + 2n + 3 = T(n+1) + 3n + 3 = T(n) + 4n + 2
704                            And, because T(0) and T(1) are even while T(2) and T(3) are odd an n-form requires:
705                            an odd number of transpositions to revert if n = 2 or n = 3 modulo 4.
706                            In code, this translates to whether the second bit of the grade is set.
707                            */
708                            result = ((gradeTable[bld.blade] & 2) != 0) ? -val.value : val.value;
709                            //Adding new blade-value mapping to newcl
710                            newcl.map.put(new Blade(bld.blade), new Value(result));
711                    }
712                    return newcl;
713            }
714    
715            /**
716             * Computes the inverse of this element.
717             * @return a new element from the inversion of this element.
718             */
719            public final Clifford inv(){
720                    //Creating a new empty element
721                    Clifford newcl = new Clifford();
722                    //Temporary sign
723                    boolean sign;
724                    //Resulting value
725                    double result;
726                    //Temporary module
727                    double module = 0.0;
728                    //Temporary reference variables
729                    Map.Entry entry;
730                    Blade bld;
731                    Value val;
732                    //For all blade-value mappings
733                    Iterator it = map.entrySet().iterator();
734                    while(it.hasNext()) {
735                            //Getting blade and value
736                            entry = (Map.Entry) it.next();
737                            bld = (Blade) entry.getKey();
738                            val = (Value) entry.getValue();
739                            //Computing resulting sign
740                            sign = ((gradeTable[bld.blade] & 2) != 0) ? true : false;
741                            //Computing resulting value
742                            result = sign ? -val.value : val.value;
743                            //Adding new blade-value mapping to newcl
744                            newcl.map.put(new Blade(bld.blade), new Value(result));
745                            //Updating temporary module
746                            module += val.value * ((signTable[bld.blade][bld.blade] ^ sign) ? -val.value : val.value);
747                    }
748                    return newcl.gP(1/module);
749            }
750    
751            /**
752             * Computes the conjugation of this element.
753             * The conjugation of an element is a grade involution and a reversion.
754             * @return a new element from the conjugation of the specified element.
755             */
756            public final Clifford conj(){
757                    //Creating a new empty element
758                    Clifford newcl = new Clifford();
759                    //Resulting value
760                    double result = 0.0;
761                    //Temporary reference variables
762                    Map.Entry entry;
763                    Blade bld;
764                    Value val;
765                    //For all blade-value mappings
766                    Iterator it = map.entrySet().iterator();
767                    while(it.hasNext()) {
768                            //Getting blade and value
769                            entry = (Map.Entry) it.next();
770                            bld = (Blade) entry.getKey();
771                            val = (Value) entry.getValue();
772                            //Computing resulting value: it is a grade involution and a reversion.
773                            switch(gradeTable[bld.blade] & 3){
774                                    case 0:
775                                    case 3:
776                                            result = val.value;
777                                    break;
778                                    case 1:
779                                    case 2:
780                                            result = -val.value;
781                                    break;
782                            }
783                            //Adding new blade-value mapping to newcl
784                            newcl.map.put(new Blade(bld.blade), new Value(result));
785                    }
786                    return newcl;
787            }
788    
789            /**
790             * Grades an element.
791             * @param grade the specified grade of the grading operation.
792             * @return a new element with only terms of the specified grade.
793             */
794            public final Clifford grade(int grade){
795                    //Creating a new empty element
796                    Clifford newcl = new Clifford();
797                    //Temporary reference variables
798                    Map.Entry entry;
799                    Blade bld;
800                    Value val;
801                    //For all blade-value mappings
802                    Iterator it = map.entrySet().iterator();
803                    while(it.hasNext()) {
804                            //Getting blade and value
805                            entry = (Map.Entry) it.next();
806                            bld = (Blade) entry.getKey();
807                            val = (Value) entry.getValue();
808                            //Selecting blades
809                            if(gradeTable[bld.blade] == grade)
810                                    //Adding new blade-value mapping to newcl
811                                    newcl.map.put(new Blade(bld.blade), new Value(val.value));
812                    }
813                    return newcl;
814            }
815    
816            /**
817             * Computes the geometric product of an element and a scalar.
818             * @param scalar the scalar of the geometric product.
819             * @return a new element from the geometric product of the specified element and the specified scalar.
820             */
821            public final Clifford gP(double scalar){
822                    //Creating a new empty element
823                    Clifford newcl = new Clifford();
824                    double newvalue;
825                    //Temporary reference variables
826                    Map.Entry entry;
827                    Blade bld;
828                    Value val;
829                    //For all blade-value mappings
830                    Iterator it = map.entrySet().iterator();
831                    while(it.hasNext()) {
832                            //Getting blade and value
833                            entry = (Map.Entry) it.next();
834                            bld = (Blade) entry.getKey();
835                            val = (Value) entry.getValue();
836                            //Resulting value for newcl bld
837                            newvalue = val.value * scalar;
838                            //Tresholding
839                            if(java.lang.Math.abs(newvalue) > eps)
840                                    //Adding new blade-value mapping to newcl blades map
841                                    newcl.map.put(new Blade(bld.blade), new Value(newvalue));
842                    }
843                    return newcl;
844            }
845    
846            /**
847             * Computes the geometric product of two elements.
848             * @param cl the second element of the geometric product.
849             * @return a new element from the geometric product of the two specified elements.
850             */
851            public final Clifford gP(final Clifford cl){
852    
853                    //Creating a new empty element
854                    Clifford newcl = new Clifford();
855                    //Temporary reference variables
856                    Blade bld1, bld2, newbld;
857                    Value val1, val2, newval;
858                    double result;
859                    //Defining ordered arrays of blades and values of cl1 and cl2
860                    Object[] arrbld1 = map.keySet().toArray();
861                    Object[] arrbld2 = cl.map.keySet().toArray();
862                    Object[] arrval1 = map.values().toArray();
863                    Object[] arrval2 = cl.map.values().toArray();
864                    //For all blade-value mappings
865                    for(int x = 0; x < arrbld1.length; x ++){
866                            bld1 = (Blade) arrbld1[x];
867                            val1 = (Value) arrval1[x];
868                            //For all cl blade-value mappings
869                            for(int y = 0; y < arrbld2.length; y ++){
870                                    bld2 = (Blade) arrbld2[y];
871                                    val2 = (Value) arrval2[y];
872                                    //Computing resulting blade
873                                    newbld = new Blade(bld1.blade ^ bld2.blade);
874                                    //Computing resulting value (regarding the sign)
875                                    result = val1.value * (signTable[bld1.blade][bld2.blade] ? -val2.value : val2.value);
876                                    //Searching newbld in newcl
877                                    newval = (Value) newcl.map.get(newbld);
878                                    //Updating newval by adding result
879                                    if(newval != null)
880                                            newval.value += result;
881                                    //Adding new blade-value mapping to newcl
882                                    else
883                                            newcl.map.put(newbld, new Value(result));
884                            }
885                    }
886                    //Returning tresholded element
887                    newcl.noZero();
888                    return newcl;
889    /*
890                    //Using iterators
891    
892                    //Creating a new empty element
893                    Clifford newcl = new Clifford();
894                    //Temporary reference variables
895                    Map.Entry entry1, entry2;
896                    Blade bld1, bld2, newbld;
897                    Value val1, val2, newval;
898                    double result;
899                    //Defining an iterator on cl1 blade-value mappings
900                    Iterator it1 = cl1.map.entrySet().iterator(), it2;
901                    //For all cl1 blade-value mappings
902                    while(it1.hasNext()) {
903                            //Getting blade and value
904                            entry1 = (Map.Entry) it1.next();
905                            bld1 = (Blade) entry1.getKey();
906                            val1 = (Value) entry1.getValue();
907                            //Defining an iterator on cl2 blade-value mappings
908                            it2 = cl2.map.entrySet().iterator();
909                            //For all cl1 blade-value mappings
910                            while(it2.hasNext()) {
911                                    //Getting blade and value
912                                    entry2 = (Map.Entry) it2.next();
913                                    bld2 = (Blade) entry2.getKey();
914                                    val2 = (Value) entry2.getValue();
915                                    //Computing resulting blade
916                                    newbld = new Blade(bld1.blade ^ bld2.blade);
917                                    //Computing resulting value (regarding the sign)
918                                    result = val1.value * (signTable[bld1.blade][bld2.blade] ? -val2.value : val2.value);
919                                    //Searching newbld in newcl
920                                    newval = (Value) newcl.map.get(newbld);
921                                    //Updating newval by adding result
922                                    if(newval != null)
923                                            newval.value += result;
924                                    //Adding new blade-value mapping to newcl
925                                    else
926                                            newcl.map.put(newbld, new Value(result));
927                            }
928                    }
929                    //Returning tresholded element
930                    newcl.noZero();
931                    return newcl;
932    */
933            }
934    
935            /**
936             * Computes the wedge product of two elements.
937             * @param cl the second element of the wedge product.
938             * @return a new element from the wedge product of the two specified elements.
939             */
940            public final Clifford wP(final Clifford cl){
941                    //Creating a new empty element
942                    Clifford newcl = new Clifford();
943                    //Temporary reference variables
944                    Blade bld1, bld2, newbld;
945                    Value val1, val2, newval;
946                    double result;
947                    //Defining ordered arrays of blades and values
948                    Object[] arrbld1 = map.keySet().toArray();
949                    Object[] arrbld2 = cl.map.keySet().toArray();
950                    Object[] arrval1 = map.values().toArray();
951                    Object[] arrval2 = cl.map.values().toArray();
952                    //For all blade-value mappings
953                    for(int x = 0; x < arrbld1.length; x ++){
954                            bld1 = (Blade) arrbld1[x];
955                            val1 = (Value) arrval1[x];
956                            //For all cl blade-value mappings
957                            for(int y = 0; y < arrbld2.length; y ++){
958                                    bld2 = (Blade) arrbld2[y];
959                                    val2 = (Value) arrval2[y];
960                                    //
961                                    if((bld1.blade & bld2.blade) != 0)
962                                            continue;
963                                    //Computing resulting blade
964                                    newbld = new Blade(bld1.blade ^ bld2.blade);
965                                    //Computing resulting value (regarding the sign)
966                                    result = val1.value * (signTable[bld1.blade][bld2.blade] ? -val2.value : val2.value);
967                                    //Searching newbld in newcl
968                                    newval = (Value) newcl.map.get(newbld);
969                                    //Updating newval by adding result
970                                    if(newval != null)
971                                            newval.value += result;
972                                    //Adding new blade-value mapping to newcl
973                                    else
974                                            newcl.map.put(newbld, new Value(result));
975                            }
976                    }
977                    //Returning tresholded element
978                    newcl.noZero();
979                    return newcl;
980            }
981    
982            /**
983             * Computes the left contraction with the specified element.
984             * @param cl the second element of the left contraction.
985             * @return a new element from the left contraction with the specified element.
986             */
987            public final Clifford lC(final Clifford cl){
988                    //Creating a new empty element
989                    Clifford newcl = new Clifford();
990                    //Temporary reference variables
991                    Blade bld1, bld2, newbld;
992                    Value val1, val2, newval;
993                    double result;
994                    //Defining ordered arrays of blades and values
995                    Object[] arrbld1 = map.keySet().toArray();
996                    Object[] arrbld2 = cl.map.keySet().toArray();
997                    Object[] arrval1 = map.values().toArray();
998                    Object[] arrval2 = cl.map.values().toArray();
999                    //For all blade-value mappings
1000                    for(int x = 0; x < arrbld1.length; x ++){
1001                            bld1 = (Blade) arrbld1[x];
1002                            val1 = (Value) arrval1[x];
1003                            //For all cl blade-value mappings
1004                            for(int y = 0; y < arrbld2.length; y ++){
1005                                    bld2 = (Blade) arrbld2[y];
1006                                    val2 = (Value) arrval2[y];
1007                                    //Continue if bld1 has same versor not in bld2
1008                                    if((bld1.blade & ~(bld2.blade)) != 0)
1009                                            continue;
1010                                    //Computing resulting blade
1011                                    newbld = new Blade(bld1.blade ^ bld2.blade);
1012                                    //Computing resulting value (regarding the sign)
1013                                    result = val1.value * (signTable[bld1.blade][bld2.blade] ? -val2.value : val2.value);
1014                                    //Searching newbld in newcl
1015                                    newval = (Value) newcl.map.get(newbld);
1016                                    //Updating newval by adding result
1017                                    if(newval != null)
1018                                            newval.value += result;
1019                                    //Adding new blade-value mapping to newcl
1020                                    else
1021                                            newcl.map.put(newbld, new Value(result));
1022                            }
1023                    }
1024                    //Returning tresholded element
1025                    newcl.noZero();
1026                    return newcl;
1027            }
1028    
1029            /**
1030             * Computes the right contraction with the specified element.
1031             * @param cl the second element of the right contraction.
1032             * @return a new element from the right contraction with the specified element.
1033             */
1034            public final Clifford rC(final Clifford cl){
1035                    //Creating a new empty element
1036                    Clifford newcl = new Clifford();
1037                    //Temporary reference variables
1038                    Blade bld1, bld2, newbld;
1039                    Value val1, val2, newval;
1040                    double result;
1041                    //Defining ordered arrays of map and values
1042                    Object[] arrbld1 = map.keySet().toArray();
1043                    Object[] arrbld2 = cl.map.keySet().toArray();
1044                    Object[] arrval1 = map.values().toArray();
1045                    Object[] arrval2 = cl.map.values().toArray();
1046                    //For all blade-value mappings
1047                    for(int x = 0; x < arrbld1.length; x ++){
1048                            bld1 = (Blade) arrbld1[x];
1049                            val1 = (Value) arrval1[x];
1050                            //For all cl blade-value mappings
1051                            for(int y = 0; y < arrbld2.length; y ++){
1052                                    bld2 = (Blade) arrbld2[y];
1053                                    val2 = (Value) arrval2[y];
1054                                    //Continue if bld2 has same versor not in bld1
1055                                    if((~ (bld1.blade) & bld2.blade) != 0)
1056                                            continue;
1057                                    //Computing resulting blade
1058                                    newbld = new Blade(bld1.blade ^ bld2.blade);
1059                                    //Computing resulting value (regarding the sign)
1060                                    result = val1.value * (signTable[bld1.blade][bld2.blade] ? -val2.value : val2.value);
1061                                    //Searching newbld in newcl
1062                                    newval = (Value) newcl.map.get(newbld);
1063                                    //Updating newval by adding result
1064                                    if(newval != null)
1065                                            newval.value += result;
1066                                    //Adding new blade-value mapping to newcl
1067                                    else
1068                                            newcl.map.put(newbld, new Value(result));
1069                            }
1070                    }
1071                    //Returning tresholded element
1072                    newcl.noZero();
1073                    return newcl;
1074            }
1075    
1076            /**
1077             * Computes the fast dot product of two vector.
1078             * @throws IllegalArgumentException if elements are not vectors.
1079             * @param cl the second vector of the fast dot product.
1080             * @return the dot product of the two specified vector.
1081             */
1082            public final double dot(final Clifford cl) throws IllegalArgumentException{
1083                    //Temporary dot product
1084                    double d = 0.0;
1085                    //Temporary reference variables
1086                    Map.Entry entry1;
1087                    Blade bld1;
1088                    Value val1,val2;
1089                    //For all blade-value mappings
1090                    Iterator it = map.entrySet().iterator();
1091                    while(it.hasNext()) {
1092                            //Getting blade and value
1093                            entry1 = (Map.Entry) it.next();
1094                            bld1 = (Blade) entry1.getKey();
1095                            val1 = (Value) entry1.getValue();
1096                            //Checking for grade 1 blade
1097                            if(gradeTable[bld1.blade] != 1)
1098                                    throw new RuntimeException("Invalid argument: fast dot product is only for vectors.");
1099                            //Searching bld1 in cl
1100                            val2 = (Value) cl.map.get(bld1);
1101                            //Updating temporary dot product
1102                            if(val2 != null)
1103                                    d += (val1.value * (((bld1.blade & signMask) != 0) ? -val2.value : val2.value));
1104                    }
1105                    return d;
1106            }
1107    
1108            /**
1109             * Computes the commutation with the specified element.
1110             * @param cl the second element of the commutation.
1111             * @return a new element from the commutation with the specified element.
1112             */
1113            public final Clifford comm(final Clifford cl){
1114                    return ((gP(cl)).sub(cl.gP(this))).gP(0.5);
1115            }
1116    
1117            /**
1118             * Computes the dual of this element.
1119             * @return a new element that is the dual of this element.
1120             */
1121            public final Clifford dual(){
1122                    return gP(pseudoScalar.rev());
1123            }
1124    
1125            /**
1126             * Computes the meet with the specified element.
1127             * @param cl the second element of the meet.
1128             * @return a new element from the meet with the specified element.
1129             */
1130            public final Clifford meet(final Clifford cl){
1131                    return (gP(pseudoScalar)).lC(cl);
1132            }
1133    
1134            /**
1135             * Computes the meet with the specified element in a common subspace.
1136             * @param cl the second element of the meet.
1137             * @param is the element representing a common subspace.
1138             * @return a new element from the meet with the specified element.
1139             */
1140            public final Clifford meet(final Clifford cl, final Clifford is){
1141                    return (gP(is)).lC(cl);
1142            }
1143    
1144            /**
1145             * Computes the reflection against the specified vector.
1146             * @param n the vector against wich reflect.
1147             * @return a new element from the reflection against the specified vector.
1148             */
1149            public final Clifford reflect(final Clifford n){
1150                    return (gP(n.sQuadMod())).sub(n.gP(2.0 * dot(n)));
1151            }
1152    
1153    }