RPN CALCULATOR Written by N.R. Wagner, 2006-02-10 Small Description: 485-490 Longer description in comments for initial paper tape: 857-967 Underlying design comes from the course: CS 6693: User interfaces and usability, taught at UTSA by Kay Robbins, Spring 2006 http://vip.cs.utsa.edu/classes/cs6693s2006/index.html The design is under "Lectures", "Week 2", or without the frames: http://vip.cs.utsa.edu/classes/cs6693s2006/lectures/cs6693week2.html which includes code for a simple calculator to use a a framework: http://vip.cs.utsa.edu/classes/cs6693s2006/lectures/materials/multiplyBy2New.zip The simple calculator is also at: http://www.cs.utsa.edu/~wagner/calculator/kays2012/multiplyBy2New.zip Source for classes listed below is at: http://www.cs.utsa.edu/~wagner/calculator/newcalc/final/ and the listing below is at: http://www.cs.utsa.edu/~wagner/calculator/newcalc/final/calcsource.txt CONTENTS Class Name Start End Calc 1 20 Controller 22 72 ModelEvent 74 101 ModelListener 103 107 ModelState 109 185 View 187 481 Model 483 798 PaperTape 800 1025 Eval 1027 1216 Gamma 1218 1278 1 // Calc: a Java calculator 2 // Written by N.R. Wagner, 2006-02-10 3 public class Calc { 4 public static void main(String[] args) { 5 Model model = new Model(); // new model 6 int fields = 8; 7 if (args.length != 0) { 8 try { 9 fields = Integer.parseInt(args[0]); 10 } 11 catch (NumberFormatException e) { 12 fields = 8; 13 } 14 } 15 View view = new View(model, fields, 0); // new view can access model 16 17 // View view2 = new View(model, 4, 620); // and a second view 18 PaperTape tape = new PaperTape(model); 19 } 20 } 21 ============================================================= 22 // Controller: handle events 23 import java.awt.event.*; 24 25 public class Controller implements ModelListener { 26 private Model model; 27 private View view; 28 29 public Controller(Model model, View view) { 30 this.model = model; 31 this.view = view; 32 model.addModelListener(this); 33 } 34 35 public ActionListener createOpInputAction(int opNum) { 36 final int opN = opNum; 37 return new ActionListener() { 38 public void actionPerformed(ActionEvent e) { 39 String s = view.field[8].getText(); 40 // new stuff in here ... 41 // process input arithmetic expression 42 char[] t = {'+','-','*','/','^','(',')','{','}','[',']'}; 43 // check if s holds non-trivial arith expr 44 boolean isArith = false; 45 for (int i = 0; i < t.length; i++) 46 if (s.indexOf(t[i]) != -1) isArith = true; 47 if (isArith) { 48 String sOld = s; 49 Eval eval = new Eval(s); 50 s = eval.P(); 51 view.field[8].setText(sOld); 52 view.setArith(sOld); 53 } 54 model.op(opN, s); 55 //view.repaint(); 56 } 57 }; 58 } 59 60 public ActionListener createOpAction(int opNum) { 61 final int opN = opNum; 62 return new ActionListener() { 63 public void actionPerformed(ActionEvent e) { 64 model.op(opN); 65 //view.repaint(); 66 } 67 }; 68 } 69 70 public void update(ModelEvent e) { 71 } 72 } 73 ========================================================== 74 import java.util.*; 75 76 public class ModelEvent extends EventObject { 77 private ModelState state; 78 79 public ModelEvent(Object source, ModelState state) { 80 super(source); 81 this.state = state; 82 } 83 84 public ModelState getModelState() { 85 return state; 86 } 87 } 88 import java.util.*; 89 90 public class ModelEvent extends EventObject { 91 private ModelState state; 92 93 public ModelEvent(Object source, ModelState state) { 94 super(source); 95 this.state = state; 96 } 97 98 public ModelState getModelState() { 99 return state; 100 } 101 } 102 ================================================== 103 import java.util.*; 104 105 public interface ModelListener extends EventListener { 106 void update(ModelEvent e); 107 } 108 ================================================== 109 public class ModelState { 110 111 public ModelState(int opNum, boolean inverse, boolean degrees, 112 boolean hyper, int args) { 113 this.opNum = opNum; 114 this.inverse = inverse; 115 this.degrees = degrees; 116 this.hyper = hyper; 117 this.args = args; 118 errorCode = -1; 119 // values = new double[8]; // up to top 8 elts on stack 120 } 121 122 // parameters coming in with last call to Model: 123 private int opNum; // operator #, -1 to 39 124 private boolean inverse; // inverse? 125 private boolean degrees; // degrees? 126 private boolean hyper; // hyperbolic? 127 private int args; // number of arguments (0, 1, or 2) 128 private double value; // value from Input field to be pushed 129 private String valueString; // erroneous value in Input field 130 131 // initial values of args used on stack (0, 1, or 2) 132 private double arg1; 133 private double arg2; 134 135 // resulting value, if any 136 private double x; 137 138 // code for errors (-1 means no error) 139 private int errorCode; 140 141 // up to top 8 elts on the stack 142 private double[] values; 143 144 // stacksize at the end (after operation) 145 private int stackSize; 146 147 // value popped, if any, destined for Input field 148 private double valuePopped; 149 150 // accessors 151 public int getOpNum() { return opNum; } 152 public boolean getInverse() { return inverse; } 153 public boolean getDegrees() { return degrees; } 154 public boolean getHyper() { return hyper; } 155 public int getArgs() { return args; } 156 public double getValue() { return value; } 157 public String getValueString() { return valueString; } 158 public int getErrorCode() { return errorCode; } 159 public double getArg1() { return arg1; } 160 public double getArg2() { return arg2; } 161 public double getX() { return x; } 162 public int getStackSize() { return stackSize; } 163 public double getValuePopped() { return valuePopped; } 164 public double[] getValues() { return values; } 165 166 // put methods 167 public void putErrorCode(int errorCode) 168 { this.errorCode = errorCode; } 169 public void putArg1(double arg1) 170 { this.arg1 = arg1; } 171 public void putArg2(double arg2) 172 { this.arg2 = arg2; } 173 public void putX(double x) 174 { this.x = x; } 175 public void putValue(double value) 176 { this.value = value; } 177 public void putValueString(String valueString) 178 { this.valueString = valueString; } 179 public void putValues(double[] values) 180 { this.values = values; } 181 public void putStackSize(int stackSize) 182 { this.stackSize = stackSize; } 183 public void putValuePopped(double valuePopped) 184 { this.valuePopped = valuePopped; } 185 } 186 ================================================== 187 // View: create GUI components, do layout 188 import java.awt.*; 189 import javax.swing.*; 190 191 public class View extends JFrame implements ModelListener { 192 private final int BUTTON_NUM = 40; 193 private final int INPUT_FIELD = 8; 194 private final int ERROR_FIELD = 9; 195 private final int FIELD_NUM = 10; 196 private int fields; // number of fields to use in view: 1-8 197 private int xStart = 0; 198 private Controller control; // control component 199 private Model model; // ref to model passed in 200 private String[] errorMsg = { 201 "Empty row", 202 "Bad format in Input", 203 "Too few args on stack", 204 "Result too large", 205 "Result undefined"}; 206 private boolean inverse; // for the inverse operators 207 private boolean degrees; // radians or degrees 208 private boolean hyper; // norm or hyperbolic 209 private boolean edit; // are fields editable? 210 211 private String[] bname; // types of buttons 212 public JTextField[] field; // all fields 213 private JLabel[] label; // all labels 214 private JButton[] b; // all buttons 215 private Icon[][] bgif; // 3 or 6 gifs for each button 216 // layout stuff. 217 private GridBagLayout gbLayout; // for layout 218 private GridBagConstraints gbConstraints; // for layout 219 private Container container; // start with a Container 220 221 public View(Model model, int fields, int xStart) { 222 this.model = model; // ref to model passed in 223 this.fields = fields; 224 this.xStart = xStart; 225 if (fields < 1 || fields > 8) this.fields = 8; 226 control = new Controller(model, this); // pass ref to model 227 model.addModelListener(this); 228 int xdist = 320, ydist = 36 + 179 + 24*(fields+2); 229 // 36 is for top and bottom border (both) 230 // 143 is for lower keys, 24 for each field 231 setSize(xdist, ydist); 232 setLocation(xStart, 0); 233 Dimension d = new Dimension(xdist, ydist); 234 setMinimumSize(d); // not working! Why? 235 setTitle("RPN Calculator"); 236 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // close window 237 initialize(); 238 setVisible(true); 239 } 240 241 private void initialize() { 242 inverse = false; 243 degrees = false; 244 hyper = false; 245 bname = new String[] { // types of buttons 246 "pop","pop","pop","pop","pop","pop","pop","pop","clr","all", 247 "add","sub","mul","div","dup","exc","sqr","cub","exp","pie", 248 "rot","tog","ln2","lne","lnt","phg","flr","fra","gam","one", 249 "xpp","t10","inv","rnd","sin","cos","tan","rad","hyp","edt"}; 250 String mess[] = { // text for JLabels 251 "Top","Top-1","Top-2","Top-3","Top-4", 252 "Top-5","Top-6","Top-7","Input","Error"}; 253 Insets border = new Insets(0, 0, 0, 0); // to get rid of button border 254 255 Font boldFont = new Font("Arial Bold", Font.BOLD, 12); 256 field = new JTextField[FIELD_NUM]; // allocate textfields 257 for (int i = 0; i < FIELD_NUM; i++) { // create all 9 JTextFields 258 field[i] = new JTextField("empty", (i != 8 ? 16 : 16)); 259 field[i].setEditable( (i != 8 ? false : true) ); // only # 8 editable 260 field[i].setFont(boldFont); 261 } 262 field[INPUT_FIELD].addActionListener(control.createOpInputAction(-1)); // link listener for Input 263 field[INPUT_FIELD].setForeground(new Color(0, 0, 200)); // dark blue 264 field[INPUT_FIELD].setText(integerString(0.0)); // initialize Input field 265 field[INPUT_FIELD].setBackground(new Color(210, 210, 255)); // light blue, for Input field 266 field[ERROR_FIELD].setText("Stack Size: 0"); // error field 267 field[ERROR_FIELD].setBackground(Color.LIGHT_GRAY); // error field 268 269 label = new JLabel[FIELD_NUM]; // allocate labels 270 for (int i = 0; i < FIELD_NUM; i++) { // create all 9 labels 271 label[i] = new JLabel(mess[i], JLabel.CENTER); 272 label[i].setForeground(Color.GRAY); 273 label[i].setIconTextGap(0); 274 } 275 label[ERROR_FIELD].setForeground(Color.BLACK); // error label 276 label[INPUT_FIELD].setForeground(new Color(150, 150, 255)); // light blue 277 278 b = new JButton[BUTTON_NUM]; // allocate buttons 279 bgif = new Icon[BUTTON_NUM][6]; // allocate Icons 280 for (int i = 0; i < BUTTON_NUM; i++) { 281 int lim = (i <= 15 ? 3 : 6); // 6 gifs for many buttons 282 for (int j = 0; j < lim; j++) 283 bgif[i][j] = new ImageIcon("gifs/" + bname[i] + "." + j + ".gif"); 284 b[i] = new JButton(bgif[i][0]); 285 b[i].setRolloverIcon(bgif[i][1]); 286 b[i].setPressedIcon(bgif[i][2]); 287 b[i].setMargin(border); 288 b[i].addActionListener(control.createOpAction(i)); 289 } 290 291 // layout here 292 container = getContentPane(); 293 gbLayout = new GridBagLayout(); 294 container.setLayout( gbLayout ); 295 container.setBackground(Color.black); 296 // instantiate gridbag constraints 297 gbConstraints = new GridBagConstraints(); 298 // define GUI initial component constraints 299 gbConstraints.weightx = 0; 300 gbConstraints.weighty = 0; 301 gbConstraints.gridwidth = 1; 302 for (int i = 0; i < FIELD_NUM; i++) { // 8 fields for stack + input, error 303 if (i <= fields - 1 || i > 7) { // number laid out = fields 304 gbConstraints.fill = GridBagConstraints.NONE; 305 gbConstraints.gridwidth = 4; 306 addComponent(field[i]); 307 gbConstraints.gridwidth = 1; 308 addComponent(label[i]); 309 gbConstraints.gridwidth = GridBagConstraints.REMAINDER; 310 addComponent(b[i]); 311 } 312 } 313 314 // layout rest of the buttons 315 for (int i = FIELD_NUM; i < BUTTON_NUM; i += 6) { // 4 rows of 6 buttons each 316 gbConstraints.fill = GridBagConstraints.NONE; 317 gbConstraints.gridwidth = 1; 318 addComponent(b[i]); 319 addComponent(b[i+1]); 320 addComponent(b[i+2]); 321 addComponent(b[i+3]); 322 addComponent(b[i+4]); 323 gbConstraints.gridwidth = GridBagConstraints.REMAINDER; 324 addComponent(b[i+5]); 325 } 326 } 327 328 private void addComponent(Component c) { 329 gbLayout.setConstraints(c, gbConstraints); 330 container.add(c); // add component 331 } 332 333 // everything below is used during the update step 334 335 public void setForError() { // used in case of error 336 field[ERROR_FIELD].setBackground(Color.PINK); 337 label[ERROR_FIELD].setText("Error"); 338 label[ERROR_FIELD].setForeground(Color.RED); 339 } 340 341 public void setForInput() { // used in case of error 342 field[ERROR_FIELD].setBackground(new Color(200, 255, 255)); 343 label[ERROR_FIELD].setText("Arith"); 344 label[ERROR_FIELD].setForeground(Color.CYAN); 345 } 346 347 public void resetForError() { // reset after error 348 field[ERROR_FIELD].setBackground(Color.LIGHT_GRAY); 349 label[ERROR_FIELD].setForeground(Color.BLACK); 350 } 351 352 public void setGifNorm(int i) { // reset button to norm 353 b[i].setIcon(bgif[i][0]); 354 b[i].setRolloverIcon(bgif[i][1]); 355 b[i].setPressedIcon(bgif[i][2]); 356 } 357 358 public void setGifAlt(int i) { // set button to alt form 359 b[i].setIcon(bgif[i][3]); 360 b[i].setRolloverIcon(bgif[i][4]); 361 b[i].setPressedIcon(bgif[i][5]); 362 } 363 364 public void gifNorm() { // reset all buttons to norm 365 for (int i = 16; i < 37; i++) 366 setGifNorm(i); 367 } 368 369 public void gifAlt() { // set all buttons to alt form 370 for (int i = 16; i < 37; i++) 371 setGifAlt(i); 372 } 373 374 public void update(ModelEvent e) { 375 resetForError(); 376 ModelState state = e.getModelState(); 377 // show stack in labels 378 int stackSize = state.getStackSize(); 379 for (int i = 0; i < INPUT_FIELD; i++) { // create all 9 labels 380 if (i < stackSize) label[i].setForeground(Color.WHITE); 381 else label[i].setForeground(Color.GRAY); 382 } 383 384 // handle errors 385 if (state.getErrorCode() != -1) { // error of some sort 386 if (state.getOpNum() == -1) // here input was in error; restore it 387 field[8].setText(state.getValueString()); 388 field[9].setText(errorMsg[state.getErrorCode()]); // error 389 setForError(); 390 // return; 391 } 392 // special cases 393 else if (state.getOpNum() == -1) { // input 394 field[8].setText(integerString(state.getValue())); 395 // return; 396 } 397 else if (state.getOpNum() == 8) { // clr 398 field[8].setText(integerString(0.0)); 399 // return; 400 } 401 else if (state.getOpNum() == 9) { // c a (clear all) 402 field[8].setText(integerString(0.0)); 403 // return; 404 } 405 else if (state.getOpNum() == 21) { // main toggle key 406 if (!inverse) gifAlt(); 407 else gifNorm(); 408 inverse = !inverse; 409 // return; 410 } 411 else if (state.getOpNum() == 37) { // rad/deg key 412 if (!degrees) setGifAlt(37); 413 else setGifNorm(37); 414 degrees = !degrees; 415 // return; 416 } 417 else if (state.getOpNum() == 38) { // norm/hyper key 418 if (!hyper) setGifAlt(38); 419 else setGifNorm(38); 420 hyper = !hyper; 421 // return; 422 } 423 else if (state.getOpNum() == 39) { // edit key 424 if (!edit) setGifAlt(39); 425 else setGifNorm(39); 426 edit = !edit; 427 // return; 428 } 429 else if (state.getOpNum() >= 0 && state.getOpNum() < 8) { // pop into Input 430 field[8].setText(integerString(state.getValuePopped())); 431 } 432 433 // transfer values from state to view 434 valuesToFields(state.getValues(), state.getStackSize()); 435 if (state.getErrorCode() == -1) { 436 if (arithExpr) { 437 arithExpr = false; 438 field[9].setText(sArith); 439 setForInput(); 440 } 441 else field[9].setText("Stack Size: " + state.getStackSize()); 442 } 443 } 444 445 // the two variables and function below are patched in 446 // to display the input one time in case it is an 447 // arithmetic expression (see also code imm above) 448 private boolean arithExpr = false; 449 private String sArith; 450 public void setArith(String sOld) { 451 // note that an arithmetic expression was entered 452 // delay putting in the stack size 453 arithExpr = true; 454 sArith = sOld; 455 } 456 457 private String integerString(double d) { 458 String s = Double.toString(d); 459 if (d >= 0) s = " " + s; 460 if (Math.abs(d) < 1.0e15) { 461 long id = (long)d; 462 if ((double)id == d) { // and integer in range 463 s = Long.toString(id); 464 if (d >= 0) s = " " + s; 465 } 466 } 467 return s; 468 } 469 470 private void valuesToFields(double[] values, int stackSize) { 471 int n; 472 int lim = stackSize - 1; 473 for (n = 0; n <= lim && n <= 7; n++) { 474 field[n].setText(integerString(values[n])); 475 } 476 if (n <= 7) { 477 for (int m = n; m <= 7; m++) 478 field[m].setText("empty"); 479 } 480 } 481 } 482 ================================================== 483 import java.util.*; 484 import javax.swing.*; 485 // Model: stack-based calculator engine 486 // Inputs: Commands, with parameters, including especially 487 // an array of 9 doubles for returning the state of the top 8 elts 488 // Outputs: The array above, an error code (-1 = no error), and 489 // a separate method returning the stack size. 490 // The stack is hidden inside this class. 491 492 public class Model { 493 private Vector st; // the stack 494 private Random rand; // just for normal random numbers 495 private Gamma g; // just for Gamma function and factorial 496 private ArrayList observers; 497 private boolean inverse; 498 private boolean degrees; 499 private boolean hyper; 500 501 private int[] ops = { // # of operands for each button 502 0,0,0,0,0,0,0,0,0,0, 503 2,2,2,2,1,2,1,1,2,0, 504 0,0,1,1,1,0,1,1,1,0, 505 1,1,1,0,1,1,1,0,0,0}; 506 public Model() { 507 st = new Vector(); 508 rand = new Random(); 509 g = new Gamma(); 510 observers = new ArrayList (); 511 inverse = false; 512 degrees = false; 513 hyper = false; 514 } 515 516 public void addModelListener(ModelListener ob) { 517 observers.add(ob); 518 } 519 520 public void notifyListeners(ModelEvent e) { 521 for (ModelListener ob : observers) 522 ob.update(e); 523 } 524 525 public void op(int opNum, String s) { 526 ModelState state = new ModelState(opNum, false, false, false, 0); 527 if (opNum == -1) { // faked opNum for main Input 528 double value = 0.0; 529 try { 530 value = Double.parseDouble(s); // only use of param s here 531 } 532 catch (NumberFormatException exception) { 533 state.putErrorCode(1); 534 state.putValueString(s); 535 stackToValues(state); 536 notifyListeners(new ModelEvent(this, state)); 537 return; 538 } 539 st.add(value); // push input (only use of last param in op 540 state.putValue(value); 541 stackToValues(state); // transfer stack to values array in state 542 } 543 notifyListeners(new ModelEvent(this, state)); 544 } 545 546 public void op(int opNum) { 547 if (opNum == 21) inverse = !inverse; // norm/ inv key 548 if (opNum == 37) degrees = !degrees; // deg / rad key 549 if (opNum == 38) hyper = !hyper; // normal / hyperbolic key 550 ModelState state = new ModelState(opNum, inverse, degrees, hyper, ops[opNum]); 551 double arg1 = 0, arg2 = 0, x = 0; // keep compiler happy 552 553 if (st.size() >= ops[opNum]) { // fetch args off stack, don't delete 554 if (ops[opNum] > 0) { 555 arg1 = st.get(st.size() - 1); 556 state.putArg1(arg1); 557 } 558 if (ops[opNum] == 2) { // in case there are 2 args 559 arg2 = arg1; 560 state.putArg2(arg1); 561 arg1 = st.get(st.size() - 2); 562 state.putArg1(arg1); 563 } 564 } 565 else { 566 state.putErrorCode(2); // error: Too few args on stack 567 stackToValues(state); 568 notifyListeners(new ModelEvent(this, state)); 569 return; 570 } 571 572 if (opNum < 8) { // "pop" location opNum on stack 573 // important: here opNum != -1 574 if (opNum <= st.size() - 1) { // remove location 575 state.putValuePopped(st.get(st.size() - opNum - 1)); 576 st.removeElementAt(st.size() - opNum - 1); 577 stackToValues(state); 578 notifyListeners(new ModelEvent(this, state)); 579 return; 580 } 581 else { 582 state.putErrorCode(0); 583 stackToValues(state); 584 notifyListeners(new ModelEvent(this, state)); 585 return; 586 } 587 } 588 else if (opNum == 8) { 589 stackToValues(state); 590 notifyListeners(new ModelEvent(this, state)); 591 return; // "clr" to clear Input 592 } 593 else if (opNum == 9) { // "all", clear everything 594 st.clear(); 595 stackToValues(state); 596 notifyListeners(new ModelEvent(this, state)); 597 return; 598 } 599 600 // now long list of opNums 601 // each of these will be followed by "finishUp(x, args)" 602 // to pop the args and push the result x' 603 // and then by "stackToValues(values)", to transfer to values 604 if (opNum == 10) x = arg1 + arg2; // "add" 605 else if (opNum == 11) x = arg1 - arg2; // "sub" 606 else if (opNum == 12) x = arg1 * arg2; // "mul" 607 else if (opNum == 13) x = arg1 / arg2; // "div" 608 else if (opNum == 14) { // "dup", duplicate stacktop 609 st.add(arg1); 610 stackToValues(state); 611 notifyListeners(new ModelEvent(this, state)); 612 return; 613 } 614 else if (opNum == 15) { // "exc", exchange top 2 stack elts 615 st.remove(st.size() -1); st.remove(st.size() -1); 616 st.add(arg2); st.add(arg1); 617 stackToValues(state); 618 notifyListeners(new ModelEvent(this, state)); 619 return; 620 } 621 else if (opNum == 16) { // "sqr" 622 if (!inverse) x = arg1*arg1; 623 else x = Math.sqrt(arg1); 624 } 625 else if (opNum == 17) { // "cub" 626 if (!inverse) x = arg1*arg1*arg1; 627 else x = Math.pow(arg1, 1.0/3.0); 628 } 629 else if (opNum == 18) { // "exp" 630 if (!inverse) x = Math.pow(arg1, arg2); 631 else x = Math.log(arg2)/Math.log(arg1); 632 } 633 else if (opNum == 19) { // "pie" 634 if (!inverse) x = Math.PI; 635 else x = Math.E; 636 } 637 else if (opNum == 20) { // "rot" 638 if (!inverse) { // rotate + 639 if (st.size() > 1) { 640 double r = st.elementAt(st.size() - 1); 641 st.add(0, r); 642 st.remove(st.size() - 1); 643 } 644 stackToValues(state); 645 notifyListeners(new ModelEvent(this, state)); 646 return; 647 } 648 else { // rotate - 649 if (st.size() > 1) { 650 double r = st.elementAt(0); 651 st.remove(0); 652 st.add(r); 653 } 654 stackToValues(state); 655 notifyListeners(new ModelEvent(this, state)); 656 return; 657 } 658 } 659 else if (opNum == 21) { 660 stackToValues(state); 661 notifyListeners(new ModelEvent(this, state)); 662 return; // main norm/inv key, handled at beginning 663 } 664 else if (opNum == 22) { // "ln2" 665 if (!inverse) x = Math.pow(2.0, arg1); 666 else x = Math.log(arg1)/Math.log(2.0); 667 } 668 else if (opNum == 23) { // "lne" 669 if (!inverse) x = Math.exp(arg1); 670 else x = Math.log(arg1); 671 } 672 else if (opNum == 24) { // "lnt" 673 if (!inverse) x = Math.pow(10.0, arg1); 674 else x = Math.log10(arg1); 675 } 676 else if (opNum == 25) { // "phg" 677 if (!inverse) x = 1.6180339887498948; // golden ratio 678 else x = 0.57721566490153286; // Euler's constant 679 } 680 else if (opNum == 26) { // "flr" 681 if (!inverse) x = Math.floor(arg1); 682 else x = Math.ceil(arg1); 683 } 684 else if (opNum == 27) { // "fra" 685 if (!inverse) x = arg1 - Math.floor(arg1); 686 else x = Math.round(arg1); 687 } 688 else if (opNum == 28) { // "gam" 689 if (!inverse) x = g.gamma(arg1); 690 else x = g.gamma(arg1); 691 } 692 else if (opNum == 29) { // "one" 693 if (!inverse) x = 0.0; 694 else x = 1.0; 695 } 696 else if (opNum == 30) { // "xpp" 697 if (!inverse) x = arg1+1.0; 698 else x = arg1-1.0; 699 } 700 else if (opNum == 31) { // "t10" 701 if (!inverse) x = arg1*10.0; 702 else x = arg1/10.0; 703 } 704 else if (opNum == 32) { // "inv" 705 if (!inverse) x = -arg1; 706 else x = 1.0/arg1; 707 } 708 else if (opNum == 33) { // "rnd" 709 if (!inverse) x = Math.random(); // uniform dist on (0,1) 710 else x = rand.nextGaussian(); // normal dist, mean 0, STD 1 711 } 712 else if (opNum == 34) { // "sin" 713 if (!inverse) { 714 if (hyper) x = Math.sinh(arg1); 715 else { 716 if (!degrees) x = Math.sin(arg1); 717 else x = Math.sin(arg1*Math.PI/180.0); 718 } 719 } 720 else { 721 if (hyper) x = Math.log(arg1 + Math.sqrt(arg1*arg1 + 1)); 722 else { 723 if (!degrees) x = Math.asin(arg1); 724 else x = Math.asin(arg1)*180.0/Math.PI; 725 } 726 } 727 } 728 else if (opNum == 35) { // "cos" 729 if (!inverse) { 730 if (hyper) x = Math.cosh(arg1); 731 else { 732 if (!degrees) x = Math.cos(arg1); 733 else x = Math.cos(arg1*Math.PI/180.0); 734 } 735 } 736 else { 737 if (hyper) x = Math.log(arg1 + Math.sqrt((arg1 - 1)*(arg1 + 1))); 738 else { 739 if (!degrees) x = Math.acos(arg1); 740 else x = Math.acos(arg1)*180.0/Math.PI; 741 } 742 } 743 } 744 else if (opNum == 36) { // "tan" 745 if (!inverse) { 746 if (hyper) x = Math.sinh(arg1)/Math.cosh(arg1); 747 else { 748 if (!degrees) x = Math.tan(arg1); 749 else x = Math.tan(arg1*Math.PI/180.0); 750 } 751 } 752 else { 753 if (hyper) x = 0.5*(Math.log(1 + arg1) - Math.log(1 - arg1)); 754 else { 755 if (!degrees) x = Math.atan(arg1); 756 else x = Math.atan(arg1)*180.0/Math.PI; 757 } 758 } 759 } 760 else if (opNum == 37 || opNum == 38 || opNum == 39) { 761 stackToValues(state); 762 notifyListeners(new ModelEvent(this, state)); 763 return; // rad/deg, norm/hyper, no edit/edit keys 764 } 765 state.putErrorCode(finishUp(x, ops[opNum])); 766 stackToValues(state); 767 state.putX(x); 768 notifyListeners(new ModelEvent(this, state)); 769 } 770 771 private int finishUp(double x, int args) { 772 if (Double.isInfinite(x)) // check for bad result 773 return 3; // error 774 if (Double.isNaN(x)) 775 return 4; // error 776 for (int j = 0; j < args; j++) // pop proper # of args 777 st.removeElementAt(st.size() - 1); 778 st.add(x); // push result 779 return -1; // no error 780 } 781 782 // stackToValues: transfer top portion of stack to values array 783 private void stackToValues(ModelState state) { 784 int k = st.size() - 1; 785 int n; 786 double[] values = new double[8]; 787 for (n = 0; n <= st.size() - 1 && n <= 7; n++) { 788 values[n] = st.get(k); 789 k--; 790 } 791 if (n <= 7) { // not really needed 792 for (int m = n; m <= 7; m++) 793 values[m] = 0.0; 794 } 795 state.putValues(values); 796 state.putStackSize(st.size()); 797 } 798 } 799 ================================================== 800 import java.awt.*; 801 import javax.swing.*; 802 803 public class PaperTape extends JFrame implements ModelListener { 804 private Controller control; 805 private Model model; 806 private JTextArea text; 807 private boolean firstTime = true; 808 private Font boldFont = new Font("Arial Bold", Font.BOLD, 12); 809 private String[] opNames = { 810 "pop top","pop top-1","pop top-2","pop top-3","pop top-4", 811 "pop top-5","pop top-6","pop top-7","clear input","clear all", 812 "add","subtract","multiply","divide","duplicate top", 813 "exchange top two","square", "cube","x^y","push pi", 814 "rotate stack +","normal op","2^x","e^x","10^x", 815 "push golden ratio","floor","fractional part","gamma(x+1)","push 0", 816 "x++","x*10","-x","push uniform random","sin", 817 "cos","tan","radians","not hyperbolic","not implemented"}; 818 private String[] invNames = { 819 "","","","","","","","","","","","","","","", 820 "","square root", "cube root","log y (base x)","push e", 821 "rotate stack -","alternative or inverse op","log x (base 2)", 822 "log x (base e)","log x (base 10)", 823 "push euler's Constant","ceil","round","gamma(x)","push 1", 824 "x--","x/10","1/x","push normal random","arcsin", 825 "arccos","arctan","degrees","make hyperbolic","not implemented"}; 826 private String[] errorMsg = { 827 "Empty row", 828 "Bad format in Input", 829 "Too few args on stack", 830 "Result too large", 831 "Result undefined"}; 832 public PaperTape(Model model) { 833 this.model = model; 834 835 model.addModelListener(this); 836 setLocation(320, 0); 837 setSize(320, 400); 838 setTitle("Paper Tape for RPN Calculator"); 839 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 840 initialize(); 841 setVisible(true); 842 text.setFont(boldFont); 843 text.setForeground(new Color(0, 0, 100)); // dark blue 844 } 845 846 public void update(ModelEvent e) { 847 ModelState state = e.getModelState(); 848 boolean inverse = state.getInverse(); 849 boolean degrees = state.getDegrees(); 850 boolean hyper = state.getHyper(); 851 int opNum = state.getOpNum(); 852 int errorCode = state.getErrorCode(); 853 //text.setFont(boldFont); 854 //text.setForeground(new Color(0, 0, 100)); 855 if (firstTime) { 856 text.append("*******************************************\n"); 857 text.append("* RPN CALCULATOR\n"); 858 text.append("* DIRECTIONS FOR USE\n"); 859 text.append("* All calculations use doubles.\n"); 860 text.append("* Operators have 0, 1, or 2 operands.\n"); 861 text.append("* Operands are taken from the stacktop.\n"); 862 text.append("* Hit yellow \"norm\" key to alter meanings\n"); 863 text.append("* of yellow keys to blue \"Inv\" keys.\n"); 864 text.append("* Input field (in blue next to bottom):\n"); 865 text.append("* Edit in an int or a double, and\n"); 866 text.append("* hit return to push the value (double).\n"); 867 text.append("* Bottom field gives stack size (gray),\n"); 868 text.append("* or error message (pink),\n"); 869 text.append("* or recent arith expression (cyan).\n"); 870 text.append("* Other ways to push a value:\n"); 871 text.append("* Use constant keys: pi, phi, e, gamma.\n"); 872 text.append("* Use \"0.0\" or \"1.0\" to push these values.\n"); 873 text.append("* Use \"U(0,1)\" to push uniform random #.\n"); 874 text.append("* Use \"N(0,1)\" to push normal random #.\n"); 875 text.append("* Use \"pop\" to pop value into input.\n"); 876 text.append("* Stack manipulation functions:\n"); 877 text.append("* \"dup\" pushes the value of stacktop.\n"); 878 text.append("* \"exch\" exchanges top two stack elts.\n"); 879 text.append("* \"rot+\" rotates, moving top to bottom.\n"); 880 text.append("* \"rot-\" rotates, moving bottom to top.\n"); 881 text.append("* \"pop\" pops the adjacent value into input.\n"); 882 text.append("* Arithmetic operators:\n"); 883 text.append("* \"+\", \"-\", \"*\", \"/\" (2 operands from stack).\n"); 884 text.append("* \"floor\", \"ceil\", \"fract\", \"round\" (1 operand).\n"); 885 text.append("* f(x): apply f to the stacktop.\n"); 886 text.append("* f(x,y): apply f to top two numbers.\n"); 887 text.append("* Trigonometric and hyperbolic functions:\n"); 888 text.append("* \"sin\", \"cos\", \"tan\" (1 operand from stack).\n"); 889 text.append("* \"norm\" changes to hyperbolic function.\n"); 890 text.append("* \"hyper\" changes to trig function.\n"); 891 text.append("* \"Inv\" changes to inverse trig function.\n"); 892 text.append("* (or inverse hyperbolic function).\n"); 893 text.append("* \"rad\" changes to degrees (trig only).\n"); 894 text.append("* \"deg\" changes to radians (trig only).\n"); 895 text.append("* Other keys:\n"); 896 text.append("* yellow \"norm\" changes to blue \"Inv\" keys.\n"); 897 text.append("* blue \"Inv\" changes to yellow \"norm\" keys.\n"); 898 text.append("* \"clr\" clears \"Input\" field to 0.\n"); 899 text.append("* \"ca\" also clears stack to empty.\n"); 900 text.append("* \"no edit\", \"edit\" not implemented.\n"); 901 text.append("* Exact integers with abs_val < 10e15 are\n"); 902 text.append("* displayed without decimal point.\n"); 903 text.append("* Special Features of \"Input\" field:\n"); 904 text.append("* Allows arbitrary infix arithmetic\n"); 905 text.append("* expressions involving:\n"); 906 text.append("* constant ints or doubles\n"); 907 text.append("* binary operators: + - * / ^\n"); 908 text.append("* nested parens: ( ) { } [ ]\n"); 909 text.append("* unary operators: + -\n"); 910 text.append("* Grammar for input:\n"); 911 text.append("* P ---> E\n"); 912 text.append("* E ---> T { ( '+' | '-' ) T }\n"); 913 text.append("* T ---> S { ( '*' | '/' ) S }\n"); 914 text.append("* S ---> F '^' S | F\n"); 915 text.append("* F ---> D | ( '+' | '-' ) F | '( ' E ' ) ' |\n"); 916 text.append("* '{ ' E ' } ' | '[ ' E '] '\n"); 917 text.append("* D ---> [ d { d } ] [ '.' d { d } ]\n"); 918 text.append("* [ ( 'e ' | 'E ' ) [ ( '+' | '-' ) ] d { d } ]\n"); 919 text.append("* d ---> '0 '-'9 '\n"); 920 text.append("* \n"); 921 text.append("* Sample input giving pi (7 digits):\n"); 922 text.append(" 3+1/(7+1/16)\n"); 923 text.append("* Sample input giving pi (9 digits):\n"); 924 text.append(" (9^2 + 19^2/22)^.25\n"); 925 text.append("* Sample input giving pi (10 digits):\n"); 926 text.append(" 1/(5/2^4 + 2^3*47/2^16 +\n"); 927 text.append(" 6^3*89/2^28 +\n"); 928 text.append(" 20^3*131/2^40 +\n"); 929 text.append(" 70^3*173/2^52 +\n"); 930 text.append(" 252^3*215/2^64)\n"); 931 text.append("* Sample input giving pi (10 digits):\n"); 932 text.append(" 3+1/(7+1/(15+1/(1+1/292)))\n"); 933 text.append("* Sample input giving pi (15 digits):\n"); 934 text.append(" 9801/2/2^0.5/(1103+24*27493/396^4)\n"); 935 text.append("* Sample input giving pi (15 digits):\n"); 936 text.append(" 3^4*11^2/2^1.5/\n"); 937 text.append(" (1103+1447*19/2^5/3^7/11^4)\n"); 938 text.append("* Sample input giving pi (16 digits):\n"); 939 text.append(" 3+1/(7+1/(15+1/(1+1/(292+1/(1+1/(1+\n"); 940 text.append(" 1/(1+1/(2+1/(1+1/(3+1/(1+1/14)))))))))))\n"); 941 text.append("* Sample input giving pi (16 digits):\n"); 942 text.append(" 4/( 1+ 1^2/( 3+ 2^2/( 5+ 3^2/\n"); 943 text.append(" ( 7+ 4^2/( 9+ 5^2/(11+ 6^2/\n"); 944 text.append(" (13+ 7^2/(15+ 8^2/(17+ 9^2/\n"); 945 text.append(" (19+10^2/(21+11^2/(23+12^2/\n"); 946 text.append(" (25+13^2/(27+14^2/(29+15^2/\n"); 947 text.append(" (31+16^2/(33+17^2/(35+18^2/\n"); 948 text.append(" (37+19^2/(39+20^2/(41+21^2/\n"); 949 text.append(" (43+22^2/ 45))))))))))))))))))))))\n"); 950 text.append("* Sample input giving sqrt(2) (16 digits):\n"); 951 text.append(" 1+1/(2+1/(2+1/(2+1/(2+1/(2+1/(2+\n"); 952 text.append(" 1/(2+1/(2+1/(2+1/(2+1/(2+1/(2+\n"); 953 text.append(" 1/(2+1/(2+1/(2+1/(2+1/(2+1/(2+\n"); 954 text.append(" 1/(2+1/2)))))))))))))))))))\n"); 955 text.append("* Sample input giving e (16 digits):\n"); 956 text.append(" 2+1/(1+1/(2 +1/(1+1/(1+1/(4 +1/(1+\n"); 957 text.append(" 1/(1+1/(6 +1/(1+1/(1+1/(8 +1/(1+\n"); 958 text.append(" 1/(1+1/(10+1/(1+1/(1+1/(12+1/(1+\n"); 959 text.append(" 1/(1+1/(14+1))))))))))))))))))))\n"); 960 text.append("* Sample input giving phi (16 digits):\n"); 961 text.append(" 1+1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+\n"); 962 text.append(" 1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+\n"); 963 text.append(" 1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+\n"); 964 text.append(" 1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+\n"); 965 text.append(" 1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+\n"); 966 text.append(" 1/(1+1/(1+1/(1+1/(1+1/(1+1\n"); 967 text.append(" )))))))))))))))))))))))))))))))))))\n"); 968 text.append("*******************************************\n\n"); 969 text.append("Paper tape of calculations follows\n"); 970 text.append(" ------------------->\n\n"); 971 firstTime = false; 972 } 973 if (opNum == -1) { // push from Input 974 text.append("Op: " + "Push from Input" + "\n"); 975 text.append(" Value Pushed: " + state.getValue() + "\n"); 976 } 977 else if (opNum < 8 && errorCode == -1) { // pop with no error 978 text.append("Op: " + opNames[opNum] + "\n"); 979 text.append(" Value Popped: " + state.getValuePopped() + "\n"); 980 } 981 else { 982 if (opNum == 37) { 983 if (degrees) 984 text.append("Op: " + invNames[opNum] + "\n"); 985 else 986 text.append("Op: " + opNames[opNum] + "\n"); 987 return; 988 } 989 if (opNum == 38) { 990 if (hyper) 991 text.append("Op: " + invNames[opNum] + "\n"); 992 else 993 text.append("Op: " + opNames[opNum] + "\n"); 994 return; 995 } 996 997 text.append("Op: "); 998 if (hyper && (opNum == 34 || opNum == 35 || opNum == 36)) 999 text.append("hyperbolic "); 1000 if (opNum > 15 && inverse) 1001 text.append(invNames[opNum] + "\n"); 1002 else 1003 text.append(opNames[opNum] + "\n"); 1004 1005 if (errorCode != -1) text.append(" ERROR: " + errorMsg[errorCode] + 1006 "\n"); 1007 else { 1008 int args = state.getArgs(); 1009 if (args > 0) 1010 text.append(" Arg1: " + state.getArg1() + "\n"); 1011 if (args > 1) 1012 text.append(" Arg2: " + state.getArg2() + "\n"); 1013 if (opNum != 8 && opNum != 9 && opNum != 14 && opNum != 15 && 1014 opNum != 20 && opNum != 21 && opNum < 37) 1015 text.append(" Result pushed: " + state.getX() + "\n"); 1016 } 1017 } 1018 } 1019 1020 private void initialize() { 1021 text = new JTextArea(); 1022 JScrollPane pane = new JScrollPane(text); 1023 add(pane, BorderLayout.CENTER); 1024 } 1025 } 1026 ================================================== 1027 /* Eval.java: evaluate and return double 1028 * grammar: 1029 * P ---> E '#' 1030 * E ---> T {('+'|'-') T} 1031 * T ---> S {('*'|'/') S} 1032 * S ---> F '^' S | F 1033 * F ---> D | ('+'|'-') F | '(' E ')' | 1034 * '(' E ')' | '[' E ']' 1035 * D ---> [d { d }] [ '.' d { d }] [ ('e'|'E') ('+'|'-') d { d }] 1036 * d ---> '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' 1037 */ 1038 1039 class Eval { 1040 private char next; 1041 private String s; 1042 private int sloc = 0; // location in s, used in scan 1043 1044 public Eval(String s) { 1045 this.s = s; 1046 this.s += '#'; 1047 } 1048 1049 public String P() { 1050 double res; 1051 scan(); 1052 if (next == '#') return "0.0"; 1053 res = E(); 1054 if (next != '#') res = error(1); 1055 return res+""; 1056 } 1057 1058 private double E() { 1059 double res, arg1, arg2; 1060 char save; 1061 res = T(); 1062 while (next == '+' || next == '-') { 1063 save = next; 1064 arg1 = res; 1065 scan(); 1066 arg2 = T(); 1067 res = evaluate(arg1, save, arg2); 1068 } 1069 return res; 1070 } 1071 1072 private double T() { 1073 double res, arg1, arg2; 1074 char save; 1075 res = S(); 1076 while (next == '*' || next == '/') { 1077 save = next; 1078 arg1 = res; 1079 scan(); 1080 arg2 = S(); 1081 res = evaluate(arg1, save, arg2); 1082 } 1083 return res; 1084 } 1085 1086 private double S() { 1087 double res, arg1, arg2; 1088 char save; 1089 res = F(); 1090 if (next == '^') { 1091 save = next; 1092 arg1 = res; 1093 scan(); 1094 arg2 = S(); 1095 res = evaluate(arg1, save, arg2); 1096 } 1097 return res; 1098 } 1099 1100 private double F() { 1101 double res = 0, res2 = 0, mul; 1102 char save; 1103 boolean digitPresent = false; 1104 // check for double or int constant 1105 if (Character.isDigit(next) || next == '.') { 1106 // initial digit sequence (at least one) 1107 if (Character.isDigit(next)) { 1108 digitPresent = true; 1109 res = (double)(next - '0'); 1110 scan(); 1111 while (Character.isDigit(next)) { 1112 res = res*10.0 + (double)(next - '0'); 1113 scan(); 1114 } 1115 } 1116 // after optional '.', digit sequence (at least one) 1117 if (next == '.') { 1118 scan(); 1119 mul = 10.0; 1120 if (Character.isDigit(next)) { 1121 res2 = (double)(next - '0')/mul; 1122 scan(); 1123 while (Character.isDigit(next)) { 1124 mul = mul * 10.0; 1125 res2 = res2 + (double)(next - '0')/mul; 1126 scan(); 1127 } 1128 res = res + res2; 1129 } 1130 else if (!digitPresent) error(4); 1131 } 1132 if (next == 'e' || next == 'E') { 1133 int expon = 0, sign = 1; 1134 scan(); 1135 if (next == '-' || next == '+') { 1136 if (next == '-') sign = -1; 1137 scan(); 1138 } 1139 if (Character.isDigit(next)) { 1140 expon = (next - '0'); 1141 scan(); 1142 while (Character.isDigit(next)) { 1143 expon = expon*10 + (next - '0'); 1144 scan(); 1145 } 1146 } 1147 else res = error(6); 1148 // add e part to res value 1149 for (int i = 1; i <= expon; i++) { 1150 if (sign == 1) res = res*10.0; 1151 else res = res/10.0; 1152 } 1153 } 1154 } 1155 else if (next == '(') { 1156 scan(); 1157 res = E(); 1158 if (next == ')') scan(); 1159 else res = error(2); 1160 } 1161 else if (next == '{') { 1162 scan(); 1163 res = E(); 1164 if (next == '}') scan(); 1165 else res = error(2); 1166 } 1167 else if (next == '[') { 1168 scan(); 1169 res = E(); 1170 if (next == ']') scan(); 1171 else res = error(2); 1172 } 1173 else if (next == '-' || next == '+') { 1174 save = next; 1175 scan(); 1176 if (save == '-') 1177 res = -F(); 1178 else res = F(); 1179 } 1180 else { 1181 res = error(3); 1182 } 1183 return res; 1184 } 1185 1186 private double evaluate(double arg1, char op, double arg2) { 1187 if (Double.isNaN(arg1) || Double.isNaN(arg2)) 1188 return Double.NaN; // guess this isn't needed 1189 switch (op) { 1190 case '+': return arg1 + arg2; 1191 case '-': return arg1 - arg2; 1192 case '*': return arg1 * arg2; 1193 case '/': return arg1 / arg2; 1194 case '^': return Math.pow(arg1, arg2); 1195 default: return error(7); 1196 } 1197 } 1198 1199 private void scan() { 1200 while (Character.isWhitespace(next = s.charAt(sloc++))) 1201 ; 1202 } 1203 1204 private double error(int n) { 1205 System.out.println("*** ERROR: " + n); 1206 return Double.NaN; 1207 } 1208 1209 public static void main(String[] args) { 1210 String arith = args[0]; 1211 System.out.println("args[0]: \"" + arith + "\""); 1212 Eval eval = new Eval(arith); 1213 String res = eval.P(); 1214 System.out.println("Result: " + res); 1215 } 1216 } 1217 ================================================== 1218 // The techniques in this class were adapted from the webpage: 1219 // http://www.rskey.org/gamma.htm 1220 // by Viktor T. Toth 1221 // Gives nearly 15 signficant digits over the entire range of x > 0. 1222 public class Gamma { 1223 1224 double[] p; 1225 1226 public Gamma() { 1227 p = new double[7]; 1228 p[0] = 1.000000000190015; 1229 p[1] = 76.18009172947146; 1230 p[2] = -86.50532032941677; 1231 p[3] = 24.01409824083091; 1232 p[4] = -1.231739572450155; 1233 p[5] = 1.208650973866179e-3; 1234 p[6] = -5.395239384953e-6; 1235 } 1236 1237 private double g1(double x) { // Lanczos Appox: good for small x 1238 double sum = p[0]; 1239 for (int i = 1; i <= 6; i++) 1240 sum += p[i]/(x + i); 1241 sum = sum*(Math.sqrt(2.0*Math.PI)/x) * 1242 Math.pow(x + 5.5, x + 0.5) * Math.exp(-x - 5.5); 1243 return sum; 1244 } 1245 1246 private double g2(double x) { // Stirling's Formula: good for large x 1247 double sum = x * Math.log(x) - x + Math.log(Math.sqrt(2.0*Math.PI/x)); 1248 double xpow = x; 1249 sum += 1.0/(12.0*xpow); 1250 xpow *= x*x; 1251 sum -= 1.0/(360.0*xpow); 1252 xpow *= x*x; 1253 sum += 1.0/(1260.0*xpow); 1254 xpow *= x*x; 1255 sum -= 1.0/(1680.0*xpow); 1256 xpow *= x*x; 1257 sum += 1.0/(1188.0*xpow); 1258 return Math.exp(sum); 1259 } 1260 1261 private double g3(double x) { // Cheat with Stirling, make input larger 1262 return g2(x+4)/(x+3)/(x+2)/(x+1)/(x); 1263 } 1264 1265 public double gamma(double x) { 1266 x++; // x! = gamma(x+1) 1267 int z = (int)x; 1268 if ((double)z == x) { 1269 double prod = 1.0; 1270 for (int i = 2; i <= z-1; i++) 1271 prod *= i; 1272 return prod; 1273 } 1274 if (x <= 6.0) return g1(x); 1275 if (x <= 10) return g3(x); 1276 return g2(x); 1277 } 1278 }