1 // Ring operations.
2 
3 #ifndef _CL_RING_H
4 #define _CL_RING_H
5 
6 #include "cln/object.h"
7 #include "cln/malloc.h"
8 #include "cln/proplist.h"
9 #include "cln/number.h"
10 #include "cln/exception.h"
11 #include "cln/io.h"
12 
13 namespace cln {
14 
15 class cl_I;
16 
17 // This file defines the general layout of rings, ring elements, and
18 // operations available on ring elements. Any subclass of `cl_ring'
19 // must implement these operations, with the same memory layout.
20 // (Because generic packages like the polynomial rings access the base
21 // ring's operation vectors through inline functions defined in this file.)
22 
23 class cl_heap_ring;
24 
25 // Rings are reference counted, but not freed immediately when they aren't
26 // used any more. Hence they inherit from `cl_rcpointer'.
27 
28 // Vectors of function pointers are more efficient than virtual member
29 // functions. But it constrains us not to use multiple or virtual inheritance.
30 //
31 // Note! We are passing raw `cl_heap_ring*' pointers to the operations
32 // for efficiency (compared to passing `const cl_ring&', we save a memory
33 // access, and it is easier to cast to a `cl_heap_ring_specialized*').
34 // These raw pointers are meant to be used downward (in the dynamic extent
35 // of the call) only. If you need to save them in a data structure, cast
36 // to `cl_ring'; this will correctly increment the reference count.
37 // (This technique is safe because the inline wrapper functions make sure
38 // that we have a `cl_ring' somewhere containing the pointer, so there
39 // is no danger of dangling pointers.)
40 //
41 // Note! Because the `cl_heap_ring*' -> `cl_ring' conversion increments
42 // the reference count, you have to use the `cl_private_thing' -> `cl_ring'
43 // conversion if the reference count is already incremented.
44 
45 class cl_ring : public cl_rcpointer {
46 public:
47 	// Constructor. Takes a cl_heap_ring*, increments its refcount.
48 	cl_ring (cl_heap_ring* r);
49 	// Private constructor. Doesn't increment the refcount.
50 	cl_ring (cl_private_thing);
51 	// Copy constructor.
52 	cl_ring (const cl_ring&);
53 	// Assignment operator.
54 	cl_ring& operator= (const cl_ring&);
55 	// Default constructor.
56 	cl_ring ();
57 	// Automatic dereferencing.
58 	cl_heap_ring* operator-> () const
59 	{ return (cl_heap_ring*)heappointer; }
60 };
CL_DEFINE_COPY_CONSTRUCTOR2(cl_ring,cl_rcpointer)61 CL_DEFINE_COPY_CONSTRUCTOR2(cl_ring,cl_rcpointer)
62 CL_DEFINE_ASSIGNMENT_OPERATOR(cl_ring,cl_ring)
63 
64 // Normal constructor for `cl_ring'.
65 inline cl_ring::cl_ring (cl_heap_ring* r)
66 { cl_inc_pointer_refcount((cl_heap*)r); pointer = r; }
67 // Private constructor for `cl_ring'.
cl_ring(cl_private_thing p)68 inline cl_ring::cl_ring (cl_private_thing p)
69 { pointer = p; }
70 
71 inline bool operator== (const cl_ring& R1, const cl_ring& R2)
72 { return (R1.pointer == R2.pointer); }
73 inline bool operator!= (const cl_ring& R1, const cl_ring& R2)
74 { return (R1.pointer != R2.pointer); }
75 inline bool operator== (const cl_ring& R1, cl_heap_ring* R2)
76 { return (R1.pointer == R2); }
77 inline bool operator!= (const cl_ring& R1, cl_heap_ring* R2)
78 { return (R1.pointer != R2); }
79 
80 // Representation of an element of a ring.
81 //
82 // In order to support true polymorphism (without C++ templates), all
83 // ring elements share the same basic layout:
84 //      cl_ring ring;     // the ring
85 //      cl_gcobject rep;  // representation of the element
86 // The representation of the element depends on the ring, of course,
87 // but we constrain it to be a single pointer into the heap or an immediate
88 // value.
89 //
90 // Any arithmetic operation on a ring R (like +, -, *) must return a value
91 // with ring = R. This is
92 // a. necessary if the computation is to proceed correctly (e.g. in cl_RA,
93 //    ((3/4)*4 mod 3) is 0, simplifying it to ((cl_I)4 mod (cl_I)3) = 1
94 //    wouldn't be correct),
95 // b. possible even if R is an extension ring of some ring R1 (e.g. cl_N
96 //    being an extension ring of cl_R). Automatic retraction from R to R1
97 //    can be done through dynamic typing: An element of R which happens
98 //    to lie in R1 is stored using the internal representation of R1,
99 //    but with ring = R. Elements of R1 and R\R1 can be distinguished
100 //    through rep's type.
101 // c. an advantage for the implementation of polynomials and other
102 //    entities which contain many elements of the same ring. They need
103 //    to store only the elements' representations, and a single pointer
104 //    to the ring.
105 //
106 // The ring operations exist in two versions:
107 // - Low-level version, which only operates on the representation.
108 // - High-level version, which operates on full cl_ring_elements.
109 // We make this distinction for performance: Multiplication of polynomials
110 // over Z/nZ, operating on the high-level operations, spends 40% of its
111 // computing time with packing and unpacking of cl_ring_elements.
112 // The low-level versions have an underscore prepended and are unsafe.
113 
114 class _cl_ring_element {
115 public:
116 	cl_gcobject rep;	// representation of the element
117 	// Default constructor.
118 	_cl_ring_element ();
119 public: /* ugh */
120 	// Constructor.
_cl_ring_element(const cl_heap_ring * R,const cl_gcobject & r)121 	_cl_ring_element (const cl_heap_ring* R, const cl_gcobject& r) : rep (as_cl_private_thing(r)) { (void)R; }
_cl_ring_element(const cl_ring & R,const cl_gcobject & r)122 	_cl_ring_element (const cl_ring& R, const cl_gcobject& r) : rep (as_cl_private_thing(r)) { (void)R; }
123 public:	// Ability to place an object at a given address.
new(size_t size)124 	void* operator new (size_t size) { return malloc_hook(size); }
new(size_t size,void * ptr)125 	void* operator new (size_t size, void* ptr) { (void)size; return ptr; }
delete(void * ptr)126 	void operator delete (void* ptr) { free_hook(ptr); }
127 };
128 
129 class cl_ring_element : public _cl_ring_element {
130 protected:
131 	cl_ring _ring;			// ring
132 public:
ring()133 	const cl_ring& ring () const { return _ring; }
134 	// Default constructor.
135 	cl_ring_element ();
136 public: /* ugh */
137 	// Constructor.
cl_ring_element(const cl_ring & R,const cl_gcobject & r)138 	cl_ring_element (const cl_ring& R, const cl_gcobject& r) : _cl_ring_element (R,r), _ring (R) {}
cl_ring_element(const cl_ring & R,const _cl_ring_element & r)139 	cl_ring_element (const cl_ring& R, const _cl_ring_element& r) : _cl_ring_element (r), _ring (R) {}
140 public:	// Debugging output.
141 	void debug_print () const;
142 	// Ability to place an object at a given address.
new(size_t size)143 	void* operator new (size_t size) { return malloc_hook(size); }
new(size_t size,void * ptr)144 	void* operator new (size_t size, void* ptr) { (void)size; return ptr; }
delete(void * ptr)145 	void operator delete (void* ptr) { free_hook(ptr); }
146 };
147 
148 // The ring operations are encoded as vectors of function pointers. You
149 // can add more operations to the end of each vector or add new vectors,
150 // but you must not reorder the operations nor reorder the vectors nor
151 // change the functions' signatures incompatibly.
152 
153 // There should ideally be a template class for each vector, but unfortunately
154 // you lose the ability to initialize the vector using "= { ... }" syntax
155 // when you subclass it.
156 
157 struct _cl_ring_setops {
158 	// print
159 	void (* fprint) (cl_heap_ring* R, std::ostream& stream, const _cl_ring_element& x);
160 	// equality
161 	bool (* equal) (cl_heap_ring* R, const _cl_ring_element& x, const _cl_ring_element& y);
162 	// ...
163 };
164 struct _cl_ring_addops {
165 	// 0
166 	const _cl_ring_element (* zero) (cl_heap_ring* R);
167 	bool (* zerop) (cl_heap_ring* R, const _cl_ring_element& x);
168 	// x+y
169 	const _cl_ring_element (* plus) (cl_heap_ring* R, const _cl_ring_element& x, const _cl_ring_element& y);
170 	// x-y
171 	const _cl_ring_element (* minus) (cl_heap_ring* R, const _cl_ring_element& x, const _cl_ring_element& y);
172 	// -x
173 	const _cl_ring_element (* uminus) (cl_heap_ring* R, const _cl_ring_element& x);
174 	// ...
175 };
176 struct _cl_ring_mulops {
177 	// 1
178 	const _cl_ring_element (* one) (cl_heap_ring* R);
179 	// canonical homomorphism
180 	const _cl_ring_element (* canonhom) (cl_heap_ring* R, const cl_I& x);
181 	// x*y
182 	const _cl_ring_element (* mul) (cl_heap_ring* R, const _cl_ring_element& x, const _cl_ring_element& y);
183 	// x^2
184 	const _cl_ring_element (* square) (cl_heap_ring* R, const _cl_ring_element& x);
185 	// x^y, y Integer >0
186 	const _cl_ring_element (* expt_pos) (cl_heap_ring* R, const _cl_ring_element& x, const cl_I& y);
187 	// ...
188 };
189   typedef const _cl_ring_setops  cl_ring_setops;
190   typedef const _cl_ring_addops  cl_ring_addops;
191   typedef const _cl_ring_mulops  cl_ring_mulops;
192 
193 // Representation of a ring in memory.
194 
195 class cl_heap_ring : public cl_heap {
196 public:
197 	// Allocation.
new(size_t size)198 	void* operator new (size_t size) { return malloc_hook(size); }
199 	// Deallocation.
delete(void * ptr)200 	void operator delete (void* ptr) { free_hook(ptr); }
201 private:
202 	cl_property_list properties;
203 protected:
204 	cl_ring_setops* setops;
205 	cl_ring_addops* addops;
206 	cl_ring_mulops* mulops;
207 public:
208 	// More information comes here.
209 	// ...
210 public:
211 	// Low-level operations.
_fprint(std::ostream & stream,const _cl_ring_element & x)212 	void _fprint (std::ostream& stream, const _cl_ring_element& x)
213 		{ setops->fprint(this,stream,x); }
_equal(const _cl_ring_element & x,const _cl_ring_element & y)214 	bool _equal (const _cl_ring_element& x, const _cl_ring_element& y)
215 		{ return setops->equal(this,x,y); }
_zero()216 	const _cl_ring_element _zero ()
217 		{ return addops->zero(this); }
_zerop(const _cl_ring_element & x)218 	bool _zerop (const _cl_ring_element& x)
219 		{ return addops->zerop(this,x); }
_plus(const _cl_ring_element & x,const _cl_ring_element & y)220 	const _cl_ring_element _plus (const _cl_ring_element& x, const _cl_ring_element& y)
221 		{ return addops->plus(this,x,y); }
_minus(const _cl_ring_element & x,const _cl_ring_element & y)222 	const _cl_ring_element _minus (const _cl_ring_element& x, const _cl_ring_element& y)
223 		{ return addops->minus(this,x,y); }
_uminus(const _cl_ring_element & x)224 	const _cl_ring_element _uminus (const _cl_ring_element& x)
225 		{ return addops->uminus(this,x); }
_one()226 	const _cl_ring_element _one ()
227 		{ return mulops->one(this); }
_canonhom(const cl_I & x)228 	const _cl_ring_element _canonhom (const cl_I& x)
229 		{ return mulops->canonhom(this,x); }
_mul(const _cl_ring_element & x,const _cl_ring_element & y)230 	const _cl_ring_element _mul (const _cl_ring_element& x, const _cl_ring_element& y)
231 		{ return mulops->mul(this,x,y); }
_square(const _cl_ring_element & x)232 	const _cl_ring_element _square (const _cl_ring_element& x)
233 		{ return mulops->square(this,x); }
_expt_pos(const _cl_ring_element & x,const cl_I & y)234 	const _cl_ring_element _expt_pos (const _cl_ring_element& x, const cl_I& y)
235 		{ return mulops->expt_pos(this,x,y); }
236 	// High-level operations.
fprint(std::ostream & stream,const cl_ring_element & x)237 	void fprint (std::ostream& stream, const cl_ring_element& x)
238 	{
239 		if (!(x.ring() == this)) throw runtime_exception();
240 		_fprint(stream,x);
241 	}
equal(const cl_ring_element & x,const cl_ring_element & y)242 	bool equal (const cl_ring_element& x, const cl_ring_element& y)
243 	{
244 		if (!(x.ring() == this)) throw runtime_exception();
245 		if (!(y.ring() == this)) throw runtime_exception();
246 		return _equal(x,y);
247 	}
zero()248 	const cl_ring_element zero ()
249 	{
250 		return cl_ring_element(this,_zero());
251 	}
zerop(const cl_ring_element & x)252 	bool zerop (const cl_ring_element& x)
253 	{
254 		if (!(x.ring() == this)) throw runtime_exception();
255 		return _zerop(x);
256 	}
plus(const cl_ring_element & x,const cl_ring_element & y)257 	const cl_ring_element plus (const cl_ring_element& x, const cl_ring_element& y)
258 	{
259 		if (!(x.ring() == this)) throw runtime_exception();
260 		if (!(y.ring() == this)) throw runtime_exception();
261 		return cl_ring_element(this,_plus(x,y));
262 	}
minus(const cl_ring_element & x,const cl_ring_element & y)263 	const cl_ring_element minus (const cl_ring_element& x, const cl_ring_element& y)
264 	{
265 		if (!(x.ring() == this)) throw runtime_exception();
266 		if (!(y.ring() == this)) throw runtime_exception();
267 		return cl_ring_element(this,_minus(x,y));
268 	}
uminus(const cl_ring_element & x)269 	const cl_ring_element uminus (const cl_ring_element& x)
270 	{
271 		if (!(x.ring() == this)) throw runtime_exception();
272 		return cl_ring_element(this,_uminus(x));
273 	}
one()274 	const cl_ring_element one ()
275 	{
276 		return cl_ring_element(this,_one());
277 	}
canonhom(const cl_I & x)278 	const cl_ring_element canonhom (const cl_I& x)
279 	{
280 		return cl_ring_element(this,_canonhom(x));
281 	}
mul(const cl_ring_element & x,const cl_ring_element & y)282 	const cl_ring_element mul (const cl_ring_element& x, const cl_ring_element& y)
283 	{
284 		if (!(x.ring() == this)) throw runtime_exception();
285 		if (!(y.ring() == this)) throw runtime_exception();
286 		return cl_ring_element(this,_mul(x,y));
287 	}
square(const cl_ring_element & x)288 	const cl_ring_element square (const cl_ring_element& x)
289 	{
290 		if (!(x.ring() == this)) throw runtime_exception();
291 		return cl_ring_element(this,_square(x));
292 	}
expt_pos(const cl_ring_element & x,const cl_I & y)293 	const cl_ring_element expt_pos (const cl_ring_element& x, const cl_I& y)
294 	{
295 		if (!(x.ring() == this)) throw runtime_exception();
296 		return cl_ring_element(this,_expt_pos(x,y));
297 	}
298 	// Property operations.
get_property(const cl_symbol & key)299 	cl_property* get_property (const cl_symbol& key)
300 		{ return properties.get_property(key); }
add_property(cl_property * new_property)301 	void add_property (cl_property* new_property)
302 		{ properties.add_property(new_property); }
303 // Constructor.
cl_heap_ring(cl_ring_setops * setopv,cl_ring_addops * addopv,cl_ring_mulops * mulopv)304 	cl_heap_ring (cl_ring_setops* setopv, cl_ring_addops* addopv, cl_ring_mulops* mulopv)
305 		: setops (setopv), addops (addopv), mulops (mulopv)
306 		{ refcount = 0; } // will be incremented by the `cl_ring' constructor
307 };
308 #define SUBCLASS_cl_heap_ring() \
309 public:									  \
310 	/* Allocation. */						  \
311 	void* operator new (size_t size) { return malloc_hook(size); } \
312 	/* Deallocation. */						  \
313 	void operator delete (void* ptr) { free_hook(ptr); }
314 
315 // Operations on ring elements.
316 
317 // Output.
fprint(std::ostream & stream,const cl_ring_element & x)318 inline void fprint (std::ostream& stream, const cl_ring_element& x)
319 	{ x.ring()->fprint(stream,x); }
320 CL_DEFINE_PRINT_OPERATOR(cl_ring_element)
321 
322 // Add.
323 inline const cl_ring_element operator+ (const cl_ring_element& x, const cl_ring_element& y)
324 	{ return x.ring()->plus(x,y); }
325 
326 // Negate.
327 inline const cl_ring_element operator- (const cl_ring_element& x)
328 	{ return x.ring()->uminus(x); }
329 
330 // Subtract.
331 inline const cl_ring_element operator- (const cl_ring_element& x, const cl_ring_element& y)
332 	{ return x.ring()->minus(x,y); }
333 
334 // Equality.
335 inline bool operator== (const cl_ring_element& x, const cl_ring_element& y)
336 	{ return x.ring()->equal(x,y); }
337 inline bool operator!= (const cl_ring_element& x, const cl_ring_element& y)
338 	{ return !x.ring()->equal(x,y); }
339 
340 // Compare against 0.
zerop(const cl_ring_element & x)341 inline bool zerop (const cl_ring_element& x)
342 	{ return x.ring()->zerop(x); }
343 
344 // Multiply.
345 inline const cl_ring_element operator* (const cl_ring_element& x, const cl_ring_element& y)
346 	{ return x.ring()->mul(x,y); }
347 
348 // Squaring.
square(const cl_ring_element & x)349 inline const cl_ring_element square (const cl_ring_element& x)
350 	{ return x.ring()->square(x); }
351 
352 // Exponentiation x^y, where y > 0.
expt_pos(const cl_ring_element & x,const cl_I & y)353 inline const cl_ring_element expt_pos (const cl_ring_element& x, const cl_I& y)
354 	{ return x.ring()->expt_pos(x,y); }
355 
356 // Scalar multiplication.
357 // [Is this operation worth being specially optimized for the case of
358 // polynomials?? Polynomials have a faster scalar multiplication.
359 // We should use it.??]
360 inline const cl_ring_element operator* (const cl_I& x, const cl_ring_element& y)
361 	{ return y.ring()->mul(y.ring()->canonhom(x),y); }
362 inline const cl_ring_element operator* (const cl_ring_element& x, const cl_I& y)
363 	{ return x.ring()->mul(x.ring()->canonhom(y),x); }
364 
365 
366 // Ring of uninitialized elements.
367 // Any operation results in an exception being thrown.
368 
369 // Thrown when an attempt is made to perform an operation on an uninitialized ring.
370 class uninitialized_ring_exception : public runtime_exception {
371 public:
372 	uninitialized_ring_exception ();
373 };
374 
375 // Thrown when a ring element is uninitialized.
376 class uninitialized_exception : public runtime_exception {
377 public:
378 	explicit uninitialized_exception (const _cl_ring_element& obj);
379 	uninitialized_exception (const _cl_ring_element& obj_x, const _cl_ring_element& obj_y);
380 };
381 
382 extern const cl_ring cl_no_ring;
383 extern cl_class cl_class_no_ring;
384 
385 class cl_no_ring_init_helper
386 {
387 	static int count;
388 public:
389 	cl_no_ring_init_helper();
390 	~cl_no_ring_init_helper();
391 };
392 static cl_no_ring_init_helper cl_no_ring_init_helper_instance;
393 
cl_ring()394 inline cl_ring::cl_ring ()
395 	: cl_rcpointer (as_cl_private_thing(cl_no_ring)) {}
_cl_ring_element()396 inline _cl_ring_element::_cl_ring_element ()
397 	: rep ((cl_private_thing) cl_combine(cl_FN_tag,0)) {}
cl_ring_element()398 inline cl_ring_element::cl_ring_element ()
399 	: _cl_ring_element (), _ring () {}
400 
401 
402 // Support for built-in number rings.
403 // Beware, they are not optimally efficient.
404 
405 template <class T>
406 struct cl_number_ring_ops {
407 	bool (* contains) (const cl_number&);
408 	bool (* equal) (const T&, const T&);
409 	bool (* zerop) (const T&);
410 	const T (* plus) (const T&, const T&);
411 	const T (* minus) (const T&, const T&);
412 	const T (* uminus) (const T&);
413 	const T (* mul) (const T&, const T&);
414 	const T (* square) (const T&);
415 	const T (* expt_pos) (const T&, const cl_I&);
416 };
417 class cl_heap_number_ring : public cl_heap_ring {
418 public:
419 	cl_number_ring_ops<cl_number>* ops;
420 	// Constructor.
cl_heap_number_ring(cl_ring_setops * setopv,cl_ring_addops * addopv,cl_ring_mulops * mulopv,cl_number_ring_ops<cl_number> * opv)421 	cl_heap_number_ring (cl_ring_setops* setopv, cl_ring_addops* addopv, cl_ring_mulops* mulopv, cl_number_ring_ops<cl_number>* opv)
422 		: cl_heap_ring (setopv,addopv,mulopv), ops (opv) {}
423 };
424 
425 class cl_number_ring : public cl_ring {
426 public:
cl_number_ring(cl_heap_number_ring * r)427 	cl_number_ring (cl_heap_number_ring* r)
428 		: cl_ring (r) {}
429 };
430 
431 template <class T>
432 class cl_specialized_number_ring : public cl_number_ring {
433 public:
434 	cl_specialized_number_ring ();
435 };
436 
437 // Type test.
instanceof(const cl_number & x,const cl_number_ring & R)438 inline bool instanceof (const cl_number& x, const cl_number_ring& R)
439 {
440 	return ((cl_heap_number_ring*) R.heappointer)->ops->contains(x);
441 }
442 
443 
444 // Hack section.
445 
446 // Conversions to subtypes without checking:
447 // The2(cl_MI)(x) converts x to a cl_MI, without change of representation!
448   #define The(type)  *(const type *) & cl_identity
449   #define The2(type)  *(const type *) & cl_identity2
450 // This inline function is for type checking purposes only.
cl_identity(const cl_ring & r)451   inline const cl_ring& cl_identity (const cl_ring& r) { return r; }
cl_identity2(const cl_ring_element & x)452   inline const cl_ring_element& cl_identity2 (const cl_ring_element& x) { return x; }
cl_identity(const _cl_ring_element & x)453   inline const cl_gcobject& cl_identity (const _cl_ring_element& x) { return x.rep; }
454 
455 
456 // Debugging support.
457 #ifdef CL_DEBUG
458 extern int cl_ring_debug_module;
459 CL_FORCE_LINK(cl_ring_debug_dummy, cl_ring_debug_module)
460 #endif
461 
462 }  // namespace cln
463 
464 #endif /* _CL_RING_H */
465