File:  [CENS] / python / pyGiNaC / wrappers / matrix.py
Revision 1.4: download - view: text, annotated - select for diffs - revision graph
Tue Apr 17 22:39:25 2001 UTC (16 years, 7 months ago) by pearu
Branches: MAIN
CVS tags: HEAD
Fixed bugs. Exposed/checked functions. Started testing framework.

# This file is part of the PyGiNaC package.
# http://cens.ioc.ee/projects/pyginac/
#
# $Revision: 1.4 $
# $Id: matrix.py,v 1.4 2001-04-17 22:39:25 pearu Exp $
#
# Copyright 2001 Pearu Peterson all rights reserved,
# Pearu Peterson <pearu@cens.ioc.ee>
# Permission to use, modify, and distribute this software is given under the
# terms of the LGPL.  See http://www.fsf.org
#
# NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
#


depends = ['basic']
uses = []

class determinant_algo:
    automatic = 0

#STARTPROTO

class matrix(basic):
    """Symbolic matrices."""
    global determinant_algo
    def __init__(self,*args):
        """matrix(r,c) - construct a (r x c) matrix filled with zeros.
    matrix(r,c,seq) - construct a (r x c) matrix from a flat
        (r*c)-sequence `seq'. `seq' can be any Python sequence (list,tuple),
        exvector, lst, or matrix.
    matrix(seq) - construct a matrix from a sequence (or a sequence of
        sequences) `seq'. If `seq' is flat, resulting matrix is constructed
        as a column vector. If `seq' is lst, GiNaC internal lst_to_matrix
        constructor is applied. Then `seq' must be a lst of lst's.
    matrix(ex(matrix(...))) - returns matrix(...)
    """
    def __getitem__(self,key):
        """Return matrix items.
    m[i,j]    -> an element in i-th row and j-th column.
    m[i]      -> an i-th row of a matrix m.
    m[:,j]    -> a j-th column.
    m[i1:i2, j1:j2] -> a submatrix starting at the element m[i1,j1] and
                 ending at the element m[i2-1,j2-1].
    m[i1:i2:s] -> a submatrix composed by i1,i1+s,i1+2s,...(,i2) -th
                 rows of the matrix m. Step `s' can be also negative.
    etc. See also __setitem__.
    """
    def __setitem__(self,key,value):
        """Set matrix items.
    m[i,j] = v   - sets an element in i-th row and j-th column equal
                 to v.
    m[i1:i2:si,j1:j2:sj] = v - sets a submatrix defined by given slices
                  equal to a matrix v. It must be properly sized.
    etc. See also __getitem__.
    """
    def __len__(self):
        """Returns the number of rows."""
    def rows(self):
        """Get number of rows."""
    def cols(self):
        """Get number of columns."""
    def set(self,i,j,value):
        """Set individual elements manually in-place. Returns self.
    """
    def transpose(self):
        """A new transposed matrix."""
    def determinant(self,algo=determinant_algo.automatic):
        """Determinant of a square matrix.
    If all the elements of the matrix are elements of an integral
    domain the determinant is also in that integral domain and the
    result is expanded only. If one or more elements are from a
    quotient field the determinant is usually also in that quotient
    field and the result is normalized before it is returned. This
    implies that the determinant of the symbolic 2x2 matrix
    [[a/(a-b),1],[b/(a-b),1]] is returned as unity. (In this respect,
    it behaves like MapleV and unlike Mathematica.)

    algo - allows to chose an algorithm. See determinant_algo.
    """
    def trace(self):
        """Trace of a square matrix.        
    The result is normalized if it is in some quotient field and
    expanded only otherwise. This implies that the trace of the
    symbolic 2x2 matrix [[a/(a-b),x],[y,b/(b-a)]] is recognized to be
    unity.
    Returns the sum of diagonal elements.
    """
    def charpoly(self,lambda_):
        """Characteristic Polynomial.
    Following mathematica notation the characteristic polynomial of a
    matrix M is defined as the determiant of (M - lambda * 1) where 1
    stands for the unit matrix of the same dimension as M. Note that
    some CASs define it with a sign inside the determinant which gives
    rise to an overall sign if the dimension is odd. This method
    returns the characteristic polynomial collected in powers of
    lambda as a new expression.
    lambda must be a symbol or Python string.
        """
    def inverse(self):
        """Inverse of a square matrix."""
    def solve(self,vars,rhs,algo=determinant_algo.automatic):
        """Solve a linear system consisting of a m x n matrix and
    a m x p right hand side by applying an elimination scheme to the
    augmented matrix.
    vars - n x p matrix, all elements must be symbols
    rhs  - m x p matrix
    Returns n x p solution matrix.
    """

