33 double* fp =
d.data();
34 char** str =
s.data();
41 block->
d.resize(
d.size());
43 memcpy(fp,
d.data(),
d.size() *
sizeof(
double));
46 block->
s.resize(
s.size());
47 str = block->
s.data();
48 memcpy(str,
s.data(),
s.size() *
sizeof(
char*));
52 str[0] =
reinterpret_cast<char*
>(block->
data());
53 str[1] =
reinterpret_cast<char*
>(
static_cast<size_t>(block->
indirectIndex));
57 int end =
static_cast<int>(
ops.size());
60 std::cerr <<
"Running op at " << pc << std::endl;
63 const std::pair<OpF, int>& op =
ops[pc];
64 int* opCurr = &
opData[0] + op.second;
65 pc += op.first(opCurr, fp, str,
callStack);
70 std::cerr <<
"---- ops ----------------------" << std::endl;
71 for (
size_t i = 0; i <
ops.size(); i++) {
72 const char* name =
"";
75 if (dladdr((
void*)
ops[i].first, &info)) name = info.dli_sname;
77 fprintf(stderr,
"%s %s %p (", pc == (
int)i ?
"-->" :
" ", name,
ops[i].first);
78 int nextGuy = (i ==
ops.size() - 1 ?
static_cast<int>(
opData.size()) :
ops[i + 1].second);
79 for (
int k =
ops[i].second; k < nextGuy; k++) {
80 fprintf(stderr,
" %d",
opData[k]);
82 fprintf(stderr,
")\n");
84 std::cerr <<
"---- opdata ----------------------" << std::endl;
85 for (
size_t k = 0; k <
opData.size(); k++) {
86 std::cerr <<
"opData[" << k <<
"]= " <<
opData[k] << std::endl;
89 std::cerr <<
"----- fp --------------------------" << std::endl;
90 for (
size_t k = 0; k <
d.size(); k++) {
91 std::cerr <<
"fp[" << k <<
"]= " <<
d[k] << std::endl;
94 std::cerr <<
"---- str ----------------------" << std::endl;
95 std::cerr <<
"s[0] reserved for datablock = " <<
reinterpret_cast<size_t>(
s[0]) << std::endl;
96 std::cerr <<
"s[1] is indirectIndex = " <<
reinterpret_cast<size_t>(
s[1]) << std::endl;
97 for (
size_t k = 2; k <
s.size(); k++) {
98 std::cerr <<
"s[" << k <<
"]= 0x" <<
s[k];
99 if (
s[k]) std::cerr <<
" '" <<
s[k][0] <<
s[k][1] <<
s[k][2] <<
s[k][3] <<
"...'";
100 std::cerr << std::endl;
110template <
char c,
template <
char c1,
int d>
class T>
146 assert(
false &&
"Invalid dynamic parameter (not supported template)");
155struct BinaryStringOp {
156 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
158 char*& out = *(
char**)c[opData[0]];
159 char* in1 = c[opData[1]];
160 char* in2 = c[opData[2]];
165 int len1 =
static_cast<int>(strlen(in1));
166 int len2 =
static_cast<int>(strlen(in2));
167 if (out == 0 || len1 + len2 + 1 > strlen(out))
170 out =
new char [len1 + len2 + 1];
174 memset(out, 0, len1 + len2 + 1);
178 strcat(out + len1, in2);
179 out[len1 + len2] =
'\0';
189template <
char op,
int d>
191 static double niceMod(
double a,
double b) {
192 if (b == 0)
return 0;
193 return a - floor(a / b) *
b;
196 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
197 double* in1 = fp + opData[0];
198 double* in2 = fp + opData[1];
199 double* out = fp + opData[2];
201 for (
int k = 0; k < d; k++) {
204 *out = (*in1) + (*in2);
207 *out = (*in1) - (*in2);
210 *out = (*in1) * (*in2);
213 *out = (*in1) / (*in2);
216 *out = niceMod(*in1, *in2);
219 *out =
pow(*in1, *in2);
223 *out = (*in1) < (*in2);
226 *out = (*in1) > (*in2);
229 *out = (*in1) <= (*in2);
232 *out = (*in1) >= (*in2);
235 *out = (*in1) && (*in2);
238 *out = (*in1) || (*in2);
252template <
char op,
int d>
254 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
255 double* in = fp + opData[0];
256 double* out = fp + opData[1];
257 for (
int k = 0; k < d; k++) {
281 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
282 int tuple = opData[0];
283 int subscript = int(fp[opData[1]]);
285 if (subscript >= d || subscript < 0)
288 fp[out] = fp[tuple + subscript];
296 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
298 for (
int k = 0; k < d; k++) {
299 fp[out + k] = fp[opData[k]];
308 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
311 for (
int k = 0; k < d; k++) {
312 fp[out + k] = fp[in + k];
320 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
329struct CondJmpRelativeIfFalse {
330 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
331 bool cond = (bool)fp[opData[0]];
340struct CondJmpRelativeIfTrue {
341 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
342 bool cond = (bool)fp[opData[0]];
352 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
return opData[0]; }
357 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
358 ExprVarRef* ref =
reinterpret_cast<ExprVarRef*
>(c[opData[0]]);
359 if (ref->type().isFP()) {
360 ref->eval(fp + opData[1]);
362 ref->eval(
const_cast<const char**
>(c + opData[1]));
371 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
373 double* basePointer =
reinterpret_cast<double*
>(c[0]) + opData[0];
374 double* destPointer = fp + opData[1];
375 for (
int i = 0; i < dim; i++) destPointer[i] = basePointer[i];
382template <
char uniform,
int dim>
383struct EvalVarBlockIndirect {
384 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
386 int stride = opData[2];
387 int outputVarBlockOffset = opData[0];
388 int destIndex = opData[1];
389 size_t indirectIndex =
reinterpret_cast<size_t>(c[1]);
390 double* basePointer =
391 reinterpret_cast<double**
>(c[0])[outputVarBlockOffset] + (uniform ? 0 : (stride * indirectIndex));
392 double* destPointer = fp + destIndex;
393 for (
int i = 0; i < dim; i++) destPointer[i] = basePointer[i];
403template <
char op,
int d>
405 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
407 double* in0 = fp + opData[0];
408 double* in1 = fp + opData[1];
409 double* out = fp + opData[2];
410 for (
int k = 0; k < d; k++) {
413 result &= (*in0) == (*in1);
416 result &= (*in0) != (*in1);
430struct CompareEqOp<op, 3> {
431 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
432 bool eq = fp[opData[0]] == fp[opData[1]] && fp[opData[0] + 1] == fp[opData[1] + 1] &&
433 fp[opData[0] + 2] == fp[opData[1] + 2];
434 if (op ==
'=') fp[opData[2]] = eq;
435 if (op ==
'!') fp[opData[2]] = !eq;
440template <
char op,
int d>
441struct StrCompareEqOp {
443 static int f(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
446 fp[opData[2]] = strcmp(c[opData[0]], c[opData[1]]) == 0;
449 fp[opData[2]] = strcmp(c[opData[0]], c[opData[1]]) != 0;
458int ProcedureReturn(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
459 int newPC = callStack.back();
460 callStack.pop_back();
461 return newPC - opData[0];
466int ProcedureCall(
int* opData,
double* fp,
char** c, std::vector<int>& callStack) {
467 callStack.push_back(opData[0]);
476 int basePC = interpreter->
nextPC();
478 interpreter->
addOp(ProcedureReturn);
481 interpreter->
endOp(
false);
488 std::vector<int> operands;
489 for (
int c = 0; c < callerNode->
numChildren(); c++) {
494 if (callerNode->
promote(c) != 0) {
496 interpreter->
addOp(getTemplatizedOp<Promote>(callerNode->
promote(c)));
500 interpreter->
endOp();
505 interpreter->
endOp();
511 operands.push_back(operand);
517 outoperand = interpreter->
allocPtr();
521 int basePC = interpreter->
nextPC();
522 interpreter->
addOp(ProcedureCall);
523 int returnAddress = interpreter->
addOperand(0);
525 interpreter->
endOp(
false);
527 interpreter->
opData[returnAddress] = interpreter->
nextPC();
530 interpreter->
addOp(getTemplatizedOp<AssignOp>(callerNode->
type().
dim()));
533 interpreter->
endOp();
544 int loc = interpreter->
allocFP(1);
545 interpreter->
d[loc] =
value();
551 interpreter->
s[loc] =
const_cast<char*
>(
_str.c_str());
556 std::vector<int> locs;
565 interpreter->
endOp();
571 int dim0 = child0->
type().
dim(), dim1 = child1->type().dim(), dimout =
type().
dim();
573 int op1 = child1->buildInterpreter(interpreter);
575 if (dim0 != dimout) {
576 interpreter->
addOp(getTemplatizedOp<Promote>(dimout));
577 int promoteOp0 = interpreter->
allocFP(dimout);
581 interpreter->
endOp();
583 if (dim1 != dimout) {
584 interpreter->
addOp(getTemplatizedOp<Promote>(dimout));
585 int promoteOp1 = interpreter->
allocFP(dimout);
589 interpreter->
endOp();
600 interpreter->
addOp(getTemplatizedOp2<'+', BinaryOp>(dimout));
603 interpreter->
addOp(getTemplatizedOp2<'-', BinaryOp>(dimout));
606 interpreter->
addOp(getTemplatizedOp2<'*', BinaryOp>(dimout));
609 interpreter->
addOp(getTemplatizedOp2<'/', BinaryOp>(dimout));
612 interpreter->
addOp(getTemplatizedOp2<'^', BinaryOp>(dimout));
615 interpreter->
addOp(getTemplatizedOp2<'%', BinaryOp>(dimout));
623 interpreter->
addOp(BinaryStringOp::f);
624 int intermediateOp = interpreter->
allocPtr();
625 interpreter->
s[intermediateOp] = (
char*)(&
_out);
637 op2 = interpreter->
allocFP(dimout);
665 interpreter->
addOp(getTemplatizedOp2<'-', UnaryOp>(dimout));
668 interpreter->
addOp(getTemplatizedOp2<'~', UnaryOp>(dimout));
671 interpreter->
addOp(getTemplatizedOp2<'!', UnaryOp>(dimout));
676 int op1 = interpreter->
allocFP(dimout);
679 interpreter->
endOp();
686 int dimin = child0->
type().
dim();
688 int op1 = child1->buildInterpreter(interpreter);
689 int op2 = interpreter->
allocFP(1);
691 interpreter->
addOp(getTemplatizedOp<Subscript>(dimin));
695 interpreter->
endOp();
702 Interpreter::VarToLoc::iterator i = interpreter->
varToLoc.find(
var);
703 if (i != interpreter->
varToLoc.end())
706 throw std::runtime_error(
"Unallocated variable encountered.");
712 destLoc = interpreter->
allocFP(dim);
717 if (blockVarRef->type().isLifetimeUniform())
718 interpreter->
addOp(getTemplatizedOp2<1, EvalVarBlockIndirect>(
type.
dim()));
720 interpreter->
addOp(getTemplatizedOp2<0, EvalVarBlockIndirect>(
type.
dim()));
721 interpreter->
addOperand(blockVarRef->offset());
723 interpreter->
addOperand(blockVarRef->stride());
724 interpreter->
endOp();
726 int varRefLoc = interpreter->
allocPtr();
727 interpreter->
addOp(EvalVar::f);
728 interpreter->
s[varRefLoc] =
const_cast<char*
>(
reinterpret_cast<const char*
>(
var));
731 interpreter->
endOp();
739 return interpreter->
varToLoc[
this] =
745 assert(loc != -1 &&
"Invalid type found");
749 if (child0Type.
isFP()) {
750 interpreter->
addOp(getTemplatizedOp<AssignOp>(child0Type.
dim()));
752 interpreter->
addOp(AssignStrOp::f);
754 assert(
false &&
"Invalid desired assign type");
765 int destDim = varDest->
type().
dim();
766 if (destDim != varSource->
type().
dim()) {
767 assert(varSource->
type().
dim() == 1);
768 interpreter->
addOp(getTemplatizedOp<Promote>(destDim));
770 interpreter->
addOp(getTemplatizedOp<AssignOp>(destDim));
774 interpreter->
endOp();
776 interpreter->
addOp(AssignStrOp::f);
779 interpreter->
endOp();
781 assert(
false &&
"failed to promote invalid type");
787 int basePC = interpreter->
nextPC();
793 for (
auto&
it : merges) {
795 if (finalVar->
valid()) {
801 interpreter->
addOp(CondJmpRelativeIfFalse::f);
804 interpreter->
endOp();
808 for (
auto&
it : merges) {
810 if (finalVar->
valid()) {
814 interpreter->
addOp(JmpRelative::f);
816 interpreter->
endOp();
819 int child2PC = interpreter->
nextPC();
821 for (
auto&
it : merges) {
823 if (finalVar->
valid()) {
829 interpreter->
opData[destFalse] = child2PC - basePC;
830 interpreter->
opData[destEnd] = interpreter->
nextPC() - (child2PC - 1);
837 assert(
type().dim() == 1 &&
type().isFP());
839 if (
_op ==
'&' ||
_op ==
'|') {
843 int op2 = interpreter->
allocFP(1);
847 int basePC = (interpreter->
nextPC());
848 interpreter->
addOp(
_op ==
'&' ? CondJmpRelativeIfFalse::f : CondJmpRelativeIfTrue::f);
851 interpreter->
endOp();
853 int op1 = child1->buildInterpreter(interpreter);
855 interpreter->
addOp(
_op ==
'&' ? getTemplatizedOp2<'&', BinaryOp>(1) : getTemplatizedOp2<'|', BinaryOp>(1));
859 interpreter->
endOp();
860 interpreter->
addOp(JmpRelative::f);
862 interpreter->
endOp();
865 int falseConditionPC = interpreter->
nextPC();
866 interpreter->
addOp(AssignOp<1>::f);
869 interpreter->
endOp();
872 interpreter->
opData[destFalse] = falseConditionPC - basePC;
873 interpreter->
opData[destEnd] = interpreter->
nextPC() - (falseConditionPC - 1);
880 int op1 = child1->buildInterpreter(interpreter);
886 interpreter->
addOp(getTemplatizedOp2<'>
', BinaryOp>(1));
889 interpreter->addOp(getTemplatizedOp2<'l
', BinaryOp>(1));
892 interpreter->addOp(getTemplatizedOp2<'g
', BinaryOp>(1));
895 assert(false); // interpreter->addOp(getTemplatizedOp2<'&
',BinaryOp>(1));break;
897 assert(false); // interpreter->addOp(getTemplatizedOp2<'|
',BinaryOp>(1));break;
901 int op2 = interpreter->allocFP(1);
902 interpreter->addOperand(op0);
903 interpreter->addOperand(op1);
904 interpreter->addOperand(op2);
905 interpreter->endOp();
910int ExprPrototypeNode::buildInterpreter(Interpreter* interpreter) const {
912 _interpreterOps.clear();
913 for (int c = 0; c < numChildren(); c++) {
914 if (const ExprVarNode* childVarNode = dynamic_cast<const ExprVarNode*>(child(c))) {
915 ExprType childType = childVarNode->type();
916 if (childType.isFP()) {
917 int operand = interpreter->allocFP(childType.dim());
918 _interpreterOps.push_back(operand);
919 interpreter->varToLoc[childVarNode->localVar()] = operand;
924 child(c)->buildInterpreter(interpreter);
926 // make sure we have a slot in our global activation record for the variables!
931int ExprCompareEqNode::buildInterpreter(Interpreter* interpreter) const {
932 const ExprNode* child0 = child(0), *child1 = child(1);
933 int op0 = child0->buildInterpreter(interpreter);
934 int op1 = child1->buildInterpreter(interpreter);
936 if (child0->type().isFP()) {
937 int dim0 = child0->type().dim(), dim1 = child1->type().dim();
938 int dimCompare = std::max(dim0, dim1);
939 if (dimCompare > 1) {
941 interpreter->addOp(getTemplatizedOp<Promote>(dim1));
942 int promotedOp0 = interpreter->allocFP(dim1);
943 interpreter->addOperand(op0);
944 interpreter->addOperand(promotedOp0);
945 interpreter->endOp();
949 interpreter->addOp(getTemplatizedOp<Promote>(dim0));
950 int promotedOp1 = interpreter->allocFP(dim0);
951 interpreter->addOperand(op1);
952 interpreter->addOperand(promotedOp1);
953 interpreter->endOp();
958 interpreter->addOp(getTemplatizedOp2<'=
', CompareEqOp>(dimCompare));
960 interpreter->addOp(getTemplatizedOp2<'!
', CompareEqOp>(dimCompare));
962 assert(false && "Invalid operation");
963 } else if (child0->type().isString()) {
965 interpreter->addOp(getTemplatizedOp2<'=
', StrCompareEqOp>(1));
967 interpreter->addOp(getTemplatizedOp2<'!
', StrCompareEqOp>(1));
969 assert(false && "Invalid operation");
971 assert(false && "Invalid type for comparison");
972 int op2 = interpreter->allocFP(1);
973 interpreter->addOperand(op0);
974 interpreter->addOperand(op1);
975 interpreter->addOperand(op2);
976 interpreter->endOp(child0->type().isString() == false);
980int ExprCondNode::buildInterpreter(Interpreter* interpreter) const {
982 // TODO: handle strings!
983 int dimout = type().dim();
986 int condOp = child(0)->buildInterpreter(interpreter);
987 int basePC = (interpreter->nextPC());
988 interpreter->addOp(CondJmpRelativeIfFalse::f);
989 interpreter->addOperand(condOp);
990 int destFalse = interpreter->addOperand(0);
991 interpreter->endOp();
993 // true way of working
994 int op1 = child(1)->buildInterpreter(interpreter);
996 interpreter->addOp(getTemplatizedOp<AssignOp>(dimout));
997 else if (type().isString())
998 interpreter->addOp(AssignStrOp::f);
1001 interpreter->addOperand(op1);
1002 int dataOutTrue = interpreter->addOperand(-1);
1003 interpreter->endOp(false);
1005 // jump past false way of working
1006 interpreter->addOp(JmpRelative::f);
1007 int destEnd = interpreter->addOperand(0);
1008 interpreter->endOp();
1010 // record start of false condition
1011 int child2PC = interpreter->nextPC();
1013 // false way of working
1014 int op2 = child(2)->buildInterpreter(interpreter);
1016 interpreter->addOp(getTemplatizedOp<AssignOp>(dimout));
1017 else if (type().isString())
1018 interpreter->addOp(AssignStrOp::f);
1021 interpreter->addOperand(op2);
1022 int dataOutFalse = interpreter->addOperand(-1);
1023 interpreter->endOp(false);
1025 // patch up relative jumps
1026 interpreter->opData[destFalse] = child2PC - basePC;
1027 interpreter->opData[destEnd] = interpreter->nextPC() - (child2PC - 1);
1031 opOut = interpreter->allocFP(type().dim());
1032 else if (type().isString())
1033 opOut = interpreter->allocPtr();
1037 // patch outputs on assigns in each condition
1038 interpreter->opData[dataOutTrue] = opOut;
1039 interpreter->opData[dataOutFalse] = opOut;
1044int ExprBlockNode::buildInterpreter(Interpreter* interpreter) const {
1045 assert(numChildren() == 2);
1046 child(0)->buildInterpreter(interpreter);
1047 return child(1)->buildInterpreter(interpreter);
1050int ExprModuleNode::buildInterpreter(Interpreter* interpreter) const {
1052 for (int c = 0; c < numChildren(); c++) {
1053 if (c == numChildren() - 1) interpreter->setPCStart(interpreter->nextPC());
1054 lastIdx = child(c)->buildInterpreter(interpreter);
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
char _op
_op '<' less-than, 'l' less-than-eq, '>' greater-than, 'g' greater-than-eq
Node that calls a function.
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
const ExprPrototypeNode * prototype() const
TODO: Accessor for prototype (probably not needed when we use prep right)
int buildInterpreter(Interpreter *interpreter) const
Build the interpreter.
int buildInterpreterForCall(const ExprFuncNode *callerNode, Interpreter *interpreter) const
Build interpreter if we are called.
ExprLocalVar join (merge) references. Remembers which variables are possible assigners to this.
ExprLocalVar reference, all local variables in seexpr are subclasses of this or this itself.
ExprType type() const
returns type of the variable
int buildInterpreter(Interpreter *interpreter) const
Allocates variable for interpreter.
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
int numChildren() const
Number of children.
const ExprNode * child(size_t i) const
Get 0 indexed child.
const ExprType & type() const
The type of the node.
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
bool isFP() const
Direct is predicate checks.
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
std::vector< std::pair< std::string, ExprLocalVarPhi * > > & merge(size_t index)
const ExprVarRef * var() const
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
abstract class for implementing variable references
virtual ExprType type() const
returns (current) type
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
int allocFP(int n)
! Allocate a floating point set of data of dimension n
int addOp(OpF op)
! adds an operator to the program (pointing to the data at the current location)
int(* OpF)(int *, double *, char **, std::vector< int > &)
Op function pointer arguments are (int* currOpData,double* currD,char** c,std::stack<int>& callStacku...
std::vector< std::pair< OpF, int > > ops
void eval(VarBlock *varBlock, bool debug=false)
Evaluate program.
int addOperand(int param)
! Adds an operand. Note this should be done after doing the addOp!
int allocPtr()
Allocate a pointer location (can be anything, but typically space for char*)
int nextPC()
Return the position that the next instruction will be placed at.
void endOp(bool execute=true)
std::vector< int > opData
Ooperands to op.
std::vector< double > d
Double data (constants and evaluated)
std::vector< char * > s
constant and evaluated pointer data
void print(int pc=-1) const
Debug by printing program.
std::vector< int > callStack
Internally implemented var ref used by SeExpr.
A thread local evaluation context. Just allocate and fill in with data.
std::vector< double > d
copy of Interpreter's double data
int indirectIndex
indirect index to add to pointer based data
char ** data()
Raw data of the data block pointer (used by compiler)
bool threadSafe
if true, interpreter's data will be copied to this instance before evaluation.
std::vector< char * > s
copy of Interpreter's str data
you may not use this file except in compliance with the License and the following modification to it
const ExprStrNode * isString(const ExprNode *testee)
void copyVarToPromotedPosition(Interpreter *interpreter, ExprLocalVar *varSource, ExprLocalVar *varDest)
static Interpreter::OpF getTemplatizedOp2(int i)
Return the function f encapsulated in class T for the dynamic i converted to a static d....
< br > pow($a, 0.5)+ $b< br >< br ></div > External variables can also be overridden by local assignment.  
Defined as a *alpha b *alpha< br ></div >< br > float< b > float a
with numParticles numAttributes A variable block contains variable names and types but doesn t care what the values are< pre > void f(const std::string &s, MyParticleData *p, int outputDim=3)