Commits

Barry Schwartz committed de630a6

Start of geometric algebra support (which will replace complex numbers for the representation of points, vectors, etc.).

  • Participants
  • Parent commits 594944f

Comments (0)

Files changed (8)

 
 ACLOCAL_AMFLAGS = -I m4
 
+force_rebuild = if test -f $@; then :; else \
+        $(RM) $<; \
+	    $(MAKE) $(AM_MAKEFLAGS) $<; \
+     fi
+
+CPPFLAGS += -I"${top_srcdir}/c" -I"${top_builddir}/c"
+
 lib_LTLIBRARIES = libebeno.la
-libebeno_la_SOURCES = c/nan.c fortran/pascals_triangle.f90	\
-	fortran/sorting.f90 fortran/basic_algebra.f90			\
-	fortran/linear_algebra.f90 fortran/basic_geometry.f90	\
-	fortran/convex_hull.f90 fortran/brentroot.f90			\
-	fortran/curve.f90
+libebeno_la_SOURCES = c/nan.c ${builddir}/c/e2ga.c					\
+	fortran/pascals_triangle.f90 fortran/sorting.f90				\
+	fortran/basic_algebra.f90 fortran/linear_algebra.f90			\
+	fortran/geometric_algebra.f90 fortran/basic_geometry.f90		\
+	fortran/convex_hull.f90 fortran/brentroot.f90 fortran/curve.f90
 
 pkginclude_HEADERS = c/basic_algebra.h c/basic_geometry.h	\
-	c/convex_hull.h c/curve.h
+	c/convex_hull.h c/curve.h c/geometric_algebra.h
 
 pkgconfigdir="${libdir}/pkgconfig"
 nodist_pkgconfig_DATA = ebeno.pc
 
+${builddir}/c/e2ga.h: ${srcdir}/c/e2ga.xml
+	$(G25) $<
+	-$(MKDIR_P) ${builddir}/c
+	mv e2ga.c e2ga.h c
+${builddir}/c/e2ga.c: ${builddir}/c/e2ga.h ${srcdir}/c/e2ga.xml
+	@$(force_rebuild)
+
+EXTRA_DIST = ${srcdir}/c/e2ga.xml c/e2ga.c c/e2ga.h
+
 #--------------------------------------------------------------------------
 
 if PURE_ENABLED
 	`$(PKG_CONFIG) --libs lapack`
 
 dist_pure_lib_DATA = pure/ebeno.pure
-dist_pure_ebeno_DATA = pure/ebeno/basic_algebra.pure \
-	pure/ebeno/basic_algebra_h.pure pure/ebeno/basic_geometry.pure	\
-	pure/ebeno/basic_geometry_h.pure pure/ebeno/brentroot.pure	\
-	pure/ebeno/convex_hull.pure pure/ebeno/convex_hull_h.pure	\
-	pure/ebeno/curve.pure pure/ebeno/curve_h.pure				\
-	pure/ebeno/linear_algebra.pure pure/ebeno/machine.pure		\
-	pure/ebeno/pascals_triangle.pure
+dist_pure_ebeno_DATA = pure/ebeno/basic_algebra.pure					\
+	pure/ebeno/basic_algebra_h.pure pure/ebeno/basic_geometry.pure		\
+	pure/ebeno/basic_geometry_h.pure pure/ebeno/brentroot.pure			\
+	pure/ebeno/convex_hull.pure pure/ebeno/convex_hull_h.pure			\
+	pure/ebeno/curve.pure pure/ebeno/curve_h.pure						\
+	pure/ebeno/geometric_algebra_h.pure									\
+	pure/ebeno/geometric_algebra.pure pure/ebeno/linear_algebra.pure	\
+	pure/ebeno/machine.pure pure/ebeno/pascals_triangle.pure
 
 pure/ebeno/%_h.pure: c/%.h
+	-$(MKDIR_P) ${builddir}/pure/ebeno
 	$(PUREGEN) -o $@ $< -llibebeno
 
 TESTS_ENVIRONMENT = \
 
 if HAVE_MODFILES
 
-MODFILES = ebeno_pascals_triangle ebeno_sorting ebeno_basic_algebra	\
-	ebeno_linear_algebra ebeno_basic_geometry ebeno_convex_hull		\
-	ebeno_brentroot ebeno_curve
+MODFILES = ebeno_pascals_triangle ebeno_sorting ebeno_basic_algebra		\
+	ebeno_linear_algebra ebeno_geometric_algebra ebeno_basic_geometry	\
+	ebeno_convex_hull ebeno_brentroot ebeno_curve
 
 include_HEADERS = $(foreach f, $(MODFILES), $(call modfile,${f}))
 