def rows(m):
    """rows(m) - the number of rows of a matrix m."""
def cols(m):
    """cols(m) - the number of columns of a matrix m."""
def transpose(m):
    """transpose(m) - returns new transposed matrix of m."""
def determinant(m, algo = determinant_algo.automatic):
    """determinant(m, algo = determinant_algo.automatic) - returns
    determinant of a square matrix m. See also determinant_algo."""
def trace(m):
    """trace(m) - returns trace of a square matrix m."""
def charpoly(m,lambda_):
    """charpoly(m,lambda) - returns Characteristic Polynomial of a
    square matrix m: det(m-lambda*1)."""
def inverse(m):
    """inverse(m) - returns inverse matrix of a square matrix m."""
def diag_matrix(seq):
    """diag_matrix(seq) - returns a diagonal matrix with a diagonal
    of a flat sequence `seq'."""

#ENDPROTO

wrapperclass = '''

GiNaC::matrix matrix_from_ref(python::ref m);

class matrix_w : public GiNaC::matrix {
  PyObject * self;
public:
  matrix_w(python::ref m2)
  : GiNaC::matrix(matrix_from_ref(m2)) {
    DEBUG_C("matrix_w(raw:ref)");
  }
  matrix_w(PyObject * self_, const GiNaC::matrix & p)
  : GiNaC::matrix(p), self(self_) {
    DEBUG_C("matrix_w(matrix)");
  }
  matrix_w(PyObject * self_, const GiNaC::ex & p)
  : GiNaC::matrix(ex_to_matrix_w(p)), self(self_) {
    DEBUG_C("matrix_w(ex)");
  }
#ifdef PYGINAC_lst
  matrix_w(PyObject * self_, const GiNaC::lst & p)
  : GiNaC::matrix(ex_to_matrix(GiNaC::lst_to_matrix(p))), self(self_) {
    DEBUG_C("matrix_w(lst)");
  }
#endif
  matrix_w(PyObject * self_, long r, long c)
  : GiNaC::matrix(check_rows(r),check_cols(c)), self(self_) {
    DEBUG_C("matrix_w(unsigned,unsigned)");
  }
#ifdef PYGINAC_exvector
  matrix_w(PyObject * self_, long r, long c, const GiNaC::exvector & m2)
  : GiNaC::matrix(check_rows(r),check_cols(c),check_length(m2,r*c)), self(self_) {
    DEBUG_C("matrix_w(unsigned,unsigned,exvector)");
  }
  matrix_w(PyObject * self_, long r, long c, python::ref m2)
  : GiNaC::matrix(check_rows(r),check_cols(c),check_length(as_exvector_w(m2),r*c)), self(self_) {
    DEBUG_C("matrix_w(unsigned,unsigned,ref)");
  }
#endif
  matrix_w(PyObject * self_, python::ref m2)
  : GiNaC::matrix(matrix_from_ref(m2)), self(self_) {
    DEBUG_C("matrix_w(ref)");
  }

  ~matrix_w() {
    DEBUG_C("matrix_w::~matrix_w()");
  }

  static unsigned check_rows(long n) {
    DEBUG_M("matrix_w::check_rows("<<n<<")");
    if (n>0) return n;
    PyErr_SetString(PyExc_ValueError, "matrix() number of rows must be positive");
    throw python::error_already_set();
  }
  static unsigned check_cols(long n) {
    DEBUG_M("matrix_w::check_cols(" << n << ")");
    if (n>0) return n;
    PyErr_SetString(PyExc_ValueError, "matrix() number of columns must be positive");
    throw python::error_already_set();
  }
  static const GiNaC::exvector & check_length(const GiNaC::exvector & v,unsigned l) {
    if (v.size()>=l)
      return v;
    PyErr_SetString(PyExc_ValueError, "exvector() is too short for a requested matrix size");
    throw python::error_already_set();
  }
  static unsigned check_nonnegative_index(long n,long m) {
    DEBUG_M("matrix_w::check_positive_index(" << n << ", " << m << ")");
    if (n<0)
      PyErr_SetString(PyExc_IndexError, "matrix() index must be nonnegative integer");
    else if (n>=m)
      PyErr_SetString(PyExc_IndexError, "matrix() index out of range");
    else
      return n;
    throw python::error_already_set();
  }

  PyObject* getitem(python::ref) const;
  void setitem(python::ref,python::ref);

  GiNaC::matrix mul_w(python::ref other) const {
#ifdef PYGINAC_numeric
    PyObject * o = other.get();
    if (PyInt_Check(o) || PyFloat_Check(o) || PyComplex_Check(o) || PyString_Check(o) || NumericInstance_Check(o))
      return this->mul(as_numeric_w(other));
#endif
    return this->mul(matrix_w(other));
    }
  GiNaC::matrix set_w(unsigned ro, unsigned co, python::ref o) { return this->set(ro,co,ex_w(o)); }
  UNEX_RET determinant1_w(python::ref m) const { return UNEX(this->determinant()); }
  UNEX_RET determinant2_w(python::ref m, unsigned options) const { return UNEX(this->determinant(options)); }
#ifdef PYGINAC_symbol
  UNEX_RET charpoly_w(python::ref s) const { return UNEX(this->charpoly(as_symbol_w(s))); }
#endif
  GiNaC::matrix solve2_w(python::ref vars, python::ref rhs) const {
    return this->solve(matrix_w(vars),matrix_w(rhs));
  }
  GiNaC::matrix solve3_w(python::ref vars, python::ref rhs, unsigned algo) const {
    return this->solve(matrix_w(vars),matrix_w(rhs), algo);
  }



  UNEX_RET op_pos(void) const {return UNEX(GiNaC::ex(*this)); }
  UNEX_RET op_neg(void) const {return UNEX(GiNaC::ex(this->mul(GiNaC::numeric(-1)))); }
  UNEX_RET op_add_matrix(const GiNaC::matrix & other) const {
    return UNEX(GiNaC::ex(this->add(other)));
  }
  UNEX_RET op_sub_matrix(const GiNaC::matrix & other) const {
    return UNEX(GiNaC::ex(this->sub(other)));
  }
  UNEX_RET op_mul_matrix(const GiNaC::matrix & other) const {
    return UNEX(GiNaC::ex(this->mul(other)));
  }
#ifdef PYGINAC_numeric
  UNEX_RET op_mul_numeric(const GiNaC::numeric & other) const {
    return UNEX(GiNaC::ex(this->mul(other)));
  }
  UNEX_RET op_div_numeric(const GiNaC::numeric & other) const {
    return UNEX(GiNaC::ex(this->mul(other.inverse())));
  }
#endif
  UNEX_RET op_div_matrix(const GiNaC::matrix & other) const {
    return UNEX(GiNaC::ex(this->mul(other.inverse())));
  }
  
};

 unsigned rows_w(python::ref m) { return as_matrix_w(m).rows(); }
 unsigned cols_w(python::ref m) { return as_matrix_w(m).cols(); }
 GiNaC::matrix transpose_w(python::ref m) { return as_matrix_w(m).transpose(); }
 UNEX_RET determinant1_w(python::ref m) { return UNEX(as_matrix_w(m).determinant()); }
 UNEX_RET determinant2_w(python::ref m, unsigned options) { return UNEX(matrix_w(m).determinant(options)); }
 UNEX_RET trace_w(python::ref m) { return UNEX(matrix_w(m).trace()); }
#ifdef PYGINAC_symbol
 UNEX_RET charpoly_w(python::ref m, python::ref s) { return UNEX(matrix_w(m).charpoly(as_symbol_w(s))); }
#endif
 python::ref inverse_w(python::ref obj) {
#ifdef PYGINAC_numeric
  PyObject * o = obj.get();
  if (PyInt_Check(o) || PyFloat_Check(o) || PyComplex_Check(o) || PyString_Check(o) || NumericInstance_Check(o))
    return BOOST_PYTHON_CONVERSION::to_python(GiNaC::inverse(as_numeric_w(obj)));
#endif
  GiNaC::matrix m = as_matrix_w(obj);
  cout<<"m=";m.print(cout);cout<<endl;
  return BOOST_PYTHON_CONVERSION::to_python(m.inverse());
}
#ifdef PYGINAC_lst
 UNEX_RET diag_matrix_w(python::ref l) {return UNEX(GiNaC::diag_matrix(as_lst_w(l))); }
#endif


'''

