Carl Love

Carl Love

28025 Reputation

25 Badges

12 years, 313 days
Himself
Wayland, Massachusetts, United States
My name was formerly Carl Devore.

MaplePrimes Activity


These are answers submitted by Carl Love

Here's a complete implementation of RSA (key generation, encryption, and decryption). Maple makes this surprisingly easy. Notice that it's not much longer than your procedure, whose function is performed by my one-line procedure Stringify.

RSA:= module()
export
   bytelen:= 8,
   GenKey:= proc(B::And(posint, satisfies(B-> B>3)), seed::posint:= ())
   local r:= randomize(seed), p, q, C, e, d, R;
      p:= nextprime(rand(2^B..2^(B+1))());
      R:= rand(2^(B+5)..2^(B+6));
      do q:= nextprime(R()) until igcd(p-1, q-1) = 2;    
      C:= (p-1)*(q-1)/2;
      R:= rand(3..C-1);
      do e:= R() until igcd(e, C) = 1;
      if seed=() then randomize(r) fi;
      p*q, e, (1/e mod C)
   end proc,

   Encrypt:= (S::string, n::posint, e::posint)-> 
      Chunkify(
         sprintf("%m", Numerify~(Chunkify(S, iquo(ilog2(n),bytelen))) &^ ~ e mod n),
         interface('screenwidth')
      ),
   Decrypt:= (S::list(string), n::posint, d::posint)->
      cat(Stringify~(sscanf(cat(S[]), "%m")[] &^ ~ d mod n)[]),

   Chunkify:= proc(S::string, L::posint)
   local k, r, len:= length(S);
      [seq(S[1+L*(k-1)..min(L*k,len)], k= 1..iquo(len,L,r)+`if`(r>0,1,0))]
   end proc,

   Numerify:= (S::string)-> 
      convert(convert(S, bytes), base, 2^bytelen, 2^(length(S)*bytelen))[],
   Stringify:= (N::posint)-> cat(convert(convert(N, base, 2^bytelen), bytes)),
`;`;
end module:

Example usage:

(n,e,d):= RSA:-GenKey(32);
   n, e, d := 1570972592032860729287, 122831757217576938571, 
     678402947328820371811
S:= "It's so easy and so much fun to write an RSA module in Maple!":
E:= RSA:-Encrypt(S, n, e);
E := 
  ["7*"7>xa***p9OT`O""7&prhA'H;w!37""7p#**zDPbe$yf:"7AJG!Q%e'*)*[a5"6Ty!yo%*\+8T]"7", 
  "rB?_()RpV(3d""7$="y+67I`,^7"6[6%4Tr(ogYe("]

RSA:-Decrypt(E, n, d);
"It's so easy and so much fun to write an RSA module in Maple!"

Maple has special commands (such as ilog10, ilog2, iquo, irem, ilcm, igcd, &^, and mod) for high-speed large-integer arithmetic. You should avoid floating-point and/or symbolic commands such as log10floorevalf, and frac when doing large-integer arithmetic.

I confirm Tom's work; there are no typos. I obtained identical results independently.

restart:
(k,j):= (3,0):
add(
   add(`if`(l=m, 0, mul(`if`(q=m or q=l, 1, r-q+1), q= 0..k)), l= 0..k)
   /mul(`if`(l=m, 1, m-l), l= 0..k),
   m= j+1..k
);
eval(%, r= 1); 

My code is almost identical to Tom's, which is just a coincidence.