-force_rebuild = if test -f $@; then :; else \
-        $(RM) $<; \
-	    $(MAKE) $(AM_MAKEFLAGS) $<; \
-     fi
-
 $(call modfile,ebeno_basic_algebra): basic_algebra.lo
 	@$(force_rebuild)
 $(call modfile,ebeno_basic_geometry): basic_geometry.lo
 	@$(force_rebuild)
 $(call modfile,ebeno_convex_hull): convex_hull.lo
 	@$(force_rebuild)
+$(call modfile,ebeno_geometric_algebra): geometric_algebra.lo
+	@$(force_rebuild)
 $(call modfile,ebeno_linear_algebra): linear_algebra.lo
 	@$(force_rebuild)
 $(call modfile,ebeno_pascals_triangle): pascals_triangle.lo
 
 #--------------------------------------------------------------------------
 
-EXTRA_DIST = COPYING INSTALL
-MOSTLYCLEANFILES = $(foreach f, $(MODFILES), $(call modfile,${f}))
-MAINTAINERCLEANFILES = pure/basic_algebra_h.pure		\
-	pure/basic_geometry_h.pure pure/convex_hull_h.pure	\
-	pure/curve_h.pure
+EXTRA_DIST += COPYING INSTALL
+MOSTLYCLEANFILES = $(foreach f, $(MODFILES), $(call modfile,${f}))	\
+	Doxyfile
+MAINTAINERCLEANFILES = c/e2ga.c c/e2ga.h								\
+	pure/ebeno/basic_algebra_h.pure pure/ebeno/basic_geometry_h.pure	\
+	pure/ebeno/convex_hull_h.pure pure/ebeno/curve_h.pure				\
+	pure/ebeno/geometric_algebra_h.pure
 
 #--------------------------------------------------------------------------
+<?xml version="1.0" encoding="utf-8" ?>
+
+<g25spec license="custom" language="c" namespace="e2ga"
+         coordStorage="array" defaultOperatorBindings="false"
+         dimension="2" testSuite="false" reportUsage="false" 
+         gmvCode="expand" parser="none"
+         copyright="Copyright (c) 2012 Barry Schwartz">
+
+  <customLicense>
+    Copyright (c) 2012 Barry Schwartz
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
+  </customLicense>
+
+  <inline constructors="false" set="false" assign="false"
+          operators="false" functions="false"/>
+  <floatType type="double"/>
+
+  <basisVectorNames name1="e1" name2="e2"/>
+  <metric name="default"> e1.e1 = e2.e2 = 1 </metric>
+
+  <mv name="mv" compress="byGroup" coordinateOrder="custom" memAlloc="full">
+    <group> scalar </group>
+    <group> e1 e2  </group>
+    <group> e1^e2  </group>
+  </mv>
+
+  <function name="add" arg1="mv" arg2="mv"/>
+  <function name="applyUnitVersor" arg1="mv" arg2="mv"/>
+  <function name="applyVersor" arg1="mv" arg2="mv"/>
+  <function name="applyVersorWI" arg1="mv" arg2="mv" arg3="mv"/>
+  <function name="div" arg1="mv" arg2="double"/>
+  <function name="dual" arg1="mv"/>
+  <function name="equals" arg1="mv" arg2="mv" arg3="double"/>
+  <function name="extractGrade" arg1="mv"/>
+  <function name="extractGrade0" arg1="mv"/>
+  <function name="extractGrade1" arg1="mv"/>
+  <function name="extractGrade2" arg1="mv"/>
+  <function name="gp" arg1="mv" arg2="mv"/>
+  <function name="norm2" arg1="mv"/>
+  <function name="reverse" arg1="mv"/>
+  <function name="subtract" arg1="mv" arg2="mv"/>
+  <function name="undual" arg1="mv"/>
+  <function name="versorInverse" arg1="mv"/>
+
+</g25spec>

c/geometric_algebra.h