builder = '''
python::class_builder<matrix_w> matrix_w_class(this_module, "_matrix_w");
python::class_builder<GiNaC::matrix, matrix_w> matrix_class(this_module, "matrix");
matrix_py_class = python::as_object(matrix_class.get_extension_class());
matrix_class.declare_base(matrix_w_class);
matrix_class.declare_base(basic_class);
'''

constructors = '''
matrix_class.def(python::constructor<const GiNaC::matrix &>());
#ifdef PYGINAC_lst
matrix_class.def(python::constructor<const GiNaC::lst &>());
#endif
matrix_class.def(python::constructor<const GiNaC::ex &>());
matrix_class.def(python::constructor<long, long>());
#ifdef PYGINAC_exvector
matrix_class.def(python::constructor<long, long, const GiNaC::exvector &>());
matrix_class.def(python::constructor<long, long, python::ref>());
#endif
matrix_class.def(python::constructor<python::ref>());
'''

defs = '''
matrix_class.def(&basic_w::python_str, "__str__");
matrix_class.def(&basic_w::python_repr, "__repr__");
matrix_class.def(&matrix_w::getitem, "__getitem__");
matrix_class.def(&matrix_w::setitem, "__setitem__");
matrix_class.def(&matrix_w::rows, "__len__");
matrix_class.def(&matrix_coerce, "__coerce__");

matrix_class.def(&matrix_w::op_pos, "__pos__");
matrix_class.def(&matrix_w::op_neg, "__neg__");
matrix_class.def(&matrix_w::op_add_matrix, "__add__");
matrix_class.def(&matrix_w::op_sub_matrix, "__sub__");

#ifdef PYGINAC_numeric
matrix_class.def(&matrix_w::op_div_numeric, "__div__");
matrix_class.def(&matrix_w::op_mul_numeric, "__mul__");
#endif
matrix_class.def(&matrix_w::op_div_matrix, "__div__");
matrix_class.def(&matrix_w::op_mul_matrix, "__mul__");

//matrix_class.def(&matrix_w::op_div, "__div__");
//matrix_class.def(&matrix_w::op_pow, "__pow__");

matrix_class.def(&matrix_w::rows, "rows");
matrix_class.def(&matrix_w::cols, "cols");
//matrix_class.def(&matrix_w::add, "add");
//matrix_class.def(&matrix_w::sub, "sub");
//matrix_class.def(&matrix_w::mul_w, "mul");
matrix_class.def(&matrix_w::set_w, "set"); // use more general __setitem__
matrix_class.def(&matrix_w::transpose, "transpose");
matrix_class.def(&matrix_w::determinant1_w, "determinant");
matrix_class.def(&matrix_w::determinant2_w, "determinant");
matrix_class.def(&matrix_w::trace, "trace");
matrix_class.def(&matrix_w::inverse, "inverse");
matrix_class.def(&matrix_w::solve2_w, "solve");
matrix_class.def(&matrix_w::solve3_w, "solve");
#ifdef PYGINAC_symbol
matrix_class.def(&matrix_w::charpoly_w, "charpoly");
#endif

this_module.def(rows_w, "rows");
this_module.def(cols_w, "cols");
this_module.def(transpose_w, "transpose");
this_module.def(determinant1_w, "determinant");
this_module.def(determinant2_w, "determinant");
this_module.def(trace_w, "trace");
#ifdef PYGINAC_symbol
this_module.def(charpoly_w, "charpoly");
#endif
this_module.def(inverse_w, "inverse");
#ifdef PYGINAC_lst
this_module.def(diag_matrix_w, "diag_matrix");
#endif
'''

