/* * #%~ * VDM Code Generator Runtime * %% * Copyright (C) 2008 - 2014 Overture * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #~% */ package org.overture.codegen.runtime; public class Utils { public static final Object VOID_VALUE = new Object(); public static boolean isVoidValue(Object value) { return value == VOID_VALUE; } public static boolean empty(Object col) { if (col instanceof VDMSet) { return ((VDMSet) col).isEmpty(); } else if (col instanceof VDMSeq) { return ((VDMSeq) col).isEmpty(); } else if (col instanceof VDMMap) { return ((VDMMap) col).isEmpty(); } else { throw new IllegalArgumentException("Expected collection to be either a VDM set, map or sequence. Got: " + col); } } public static int hashCode(Object... fields) { if (fields == null) { throw new IllegalArgumentException("Fields cannot be null"); } int hashcode = 0; for (int i = 0; i < fields.length; i++) { Object currentField = fields[i]; hashcode += currentField != null ? currentField.hashCode() : 0; } return hashcode; } public static Object get(Object col, Object index) { if (col instanceof VDMSeq) { VDMSeq seq = (VDMSeq) col; return seq.get(Utils.index(index)); } else if (col instanceof VDMMap) { return MapUtil.get((VDMMap) col, index); } else { throw new IllegalArgumentException("Only a map or a sequence can be read"); } } @SuppressWarnings("unchecked") public static void mapSeqUpdate(Object col, Object index, Object value) { if (col instanceof VDMSeq) { VDMSeq seq = (VDMSeq) col; seq.set(index(index), value); } else if (col instanceof VDMMap) { VDMMap map = (VDMMap) col; map.put(index, value); } else { throw new IllegalArgumentException("Only a map or a sequence can be updated"); } } public static int index(Object value) { if (!(value instanceof Number)) { throw new IllegalArgumentException("The value to be converted must be a java.lang.Number"); } Number numberValue = (Number) value; if (numberValue.longValue() < 1) { throw new IllegalArgumentException("VDM subscripts must be >= 1"); } return toInt(numberValue) - 1; } public static String formatFields(Object... fields) { if (fields == null) { throw new IllegalArgumentException("Fields cannot be null in formatFields"); } StringBuilder str = new StringBuilder(); if (fields.length > 0) { str.append(Utils.toString(fields[0])); for (int i = 1; i < fields.length; i++) { str.append(", " + Utils.toString(fields[i])); } } return "(" + str.toString() + ")"; } @SuppressWarnings("unchecked") public static <T> T copy(T t) { if (t instanceof ValueType) { return (T) ((ValueType) t).copy(); } else { return t; } } public static String toString(Object obj) { if (obj == null) { return "nil"; } else if (obj == VOID_VALUE) { return "()"; } else if (obj instanceof Number) { Number n = (Number) obj; if (n.doubleValue() % 1 == 0) { return Long.toString(n.longValue()); } else { return Double.toString(n.doubleValue()); } } else if (obj instanceof Character) { return "'" + obj + "'"; } else if (obj instanceof String) { return "\"" + obj.toString() + "\""; } return obj.toString(); } public static boolean equals(Object left, Object right) { if (left instanceof Long && right instanceof Long) { Long leftLong = (Long) left; Long rightLong = (Long) right; return leftLong.compareTo(rightLong) == 0; } if (left instanceof Integer && right instanceof Integer) { Integer leftInt = (Integer) left; Integer rightInt = (Integer) right; return leftInt.compareTo(rightInt) == 0; } if (left instanceof Number && right instanceof Number) { Double leftNumber = ((Number) left).doubleValue(); Double rightNumber = ((Number) right).doubleValue(); return leftNumber.compareTo(rightNumber) == 0; } return left != null ? left.equals(right) : right == null; } public static <T> T postCheck(T returnValue, boolean postResult, String name) { if (postResult) { return returnValue; } throw new RuntimeException("Postcondition failure: post_" + name); } public static boolean is_bool(Object value) { return value instanceof Boolean; } public static boolean is_nat(Object value) { return isIntWithinRange(value, 0); } public static boolean is_nat1(Object value) { return isIntWithinRange(value, 1); } public static boolean is_int(Object value) { Double doubleValue = getDoubleValue(value); return is_int(doubleValue); } public static boolean is_rat(Object value) { return value instanceof Number; } public static boolean is_real(Object value) { return value instanceof Number; } public static boolean is_char(Object value) { return value instanceof Character; } public static boolean is_token(Object value) { return value instanceof Token; } @SuppressWarnings("rawtypes") public static boolean is_Tuple(Object exp, Class... types) { return exp instanceof Tuple && ((Tuple) exp).compatible(types); } @SuppressWarnings("rawtypes") public static boolean is_(Object exp, Class type) { return exp != null && exp.getClass() == type; } public static double divide(Object left, Object right) { validateNumbers(left, right, "divide"); double leftDouble = ((Number) left).doubleValue(); double rightDouble = ((Number) right).doubleValue(); if (rightDouble == 0L) { throw new ArithmeticException("Division by zero is undefined"); } return leftDouble / rightDouble; } public static long div(Object left, Object right) { validateIntOperands(left, right); Number leftInt = (Number) left; Number rightInt = (Number) right; return computeDiv(leftInt.doubleValue(), rightInt.doubleValue()); } public static long mod(Object left, Object right) { validateIntOperands(left, right); double leftInt = ((Number) left).doubleValue(); double rightInt = ((Number) right).doubleValue(); return (long) (leftInt - rightInt * (long) Math.floor(leftInt / rightInt)); } public static long rem(Object left, Object right) { validateIntOperands(left, right); double leftInt = ((Number) left).doubleValue(); double rightInt = ((Number) right).doubleValue(); return (long) (leftInt - rightInt * computeDiv(leftInt, rightInt)); } public static double floor(Object arg) { validateNumber(arg, "floor"); Number number = (Number) arg; return Math.floor(number.doubleValue()); } public static double abs(Object arg) { validateNumber(arg, "abs"); Number number = (Number) arg; return Math.abs(number.doubleValue()); } public static double pow(Object a, Object b) { validateNumbers(a, b, "pow"); Number aNumber = (Number) a; Number bNumber = (Number) b; return Math.pow(aNumber.doubleValue(), bNumber.doubleValue()); } /* @ pure @ */ public static boolean report(String name, boolean res, Object... params) { if (res) { return true; } StringBuilder sb = new StringBuilder(); sb.append(name); sb.append('('); if (params.length > 1) { String del = ", "; String eq = " = "; sb.append(params[0]); sb.append(eq); sb.append(Utils.toString(params[1])); for (int i = 2; i < params.length; i = i + 2) { sb.append(del); sb.append(params[i]); sb.append(eq); sb.append(Utils.toString(params[i + 1])); } } sb.append(')'); sb.append(" is " + res); sb.append('\n'); System.out.println(sb.toString()); AssertionError error = new AssertionError(); error.printStackTrace(System.out); throw error; } static int toInt(Number value) { long valueLong = value.longValue(); if (valueLong < Integer.MIN_VALUE || valueLong > Integer.MAX_VALUE) { throw new IllegalArgumentException(valueLong + " Casting the long to an int will change its value"); } return (int) valueLong; } private static void validateIntOperands(Object left, Object right) { if (!(is_int(left) && is_int(right))) { throw new ArithmeticException("Operands must be integers. Got left " + left + " and right" + right); } if (((Number) right).longValue() == 0L) { throw new ArithmeticException("Division by zero is undefined"); } } private static long computeDiv(double lv, double rv) { if (lv / rv < 0) { return (long) -Math.floor(Math.abs(lv / rv)); } else { return (long) Math.floor(Math.abs(-lv / rv)); } } private static boolean is_int(Double doubleValue) { return doubleValue != null && doubleValue == Math.floor(doubleValue) && !Double.isInfinite(doubleValue); } private static boolean isIntWithinRange(Object value, int lowerLimit) { Double doubleValue = getDoubleValue(value); if (!is_int(doubleValue)) { return false; } return doubleValue >= lowerLimit; } private static Double getDoubleValue(Object value) { if (!(value instanceof Number)) { return null; } Double doubleValue = ((Number) value).doubleValue(); return doubleValue; } static void validateNumbers(Object left, Object right, String operator) { if (!(left instanceof Number) || !(right instanceof Number)) { throw new IllegalArgumentException(operator + " is only supported for numbers. Got " + left + " and " + right); } } private static void validateNumber(Object arg, String operator) { if (!(arg instanceof Number)) { throw new IllegalArgumentException(operator + " is only supported for numbers. Got " + arg); } } }