You wrote the following the very hand-wavy excuse:

  • (i know the command is weird, i.e. subs(beta=beta, ..), etc.. this is part of some larger code base and i just put this in when trying to undertand what's going on).

Your command is weird, period. "When trying to understand what's going on" is the last place that you'd want to do something weird. The weirdness is not so much the beta= beta part; it's when that's combined with an assuming clause that uses beta. Personally, I'd steer clear of any use of subs inside assuming.

These things might not have anything to do with your issue. I can't tell until you post the executable code.

The backslash is the escape character in Maple. So, if you want to pass a string literal that contains backslashes, then you must code each backslash as \\. So, if you want to pass a literal that has four backslashes, as you show (two at the beginning and two at the end), then it needs to be

"\\\\((.+)\\\\)"

You almost have it. I like your symbolic approach. Some tips and corrections:

  • If gamma is supposed to be a constant that you will specify, then you should use a different name for it, because gamma is a pre-defined constant in Maple, similar to Pi. While it's not an error to use your own value for gamma (or Pi for that matter), doing so can lead to incorrect numeric results being returned in some situations which otherwise would've (and should've!) returned error messages.
  • f should return a column vector, not a matrix.
  • In the definition of f, you've inappropriately used square brackets for parentheses.
  • You need to give a little bit of structural definition before you can do diff~(X, ...), etc. X:= t-> Vector(n, i-> x[i](t)) is sufficient (or use any other unused symbol for x).
  • Shouldn't tau become in the sys:= ... definition? Mathematically, it makes sense either way, but there's a big difference in meaning. Using tau would make it a constant-coefficient system (and perhaps that's what you intend).

There are commands numer and denom that extract the numerator and denominator of expressions. So,

coeff(denom(expr2), sin(a)^2)

I was surprised at how easy this was: To find (and print neatly) all references to _SolutionsMayBeLost in all library procedures whose names begin with isolve (This could be repeated for any names):

macro(pre= "isolve", SMBL= "_SolutionsMayBeLost", ST= StringTools):

#Select all library entries (19) whose names begin "isolve":
IP:= map(
   p-> convert(p[1][1..-3], name), #-3 to remove .m file extension
   select(p-> p[1][1..length(pre)]=pre, LibraryTools:-ShowContents())
);   
#Select those (4) that contain any reference to _SolutionsMayBeLost:
SIP:= select(p-> has(ToInert(eval(p)), SMBL), IP);

#Select the lines in those procedures that contain _SolutionsMayBeLost:
R:= map(
   p-> [
      p, 
      select(
         L-> ST:-Search(SMBL,L) > 0, ST:-Split(debugopts(procpretty= p), `\n`))[]
   ], 
   SIP
):
#Formatted print:
printf(cat("\n", seq(cat("%s\n"$r, "\n\n"), r= nops~(R))), op~(R)[]);  

isolve
global _SolutionsMayBeLost;
  26   _SolutionsMayBeLost := '_SolutionsMayBeLost';
  28   if _SolutionsMayBeLost = true then


isolve/inequalities
global _SolutionsMayBeLost;
  10       _SolutionsMayBeLost := true;


isolve/inequalities/convert_to_Z
global _SolutionsMayBeLost;
  21           _SolutionsMayBeLost := true;
  28           _SolutionsMayBeLost := true;
  34               _SolutionsMayBeLost := true;
  39                   _SolutionsMayBeLost := true;
  41           _SolutionsMayBeLost := true;
  43       _SolutionsMayBeLost := true;


isolve/inequalities/internal
global _SolutionsMayBeLost;
   6           _SolutionsMayBeLost := true;

So, if you execute the above in Maple 2018, you'll see that there are 19 library entries whose names begin with isolve. All are procedures. Of those, there are only 4 that contain any reference at all to _SolutionsMayBeLost. Only 3 of those set the value, and all 3 of those are specific to inequalities.

Here is another workaround. It is similar to yours, but it doesn't require any foreknowledge about which variable(s) are good candidates for elimination.

`isolve/sep_degrees`:= proc(Sys::set({algebraic, `=`}))
local L, N, S, LS, LN;
   (L,N):= selectremove(type, subsindets(Sys, `=`, lhs-rhs), 'linear');  
   S:= {isolve(eval(N, (L:= isolve(L))), _rest)};
   (LN,LS):= selectremove(type, L, name=name);
   eval(map(`union`, S, LS), (rhs=lhs)~(LN))[]
end proc:

sys:={x+y=10, x^2-y^2+z^2=1}:
`isolve/sep_degrees`(sys, n);

 

Maple 18 was wrong because the "closed form" that you give is not valid for all x. In Maple 2018, this error has been corrected. Now you either need to put an assuming clause (such as assuming x > 0) or throw caution to the wind and include the formal option:

sum(1/(1+x)^t, t= 1..infinity, 'formal');
sum(1/(1+x)^t, t= 1..infinity) assuming x > 0;

Here's an example of what you're trying to do. The procedure immediately below takes an expression f and returns the Newton's method iteration that can be used to solve f=0 for "some variable". The user can specify the variable to use as that "with respect to" variable (to bind that variable is the logician's parlance) in the second argument or leave the second argument blank to let the procedure try to figure that out on its own.

Newton:= proc(f::algebraic, var::name:= ())
local x:= var;
   if x=() then #User didn't pass a 2nd argument.
      #The Not(constant) part is to prevent things like 
      #Pi from being considered variables. 
      x:= indets(f, And(name, Not(constant)));
      #indets always returns a set, which we process based on its
      #number of elements, nops.
      if nops(x)=1 then x:= x[] #Extract the one and only member.
      elif x={} then error "No variables found"
      else error "Multiple variables found"
      fi
   fi;
   x - f/diff(f,x) #Return Newton iteration.
end proc: 

Then try

Newton(y^2 - 2);
Newton(x^2 - a);
Newton(x^2 - a, x);
Newton(2);
Newton(2, x); #Intentional divide-by-zero error

Note that the parameter specification var::name:= () has both a type, name, and a default value, (), and that that default value doesn't match the type. Thus the procedure knows that if var=(), then the user definitely didn't pass a second argument. (Actually, it's impossible to pass the particular value () as an argument, but that's beside the point. This type/default mismatch trick works for any default.)

The example that you showed of plot(x^2) is unusual compared to most Maple commands in that the input is an expression, yet the command doesn't require the user to specify the variable to bind. (And that's what I did in Newton.) But the far more common situation is that the procedure assumes that if no variable is specifed then the function/expression argument is a procedure whose (first) argument is the variable to bind (as in the commands plot(x-> x^2, -2..2) and plot(sin, -Pi..Pi)). This is simpler to code. Here's an example:

Newton2:= proc(f, var::name:= ())
local x;
   if var=() then #User didn't pass a 2nd argument.
      #Treat f as a procedure, temporarily bind a local, and return a procedure:
      unapply(x - f(x)/D(f)(x), x)
   else
      #Bind var and return an expression
      var - f/diff(f,var)
   fi
end proc:

Note that binding a variable is mostly a logical/mental process rather than something that happens inside the computer. It is something that happens in the minds of the writer and user of the procedure that gives a particular variable a different status.

When Maple needs to bind a variable that will appear in the output of a user-level procedure but which was not specifed in the input, it almost always chooses one whose name is an underscore, followed by one or two letters, followed by a nonnegative integer. Mostly, this is a convention that is respected by programmers rather than something that is enforced by a compiler.

The relation is not generally true. It would be true if you changed 2*Pi to Pi. The argument of a complex number is defined (in Maple, and perhaps generally) to be in the interval (-Pi, Pi]. I think you're assuming that that interval is [0, 2*Pi).

Your expression doesn't necessarily simplify to 0, even with the real assumption. Let phi be 3*Pi/4, for example. Actually, it's only true for angles in the 1st quadrant.

I think that a dedicated procedure is a bit of overkill for this. Also, calling RandomVariable is redundant. If Lambda is a "container" (such as a Matrix or Vector from Excel, or any other source) of parameter values, and Alpha is either a single percentile level (e.g, Alpha:= 0.98) or a container of percentile levels matched to Lambda, then all that you need to do is

Statistics:-Quantile~(Poisson~(Lambda), Alpha);

The following procedure will extract all products of two or more functions from an expression, with the only exponent allowed being -1 (which in Maple is equivalent to being a factor of the denominator).

FunctionProducts:= e-> 
   select(
      type, 
      map2(select, type, indets(e, `*`), {function, function^identical(-1)}), 
      `*`
   )
:

This procedure extracts a nonnegative integer from the end of any symbol or string, using all available trailing digits from the end.

GetTail:= proc(X::{symbol, string})
local E:= ["A", StringTools:-Explode(X)[]], k;
     for k to nops(E) while StringTools:-IsDigit(E[-k]) do od;
     `if`(k=1, FAIL, parse(substring(X, -(k-1)..-1)))
end proc:

 

First 156 157 158 159 160 161 162 Last Page 158 of 395