+extern void ebeno_mv_of_scalar(int *gu, double *c, double r);
+extern double ebeno_mv_scalar_coord(int gu, double *c);
+extern double ebeno_mv_e1_coord(int gu, double *c);
+extern double ebeno_mv_e2_coord(int gu, double *c);
+extern double ebeno_mv_e1_e2_coord(int gu, double *c);
+extern void ebeno_mv_with_scalar(int *gu, double *c, double r);
+extern void ebeno_gp_mv_mv(int *gu_out, double *c_out, int gu1, double *c1, int gu2, double *c2);
 AC_INIT([ebeno],[0.1],[https://bitbucket.org/chemoelectric/ebeno])
 AM_INIT_AUTOMAKE([foreign])
 AC_CONFIG_MACRO_DIR([m4])
+LT_INIT
 
 
 # Checks for programs.
 AC_PROG_CC
 AC_PROG_FC
-LT_INIT
+AC_PROG_MKDIR_P
 EBENO_OPTIONAL_PURE
 if test x"$pure_enabled" = xyes; then
    EBENO_FIND_PUREGEN
 fi
+EBENO_FIND_G25
 
 
 # Checks for libraries.

fortran/geometric_algebra.f90

+! Copyright (c) 2012 Barry Schwartz
+! 
+! Permission is hereby granted, free of charge, to any person obtaining a copy
+! of this software and associated documentation files (the "Software"), to deal
+! in the Software without restriction, including without limitation the rights
+! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+! copies of the Software, and to permit persons to whom the Software is
+! furnished to do so, subject to the following conditions:
+! 
+! The above copyright notice and this permission notice shall be included in
+! all copies or substantial portions of the Software.
+! 
+! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+! THE SOFTWARE.
+
+module ebeno_geometric_algebra
+  use, intrinsic :: iso_c_binding
+
+  implicit none
+
+  integer, parameter, private :: dp = selected_real_kind(15,307)
+  real(dp), parameter, private :: eps = epsilon(1._dp)
+
+  integer, parameter :: mv_size = 4
+
+  private :: nan
+
+  type, bind(c) :: mv_
+     integer(c_int) :: gu
+     real(c_double), dimension(mv_size) :: c
+  end type mv_
+
+  type :: mv
+     integer :: gu                     ! Group usage bitmap.
+     real(dp), dimension(mv_size) :: c ! Coordinates.
+  end type mv
+
+  interface
+     ! FIXME: Rewrite this to generate a NaN via a suitable IEEE
+     ! arithmetic module, when one becomes available in our compilers.
+     pure function nan()
+       implicit none
+       real(selected_real_kind(15,307)) :: nan
+     end function nan
+  end interface
+
+  interface operator (.multivector.)
+     module procedure mv_of_scalar
+  end interface operator (.multivector.)
+
+  interface operator (.scalarcoord.)
+     module procedure mv_scalar_coord
+  end interface operator (.scalarcoord.)
+
+  interface operator (.eonecoord.)
+     module procedure mv_e1_coord
+  end interface operator (.eonecoord.)
+
+  interface operator (.etwocoord.)
+     module procedure mv_e2_coord
+  end interface operator (.etwocoord.)
+
+  interface operator (.bivectorcoord.)
+     module procedure mv_e1_e2_coord
+  end interface operator (.bivectorcoord.)
+
+  interface operator (.withscalar.)
+     module procedure mv_with_scalar
+  end interface operator (.withscalar.)
+
+  interface operator (*)
+     module procedure gp_mv_mv
+  end interface operator (*)
+
+contains
+
+  function mv_of_scalar(r) result(A)
+    real(dp), intent(in) :: r
+    type(mv) :: A
+
+    interface
+       subroutine mv_setScalar_(A,r) bind(c, name="mv_setScalar")
+         use, intrinsic :: iso_c_binding
+         implicit none
+         type(c_ptr), value :: A
+         real(c_double), value :: r
+       end subroutine mv_setScalar_
+    end interface
+
+    type(mv_), target :: A_
+
+    call mv_setScalar_(c_loc(A_),r)
+    A%gu = A_%gu
+    A%c = A_%c
+  end function mv_of_scalar
+
+  function mv_scalar_coord(A) result(r)
+    type(mv), intent(in) :: A
+    real(dp) :: r
+
+    interface
+       function mv_scalar_(A) result(r) bind(c, name="mv_scalar")
+         use, intrinsic :: iso_c_binding
+         type(c_ptr), value :: A
+         real(c_double) :: r
+       end function mv_scalar_
+    end interface
+
+    type(mv_), target :: A_
+
+    A_%gu = A%gu
+    A_%c = A%c
+    r = mv_scalar_(c_loc(A_))
+  end function mv_scalar_coord
+
+  function mv_e1_coord(A) result(r)
+    type(mv), intent(in) :: A
+    real(dp) :: r
+
+    interface
+       function mv_e1_(A) result(r) bind(c, name="mv_e1")
+         use, intrinsic :: iso_c_binding
+         type(c_ptr), value :: A
+         real(c_double) :: r
+       end function mv_e1_
+    end interface
+
+    type(mv_), target :: A_
+
+    A_%gu = A%gu
+    A_%c = A%c
+    r = mv_e1_(c_loc(A_))
+  end function mv_e1_coord
+
+  function mv_e2_coord(A) result(r)
+    type(mv), intent(in) :: A
+    real(dp) :: r
+
+    interface
+       function mv_e2_(A) result(r) bind(c, name="mv_e2")
+         use, intrinsic :: iso_c_binding
+         type(c_ptr), value :: A
+         real(c_double) :: r
+       end function mv_e2_
+    end interface
+
+    type(mv_), target :: A_
+
+    A_%gu = A%gu
+    A_%c = A%c
+    r = mv_e2_(c_loc(A_))
+  end function mv_e2_coord
+
+  function mv_e1_e2_coord(A) result(r)
+    type(mv), intent(in) :: A
+    real(dp) :: r
+
+    interface
+       function mv_e1_e2_(A) result(r) bind(c, name="mv_e1_e2")
+         use, intrinsic :: iso_c_binding
+         type(c_ptr), value :: A
+         real(c_double) :: r
+       end function mv_e1_e2_
+    end interface
+
+    type(mv_), target :: A_
+
+    A_%gu = A%gu
+    A_%c = A%c
+    r = mv_e1_e2_(c_loc(A_))
+  end function mv_e1_e2_coord
+
+  function mv_with_scalar(A,r) result(B)
+    type(mv), intent(in) :: A
+    real(dp), intent(in) :: r
+    type(mv) :: B
+
+    interface
+       subroutine mv_set_scalar_(A,r) bind(c, name="mv_set_scalar")
+         use, intrinsic :: iso_c_binding
+         type(c_ptr), value :: A
+         real(c_double), value :: r
+       end subroutine mv_set_scalar_
+    end interface
+
+    type(mv_), target :: A_
+
+    A_%gu = A%gu
+    A_%c = A%c
+    call mv_set_scalar_(c_loc(A_),r)
+    B%gu = A_%gu
+    B%c = A_%c
+  end function mv_with_scalar
+
+  function gp_mv_mv(A,B) result(C)
+    type(mv), intent(in) :: A,B
+    type(mv) :: C
+
+    interface
+       subroutine gp_mv_mv_(dst,a,b) bind(c, name="gp_mv_mv")
+         use, intrinsic :: iso_c_binding
+         type(c_ptr), value :: dst, a, b
+       end subroutine gp_mv_mv_
+    end interface
+
+    type(mv_), target :: A_,B_,C_
+
+    A_%gu = A%gu
+    A_%c = A%c
+    B_%gu = B%gu
+    B_%c = B%c
+    call gp_mv_mv_(c_loc(C_), c_loc(A_), c_loc(B_))
+    C%gu = C_%gu
+    C%c = C_%c
+  end function gp_mv_mv
+
+
+end module ebeno_geometric_algebra
+
+!--------------------------------------------------------------------------
+!
+! Interfaces for the C and Pure languages.
+
+subroutine ebeno_mv_of_scalar(gu,c,r) bind(c)
+  use, intrinsic :: iso_c_binding
+  use :: ebeno_geometric_algebra
+  implicit none
+  integer(c_int), intent(out) :: gu
+  real(c_double), dimension(mv_size), intent(out) :: c
+  real(c_double), intent(in), value :: r
+
+  type(mv) :: A
+
+  A = .multivector. r
+  gu = A%gu
+  c = A%c
+end subroutine ebeno_mv_of_scalar
+
+function ebeno_mv_scalar_coord(gu,c) result(r) bind(c)
+  use, intrinsic :: iso_c_binding
+  use :: ebeno_geometric_algebra
+  implicit none
+  integer(c_int), intent(in), value :: gu
+  real(c_double), dimension(mv_size), intent(in) :: c
+  real(c_double) :: r
+
+  type(mv) :: A
+
+  A%gu = gu
+  A%c = c
+  r = .scalarcoord. A
+end function ebeno_mv_scalar_coord
+
+function ebeno_mv_e1_coord(gu,c) result(r) bind(c)
+  use, intrinsic :: iso_c_binding
+  use :: ebeno_geometric_algebra
+  implicit none
+  integer(c_int), intent(in), value :: gu
+  real(c_double), dimension(mv_size), intent(in) :: c
+  real(c_double) :: r
+
+  type(mv) :: A
+
+  A%gu = gu
+  A%c = c
+  r = .eonecoord. A
+end function ebeno_mv_e1_coord
+
+function ebeno_mv_e2_coord(gu,c) result(r) bind(c)
+  use, intrinsic :: iso_c_binding
+  use :: ebeno_geometric_algebra
+  implicit none
+  integer(c_int), intent(in), value :: gu
+  real(c_double), dimension(mv_size), intent(in) :: c
+  real(c_double) :: r
+
+  type(mv) :: A
+
+  A%gu = gu
+  A%c = c
+  r = .etwocoord. A
+end function ebeno_mv_e2_coord
+
+function ebeno_mv_e1_e2_coord(gu,c) result(r) bind(c)
+  use, intrinsic :: iso_c_binding
+  use :: ebeno_geometric_algebra
+  implicit none
+  integer(c_int), intent(in), value :: gu
+  real(c_double), dimension(mv_size), intent(in) :: c
+  real(c_double) :: r
+
+  type(mv) :: A
+
+  A%gu = gu
+  A%c = c
+  r = .bivectorcoord. A
+end function ebeno_mv_e1_e2_coord
+
+subroutine ebeno_mv_with_scalar(gu,c,r) bind(c)
+  use, intrinsic :: iso_c_binding
+  use :: ebeno_geometric_algebra
+  implicit none
+  integer(c_int), intent(inout) :: gu
+  real(c_double), dimension(mv_size), intent(inout) :: c
+  real(c_double), intent(in), value :: r
+
+  type(mv) :: A
+
+  A%gu = gu
+  A%c = c
+  A = A .withscalar. r
+  gu = A%gu
+  c = A%c
+end subroutine ebeno_mv_with_scalar
+
+subroutine ebeno_gp_mv_mv(gu_out, c_out, gu1, c1, gu2, c2) bind(c)
+  use, intrinsic :: iso_c_binding
+  use :: ebeno_geometric_algebra
+  implicit none
+  integer(c_int), intent(out) :: gu_out
+  real(c_double), dimension(mv_size), intent(out) :: c_out
+  integer(c_int), intent(in), value :: gu1, gu2
+  real(c_double), dimension(mv_size), intent(in) :: c1, c2
+
+  type(mv) :: A,B,C
+
+  A%gu = gu1
+  A%c = c1
+  B%gu = gu2
+  B%c = c2
+  C = A*B
+  gu_out = C%gu
+  c_out = C%c
+end subroutine ebeno_gp_mv_mv
+
+!--------------------------------------------------------------------------
+# EBENO_FIND_G25
+# --------------
+AC_DEFUN([EBENO_FIND_G25],
+[
+AC_ARG_VAR([G25],[Gaigen 2.5 command (geometric algebra implementation generator)])
+if test x"${G25}" = x ; then
+   AC_PATH_PROG([G25],[g25])
+   test x"${G25}" = x && AC_MSG_WARN([could not find g25])
+else
+   AC_MSG_CHECKING([for g25])
+   AC_MSG_RESULT([${G25}])
+fi
+])
   ebeno::brentroot,
   ebeno::convex_hull,
   ebeno::curve,
+  ebeno::geometric_algebra,
   ebeno::linear_algebra,
   ebeno::machine;

pure/ebeno/geometric_algebra.pure

+// Copyright (c) 2012 Barry Schwartz
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+//
+// Geometric algebra of the euclidean plane.
+//
+
+using ebeno::geometric_algebra_h;
+using math;
+
+namespace ebeno;
+
+//-------------------------------------------------------------------------
+
+nonfix mvec2;
+public bad_multivector_grade;
+
+outfix ⟨ ⟩ ; // FIXME: Do we really want these symbols?
+
+type mvec2 (mvec2 gu::int c::dmatrix) = dim c == (1,4);
+
+// Convert a scalar to a multivector.
+mvec2 r::double = mvec2 (gu!0) c
+  when
+    gu::imatrix = {0};
+    c::dmatrix = {0.0, 0.0, 0.0, 0.0};
+    ebeno_mv_of_scalar gu c r;
+  end;
+
+// Extract the individual coordinates of a multivector.
+scalar_coord mv::mvec2 =
+  case mv of
+    mvec2 gu c = ebeno_mv_scalar_coord gu (pack c);
+  end;
+e1_coord mv::mvec2 =
+  case mv of
+    mvec2 gu c = ebeno_mv_e1_coord gu (pack c);
+  end;
+e2_coord mv::mvec2 =
+  case mv of
+    mvec2 gu c = ebeno_mv_e2_coord gu (pack c);
+  end;
+e1e2_coord mv::mvec2 =
+  case mv of
+    mvec2 gu c = ebeno_mv_e1_e2_coord gu (pack c);
+  end;
+
+// Set scalar coordinate of a multivector.
+with_scalar mv::mvec2 r::double =
+  case mv of
+    mvec2 gu c = mvec2 (gu1!0) c1
+      when
+        gu1::imatrix = {0};
+        c1::dmatrix = pack c;
+        ebeno_mv_with_scalar gu1 c1 r;
+      end;
+  end;
+
+// Select one grade of a multivector.
+⟨mv::mvec2⟩ grade::int | // FIXME: Do we really want the symbols?
+select_grade grade::int mv::mvec2 =
+  case grade of
+    0 = scalar_coord mv;
+    1 = throw "NOT YET IMPLEMENTED";
+    2 = throw "NOT YET IMPLEMENTED";
+    _ = throw (bad_multivector_grade grade mv);
+  end;
+
+// Geometric product of multivectors.
+mv1::mvec2 ::* mv2::mvec2 =
+  case mv1, mv2 of
+    mvec2 gu1 c1, mvec2 gu2 c2 = mvec2 (gu!0) c
+      when
+        gu::imatrix = {0};
+        c::dmatrix = {0.0, 0.0, 0.0, 0.0};
+        ebeno_gp_mv_mv gu c gu1 (pack c1) gu2 (pack c2);
+      end;
+  end;
+
+//-------------------------------------------------------------------------
+
+/*
+nonfix Vec BVec MVec;
+
+type vec (Vec M::imatrix) = dim M == (1,2);
+type vec (Vec M::dmatrix) = dim M == (1,2);
+type vec (Vec {e1::real, e2::real});
+
+type bivec (BVec M::imatrix) = dim M == (1,1);
+type bivec (BVec M::dmatrix) = dim M == (1,1);
+type bivec (BVec {I::real});
+
+type multivec a::real;
+type multivec u::vec;
+type multivec B::bivec;
+type multivec (MVec M::imatrix) = dim M == (1,4);
+type multivec (MVec M::dmatrix) = dim M == (1,4);
+type multivec (MVec {a::real, e1::real, e2::real, I::real});
+
+const e1 = Vec {1,0};
+const e2 = Vec {0,1};
+const I  = BVec {1};
+
+infix (*) ⋀ :^: ; // Outer product.
+infix (*) ⋅ :.: ; // Dot product.
+(:^:) = (⋀);
+(:.:) = (⋅);
+
+a::real ::* Vec {u1,u2} | Vec {u1,u2} ::* a::real = Vec {a*u1,a*u2};
+Vec {u1,u2} ::/ a::real = Vec {u1/a,u2/a};
+Vec {u1,u2} ::+ Vec {v1,v2} = Vec {u1 + v1, u2 + v2};
+Vec {u1,u2} ::- Vec {v1,v2} = Vec {u1 - v1, u2 - v2};
+Vec {u1,u2} ::.* Vec {v1,v2} = Vec {u1 * v1, u2 * v2};
+Vec {u1,u2} ::./ Vec {v1,v2} = Vec {u1/v1, u2/v2};
+Vec {u1,u2} ⋅ Vec {v1,v2} = u1*v1 + u2*v2;
+Vec {u1,u2} ⋀ Vec {v1,v2} = BVec {u1*v2 - u2*v1};
+
+a::real ::* BVec {u1,u2} | BVec {u1,u2} ::* a::real = BVec {a*u1,a*u2};
+BVec {b} ::+ BVec {c} = BVec {b + c};
+BVec {b} ::- BVec {c} = BVec {b - c};
+
+MVec {a1,u1,v1,b1} * MVec {a2,u2,v2,b2} =
+  MVec {a1*a2 + u1*u2 + v1*v2 - b1*b2,
+        a1*u2 + a2*u1 - v1*b2 - v2*b1,
+        a1*v2 + a2*v1 + u1*b2 + u2*b1,
+        a1*b2 + a2*b1 + u1*v2 - u2*v1};
+*/