/* LA: linear algebra C++ interface library Copyright (C) 2008-2023 Jiri Pittner or This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "bitvector.h" #include #include "numbers.h" namespace LA { //inefficient I/O operators std::ostream & operator<<(std::ostream &s, const bitvector &x) { for(unsigned int i=0; i>(std::istream &s, bitvector &x) { std::string str; s >> str; x.resize(str.size()); for(unsigned int i=0; i>(std::istream &s, bitmatrix &x) { unsigned int n,m; s >> n >> m; x.resize(n,m); for(unsigned int i=0; i>z; if(z) x.set(i,j); else x.reset(i,j);} } return s; } void bitvector::zero_padding() const { if(!modulo) return; bitvector *p = const_cast(this); p->v[nn-1] &= (1ULL<maxnn) maxnn=rhs.nn; if(memcmp(v,rhs.v,minnn*sizeof(bitvector_block))) return true; if(minnn==maxnn) return false; if(nn==minnn) {for(int i=minnn; i(const bitvector &rhs) const { if(nn!=rhs.nn || modulo!=rhs.modulo) laerror("at the moment only bitvectors of the same length comparable"); if(v==rhs.v) return 0; if(!modulo) return memcmp(v,rhs.v,nn*sizeof(bitvector_block)>0); int r; if((r=memcmp(v,rhs.v,(nn-1)*sizeof(bitvector_block)))) return r>0; bitvector_block a=v[nn-1]; bitvector_block b=rhs.v[nn-1]; //zero out the irrelevant bits bitvector_block mask= ~((bitvector_block)0); mask <<=modulo; mask = ~mask; a&=mask; b&=mask; return a>b; } bool bitvector::operator<(const bitvector &rhs) const { if(nn!=rhs.nn || modulo!=rhs.modulo) laerror("at the moment only bitvectors of the same length comparable"); if(v==rhs.v) return 0; if(!modulo) return memcmp(v,rhs.v,nn*sizeof(bitvector_block)<0); int r; if((r=memcmp(v,rhs.v,(nn-1)*sizeof(bitvector_block)))) return r<0; bitvector_block a=v[nn-1]; bitvector_block b=rhs.v[nn-1]; //zero out the irrelevant bits bitvector_block mask= ~((bitvector_block)0); mask <<=modulo; mask = ~mask; a&=mask; b&=mask; return a=rhs.nn? 0 : rhs.v[i]); return *this; } bitvector& bitvector::operator|=(const bitvector &rhs) { if(size()>1)&0x55555555); x = (x&0x33333333) + ((x>>2)&0x33333333); x=(x + (x>>4))&0x0f0f0f0f; x+= (x>>8); x+= (x>>16); return x&0x3f; } #else //@@@@ use an efficient trick too static unsigned int word_popul(unsigned long x) { unsigned int s=0; for(int i=0; i<64; ++i) { if(x&1) ++s; x>>=1; } return s; } #endif bitvector& bitvector::operator>>=(unsigned int i) { if(i==0) return *this; copyonwrite(); unsigned int imod = i%blockbits; unsigned int ishift = i/blockbits; for(int dest=0; dest=nn) v[dest]=0; else { v[dest] = v[src]>>imod; if(imod && (src+1=0; --dest) { int src=dest-ishift; if(src<0) v[dest]=0; else { v[dest] = v[src]<=0)) v[dest] |= (v[src-1]& (((1ULL<>(blockbits-imod); } } return *this; } void bitvector::randomize() { copyonwrite(); for(int i=0; i>=1; goto L; } static unsigned int ntz64(uint64_t x) { unsigned int n; if(x==0) return 64; n=1; if((x&0xffffffff)==0) {n+=32; x>>=32;} if((x&0xffff)==0) {n+=16; x>>=16;} if((x&0xff)==0) {n+=8; x>>=8;} if((x&0xf)==0) {n+=4; x>>=4;} if((x&0x3)==0) {n+=2; x>>=2;} return n-(x&1); } unsigned int bitvector::nlz() const { int leadblock=nn-1; unsigned int n=0; while(leadblock>0 && v[leadblock] == 0) { --leadblock; n+=blockbits; } n+= nlz64(v[leadblock]); if(modulo) n-= blockbits-modulo; return n; } unsigned int bitvector::ntz() const { int tailblock=0; unsigned int n=0; if(iszero()) return size(); while(tailblockmaxsize) maxsize=rhs.size(); bitvector r(autoresize?size()+rhs.size():maxsize); r.clear(); bitvector tmp(rhs); if(autoresize) tmp.resize(size()+rhs.size(),true); for(int i=0; i<=degree(); ++i) { if((*this)[i]) r+= tmp; tmp.leftshift(1,false); } return r; } //this is GF(2^n) multiplication bitvector bitvector::field_mult(const bitvector &rhs, const bitvector &irpolynom) const { int d=irpolynom.degree(); if(d>size()||d>rhs.size()) laerror("inconsistent dimensions in field_mult"); bitvector r(size()); r.clear(); bitvector tmp(*this); tmp.resize(size()+1,true); tmp.copyonwrite(); int rd=rhs.degree(); for(int i=0; i<=rd; ++i) //avoid making a working copy of rhs and shifting it { if(rhs[i]) r+= tmp; tmp.leftshift(1,false); if(tmp[d]) tmp -= irpolynom; } return r; } //this is GF(2^n) multiplicative inverseion //cf. https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm bitvector bitvector::field_inv(const bitvector &irpolynom) const { int d=irpolynom.degree(); if(d>size()) laerror("inconsistent dimensions in field_inv"); bitvector t(size()); t.clear(); bitvector newt(size()); newt.clear(); newt.set(0); bitvector r(irpolynom); r.copyonwrite(); bitvector newr(*this); if(r.size()>newr.size()) newr.resize(r.size(),true); newr.copyonwrite(); int rs=r.size(); while(!newr.is_zero()) { //std::cout <<"r "<0) laerror("field_inv: polynomial is not irreducible or input is zero modulo the polynomial"); if(!r[0]) laerror("zero in field_inv"); return t; } void bitvector::resize(const unsigned int n, bool preserve) { if(preserve) zero_padding(); int oldnn=nn; NRVec::resize((n+blockbits-1)/blockbits,preserve); modulo=n%blockbits; if(preserve) //clear newly allocated memory { for(int i=oldnn; i= rhsd) { unsigned int pos = d-rhsd; r.set(pos); remainder -= rhs<=rhs.degree()) {big= *this; small=rhs;} else {big=rhs; small= *this;} if(big.is_zero()) { if(small.is_zero()) laerror("two zero arguments in gcd"); return small; } if(small.is_zero()) return big; if(small.is_one()) return small; if(big.is_one()) return big; do { bitvector help=small; small= big%small; big=help; } while(! small.is_zero()); return big; } //cf. Brent & Zimmermann ANZMC08t (2008) paper // bool bitvector::is_irreducible() const { bitvector tmp(size()); tmp.clear(); tmp.set(1); unsigned int d=degree(); //repeated squaring test for(unsigned int j=0; j f = factorization((uint64_t)d); if(f.begin()->first==d) return true; //d was prime //additional tests needed for non-prime degrees for(auto p=f.begin(); p!=f.end(); ++p) { unsigned int dm= d / p->first; tmp.clear(); tmp.set(1); for(unsigned int j=0; j0; --i) { if((*this)[i]) r.flip(0); r*=x; } if((*this)[0]) r.flip(0); return r; } bitvector bitvector::field_composition(const bitvector &x, const bitvector &ir) const { bitvector r(size()); r.clear(); int d=degree(); for(int i=d; i>0; --i) { if((*this)[i]) r.flip(0); r= r.field_mult(x,ir); } if((*this)[0]) r.flip(0); return r; } void bitvector::read(int fd, bool dimensions, bool transp) { if(dimensions) { int r = ::read(fd,&modulo,sizeof(modulo)); if(r!=sizeof(modulo)) laerror("cannot read in bitvector"); } NRVec::get(fd,dimensions,transp); } void bitvector::write(int fd, bool dimensions, bool transp) { if(dimensions) { int r = ::write(fd,&modulo,sizeof(modulo)); if(r!=sizeof(modulo)) laerror("cannot write in bitvector"); } NRVec::put(fd,dimensions,transp); } static bitvector *tryme; static bool irfound; static int mynth; static void irfinder(int nones, int top) { if(irfound) return; if(nones==0) //terminate recursion { bool testit = tryme->is_irreducible(); //std::cout <<"candidate = "<< *tryme<< " result = "<set(i); irfinder(nones-1,i-1); if(irfound) break; else tryme->reset(i); } } bitvector find_irreducible(int deg, int pop, int nth) { if(deg<=0) laerror("illegal degree in find_irreducible"); if(deg==1) {bitvector r(2); r.set(1); r.reset(0); return r;} bitvector r(deg+1); if(pop== -1) { do { r.randomize(); r.set(0); r.set(deg); if((r.population()&1)==0) r.flip(1+RANDINT32()%(deg-2)); } while(!r.is_irreducible()); return r; } if(pop<3 || (pop&1)==0) laerror("impossible population of irreducible polynomial requested"); r.clear(); r.set(0); r.set(deg); pop-=2; tryme= &r; irfound=false; mynth=nth; irfinder(pop,deg-1); if(!irfound) r.clear(); return r; } bitvector bitvector::pow(unsigned int n) const { if(n==0) {bitvector r(size()); r.clear(); r.set(0); return r;} if(n==1) return *this; bitvector y,z; z= *this; while(!(n&1)) { z = z*z; n >>= 1; } y=z; while((n >>= 1)/*!=0*/) { z = z*z; if(n&1) y *= z; } return y; } bitvector bitvector::field_pow(unsigned int n, const bitvector &ir) const { if(n==0) {bitvector r(size()); r.clear(); r.set(0); return r;} if(n==1) return *this; bitvector y,z; z= *this; while(!(n&1)) { z = z.field_mult(z,ir); n >>= 1; } y=z; while((n >>= 1)/*!=0*/) { z = z.field_mult(z,ir); if(n&1) y = y.field_mult(z,ir); } return y; } //sqrt(x) is x^(2^(d-1)) bitvector bitvector::field_sqrt(const bitvector &ir) const { int d=ir.degree(); bitvector r(*this); for(int i=0; i