You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In HVM, an interesting way to represent numbers is to use λ-encoded digit-strings of varying bases, parametrized by a list of numbers (a base-list). For example, by using [2,3,5], we represent numbers base 2-3-5:
n = a % 2 + b * 2 % (2 * 3) + c * (2 * 3) % (2 * 3 * 5)
Moreover, since 2 * 3 * 5 = 30, numbers larger than 30 on base 2-3-5 will wrap around. That means operations on them will be mod 30, with no need for a modulus operation. This may help implementing mod N algorithms that fuse, by avoiding calls to mod, which could interrupt fusion.
The base-list can be anything. For example, [100] would give us operations mod 100. But, in turn, each number would use a lot of space, since there are 100 digits. A better idea is to factorize 100 and use [2,2,5,5] instead, which will also give us operations mod 100, but using much less space. Of course, if we use a base-list with identical numbers, say, [2,2,2,2], we'll just have binary numbers, or N-ary, for whatever the N is. Here is the code:
// Applies `f` `n` times to `x`, directly(Repeat0fx)=x(Repeatnfx)=(f(Repeat(-n1)fx))// Given a base-list, applies `f` `n` times to `x`,// in such a way that is optimized for that base-list(ApplyNilnfx)=x(Apply(Consbbs)nfx)=(Applybs(/ n b) λk(Repeat b f k) (Repeat (% n b) f x))// Given a base-list, applies `f` `n` times to `x`,// in such a way that is optimized for that base-list(TimesNilnfx)=x(Times(Consbbs)nfx)=((TimesGobbbsn)fx)(TimesGo0bbsn)=(nλfλx(x))(TimesGoibbsn)=((TimesGo(-i1)bbsn)λpλfλx(Timesbspλk(Repeatbfk)(Repeat(-i1)fx)))// Given a base, ends a digit-string(Ebase)=λend(EGobaseend)(EGo0end)=end(EGobaseend)=λctr(EGo(-base1)end)// Given a base, appends `digit` to a digit-string(Dbasedigitpred)=λend(DGobasedigitpredλx(x))(DGo0npredctr)=(ctrpred)(DGobase0predctr)=λctr(DGo(-base1)(-01)predctr)(DGobasenpredctr)=λera(DGo(-base1)(-n1)predctr)// Given a base-list, converts a digit-string to a list(ToListNilxs)=Nil(ToList(Consbbs)xs)=(ToListGobbsxs)(ToListGo0bsxs)=(xsNil)(ToListGobbsxs)=((ToListGo(-b1)bsxs)λp(Cons(-b1)(ToListbsp)))// Given a base-list, converts a digit-string to a number(ToU32basesxs)=(ToU32Gobases(ToListbasesxs)1)(ToU32GoNilNilm)=0(ToU32Go(Consbbs)(Consxxs)m)=(+(*xm)(ToU32Gobsxs(*mb)))// Given a base-list, returns the number 0(ZeroNil)=End(Zero(Consbbs))=(Db0(Zerobs))// Giben a base-list, and a u32 `i`, returns the number `n`(Numberbasesi)=(Applybasesiλx(Incbasesx)(Zerobases))// Given a base, applies a function to the predecessor// (Inj [3] f λeλaλbλc(a pred)) == λeλaλbλc(a (f pred))(Injbasefxs)=λen(InjGobasef(xsλf(en)))(InjGo0fxs)=(xsf)(InjGobfxs)=λv(InjGo(-b1)f(xsλpλf(v(fp))))// Given a base-list, increments a digit-string(IncNilxs)=Nil(Inc(Consbbs)xs)=λenλn0(IncMake(-b1)bs(xsen)λp(n0(Incbsp)))(IncMake0bsxsic)=(xsic)(IncMakenbsxsic)=λv(IncMake(-n1)bs(xsv)ic)// Given a base-list, increments `b` a total of `a` times// This is equivalent to addition, and is fast due to fusion(Addbasesab)=(Timesbasesaλx(Incbasesx)b)// Given a base-list, creates an adder for two digit-strings, with carry bits(AdderCarryNilxs)=λys(ys)(AdderCarry(Consbbs)xs)=(AdderCarryGobbbsxs)(AdderCarryGo0bbsxs)=(xsλys(ys))(AdderCarryGoibbsxs)=((AdderCarryGo(-i1)bbsxs)(λxsλys(Repeat(-i1)λx(Inc(Consbbs)x)(Injb(AdderCarrybsxs)ys))))// Given a base-list, adds two bit-strings, with carry bits(AddCarrybasesxsys)=((AdderCarrybasesxs)ys)// FIXME: this is wrong, only works if all bases are the same(Mulbasesxsys)=(MulGobasesxsλk(Addbasesysk))(MulGoNilxsadd)=End(MulGo(Consbbs)xsadd)=(MulDobbbsxsadd)(MulDob0bsxsadd)=(xsEnd)(MulDobibsxsadd)=((MulDob(-i1)bsxsadd)λp(Repeat(-i1)add(Db0(MulGobspadd))))(Mainx)=letbases=(Cons2(Cons3(Cons5(Cons7Nil))))letto_list=λx(ToListbasesx)letto_u32=λx(ToU32basesx)lettimes=λnλfλx(Timesbasesnfx)letapply=λnλfλx(Applybasesnfx)letzero=(Zerobases)letinc=λx(Incbasesx)letadd=λaλb(Addbasesab)letaddc=λaλb(AddCarrybasesab)letmul_a=λaλb(Timesbasesa(addb)zero)// mul by repeated add by repeated incletmul_b=λaλb(Timesbasesa(addcb)zero)// mul by repeated add with carryletmul_c=λaλb(Mulbasesab)// mul using the incorrect algorithmletnum=λi(Numberbasesi)(to_u32(add(num123456789)(num987654321)))
This snippet includes an example of computing 123456789 + 987654321 mod 210, and it efficiently outputs 60, no call to mod is needed. Problems:
How do we implement mul and exp efficiently? The code above has mul, but is wrong (see the FIXME).
Given a function f(x) = a^x mod N, what is the optimal algorithm for finding the period?
The text was updated successfully, but these errors were encountered:
In HVM, an interesting way to represent numbers is to use λ-encoded digit-strings of varying bases, parametrized by a list of numbers (a base-list). For example, by using
[2,3,5]
, we represent numbers base 2-3-5:Moreover, since
2 * 3 * 5 = 30
, numbers larger than30
on base2-3-5
will wrap around. That means operations on them will bemod 30
, with no need for amodulus
operation. This may help implementingmod N
algorithms that fuse, by avoiding calls tomod
, which could interrupt fusion.The base-list can be anything. For example,
[100]
would give us operationsmod 100
. But, in turn, each number would use a lot of space, since there are 100 digits. A better idea is to factorize100
and use[2,2,5,5]
instead, which will also give us operationsmod 100
, but using much less space. Of course, if we use a base-list with identical numbers, say,[2,2,2,2]
, we'll just have binary numbers, or N-ary, for whatever theN
is. Here is the code:This snippet includes an example of computing
123456789 + 987654321 mod 210
, and it efficiently outputs60
, no call tomod
is needed. Problems:How do we implement
mul
andexp
efficiently? The code above hasmul
, but is wrong (see the FIXME).Given a function
f(x) = a^x mod N
, what is the optimal algorithm for finding the period?The text was updated successfully, but these errors were encountered: