/* * Copyright (C) 2014 peiwang * * 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/>. */ package nars.language; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import nars.storage.Memory; import nars.io.Symbols; /** * This stores the magnitude of a time difference, which is the logarithm of the time difference * in base D=duration ( @see Param.java ). The actual printed value is +1 more than the stored * magnitude, so for example, it will have name() "+1" if magnitude=0, and "+2" if magnitude=1. * * @author peiwang / SeH */ public class Interval extends Term { public static class AtomicDuration extends AtomicInteger { /** this represents the amount of time in proportion to a duration in which * Interval resolution calculates. originally, NARS had a hardcoded duration of 5 * and an equivalent Interval scaling factor of ~1/2 (since ln(E) ~= 1/2 * 5). * Since duration is now adjustable, this factor approximates the same result * with regard to a "normalized" interval scale determined by duration. */ double linear = 0.5; transient double log; //caches the value here transient int lastValue = -1; public AtomicDuration() { super(); } public AtomicDuration(int v) { super(v); } public void setLinear(double linear) { this.linear = linear; update(); } protected void update() { int val = get(); lastValue = val; this.log = Math.log(val * linear ); } public double getSubDurationLog() { int val = get(); if (lastValue != val) { update(); } return log; } } static final int INTERVAL_POOL_SIZE = 16; static Interval[] INTERVAL = new Interval[INTERVAL_POOL_SIZE]; public static Interval interval(final String i) { return interval( Integer.parseInt(i.substring(1)) - 1); } public static Interval interval(final long time, final Memory memory) { return interval( timeToMagnitude( time, memory.param.duration ) ); } public static Interval interval(final long time, final AtomicDuration duration) { return interval( timeToMagnitude( time, duration ) ); } public static Interval interval(int magnitude) { if (magnitude >= INTERVAL_POOL_SIZE) return new Interval(magnitude, true); else if (magnitude < 0) magnitude = 0; Interval existing = INTERVAL[magnitude]; if (existing == null) { existing = new Interval(magnitude, true); INTERVAL[magnitude] = existing; } return existing; } public final int magnitude; // time is a positive integer protected Interval(final long timeDiff, final AtomicDuration duration) { this(timeToMagnitude(timeDiff, duration), true); } /** this constructor has an extra unused argument to differentiate it from the other one, * for specifying magnitude directly. */ protected Interval(final int magnitude, final boolean yesMagnitude) { super(); this.magnitude = magnitude; setName(Symbols.INTERVAL_PREFIX + String.valueOf(1+magnitude)); } // protected Interval(final String s) { // magnitude = Integer.parseInt(s.substring(1)); // setName(s); // } public static int timeToMagnitude(final long timeDiff, final AtomicDuration duration) { int m = (int) Math.round(Math.log(timeDiff) / duration.getSubDurationLog()); if (m < 0) return 0; return m; } public static double magnitudeToTime(final double magnitude, final AtomicDuration duration) { if (magnitude <= 0) return 1; return Math.exp(magnitude * duration.getSubDurationLog()); } public static long magnitudeToTime(final int magnitude, final AtomicDuration duration) { return (long)Math.round(magnitudeToTime((double)magnitude, duration)); } /** Calculates the average of the -0.5, +0.5 interval surrounding the integer magnitude */ public static long magnitudeToTimeHalfRadius(final int magnitude, final AtomicDuration duration) { //TODO cache this result because it will be equal for all similar integer magnitudes double magMin = magnitude - 0.5; double magMax = magnitude + 0.5; return (long)Math.round((magnitudeToTime(magMin,duration) + magnitudeToTime(magMax, duration))/2.0); } @Deprecated public static long magnitudeToTime(int magnitude) { return (long) Math.ceil(Math.exp(magnitude)); } public final long getTime(final AtomicDuration duration) { //TODO use a lookup table for this return magnitudeToTime(magnitude, duration); } public final long getTime(final Memory memory) { return getTime(memory.param.duration); } @Override public Interval clone() { //can return this as its own clone since it's immutable. //originally: return new Interval(magnitude, true); return this; } /** returns a sequence of intervals which approximate a time period with a maximum number of consecutive Interval terms */ public static List<Interval> intervalTimeSequence(final long t, final int maxTerms, final Memory memory) { if (maxTerms == 1) return Lists.newArrayList(interval(t, memory)); long a; //current approximation value Interval first; first = interval(t, memory); a = first.getTime(memory); if (a == t) return Lists.newArrayList(first); else if (a < t) { //ok we will add to it. nothing to do here } else if ((a > t) && (first.magnitude > 0)) { //use next lower magnitude first = interval(first.magnitude - 1); a = first.getTime(memory); } List c = new ArrayList(maxTerms); c.add(first); long remaining = t - a; c.addAll( intervalTimeSequence(remaining, maxTerms-1, memory)); /* Interval approx = Interval.intervalTime(t, memory); System.out.println(t + " = " + c + "; ~= " + approx + " (t=" + t + ", seq=" + intervalSequenceTime(c, memory) + ", one=" + approx.getTime(memory) + ")"); */ return c; } /** sum the time period contained in the Intervals (if any) in a sequence of objects (usually list of Terms) */ public static long intervalSequenceTime(final Iterable s, final Memory memory) { long time = 0; for (final Object t : s) { if (t instanceof Interval) { Interval i = (Interval)t; time += i.getTime(memory); } } return time; } }