source: branches/0.17.x/abcl/src/org/armedbear/lisp/Bignum.java

Last change on this file was 12254, checked in by ehuelsmann, 16 years ago

Remove 'throws ConditionThrowable?' method annotations:

it's an unchecked exception now, so no need to declare it thrown.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 21.4 KB
Line 
1/*
2 * Bignum.java
3 *
4 * Copyright (C) 2003-2007 Peter Graves
5 * $Id: Bignum.java 12254 2009-11-06 20:07:54Z ehuelsmann $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import java.math.BigInteger;
37
38public final class Bignum extends LispInteger
39{
40  public final BigInteger value;
41
42  private static BigInteger MOST_NEGATIVE_FIXNUM =
43          BigInteger.valueOf(Integer.MIN_VALUE);
44  private static BigInteger MOST_POSITIVE_FIXNUM =
45          BigInteger.valueOf(Integer.MAX_VALUE);
46
47  public static LispInteger getInstance(long l) {
48      if (Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE)
49          return Fixnum.getInstance(l);
50      else
51          return new Bignum(l);
52  }
53
54  public static LispInteger getInstance(BigInteger n) {
55      if (MOST_NEGATIVE_FIXNUM.compareTo(n) < 0 ||
56              MOST_POSITIVE_FIXNUM.compareTo(n) > 0)
57          return new Bignum(n);
58      else
59          return Fixnum.getInstance(n.intValue());
60  }
61
62  public static LispInteger getInstance(String s, int radix) {
63      BigInteger value = new BigInteger(s, radix);
64
65      return Bignum.getInstance(value);
66  }
67
68  private Bignum(long l)
69  {
70    value = BigInteger.valueOf(l);
71  }
72
73  private Bignum(BigInteger n)
74  {
75    value = n;
76  }
77
78  @Override
79  public Object javaInstance()
80  {
81    return value;
82  }
83
84  @Override
85  public Object javaInstance(Class c) {
86    String cn = c.getName();
87    if (cn.equals("java.lang.Byte") || cn.equals("byte"))
88      return Byte.valueOf((byte)value.intValue());
89    if (cn.equals("java.lang.Short") || cn.equals("short"))
90      return Short.valueOf((short)value.intValue());
91    if (cn.equals("java.lang.Integer") || cn.equals("int"))
92      return Integer.valueOf(value.intValue());
93    if (cn.equals("java.lang.Long") || cn.equals("long"))
94      return Long.valueOf((long)value.longValue());
95    return javaInstance();
96  }
97
98
99  @Override
100  public LispObject typeOf()
101  {
102    if (value.signum() > 0)
103      return list(Symbol.INTEGER,
104                   new Bignum((long)Integer.MAX_VALUE + 1));
105    return Symbol.BIGNUM;
106  }
107
108  @Override
109  public LispObject classOf()
110  {
111    return BuiltInClass.BIGNUM;
112  }
113
114  @Override
115  public LispObject typep(LispObject type)
116  {
117    if (type instanceof Symbol)
118      {
119        if (type == Symbol.BIGNUM)
120          return T;
121        if (type == Symbol.INTEGER)
122          return T;
123        if (type == Symbol.RATIONAL)
124          return T;
125        if (type == Symbol.REAL)
126          return T;
127        if (type == Symbol.NUMBER)
128          return T;
129        if (type == Symbol.SIGNED_BYTE)
130          return T;
131        if (type == Symbol.UNSIGNED_BYTE)
132          return value.signum() >= 0 ? T : NIL;
133      }
134    else if (type instanceof LispClass)
135      {
136        if (type == BuiltInClass.BIGNUM)
137          return T;
138        if (type == BuiltInClass.INTEGER)
139          return T;
140        if (type == BuiltInClass.RATIONAL)
141          return T;
142        if (type == BuiltInClass.REAL)
143          return T;
144        if (type == BuiltInClass.NUMBER)
145          return T;
146      }
147    else if (type instanceof Cons)
148      {
149        if (type.equal(UNSIGNED_BYTE_8))
150          return NIL;
151        if (type.equal(UNSIGNED_BYTE_32))
152          {
153            if (minusp())
154              return NIL;
155            return isLessThan(UNSIGNED_BYTE_32_MAX_VALUE) ? T : NIL;
156          }
157      }
158    return super.typep(type);
159  }
160
161  @Override
162  public LispObject NUMBERP()
163  {
164    return T;
165  }
166
167  @Override
168  public boolean numberp()
169  {
170    return true;
171  }
172
173  @Override
174  public LispObject INTEGERP()
175  {
176    return T;
177  }
178
179  @Override
180  public boolean integerp()
181  {
182    return true;
183  }
184
185  @Override
186  public boolean rationalp()
187  {
188    return true;
189  }
190
191  @Override
192  public boolean realp()
193  {
194    return true;
195  }
196
197  @Override
198  public boolean eql(LispObject obj)
199  {
200    if (this == obj)
201      return true;
202    if (obj instanceof Bignum)
203      {
204        if (value.equals(((Bignum)obj).value))
205          return true;
206      }
207    return false;
208  }
209
210  @Override
211  public boolean equal(LispObject obj)
212  {
213    if (this == obj)
214      return true;
215    if (obj instanceof Bignum)
216      {
217        if (value.equals(((Bignum)obj).value))
218          return true;
219      }
220    return false;
221  }
222
223  @Override
224  public boolean equalp(LispObject obj)
225  {
226    if (obj instanceof Bignum)
227      return value.equals(((Bignum)obj).value);
228    if (obj instanceof SingleFloat)
229      return floatValue() == ((SingleFloat)obj).value;
230    if (obj instanceof DoubleFloat)
231      return doubleValue() == ((DoubleFloat)obj).value;
232    return false;
233  }
234
235  @Override
236  public LispObject ABS()
237  {
238    if (value.signum() >= 0)
239      return this;
240    return new Bignum(value.negate());
241  }
242
243  @Override
244  public LispObject NUMERATOR()
245  {
246    return this;
247  }
248
249  @Override
250  public LispObject DENOMINATOR()
251  {
252    return Fixnum.ONE;
253  }
254
255  @Override
256  public boolean evenp()
257  {
258    return !value.testBit(0);
259  }
260
261  @Override
262  public boolean oddp()
263  {
264    return value.testBit(0);
265  }
266
267  @Override
268  public boolean plusp()
269  {
270    return value.signum() > 0;
271  }
272
273  @Override
274  public boolean minusp()
275  {
276    return value.signum() < 0;
277  }
278
279  @Override
280  public boolean zerop()
281  {
282    return false;
283  }
284
285  @Override
286  public int intValue()
287  {
288    return value.intValue();
289  }
290
291  @Override
292  public long longValue()
293  {
294    return value.longValue();
295  }
296
297  @Override
298  public float floatValue()
299  {
300    float f = value.floatValue();
301    if (Float.isInfinite(f))
302      error(new TypeError("The value " + writeToString() +
303                           " is too large to be converted to a single float."));
304    return f;
305  }
306
307  @Override
308  public double doubleValue()
309  {
310    double d = value.doubleValue();
311    if (Double.isInfinite(d))
312      error(new TypeError("The value " + writeToString() +
313                           " is too large to be converted to a double float."));
314    return d;
315  }
316
317  public static BigInteger getValue(LispObject obj)
318  {
319         
320    if (obj instanceof Bignum)
321      {
322        return ((Bignum)obj).value;
323      }
324        type_error(obj, Symbol.BIGNUM);
325        // Not reached.
326        return null;
327  }
328
329  @Override
330  public final LispObject incr()
331  {
332    return number(value.add(BigInteger.ONE));
333  }
334
335  @Override
336  public final LispObject decr()
337  {
338    return number(value.subtract(BigInteger.ONE));
339  }
340
341  @Override
342  public LispObject add(int n)
343  {
344    return number(value.add(BigInteger.valueOf(n)));
345  }
346
347  @Override
348  public LispObject add(LispObject obj)
349  {
350    if (obj instanceof Fixnum)
351      return number(value.add(Fixnum.getBigInteger(obj)));
352    if (obj instanceof Bignum)
353      return number(value.add(((Bignum)obj).value));
354    if (obj instanceof Ratio)
355      {
356        BigInteger numerator = ((Ratio)obj).numerator();
357        BigInteger denominator = ((Ratio)obj).denominator();
358        return number(value.multiply(denominator).add(numerator),
359                      denominator);
360      }
361    if (obj instanceof SingleFloat)
362      return new SingleFloat(floatValue() + ((SingleFloat)obj).value);
363    if (obj instanceof DoubleFloat)
364      return new DoubleFloat(doubleValue() + ((DoubleFloat)obj).value);
365    if (obj instanceof Complex)
366      {
367        Complex c = (Complex) obj;
368        return Complex.getInstance(add(c.getRealPart()), c.getImaginaryPart());
369      }
370    return type_error(obj, Symbol.NUMBER);
371  }
372
373  @Override
374  public LispObject subtract(LispObject obj)
375  {
376    if (obj instanceof Fixnum)
377      return number(value.subtract(Fixnum.getBigInteger(obj)));
378    if (obj instanceof Bignum)
379      return number(value.subtract(((Bignum)obj).value));
380    if (obj instanceof Ratio)
381      {
382        BigInteger numerator = ((Ratio)obj).numerator();
383        BigInteger denominator = ((Ratio)obj).denominator();
384        return number(value.multiply(denominator).subtract(numerator),
385                      denominator);
386      }
387    if (obj instanceof SingleFloat)
388      return new SingleFloat(floatValue() - ((SingleFloat)obj).value);
389    if (obj instanceof DoubleFloat)
390      return new DoubleFloat(doubleValue() - ((DoubleFloat)obj).value);
391    if (obj instanceof Complex)
392      {
393        Complex c = (Complex) obj;
394        return Complex.getInstance(subtract(c.getRealPart()),
395                                   Fixnum.ZERO.subtract(c.getImaginaryPart()));
396      }
397    return type_error(obj, Symbol.NUMBER);
398  }
399
400  @Override
401  public LispObject multiplyBy(int n)
402  {
403    if (n == 0)
404      return Fixnum.ZERO;
405    if (n == 1)
406      return this;
407    return new Bignum(value.multiply(BigInteger.valueOf(n)));
408  }
409
410  @Override
411  public LispObject multiplyBy(LispObject obj)
412  {
413    if (obj instanceof Fixnum)
414      {
415        int n = ((Fixnum)obj).value;
416        if (n == 0)
417          return Fixnum.ZERO;
418        if (n == 1)
419          return this;
420        return new Bignum(value.multiply(BigInteger.valueOf(n)));
421      }
422    if (obj instanceof Bignum)
423      return new Bignum(value.multiply(((Bignum)obj).value));
424    if (obj instanceof Ratio)
425      {
426        BigInteger n = ((Ratio)obj).numerator();
427        return number(n.multiply(value), ((Ratio)obj).denominator());
428      }
429    if (obj instanceof SingleFloat)
430      return new SingleFloat(floatValue() * ((SingleFloat)obj).value);
431    if (obj instanceof DoubleFloat)
432      return new DoubleFloat(doubleValue() * ((DoubleFloat)obj).value);
433    if (obj instanceof Complex)
434      {
435        Complex c = (Complex) obj;
436        return Complex.getInstance(multiplyBy(c.getRealPart()),
437                                   multiplyBy(c.getImaginaryPart()));
438      }
439    return type_error(obj, Symbol.NUMBER);
440  }
441
442  @Override
443  public LispObject divideBy(LispObject obj)
444  {
445    if (obj instanceof Fixnum)
446      return number(value, Fixnum.getBigInteger(obj));
447    if (obj instanceof Bignum)
448      return number(value, ((Bignum)obj).value);
449    if (obj instanceof Ratio)
450      {
451        BigInteger d = ((Ratio)obj).denominator();
452        return number(d.multiply(value), ((Ratio)obj).numerator());
453      }
454    if (obj instanceof SingleFloat)
455      return new SingleFloat(floatValue() / ((SingleFloat)obj).value);
456    if (obj instanceof DoubleFloat)
457      return new DoubleFloat(doubleValue() / ((DoubleFloat)obj).value);
458    if (obj instanceof Complex)
459      {
460        Complex c = (Complex) obj;
461        LispObject realPart = c.getRealPart();
462        LispObject imagPart = c.getImaginaryPart();
463        LispObject denominator =
464          realPart.multiplyBy(realPart).add(imagPart.multiplyBy(imagPart));
465        return Complex.getInstance(multiplyBy(realPart).divideBy(denominator),
466                                   Fixnum.ZERO.subtract(multiplyBy(imagPart).divideBy(denominator)));
467      }
468    return type_error(obj, Symbol.NUMBER);
469  }
470
471  @Override
472  public boolean isEqualTo(LispObject obj)
473  {
474    if (obj instanceof Bignum)
475      return value.equals(((Bignum)obj).value);
476    if (obj instanceof SingleFloat)
477      return isEqualTo(((SingleFloat)obj).rational());
478    if (obj instanceof DoubleFloat)
479      return isEqualTo(((DoubleFloat)obj).rational());
480    if (obj.numberp())
481      return false;
482    type_error(obj, Symbol.NUMBER);
483    // Not reached.
484    return false;
485  }
486
487  @Override
488  public boolean isNotEqualTo(LispObject obj)
489  {
490    if (obj instanceof Bignum)
491      return !value.equals(((Bignum)obj).value);
492    if (obj instanceof SingleFloat)
493      return isNotEqualTo(((SingleFloat)obj).rational());
494    if (obj instanceof DoubleFloat)
495      return isNotEqualTo(((DoubleFloat)obj).rational());
496    if (obj.numberp())
497      return true;
498    type_error(obj, Symbol.NUMBER);
499    // Not reached.
500    return false;
501  }
502
503  @Override
504  public boolean isLessThan(LispObject obj)
505  {
506    if (obj instanceof Fixnum)
507      return value.compareTo(Fixnum.getBigInteger(obj)) < 0;
508    if (obj instanceof Bignum)
509      return value.compareTo(((Bignum)obj).value) < 0;
510    if (obj instanceof Ratio)
511      {
512        BigInteger n = value.multiply(((Ratio)obj).denominator());
513        return n.compareTo(((Ratio)obj).numerator()) < 0;
514      }
515    if (obj instanceof SingleFloat)
516      return isLessThan(((SingleFloat)obj).rational());
517    if (obj instanceof DoubleFloat)
518      return isLessThan(((DoubleFloat)obj).rational());
519    type_error(obj, Symbol.REAL);
520    // Not reached.
521    return false;
522  }
523
524  @Override
525  public boolean isGreaterThan(LispObject obj)
526  {
527    if (obj instanceof Fixnum)
528      return value.compareTo(Fixnum.getBigInteger(obj)) > 0;
529    if (obj instanceof Bignum)
530      return value.compareTo(((Bignum)obj).value) > 0;
531    if (obj instanceof Ratio)
532      {
533        BigInteger n = value.multiply(((Ratio)obj).denominator());
534        return n.compareTo(((Ratio)obj).numerator()) > 0;
535      }
536    if (obj instanceof SingleFloat)
537      return isGreaterThan(((SingleFloat)obj).rational());
538    if (obj instanceof DoubleFloat)
539      return isGreaterThan(((DoubleFloat)obj).rational());
540    type_error(obj, Symbol.REAL);
541    // Not reached.
542    return false;
543  }
544
545  @Override
546  public boolean isLessThanOrEqualTo(LispObject obj)
547  {
548    if (obj instanceof Fixnum)
549      return value.compareTo(Fixnum.getBigInteger(obj)) <= 0;
550    if (obj instanceof Bignum)
551      return value.compareTo(((Bignum)obj).value) <= 0;
552    if (obj instanceof Ratio)
553      {
554        BigInteger n = value.multiply(((Ratio)obj).denominator());
555        return n.compareTo(((Ratio)obj).numerator()) <= 0;
556      }
557    if (obj instanceof SingleFloat)
558      return isLessThanOrEqualTo(((SingleFloat)obj).rational());
559    if (obj instanceof DoubleFloat)
560      return isLessThanOrEqualTo(((DoubleFloat)obj).rational());
561    type_error(obj, Symbol.REAL);
562    // Not reached.
563    return false;
564  }
565
566  @Override
567  public boolean isGreaterThanOrEqualTo(LispObject obj)
568  {
569    if (obj instanceof Fixnum)
570      return value.compareTo(Fixnum.getBigInteger(obj)) >= 0;
571    if (obj instanceof Bignum)
572      return value.compareTo(((Bignum)obj).value) >= 0;
573    if (obj instanceof Ratio)
574      {
575        BigInteger n = value.multiply(((Ratio)obj).denominator());
576        return n.compareTo(((Ratio)obj).numerator()) >= 0;
577      }
578    if (obj instanceof SingleFloat)
579      return isGreaterThanOrEqualTo(((SingleFloat)obj).rational());
580    if (obj instanceof DoubleFloat)
581      return isGreaterThanOrEqualTo(((DoubleFloat)obj).rational());
582    type_error(obj, Symbol.REAL);
583    // Not reached.
584    return false;
585  }
586
587  @Override
588  public LispObject truncate(LispObject obj)
589  {
590    final LispThread thread = LispThread.currentThread();
591    LispObject value1, value2;
592    try
593      {
594        if (obj instanceof Fixnum)
595          {
596            BigInteger divisor = ((Fixnum)obj).getBigInteger();
597            BigInteger[] results = value.divideAndRemainder(divisor);
598            BigInteger quotient = results[0];
599            BigInteger remainder = results[1];
600            value1 = number(quotient);
601            value2 = (remainder.signum() == 0) ? Fixnum.ZERO : number(remainder);
602          }
603        else if (obj instanceof Bignum)
604          {
605            BigInteger divisor = ((Bignum)obj).value;
606            BigInteger[] results = value.divideAndRemainder(divisor);
607            BigInteger quotient = results[0];
608            BigInteger remainder = results[1];
609            value1 = number(quotient);
610            value2 = (remainder.signum() == 0) ? Fixnum.ZERO : number(remainder);
611          }
612        else if (obj instanceof Ratio)
613          {
614            Ratio divisor = (Ratio) obj;
615            LispObject quotient =
616              multiplyBy(divisor.DENOMINATOR()).truncate(divisor.NUMERATOR());
617            LispObject remainder =
618              subtract(quotient.multiplyBy(divisor));
619            value1 = quotient;
620            value2 = remainder;
621          }
622        else if (obj instanceof SingleFloat)
623          {
624            // "When rationals and floats are combined by a numerical
625            // function, the rational is first converted to a float of the
626            // same format." 12.1.4.1
627            return new SingleFloat(floatValue()).truncate(obj);
628          }
629        else if (obj instanceof DoubleFloat)
630          {
631            // "When rationals and floats are combined by a numerical
632            // function, the rational is first converted to a float of the
633            // same format." 12.1.4.1
634            return new DoubleFloat(doubleValue()).truncate(obj);
635          }
636        else
637          return type_error(obj, Symbol.REAL);
638      }
639    catch (ArithmeticException e)
640      {
641        if (obj.zerop())
642          return error(new DivisionByZero());
643        else
644          return error(new ArithmeticError(e.getMessage()));
645      }
646    return thread.setValues(value1, value2);
647  }
648
649  @Override
650  public LispObject ash(LispObject obj)
651  {
652    BigInteger n = value;
653    if (obj instanceof Fixnum)
654      {
655        int count = ((Fixnum)obj).value;
656        if (count == 0)
657          return this;
658        // BigInteger.shiftLeft() succumbs to a stack overflow if count
659        // is Integer.MIN_VALUE, so...
660        if (count == Integer.MIN_VALUE)
661          return n.signum() >= 0 ? Fixnum.ZERO : Fixnum.MINUS_ONE;
662        return number(n.shiftLeft(count));
663      }
664    if (obj instanceof Bignum)
665      {
666        BigInteger count = ((Bignum)obj).value;
667        if (count.signum() > 0)
668          return error(new LispError("Can't represent result of left shift."));
669        if (count.signum() < 0)
670          return n.signum() >= 0 ? Fixnum.ZERO : Fixnum.MINUS_ONE;
671        Debug.bug(); // Shouldn't happen.
672      }
673    return type_error(obj, Symbol.INTEGER);
674  }
675
676  @Override
677  public LispObject LOGNOT()
678  {
679    return number(value.not());
680  }
681
682  @Override
683  public LispObject LOGAND(int n)
684  {
685    if (n >= 0)
686      return Fixnum.getInstance(value.intValue() & n);
687    else
688      return number(value.and(BigInteger.valueOf(n)));
689  }
690
691  @Override
692  public LispObject LOGAND(LispObject obj)
693  {
694    if (obj instanceof Fixnum)
695      {
696        int n = ((Fixnum)obj).value;
697        if (n >= 0)
698          return Fixnum.getInstance(value.intValue() & n);
699        else
700          return number(value.and(BigInteger.valueOf(n)));
701      }
702    else if (obj instanceof Bignum)
703      {
704        final BigInteger n = ((Bignum)obj).value;
705        return number(value.and(n));
706      }
707    else
708      return type_error(obj, Symbol.INTEGER);
709  }
710
711  @Override
712  public LispObject LOGIOR(int n)
713  {
714    return number(value.or(BigInteger.valueOf(n)));
715  }
716
717  @Override
718  public LispObject LOGIOR(LispObject obj)
719  {
720    if (obj instanceof Fixnum)
721      {
722        final BigInteger n = ((Fixnum)obj).getBigInteger();
723        return number(value.or(n));
724      }
725    else if (obj instanceof Bignum)
726      {
727        final BigInteger n = ((Bignum)obj).value;
728        return number(value.or(n));
729      }
730    else
731      return type_error(obj, Symbol.INTEGER);
732  }
733
734  @Override
735  public LispObject LOGXOR(int n)
736  {
737    return number(value.xor(BigInteger.valueOf(n)));
738  }
739
740  @Override
741  public LispObject LOGXOR(LispObject obj)
742  {
743    final BigInteger n;
744    if (obj instanceof Fixnum)
745      n = ((Fixnum)obj).getBigInteger();
746    else if (obj instanceof Bignum)
747      n = ((Bignum)obj).value;
748    else
749      return type_error(obj, Symbol.INTEGER);
750    return number(value.xor(n));
751  }
752
753  @Override
754  public LispObject LDB(int size, int position)
755  {
756    BigInteger n = value.shiftRight(position);
757    BigInteger mask = BigInteger.ONE.shiftLeft(size).subtract(BigInteger.ONE);
758    return number(n.and(mask));
759  }
760
761  @Override
762  public int hashCode()
763  {
764    return value.hashCode();
765  }
766
767  @Override
768  public String writeToString()
769  {
770    final LispThread thread = LispThread.currentThread();
771    final int base = Fixnum.getValue(Symbol.PRINT_BASE.symbolValue(thread));
772    String s = value.toString(base).toUpperCase();
773    if (Symbol.PRINT_RADIX.symbolValue(thread) != NIL)
774      {
775        StringBuffer sb = new StringBuffer();
776        switch (base)
777          {
778          case 2:
779            sb.append("#b");
780            sb.append(s);
781            break;
782          case 8:
783            sb.append("#o");
784            sb.append(s);
785            break;
786          case 10:
787            sb.append(s);
788            sb.append('.');
789            break;
790          case 16:
791            sb.append("#x");
792            sb.append(s);
793            break;
794          default:
795            sb.append('#');
796            sb.append(String.valueOf(base));
797            sb.append('r');
798            sb.append(s);
799            break;
800          }
801        s = sb.toString();
802      }
803    return s;
804  }
805}
Note: See TracBrowser for help on using the repository browser.