Hypercomplex
Abstract & fast header-only C++ template library for lattice-based cryptosystems in high-dimensional algebras
Loading...
Searching...
No Matches
Hypercomplex.hpp
Go to the documentation of this file.
1// Copyright 2020 <Maciek Bak>
3/*
4###############################################################################
5#
6# Hypercomplex header-only library.
7#
8# AUTHOR: Maciek_Bak
9# AFFILIATION: Department_of_Mathematics_City_University_of_London
10# CONTACT: wsciekly.maciek@gmail.com
11# CREATED: 22-10-2020
12# LICENSE: Apache 2.0
13#
14###############################################################################
15*/
16
17// mark that we included this library
18#ifndef HYPERCOMPLEX_HYPERCOMPLEX_HPP_
19#define HYPERCOMPLEX_HYPERCOMPLEX_HPP_
20
21// Conditional MPFR inclusion
22#ifndef USEMPFR
25#define USEMPFR 0
26#endif
27#if USEMPFR
28#include <mpfr.h>
29#endif
30
31#include <cassert>
32#include <cmath>
33#include <iostream>
34#include <stdexcept>
35#include "./Polynomial.hpp"
36
37/*
38###############################################################################
39#
40# Header section
41#
42###############################################################################
43*/
44
47template <typename T, const uint64_t dim>
49 private:
50 T* arr = new T[dim]; // NOLINT
51 static inline uint64_t** baseprodabs;
52 static inline bool** baseprodpos;
53
54 public:
57 static void init() {
58 int64_t** M = new int64_t*[dim];
59 for (uint64_t i = 0; i < dim; i++) M[i] = new int64_t[dim];
60 M[0][0] = 1;
61 uint64_t n = 1;
62 while (n != dim) {
63 for (uint64_t i=0; i < n; i++) {
64 for (uint64_t j=0; j < n; j++) {
65 M[i][n+j] = M[j][i] > 0 ? M[j][i] + n : M[j][i] - n;
66 M[i+n][j] = M[i][j] > 0 ? M[i][j] + n : M[i][j] - n;
67 M[i+n][j] = M[i+n][j] * (j ? -1 : 1);
68 M[i+n][j+n] = -M[j][i] * (j ? -1 : 1);
69 }
70 }
71 n *= 2;
72 }
73 baseprodabs = new uint64_t*[dim];
74 baseprodpos = new bool*[dim];
75 for (uint64_t i = 0; i < dim; i++) {
76 baseprodabs[i] = new uint64_t[dim];
77 baseprodpos[i] = new bool[dim];
78 }
79 for (uint64_t i=0; i < dim; i++) {
80 for (uint64_t j=0; j < dim; j++) {
81 baseprodabs[i][j] = std::abs(M[i][j]) - 1;
82 baseprodpos[i][j] = (0 < M[i][j]);
83 }
84 }
85 for (uint64_t i = 0; i < dim; i++) {
86 delete[] M[i];
87 }
88 delete[] M;
89 }
90
93 static void clear() {
94 for (uint64_t i = 0; i < dim; i++) {
95 delete[] baseprodabs[i];
96 delete[] baseprodpos[i];
97 }
98 delete[] baseprodabs;
99 delete[] baseprodpos;
100 }
101
108 const Hypercomplex &H1,
109 const Hypercomplex &H2
110 ) {
111 T temp[dim]; // NOLINT
112 for (uint64_t i=0; i < dim; i++) temp[i] = T();
113 for (uint64_t i=0; i < dim; i++) {
114 for (uint64_t j=0; j < dim; j++) {
115 if (Hypercomplex::baseprodpos[i][j]) {
116 temp[Hypercomplex::baseprodabs[i][j]] =
117 temp[Hypercomplex::baseprodabs[i][j]] + H1[i] * H2[j];
118 } else {
119 temp[Hypercomplex::baseprodabs[i][j]] =
120 temp[Hypercomplex::baseprodabs[i][j]] - H1[i] * H2[j];
121 }
122 }
123 }
124 Hypercomplex H(temp);
125 return H;
126 }
127
135 explicit Hypercomplex(const T* ARR);
136
144 Hypercomplex(const Hypercomplex &H);
145
146 Hypercomplex() = delete;
147
149
153 uint64_t _() const { return dim; }
154
161 T norm() const;
162
166 Hypercomplex inv() const;
167
174 template <const uint64_t newdim>
176
180 Hypercomplex operator~ () const;
181
185 Hypercomplex operator- () const;
186
192
200 T const & operator[] (const uint64_t i) const;
201
209 T & operator[] (const uint64_t i);
210
216
222
228
233 Hypercomplex& operator^= (const uint64_t x);
234
240};
241
247template <typename T, const uint64_t dim>
248bool operator== (
249 const Hypercomplex<T, dim> &H1,
250 const Hypercomplex<T, dim> &H2
251);
252
258template <typename T, const uint64_t dim>
259bool operator!= (
260 const Hypercomplex<T, dim> &H1,
261 const Hypercomplex<T, dim> &H2
262);
263
269template <typename T, const uint64_t dim>
271 const Hypercomplex<T, dim> &H1,
272 const Hypercomplex<T, dim> &H2
273);
274
280template <typename T, const uint64_t dim>
282 const Hypercomplex<T, dim> &H1,
283 const Hypercomplex<T, dim> &H2
284);
285
291template <typename T, const uint64_t dim>
293 const Hypercomplex<T, dim> &H1,
294 const Hypercomplex<T, dim> &H2
295);
296
302template <typename T, const uint64_t dim>
304 const Hypercomplex<T, dim> &H,
305 const uint64_t x
306);
307
313template <typename T, const uint64_t dim>
315 const Hypercomplex<T, dim> &H1,
316 const Hypercomplex<T, dim> &H2
317);
318
324template <typename T, const uint64_t dim>
325std::ostream& operator<< (std::ostream &os, const Hypercomplex<T, dim> &H);
326
331template <typename T, const uint64_t dim>
333
338template <typename T, const uint64_t dim>
340
345template <typename T, const uint64_t dim>
347
348/*
349###############################################################################
350#
351# Implementation section
352#
353###############################################################################
354*/
355
356// Hypercomplex main constructor
357template <typename T, const uint64_t dim>
359 if (dim == 0) {
360 delete[] arr;
361 throw std::invalid_argument("invalid dimension");
362 }
363 if ((dim & (dim - 1)) != 0) {
364 delete[] arr;
365 throw std::invalid_argument("invalid dimension");
366 }
367 for (uint64_t i=0; i < dim; i++) arr[i] = ARR[i];
368}
369
370// Hypercomplex copy constructor
371template <typename T, const uint64_t dim>
373 for (uint64_t i=0; i < dim; i++) arr[i] = H[i];
374}
375
376// Hypercomplex destructor
377template <typename T, const uint64_t dim>
379 delete[] arr;
380}
381
382// calculate norm of the number
383template <typename T, const uint64_t dim>
385 T result = T();
386 for (uint64_t i=0; i < dim; i++) result += arr[i] * arr[i];
387 return sqrt(result);
388}
389
390// calculate inverse of the number
391template <typename T, const uint64_t dim>
393 T zero = T();
394 T norm = (*this).norm();
395 if (norm == zero) {
396 throw std::invalid_argument("division by zero");
397 } else {
398 T temparr[dim]; // NOLINT
399 temparr[0] = arr[0] / (norm * norm);
400 for (uint64_t i=1; i < dim; i++)
401 temparr[i] = -arr[i] / (norm * norm);
402 Hypercomplex<T, dim> H(temparr);
403 return H;
404 }
405}
406
407// cast object to a higher dimension
408template <typename T, const uint64_t dim>
409template <const uint64_t newdim>
411 if (newdim <= dim) throw std::invalid_argument("invalid dimension");
412 T temparr[newdim]; // NOLINT
413 for (uint64_t i=0; i < dim; i++) temparr[i] = arr[i];
414 for (uint64_t i=dim; i < newdim; i++) temparr[i] = 0;
415 Hypercomplex<T, newdim> H(temparr);
416 return H;
417}
418
419// overloaded ~ operator
420template <typename T, const uint64_t dim>
422 T temparr[dim]; // NOLINT
423 temparr[0] = arr[0];
424 for (uint64_t i=1; i < dim; i++) temparr[i] = -arr[i];
425 Hypercomplex<T, dim> H(temparr);
426 return H;
427}
428
429// overloaded - unary operator
430template <typename T, const uint64_t dim>
432 T temparr[dim]; // NOLINT
433 for (uint64_t i=0; i < dim; i++) temparr[i] = -arr[i];
434 Hypercomplex<T, dim> H(temparr);
435 return H;
436}
437
438// overloaded = operator
439template <typename T, const uint64_t dim>
441 const Hypercomplex &H
442) {
443 // self-assignment guard
444 if (this == &H) return *this;
445 // reassign
446 for (uint64_t i=0; i < dim; i++) arr[i] = H[i];
447 // return the existing object so we can chain this operator
448 return *this;
449}
450
451// overloaded [] operator (const)
452template <typename T, const uint64_t dim>
453inline T const & Hypercomplex<T, dim>::operator[](const uint64_t i) const {
454 assert(0 <= i && i < dim);
455 return arr[i];
456}
457
458// overloaded [] operator (non-const)
459template <typename T, const uint64_t dim>
460inline T & Hypercomplex<T, dim>::operator[](const uint64_t i) {
461 assert(0 <= i && i < dim);
462 return arr[i];
463}
464
465// overloaded == operator
466template <typename T, const uint64_t dim>
468 const Hypercomplex<T, dim> &H1,
469 const Hypercomplex<T, dim> &H2
470) {
471 for (uint64_t i=0; i < dim; i++) {
472 if (H1[i] != H2[i]) return false;
473 }
474 return true;
475}
476
477// overloaded != operator
478template <typename T, const uint64_t dim>
480 const Hypercomplex<T, dim> &H1,
481 const Hypercomplex<T, dim> &H2
482) {
483 return !(H1 == H2);
484}
485
486// overloaded + binary operator
487template <typename T, const uint64_t dim>
489 const Hypercomplex<T, dim> &H1,
490 const Hypercomplex<T, dim> &H2
491) {
492 T temparr[dim]; // NOLINT
493 for (uint64_t i=0; i < dim; i++) temparr[i] = H1[i] + H2[i];
494 Hypercomplex<T, dim> H(temparr);
495 return H;
496}
497
498// overloaded - binary operator
499template <typename T, const uint64_t dim>
501 const Hypercomplex<T, dim> &H1,
502 const Hypercomplex<T, dim> &H2
503) {
504 T temparr[dim]; // NOLINT
505 for (uint64_t i=0; i < dim; i++) temparr[i] = H1[i] - H2[i];
506 Hypercomplex<T, dim> H(temparr);
507 return H;
508}
509
510// overloaded * binary operator
511template <typename T, const uint64_t dim>
513 const Hypercomplex<T, dim> &H1,
514 const Hypercomplex<T, dim> &H2
515) {
516 // recursion base:
517 if constexpr (dim == 1) {
518 T temparr[] = { H1[0] * H2[0] };
519 Hypercomplex<T, 1> H_(temparr);
520 return H_;
521 // recursion step:
522 } else {
523 // shared objects:
524 const uint64_t halfd = dim / 2;
525 T temparr[dim]; // NOLINT
526 // construct helper objects:
527 for (uint64_t i=0; i < halfd; i++) temparr[i] = H1[i];
528 Hypercomplex<T, halfd> H1a(temparr);
529 for (uint64_t i=0; i < halfd; i++) temparr[i] = H1[i+halfd];
530 Hypercomplex<T, halfd> H1b(temparr);
531 for (uint64_t i=0; i < halfd; i++) temparr[i] = H2[i];
532 Hypercomplex<T, halfd> H2a(temparr);
533 for (uint64_t i=0; i < halfd; i++) temparr[i] = H2[i+halfd];
534 Hypercomplex<T, halfd> H2b(temparr);
535 // multiply recursively:
536 Hypercomplex<T, halfd> H1a2a = H1a * H2a;
537 Hypercomplex<T, halfd> H2b_1b = ~H2b * H1b;
538 Hypercomplex<T, halfd> H2b1a = H2b * H1a;
539 Hypercomplex<T, halfd> H1b2a_ = H1b * ~H2a;
540 // construct the final object
541 Hypercomplex<T, halfd> Ha = H1a2a - H2b_1b;
542 Hypercomplex<T, halfd> Hb = H2b1a + H1b2a_;
543 for (uint64_t i=0; i < halfd; i++) temparr[i] = Ha[i];
544 for (uint64_t i=0; i < halfd; i++) temparr[i+halfd] = Hb[i];
545 Hypercomplex<T, dim> H(temparr);
546 return H;
547 }
548}
549
550// overloaded ^ binary operator
551template <typename T, const uint64_t dim>
553 const Hypercomplex<T, dim> &H,
554 const uint64_t x
555) {
556 if (!(x)) {
557 throw std::invalid_argument("zero is not a valid argument");
558 } else {
560 for (uint64_t i=0; i < x-1; i++) Hx *= H;
561 return Hx;
562 }
563}
564
565// overloaded / binary operator
566template <typename T, const uint64_t dim>
568 const Hypercomplex<T, dim> &H1,
569 const Hypercomplex<T, dim> &H2
570) {
571 // division H1 / H2 is implemented as H1 * 1/H2
572 Hypercomplex<T, dim> H = H1 * H2.inv();
573 return(H);
574}
575
576// overloaded += operator
577template <typename T, const uint64_t dim>
579 const Hypercomplex<T, dim> &H
580) {
581 Hypercomplex<T, dim> result = (*this) + H;
582 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
583 return *this;
584}
585
586// overloaded -= operator
587template <typename T, const uint64_t dim>
589 const Hypercomplex<T, dim> &H
590) {
591 Hypercomplex<T, dim> result = (*this) - H;
592 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
593 return *this;
594}
595
596// overloaded *= operator
597template <typename T, const uint64_t dim>
599 const Hypercomplex<T, dim> &H
600) {
601 Hypercomplex<T, dim> result = (*this) * H;
602 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
603 return *this;
604}
605
606// overloaded ^= operator
607template <typename T, const uint64_t dim>
609 const uint64_t x
610) {
611 Hypercomplex<T, dim> result = (*this) ^ x;
612 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
613 return *this;
614}
615
616// overloaded /= operator
617template <typename T, const uint64_t dim>
619 const Hypercomplex<T, dim> &H
620) {
621 Hypercomplex<T, dim> result = (*this) / H;
622 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
623 return *this;
624}
625
626// overload << operator
627template <typename T, const uint64_t dim>
628std::ostream& operator<< (std::ostream &os, const Hypercomplex<T, dim> &H) {
629 for (uint64_t i=0; i < dim - 1; i++) os << H[i] << " ";
630 os << H[dim - 1];
631 return os;
632}
633
634// return the real part of the number
635template <typename T, const uint64_t dim>
637 Hypercomplex<T, dim> result = H;
638 for (uint64_t i=1; i < dim; i++) result[i] = T();
639 return result;
640}
641
642// return the imaginary part of the number
643template <typename T, const uint64_t dim>
645 Hypercomplex<T, dim> result = H;
646 result[0] = T();
647 return result;
648}
649
650// calculate e^H
651template <typename T, const uint64_t dim>
653 Hypercomplex<T, dim> result = Im(H);
654 T zero = T();
655 T norm = result.norm();
656 if (norm == zero) {
657 result[0] = exp(H[0]);
658 for (uint64_t i=1; i < dim; i++) result[i] = zero;
659 } else {
660 T sinv_v = sin(norm) / norm;
661 for (uint64_t i=0; i < dim; i++) result[i] *= sinv_v;
662 result[0] += cos(norm);
663 for (uint64_t i=0; i < dim; i++) result[i] *= exp(H[0]);
664 }
665 return result;
666}
667
668/*
669###############################################################################
670#
671# Explicit template specialisation for Polynomial class
672#
673###############################################################################
674*/
675
678template <const uint64_t MaxDeg, const uint64_t dim>
679class Hypercomplex<Polynomial<MaxDeg>, dim> {
680 private:
682 static inline uint64_t** baseprodabs;
683 static inline bool** baseprodpos;
684
685 public:
688 static void init() {
689 int64_t** M = new int64_t*[dim];
690 for (uint64_t i = 0; i < dim; i++) M[i] = new int64_t[dim];
691 M[0][0] = 1;
692 uint64_t n = 1;
693 while (n != dim) {
694 for (uint64_t i=0; i < n; i++) {
695 for (uint64_t j=0; j < n; j++) {
696 M[i][n+j] = M[j][i] > 0 ? M[j][i] + n : M[j][i] - n;
697 M[i+n][j] = M[i][j] > 0 ? M[i][j] + n : M[i][j] - n;
698 M[i+n][j] = M[i+n][j] * (j ? -1 : 1);
699 M[i+n][j+n] = -M[j][i] * (j ? -1 : 1);
700 }
701 }
702 n *= 2;
703 }
704 baseprodabs = new uint64_t*[dim];
705 baseprodpos = new bool*[dim];
706 for (uint64_t i = 0; i < dim; i++) {
707 baseprodabs[i] = new uint64_t[dim];
708 baseprodpos[i] = new bool[dim];
709 }
710 for (uint64_t i=0; i < dim; i++) {
711 for (uint64_t j=0; j < dim; j++) {
712 baseprodabs[i][j] = std::abs(M[i][j]) - 1;
713 baseprodpos[i][j] = (0 < M[i][j]);
714 }
715 }
716 for (uint64_t i = 0; i < dim; i++) {
717 delete[] M[i];
718 }
719 delete[] M;
720 }
721
724 static void clear() {
725 for (uint64_t i = 0; i < dim; i++) {
726 delete[] baseprodabs[i];
727 delete[] baseprodpos[i];
728 }
729 delete[] baseprodabs;
730 delete[] baseprodpos;
731 }
732
739 const Hypercomplex &H1,
740 const Hypercomplex &H2
741 ) {
742 Polynomial<MaxDeg> temp[dim];
743 for (uint64_t i=0; i < dim; i++) {
744 for (uint64_t j=0; j < dim; j++) {
745 if (Hypercomplex::baseprodpos[i][j]) {
746 temp[Hypercomplex::baseprodabs[i][j]] =
747 temp[Hypercomplex::baseprodabs[i][j]] + H1[i] * H2[j];
748 } else {
749 temp[Hypercomplex::baseprodabs[i][j]] =
750 temp[Hypercomplex::baseprodabs[i][j]] - H1[i] * H2[j];
751 }
752 }
753 }
755 return H;
756 }
757
764 explicit Hypercomplex(const Polynomial<MaxDeg>* ARR) {
765 if (dim == 0) {
766 delete[] arr;
767 throw std::invalid_argument("invalid dimension");
768 }
769 if ((dim & (dim - 1)) != 0) {
770 delete[] arr;
771 throw std::invalid_argument("invalid dimension");
772 }
773 for (uint64_t i=0; i < dim; i++) arr[i] = ARR[i];
774 }
775
783 for (uint64_t i=0; i < dim; i++) arr[i] = H[i];
784 }
785
786 Hypercomplex() = delete;
787
788 ~Hypercomplex() {
789 delete[] arr;
790 }
791
795 uint64_t _() const { return dim; }
796
797 #ifndef DOXYGEN_SHOULD_SKIP_THIS
798 Polynomial<MaxDeg> norm() = delete;
799 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
800
805 Polynomial<MaxDeg> norm2;
806 for (uint64_t i=0; i < dim; i++) {
807 norm2 = norm2 + arr[i] * arr[i];
808 }
809 return norm2;
810 }
811
818 template <const uint64_t newdim>
820 if (newdim <= dim) throw std::invalid_argument("invalid dimension");
821 Polynomial<MaxDeg> temparr[newdim];
823 for (uint64_t i=0; i < dim; i++) temparr[i] = arr[i];
824 for (uint64_t i=dim; i < newdim; i++) temparr[i] = zero;
825 Hypercomplex<Polynomial<MaxDeg>, newdim> H(temparr);
826 return H;
827 }
828
833 Polynomial<MaxDeg> const & operator[] (const uint64_t i) const {
834 assert(0 <= i && i < dim);
835 return arr[i];
836 }
837
842 Polynomial<MaxDeg> & operator[] (const uint64_t i) {
843 assert(0 <= i && i < dim);
844 return arr[i];
845 }
846
851 Polynomial<MaxDeg> temparr[dim];
852 temparr[0] = arr[0];
853 for (uint64_t i=1; i < dim; i++) temparr[i] = -arr[i];
854 Hypercomplex<Polynomial<MaxDeg>, dim> H(temparr);
855 return H;
856 }
857
862 Polynomial<MaxDeg> temparr[dim];
863 for (uint64_t i=0; i < dim; i++) temparr[i] = -arr[i];
864 Hypercomplex<Polynomial<MaxDeg>, dim> H(temparr);
865 return H;
866 }
867
873 if (this == &H) return *this;
874 for (uint64_t i=0; i < dim; i++) arr[i] = H[i];
875 return *this;
876 }
877
883 Hypercomplex<Polynomial<MaxDeg>, dim> result = (*this) + H;
884 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
885 return *this;
886 }
887
893 Hypercomplex<Polynomial<MaxDeg>, dim> result = (*this) - H;
894 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
895 return *this;
896 }
897
902 Hypercomplex& operator*= (const int64_t &x) {
903 // scalar-polynomial multiplication is commutative
904 Hypercomplex<Polynomial<MaxDeg>, dim> result = x * (*this);
905 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
906 return *this;
907 }
908
913 Hypercomplex& operator%= (const int64_t &mod) {
914 Hypercomplex<Polynomial<MaxDeg>, dim> result = (*this) % mod;
915 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
916 return *this;
917 }
918
924 Hypercomplex<Polynomial<MaxDeg>, dim> result = (*this) * H;
925 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
926 return *this;
927 }
928
933 Hypercomplex& operator^= (const uint64_t x) {
934 Hypercomplex<Polynomial<MaxDeg>, dim> result = (*this) ^ x;
935 for (uint64_t i=0; i < dim; i++) (*this)[i] = result[i];
936 return *this;
937 }
938
939 #ifndef DOXYGEN_SHOULD_SKIP_THIS
940 Hypercomplex& operator/= (const Hypercomplex &H) = delete;
941 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
942};
943
949template <const uint64_t MaxDeg, const uint64_t dim>
951 const Hypercomplex<Polynomial<MaxDeg>, dim> &H1,
952 const Hypercomplex<Polynomial<MaxDeg>, dim> &H2
953) {
954 for (uint64_t i=0; i < dim; i++) {
955 if (H1[i] != H2[i]) return false;
956 }
957 return true;
958}
959
965template <const uint64_t MaxDeg, const uint64_t dim>
967 const Hypercomplex<Polynomial<MaxDeg>, dim> &H1,
968 const Hypercomplex<Polynomial<MaxDeg>, dim> &H2
969) {
970 return !(H1 == H2);
971}
972
978template <const uint64_t MaxDeg, const uint64_t dim>
980 const Hypercomplex<Polynomial<MaxDeg>, dim> &H1,
981 const Hypercomplex<Polynomial<MaxDeg>, dim> &H2
982) {
983 Polynomial<MaxDeg> temparr[dim];
984 for (uint64_t i=0; i < dim; i++) temparr[i] = H1[i] + H2[i];
985 Hypercomplex<Polynomial<MaxDeg>, dim> H(temparr);
986 return H;
987}
988
994template <const uint64_t MaxDeg, const uint64_t dim>
996 const Hypercomplex<Polynomial<MaxDeg>, dim> &H1,
997 const Hypercomplex<Polynomial<MaxDeg>, dim> &H2
998) {
999 Polynomial<MaxDeg> temparr[dim];
1000 for (uint64_t i=0; i < dim; i++) temparr[i] = H1[i] - H2[i];
1001 Hypercomplex<Polynomial<MaxDeg>, dim> H(temparr);
1002 return H;
1003}
1004
1005// forbid / binary operator
1006#ifndef DOXYGEN_SHOULD_SKIP_THIS
1007template <const uint64_t MaxDeg, const uint64_t dim>
1009 const Hypercomplex<Polynomial<MaxDeg>, dim> &H1,
1010 const Hypercomplex<Polynomial<MaxDeg>, dim> &H2
1011) = delete;
1012#endif /* DOXYGEN_SHOULD_SKIP_THIS */
1013
1019template <const uint64_t MaxDeg, const uint64_t dim>
1020std::ostream& operator<<(
1021 std::ostream &os,
1022 const Hypercomplex<Polynomial<MaxDeg>, dim> &H
1023) {
1024 for (uint64_t i=0; i < dim - 1; i++) os << H[i] << std::endl;
1025 os << H[dim - 1];
1026 return os;
1027}
1028
1034template <const uint64_t MaxDeg, const uint64_t dim>
1036 const int64_t &x,
1037 const Hypercomplex<Polynomial<MaxDeg>, dim> &H
1038) {
1039 Polynomial<MaxDeg> temparr[dim];
1040 for (uint64_t i=0; i < dim; i++) temparr[i] = x * H[i];
1041 Hypercomplex<Polynomial<MaxDeg>, dim> h(temparr);
1042 return h;
1043}
1044
1050template <const uint64_t MaxDeg, const uint64_t dim>
1052 const Hypercomplex<Polynomial<MaxDeg>, dim> &H,
1053 const int64_t &mod
1054) {
1055 Polynomial<MaxDeg> temparr[dim];
1056 for (uint64_t i=0; i < dim; i++) temparr[i] = H[i] % mod;
1057 Hypercomplex<Polynomial<MaxDeg>, dim> h(temparr);
1058 return h;
1059}
1060
1066template <const uint64_t MaxDeg, const uint64_t dim>
1068 const Hypercomplex<Polynomial<MaxDeg>, dim> &H1,
1069 const Hypercomplex<Polynomial<MaxDeg>, dim> &H2
1070) {
1071 // recursion base:
1072 if constexpr (dim == 1) {
1073 Polynomial<MaxDeg> temparr[] = { H1[0] * H2[0] };
1074 Hypercomplex<Polynomial<MaxDeg>, 1> H_(temparr);
1075 return H_;
1076 // recursion step:
1077 } else {
1078 // shared objects:
1079 const uint64_t halfd = dim / 2;
1080 Polynomial<MaxDeg> temparr[dim];
1081 // construct helper objects:
1082 for (uint64_t i=0; i < halfd; i++) temparr[i] = H1[i];
1083 Hypercomplex<Polynomial<MaxDeg>, halfd> H1a(temparr);
1084 for (uint64_t i=0; i < halfd; i++) temparr[i] = H1[i+halfd];
1085 Hypercomplex<Polynomial<MaxDeg>, halfd> H1b(temparr);
1086 for (uint64_t i=0; i < halfd; i++) temparr[i] = H2[i];
1087 Hypercomplex<Polynomial<MaxDeg>, halfd> H2a(temparr);
1088 for (uint64_t i=0; i < halfd; i++) temparr[i] = H2[i+halfd];
1089 Hypercomplex<Polynomial<MaxDeg>, halfd> H2b(temparr);
1090 // multiply recursively:
1091 Hypercomplex<Polynomial<MaxDeg>, halfd> H1a2a = H1a * H2a;
1092 Hypercomplex<Polynomial<MaxDeg>, halfd> H2b_1b = ~H2b * H1b;
1093 Hypercomplex<Polynomial<MaxDeg>, halfd> H2b1a = H2b * H1a;
1094 Hypercomplex<Polynomial<MaxDeg>, halfd> H1b2a_ = H1b * ~H2a;
1095 // construct the final object
1096 Hypercomplex<Polynomial<MaxDeg>, halfd> Ha = H1a2a - H2b_1b;
1097 Hypercomplex<Polynomial<MaxDeg>, halfd> Hb = H2b1a + H1b2a_;
1098 for (uint64_t i=0; i < halfd; i++) temparr[i] = Ha[i];
1099 for (uint64_t i=0; i < halfd; i++) temparr[i+halfd] = Hb[i];
1100 Hypercomplex<Polynomial<MaxDeg>, dim> H(temparr);
1101 return H;
1102 }
1103}
1104
1110template <const uint64_t MaxDeg, const uint64_t dim>
1112 const Hypercomplex<Polynomial<MaxDeg>, dim> &H,
1113 const uint64_t x
1114) {
1115 if (!(x)) {
1116 throw std::invalid_argument("zero is not a valid argument");
1117 } else {
1119 for (uint64_t i=0; i < x-1; i++) Hx = Hx * H;
1120 return Hx;
1121 }
1122}
1123
1128template <const uint64_t MaxDeg, const uint64_t dim>
1130 const Hypercomplex<Polynomial<MaxDeg>, dim> &H
1131) {
1132 Hypercomplex<Polynomial<MaxDeg>, dim> result = H;
1133 for (uint64_t i=1; i < dim; i++) result[i] = Polynomial<MaxDeg>();
1134 return result;
1135}
1136
1141template <const uint64_t MaxDeg, const uint64_t dim>
1143 const Hypercomplex<Polynomial<MaxDeg>, dim> &H
1144) {
1145 Hypercomplex<Polynomial<MaxDeg>, dim> result = H;
1146 result[0] = Polynomial<MaxDeg>();
1147 return result;
1148}
1149
1150// forbid e^H
1151#ifndef DOXYGEN_SHOULD_SKIP_THIS
1152template <const uint64_t MaxDeg, const uint64_t dim>
1154 const Hypercomplex<Polynomial<MaxDeg>, dim> &H
1155) = delete;
1156#endif /* DOXYGEN_SHOULD_SKIP_THIS */
1157
1162template <const uint64_t MaxDeg, const uint64_t dim>
1165 const int64_t &mod
1166) {
1167 for (uint64_t i=0; i < dim; i++) CenteredLift(&(*H)[i], mod);
1168}
1169
1175template <const uint64_t MaxDeg, const uint64_t dim>
1177 const Hypercomplex<Polynomial<MaxDeg>, dim> &H,
1178 const int64_t &mod
1179) {
1180 Polynomial<MaxDeg> ringnorm2 = H.norm2() % mod;
1181 Polynomial<MaxDeg> ringinverse = RingInverse(ringnorm2, mod);
1182 Polynomial<MaxDeg> temparr[dim];
1183 temparr[0] = H[0] * ringinverse % mod;
1184 for (uint64_t i=1; i < dim; i++)
1185 temparr[i] = -H[i] * ringinverse % mod;
1186 Hypercomplex<Polynomial<MaxDeg>, dim> Hinv(temparr);
1187 // validate the inverse:
1188 Polynomial<MaxDeg> zero;
1189 Polynomial<MaxDeg> unity;
1190 unity[0] = 1;
1191 Hypercomplex<Polynomial<MaxDeg>, dim> result = (H * Hinv) % mod;
1192 assert(result[0] == unity);
1193 for (uint64_t i=1; i < dim; i++) assert(result[i] == zero);
1194 //
1195 return Hinv;
1196}
1197
1198/*
1199###############################################################################
1200#
1201# Cryptographic functions for Hypercomplex<Polynomial> specialisation
1202#
1203###############################################################################
1204*/
1205
1212template <const uint64_t MaxDeg, const uint64_t dim>
1214 const Hypercomplex<Polynomial<MaxDeg>, dim> &F,
1215 const Hypercomplex<Polynomial<MaxDeg>, dim> &G,
1216 const int64_t &q
1217) {
1218 Hypercomplex<Polynomial<MaxDeg>, dim> invFq = RingInverse(F, q);
1220 H = Hypercomplex<Polynomial<MaxDeg>, dim>::MUL(invFq, G) % q;
1221 return H;
1222}
1223
1232template <const uint64_t MaxDeg, const uint64_t dim>
1234 const Hypercomplex<Polynomial<MaxDeg>, dim> &H,
1235 const Hypercomplex<Polynomial<MaxDeg>, dim> &M,
1236 const Hypercomplex<Polynomial<MaxDeg>, dim> &PHI,
1237 const int64_t &p,
1238 const int64_t &q
1239) {
1241 E = (p * Hypercomplex<Polynomial<MaxDeg>, dim>::MUL(H, PHI) + M) % q;
1242 return E;
1243}
1244
1252template <const uint64_t MaxDeg, const uint64_t dim>
1254 const Hypercomplex<Polynomial<MaxDeg>, dim> &F,
1255 const Hypercomplex<Polynomial<MaxDeg>, dim> &E,
1256 const int64_t &p,
1257 const int64_t &q
1258) {
1259 Hypercomplex<Polynomial<MaxDeg>, dim> invFp = RingInverse(F, p);
1261 A = Hypercomplex<Polynomial<MaxDeg>, dim>::MUL(
1262 Hypercomplex<Polynomial<MaxDeg>, dim>::MUL(F, E), F) % q;
1263 CenteredLift(&A, q);
1264 Hypercomplex<Polynomial<MaxDeg>, dim> B = A % p;
1266 C = Hypercomplex<Polynomial<MaxDeg>, dim>::MUL(
1267 invFp,
1268 Hypercomplex<Polynomial<MaxDeg>, dim>::MUL(B, invFp)) % p;
1269 CenteredLift(&C, p);
1270 return C;
1271}
1272
1273#if USEMPFR
1274// Include MPFR-dedicated class specialisation
1275#include "./Hypercomplex_MPFR.hpp"
1276#endif
1277
1278#endif // HYPERCOMPLEX_HYPERCOMPLEX_HPP_
Hypercomplex< T, dim > operator-(const Hypercomplex< T, dim > &H1, const Hypercomplex< T, dim > &H2)
Subtraction operator.
Definition: Hypercomplex.hpp:500
Hypercomplex< T, dim > Re(const Hypercomplex< T, dim > &H)
Real part of a hypercomplex number.
Definition: Hypercomplex.hpp:636
Hypercomplex< Polynomial< MaxDeg >, dim > DECRYPT(const Hypercomplex< Polynomial< MaxDeg >, dim > &F, const Hypercomplex< Polynomial< MaxDeg >, dim > &E, const int64_t &p, const int64_t &q)
Decrypt a message via the cryptosystem.
Definition: Hypercomplex.hpp:1253
bool operator==(const Hypercomplex< T, dim > &H1, const Hypercomplex< T, dim > &H2)
Equality operator.
Definition: Hypercomplex.hpp:467
bool operator!=(const Hypercomplex< T, dim > &H1, const Hypercomplex< T, dim > &H2)
Inequality operator.
Definition: Hypercomplex.hpp:479
Hypercomplex< T, dim > operator+(const Hypercomplex< T, dim > &H1, const Hypercomplex< T, dim > &H2)
Addition operator.
Definition: Hypercomplex.hpp:488
Hypercomplex< Polynomial< MaxDeg >, dim > PUBLICKEY(const Hypercomplex< Polynomial< MaxDeg >, dim > &F, const Hypercomplex< Polynomial< MaxDeg >, dim > &G, const int64_t &q)
Generate public key of the cryptosystem.
Definition: Hypercomplex.hpp:1213
Hypercomplex< Polynomial< MaxDeg >, dim > RingInverse(const Hypercomplex< Polynomial< MaxDeg >, dim > &H, const int64_t &mod)
Hypercomplex inverse in a modular quotient ring.
Definition: Hypercomplex.hpp:1176
Hypercomplex< Polynomial< MaxDeg >, dim > ENCRYPT(const Hypercomplex< Polynomial< MaxDeg >, dim > &H, const Hypercomplex< Polynomial< MaxDeg >, dim > &M, const Hypercomplex< Polynomial< MaxDeg >, dim > &PHI, const int64_t &p, const int64_t &q)
Encrypt a message via the cryptosystem.
Definition: Hypercomplex.hpp:1233
void CenteredLift(Hypercomplex< Polynomial< MaxDeg >, dim > *H, const int64_t &mod)
Center-lift hypercomplex elements in a modular quotient ring.
Definition: Hypercomplex.hpp:1163
Hypercomplex< T, dim > operator*(const Hypercomplex< T, dim > &H1, const Hypercomplex< T, dim > &H2)
Multiplication operator.
Definition: Hypercomplex.hpp:512
std::ostream & operator<<(std::ostream &os, const Hypercomplex< T, dim > &H)
Print operator.
Definition: Hypercomplex.hpp:628
Hypercomplex< T, dim > operator^(const Hypercomplex< T, dim > &H, const uint64_t x)
Power operator.
Definition: Hypercomplex.hpp:552
Hypercomplex< T, dim > Im(const Hypercomplex< T, dim > &H)
Imaginary part of a hypercomplex number.
Definition: Hypercomplex.hpp:644
Hypercomplex< Polynomial< MaxDeg >, dim > operator%(const Hypercomplex< Polynomial< MaxDeg >, dim > &H, const int64_t &mod)
Modular reduction operator.
Definition: Hypercomplex.hpp:1051
Hypercomplex< T, dim > exp(const Hypercomplex< T, dim > &H)
Exponentiation operation on a hypercomplex number.
Definition: Hypercomplex.hpp:652
Hypercomplex< T, dim > operator/(const Hypercomplex< T, dim > &H1, const Hypercomplex< T, dim > &H2)
Division operator.
Definition: Hypercomplex.hpp:567
static void init()
Basis multiplication table initialiser.
Definition: Hypercomplex.hpp:688
Hypercomplex(const Polynomial< MaxDeg > *ARR)
This is the main constructor.
Definition: Hypercomplex.hpp:764
Hypercomplex< Polynomial< MaxDeg >, newdim > expand() const
Cast a number into a higher dimension.
Definition: Hypercomplex.hpp:819
static void clear()
Cleanup function: free all memory.
Definition: Hypercomplex.hpp:724
static Hypercomplex MUL(const Hypercomplex &H1, const Hypercomplex &H2)
Optimised multiplication function.
Definition: Hypercomplex.hpp:738
uint64_t _() const
Dimensionality getter.
Definition: Hypercomplex.hpp:795
Hypercomplex(const Hypercomplex &H)
This is the copy constructor.
Definition: Hypercomplex.hpp:782
Polynomial< MaxDeg > norm2() const
Calculate squared Euclidean norm of a number.
Definition: Hypercomplex.hpp:804
Definition: Hypercomplex.hpp:48
Hypercomplex & operator^=(const uint64_t x)
Power-Assignment operator.
Definition: Hypercomplex.hpp:608
static void init()
Basis multiplication table initialiser.
Definition: Hypercomplex.hpp:57
Hypercomplex & operator+=(const Hypercomplex &H)
Addition-Assignment operator.
Definition: Hypercomplex.hpp:578
Hypercomplex operator~() const
Create a complex conjugate.
Definition: Hypercomplex.hpp:421
Hypercomplex & operator/=(const Hypercomplex &H)
Division-Assignment operator.
Definition: Hypercomplex.hpp:618
Hypercomplex & operator=(const Hypercomplex &H)
Assignment operator.
Definition: Hypercomplex.hpp:440
T norm() const
Calculate Euclidean norm of a number.
Definition: Hypercomplex.hpp:384
T const & operator[](const uint64_t i) const
Access operator (const)
Definition: Hypercomplex.hpp:453
static Hypercomplex MUL(const Hypercomplex &H1, const Hypercomplex &H2)
Optimised multiplication function.
Definition: Hypercomplex.hpp:107
uint64_t _() const
Dimensionality getter.
Definition: Hypercomplex.hpp:153
Hypercomplex operator-() const
Create an additive inverse of a given number.
Definition: Hypercomplex.hpp:431
Hypercomplex & operator*=(const Hypercomplex &H)
Multiplication-Assignment operator.
Definition: Hypercomplex.hpp:598
Hypercomplex & operator-=(const Hypercomplex &H)
Subtraction-Assignment operator.
Definition: Hypercomplex.hpp:588
static void clear()
Cleanup function: free all memory.
Definition: Hypercomplex.hpp:93
Hypercomplex inv() const
Calculate inverse of a given number.
Definition: Hypercomplex.hpp:392
Hypercomplex< T, newdim > expand() const
Cast a number into a higher dimension.
Definition: Hypercomplex.hpp:410
Definition: Polynomial.hpp:41