1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
/* Definition of constants. */
/* TODO:
* There is a problem with defining I this way: if I is used, but the
* file "complex" has not been loaded, the interpreter can not deal
* with "Complex".
*/
SetGlobalLazyVariable(I,Complex(0,1));
//////////////////////////////////////////////////
/// Cached constants support and definition of Pi
//////////////////////////////////////////////////
//TODO: here we wrap the entire file in LocalSymbols, this is inefficient in that it slows loading of this file. Needs optimization.
LocalSymbols(CacheOfConstantsN) [
/// declare a new cached constant C'atom and its associated function C'atom().
/// C'atom() will call C'func() at current precision to evaluate C'atom if it has not yet been cached at that precision. (note: any arguments to C'func() must be included)
RuleBase("CachedConstant", {C'cache, C'atom, C'func});
UnFence("CachedConstant", 3); // not sure if this is useful
HoldArg("CachedConstant", C'func);
HoldArg("CachedConstant", C'cache); // name of the cache
// check syntax: must be called on an atom and a function
Rule("CachedConstant", 3, 10, And(IsAtom(C'atom), IsFunction(C'func)))
[
Local(C'name,C'functionName);
Set(C'name, String(C'atom)); // this is for later conveniences
Set(C'functionName,ConcatStrings("Internal'",C'name));
If( // create the cache it if it does not already exist
IsAtom(Eval(C'cache)),
MacroSet(Eval(C'cache), {})
);
// Write({"debug step 0: ", C'cache, Eval(C'cache), C'atom, C'func, C'name});
// check that the constant is not already defined
If(
Equals(Builtin'Assoc(C'name, Eval(C'cache)), Empty), // the constant is not already defined, so need to define "C'atom" and the corresponding function "C'atom"()
[ // e.g. C'atom evaluates to Pi, C'cache to a name e.g. CacheOfConstantsN, which is bound to a hash
MacroClear(C'atom);
// Write({"debug step 1: ", Cache'name, C'cache, Eval(C'cache)});
// add the new constant to the cache
// MacroSet(Cache'name, Insert(Eval(C'cache), 1, {C'name, 0, 0}));
DestructiveInsert(Eval(C'cache), 1, {C'name, 0, 0});
// Write({"debug step 2: ", Cache'name, C'cache, Eval(C'cache)});
// define the new function "C'atom"()
// note: this should not use N() because it may be called from inside N() itself
MacroRuleBase(C'functionName, {});
`( Rule(@C'functionName, 0, 1024, True)
[
Local(new'prec, new'C, cached'C);
Set(new'prec, BuiltinPrecisionGet());
// fetch the cache entry for this constant
// note that this procedure will store the name of the cache here in this statement as Eval(C'cache)
Set(cached'C, Builtin'Assoc(@C'name, @C'cache));
If(
LessThan(MathNth(cached'C, 2), new'prec),
[ // need to recalculate at current precision
If(Equals(InVerboseMode(),True), Echo("CachedConstant: Info: constant ", @C'name, " is being recalculated at precision ", new'prec));
Set(new'C, Eval(@C'func));
DestructiveReplace(cached'C, 2, new'prec);
DestructiveReplace(cached'C, 3, new'C);
new'C;
],
// return cached value of C'atom
MathNth(cached'C, 3)
);
]);
// calculate C'atom at current precision for the first time
// Eval(UnList({C'atom})); // "C'name"();
// we do not need this until the constant is used; it will just slow us down
],
// the constant is defined
Echo("CachedConstant: Warning: constant ", C'atom, " already defined")
);
];
Rule("CachedConstant", 3, 20, True)
Echo("CachedConstant: Error: ", C'atom, " must be an atom and ", C'func, " must be a function.");
/// assign numerical values to all cached constants: using fixed cache "CacheOfConstantsN"
// this is called from N()
Function("AssignCachedConstantsN", {})
[
Local(var,fname);
ForEach(var, AssocIndices(CacheOfConstantsN))
[
MacroClear(Atom(var));
Set(fname,ConcatStrings("Internal'",var));
Set(var,Atom(var));
// this way the routine Internal'Pi() will be actually called only when the variable 'Pi' is used, etcetera.
`SetGlobalLazyVariable((@var), UnList({Atom(fname)}));
];
];
UnFence("AssignCachedConstantsN", 0);
/// clear values from all cached constants: using fixed cache "CacheOfConstantsN"
// this is called from N()
Function("ClearCachedConstantsN", {})
[
Local(c'entry);
ForEach(c'entry, CacheOfConstantsN)
MacroClear(Atom(c'entry[1]));
];
UnFence("ClearCachedConstantsN", 0);
/// declare some constants now
CachedConstant(CacheOfConstantsN, Pi,
[// it seems necessary to precompute Pi to a few more digits
// so that Cos(0.5*Pi)=0 at precision 10
// FIXME: find a better solution
Local(result,old'prec);
Set(old'prec,BuiltinPrecisionGet());
If(Equals(InVerboseMode(),True), Echo("Recalculating Pi at precision ",old'prec+5));
BuiltinPrecisionSet(BuiltinPrecisionGet()+5);
result := MathPi();
If(Equals(InVerboseMode(),True),Echo("Switching back to precision ",old'prec));
BuiltinPrecisionSet(old'prec);
result;
]
);
CachedConstant(CacheOfConstantsN, gamma, GammaConstNum());
CachedConstant(CacheOfConstantsN, GoldenRatio, N( (1+Sqrt(5))/2 ) );
CachedConstant(CacheOfConstantsN, Catalan, CatalanConstNum() );
]; // LocalSymbols(CacheOfConstantsN)
|