implementation = '''
EX_TO_BASIC(matrix)

static python::tuple matrix_coerce(const GiNaC::matrix & left, python::ref right) {
  PyObject * o = right.get();
#ifdef PYGINAC_numeric
  if (PyInt_Check(o) || PyFloat_Check(o) || PyComplex_Check(o) || PyString_Check(o) || NumericInstance_Check(o))
    return python::tuple(left,(const GiNaC::numeric &)as_numeric_w(right));
#endif
  if (PySequence_Check(o))
    return python::tuple(left,(const GiNaC::matrix &)matrix_from_ref(right));
  return python::tuple((const GiNaC::ex &)GiNaC::ex(left), (const GiNaC::ex &)(ex_w(right)));
}

GiNaC::matrix matrix_from_ref(python::ref m) {
  PyObject * o = m.get();
  if (MatrixInstance_Check(o)) {
    Py_INCREF(o);
    return BOOST_PYTHON_CONVERSION::from_python(o, python::type<const GiNaC::matrix &>());
  }
  unsigned r;
  unsigned c;
  if (!PySequence_Check(o)) {
    PyErr_SetString(PyExc_TypeError, "matrix() argument must be int,int|int,int,sequence|matrix|ex|double-sequence");
    throw python::error_already_set();
  }
  r = PySequence_Length(o);
  PyObject *o2 = PySequence_GetItem(o,0);
  if (PySequence_Check(o2))
    c = PySequence_Length(o2);
  else
    c = 1;
  GiNaC::exvector v(0);
  DEBUG_M("matrix::get_exvector2(ref)");
  for (unsigned i=0;i<r;++i) {
    PyObject *o2 = PySequence_GetItem(o,i);
    if (!PySequence_Check(o2))
      if (c>1) {
        PyErr_SetString(PyExc_TypeError, "matrix() sequence argument must be a sequence");
        throw python::error_already_set();
      } else {
        v.push_back(ex_w(python::ref(o2)));
      }
    else {
      if (!(unsigned(PySequence_Length(o2))==c)) {
        PyErr_SetString(PyExc_TypeError, "matrix() sequence argument must be a sequence of equal length sequences");
        throw python::error_already_set();
      }
      for (unsigned j=0;j<c;++j)
        v.push_back(ex_w(python::ref(PySequence_GetItem(o2,j))));
    }
  }
  return GiNaC::matrix(r,c,v);
}

PyObject* matrix_w::getitem(python::ref index) const {
  PyObject * o = index.get();
  int rows = this->rows();
  int cols = this->cols();
  int start_r, step_size_r, n_steps_r;
  int start_c, step_size_c, n_steps_c;
  DEBUG_M("matrix::getitem(ref)[rows="<<rows<<",cols="<<cols<<"]");
  if (PyTuple_Check(o))
    if (PyTuple_Size(o)==2) {
      start_r = parse_subindex(PyTuple_GetItem(o,0), &step_size_r, &n_steps_r, rows);
      start_c = parse_subindex(PyTuple_GetItem(o,1), &step_size_c, &n_steps_c, cols);
    } else {
      PyErr_SetString(PyExc_IndexError, "matrix() index must be 2-int|2-slice|int,slice|slice,int|int|slice");
      start_r = start_c = -1;
    }
  else {
    if (rows==1) {
      start_r = 0;
      step_size_r = 1;
      n_steps_r = 1;
      start_c = parse_subindex(o, &step_size_c, &n_steps_c, cols);
    } else {
      start_r = parse_subindex(o, &step_size_r, &n_steps_r, rows);
      start_c = 0;
      step_size_c = 1;
      n_steps_c = cols;
    }
  }
  DEBUG_M("start_r="<<start_r<<",step_size_r="<<step_size_r<<",n_steps_r="<<n_steps_r);
  DEBUG_M("start_c="<<start_c<<",step_size_c="<<step_size_c<<",n_steps_c="<<n_steps_c);
  if (start_r < 0 || start_c < 0)
    throw python::error_already_set();
  if (n_steps_r==PseudoIndex || n_steps_r==RubberIndex || n_steps_c==PseudoIndex || n_steps_c==RubberIndex) {
    PyErr_SetString(PyExc_NotImplementedError, "matrix() getitem PseudoIndex|RubberIndex");
    throw python::error_already_set();
  }
  if ((n_steps_r==SingleIndex && n_steps_c==SingleIndex) ||
    ((n_steps_r==SingleIndex && cols==1) || (n_steps_c==SingleIndex && rows==1)))
    return BOOST_PYTHON_CONVERSION::to_python(UNEX(this->operator()(start_r,start_c)));
  if (n_steps_r==SingleIndex)
    n_steps_r = step_size_r = 1;
  if (n_steps_c==SingleIndex)
    n_steps_c = step_size_c = 1;
  GiNaC::matrix m(n_steps_r,n_steps_c);
  int kr = start_r;
  for(int i=0;i<n_steps_r;++i,kr += step_size_r)
    for(int j=0, kc=start_c;j<n_steps_c;++j,kc += step_size_c)
      m.set(i,j,(*this)((kr==rows)?0:kr,(kc==cols)?0:kc));
  return BOOST_PYTHON_CONVERSION::to_python(m);
}

void matrix_w::setitem(python::ref index, python::ref other) {
  PyObject * o = index.get();
  int rows = this->rows();
  int cols = this->cols();
  int start_r, step_size_r, n_steps_r;
  int start_c, step_size_c, n_steps_c;
  DEBUG_M("matrix::setitem(ref,ref)[rows="<<rows<<",cols="<<cols<<"]");
  if (PyTuple_Check(o))
    if (PyTuple_Size(o)==2) {
      start_r = parse_subindex(PyTuple_GetItem(o,0), &step_size_r, &n_steps_r, rows);
      start_c = parse_subindex(PyTuple_GetItem(o,1), &step_size_c, &n_steps_c, cols);
    } else {
      PyErr_SetString(PyExc_IndexError, "matrix() index must be 2-int|2-slice|int,slice|slice,int|int|slice");
      start_r = start_c = -1;
    }
  else {
    if (rows==1) {
      start_r = 0;
      step_size_r = 1;
      n_steps_r = 1;
      start_c = parse_subindex(o, &step_size_c, &n_steps_c, cols);
    } else {
      start_r = parse_subindex(o, &step_size_r, &n_steps_r, rows);
      start_c = 0;
      step_size_c = 1;
      n_steps_c = cols;
    }
  }
  DEBUG_M("start_r="<<start_r<<",step_size_r="<<step_size_r<<",n_steps_r="<<n_steps_r);
  DEBUG_M("start_c="<<start_c<<",step_size_c="<<step_size_c<<",n_steps_c="<<n_steps_c);
  if (start_r < 0 || start_c < 0)
    throw python::error_already_set();
  if (n_steps_r==PseudoIndex || n_steps_r==RubberIndex || n_steps_c==PseudoIndex || n_steps_c==RubberIndex) {
    PyErr_SetString(PyExc_NotImplementedError, "matrix() setitem PseudoIndex|RubberIndex");
    throw python::error_already_set();
  }
  Py_INCREF(other);
  if ((n_steps_r==SingleIndex && n_steps_c==SingleIndex) ||
    ((n_steps_r==SingleIndex && cols==1) || (n_steps_c==SingleIndex && rows==1)))
    this->set(start_r,start_c,ex_w(other));
  else {
    matrix_w m(other);
    int orows = m.rows();
    int ocols = m.cols();
    if (n_steps_r==SingleIndex)
      n_steps_r = step_size_r = 1;
    if (n_steps_c==SingleIndex)
      n_steps_c = step_size_c = 1;
    if ((orows==1 || ocols==1) && (n_steps_r==1 || n_steps_c==1))
      if (orows*ocols == n_steps_r*n_steps_c ) {
        int kr = start_r;
        for(int i=0,l=0;i<n_steps_r;++i,kr += step_size_r)
          for(int j=0, kc=start_c;j<n_steps_c;++j,kc += step_size_c,++l)
            this->set((kr==rows)?0:kr,(kc==cols)?0:kc, m.op(l));
      } else {
        PyErr_SetString(PyExc_IndexError, "matrix() index error (different sizes)");
        throw python::error_already_set();
      }
    else {
      if (orows != n_steps_r || ocols != n_steps_c ) {
        PyErr_SetString(PyExc_IndexError, "matrix() index error (not aligned)");
        throw python::error_already_set();
      }
      int kr = start_r;
      for(int i=0;i<n_steps_r;++i,kr += step_size_r)
        for(int j=0, kc=start_c;j<n_steps_c;++j,kc += step_size_c)
          this->set((kr==rows)?0:kr,(kc==cols)?0:kc, m(i,j));
    }
  }
}
'''

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>