// Copyright (c) 2005-2007, Mihai Preda. // Licensed under the MIT License. import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.rms.*; import javax.microedition.io.*; import javax.wireless.messaging.*; import java.util.*; import java.io.*; public class M extends MIDlet implements CommandListener, Runnable { static UnitTest dummy; private Display display; DateCanvas dateCanvas = null; final static int HLP_COL1 = 0, HLP_COL2 = 1, HLP_ALGO = 2, HLP_ABOUT = 3, HLP_SIZE = 4; Form hlpForms[] = null; StringBuffer str = new StringBuffer(); static final int CMD_MARK=5, CMD_CANCEL=6, CMD_PERIOD_LIST=7, CMD_STATS=8, CMD_EXPLAIN=9, CMD_BACKUP=10, CMD_TRANSFER=11, CMD_HELP=12, CMD_EXIT=13, CMD_BACK=14, CMD_OK=15, CMD_MORE=16, CMD_COLOR_LIGHT = 20, CMD_COLOR_DARK = 21, CMD_START_MONDAY = 22, CMD_START_SUNDAY = 23, CMD_PERIODLEN_4 = 25, CMD_PERIODLEN_5 = 26, CMD_PERIODLEN_6 = 27, CMD_LUTEAL_13 = 30, CMD_LUTEAL_14 = 31, CMD_LUTEAL_15 = 32, CMD_GOAL_BOTH = 35, CMD_GOAL_FERTILE = 36, CMD_GOAL_INFERTILE = 37, CMD_PROFILE = 38, CMD_SMS_SEND = 39, CMD_SMS_RECEIVE = 40, CMD_SMS_SEND2 = 41, CMD_SMS_CANCEL=42, CMD_PROFILE_START = 100, CMD_PERIOD_START = 200; static private final String _(int id) { return L.s[id]; } Cmd cmdMore = new Cmd(12, CMD_MORE), cmdOk = new Cmd(11, CMD_OK, Command.BACK), cmdSmsSend = new Cmd(72, CMD_SMS_SEND2, Command.OK), cmdSmsCancel = new Cmd(73, CMD_SMS_CANCEL, Command.CANCEL), cmdCancel = new Cmd(73, CMD_CANCEL, Command.CANCEL); public M() { Menu.setBackText(L.s[9]); dateCanvas = new DateCanvas(this); String days = L.s[44]; Menu menu = new Menu("Menu", new Cmd[] { new Cmd(_(4), CMD_MARK), new Menu(_(8), new Cmd[] { new Menu(_(14), new Cmd[] { new Cmd(_(48), CMD_COLOR_LIGHT), new Cmd(_(49), CMD_COLOR_DARK) }, dateCanvas.colorSchemeNb), new Menu(_(15), new Cmd[] { new Cmd(_(50), CMD_START_MONDAY), new Cmd(_(51), CMD_START_SUNDAY), }, dateCanvas.mondayFirst?0:1), new Menu(_(16), new Cmd[] { new Cmd("4 " + days, CMD_PERIODLEN_4), new Cmd("5 " + days, CMD_PERIODLEN_5), new Cmd("6 " + days, CMD_PERIODLEN_6), }, dateCanvas.periodLen-4), new Menu(_(17), new Cmd[] { new Cmd("13 " + days, CMD_LUTEAL_13), new Cmd("14 " + days, CMD_LUTEAL_14), new Cmd("15 " + days, CMD_LUTEAL_15), }, dateCanvas.lutealLen-13), new Menu(_(18), new Cmd[] { new Cmd(_(54), CMD_GOAL_BOTH), new Cmd(_(52), CMD_GOAL_FERTILE), new Cmd(_(53), CMD_GOAL_INFERTILE), }, dateCanvas.goal), new Cmd(_(19), CMD_PROFILE), }), new Cmd(_(6), CMD_PERIOD_LIST), new Cmd(_(7), CMD_STATS), new Cmd(_(13), CMD_EXPLAIN), new Cmd(_(70), CMD_BACKUP), new Menu(_(71), new Cmd[] { new Cmd(_(82), CMD_SMS_SEND), new Cmd(_(83), CMD_SMS_RECEIVE), }), new Cmd(_(5), CMD_HELP, Command.HELP), new Cmd(_(10), CMD_EXIT, Command.EXIT) }); menu.addCommandsTo(dateCanvas); menu.setParent(this, dateCanvas); try { rs = RecordStore.openRecordStore("menstral", true); readHead(); readProfile(); dateCanvas.switchLanguage(); lastProfile = rs.getNextRecordID() -2; } catch (RecordStoreException e) { } dateCanvas.goToday(); dateCanvas.fullRedraw(); } void doMark() { //display.vibrate(40); dateCanvas.mark(); AlertType.CONFIRMATION.playSound(display); } Form formHelpColors1 = null; void addHelpColor(Form f, int c1, int c2, int c3, String label) { int s = (2 * dateCanvas.sx) / 3; Image img = Image.createImage(3*s + 4, s + 2); Graphics g = img.getGraphics(); g.setColor(0xff0000); g.fillRect(0, 0, 3*s + 4, s + 2); g.setColor(dateCanvas.colorScheme[DateCanvas.BG]); g.fillRect(0, 0, 3*s + 4, s + 2); g.setColor(c1); g.fillRect(1, 1, s, s); g.setColor(c2); g.fillRect(s + 2, 1, s, s); g.setColor(c3); g.fillRect(2*s + 3, 1, s, s); g.setColor(0x0000ff); g.drawString("", 3, 3, 0); f.append(new ImageItem(label, Image.createImage(img), /*ImageItem.LAYOUT_LEFT*/ ImageItem.LAYOUT_NEWLINE_AFTER, null)); //f.append(label); } void addCmd(Displayable d, Command cmd) { d.addCommand(cmd); d.setCommandListener(this); } void showHelp(int idx) { if (hlpForms == null) { hlpForms = new Form[HLP_SIZE]; } if (hlpForms[idx] == null) { Form f = new Form(L.s[(idx == 0) ? 14 : 19 + idx]); hlpForms[idx] = f; addCmd(f, cmdOk); if (idx < HLP_SIZE - 1) { f.addCommand(cmdMore); } switch (idx) { case HLP_COL1: addHelpColor(f, dateCanvas.m1, dateCanvas.mixedColor(dateCanvas.m1, dateCanvas.m2, 1, 2), dateCanvas.m2, L.s[23]); addHelpColor(f, dateCanvas.f1, dateCanvas.mixedColor(dateCanvas.f1, dateCanvas.f2, 1, 2), dateCanvas.f2, L.s[24]); addHelpColor(f, dateCanvas.n1, dateCanvas.mixedColor(dateCanvas.n1, dateCanvas.n2, 1, 2), dateCanvas.n2, L.s[25]); //addHelpColor(f, DateCanvas.M4, DateCanvas.F4, DateCanvas.N4, "Uncertainty\n"); break; case HLP_COL2: int sx = dateCanvas.sx; int sy = dateCanvas.sy; Image img = Image.createImage(3*sx + 1,sy + 1); Graphics g = img.getGraphics(); g.setColor(dateCanvas.colorScheme[DateCanvas.BG]); g.fillRect(0, 0, 3*sx + 1, sy + 1); g.setFont(dateCanvas.regular); dateCanvas.drawOneCell(g, dateCanvas.colorScheme[DateCanvas.N1], 1, 1, 0, new char[] {'1', '2'}); dateCanvas.drawOneCell(g, -1, sx + 1, 1, 0, new char[] {'1', '3'}); dateCanvas.drawOneCell(g, dateCanvas.colorScheme[DateCanvas.M2], 2*sx + 1, 1, 0, new char[] {'1', '4'}); //it seems some timing is needed as workaround for Sony-Ericsson bug g.setColor(0x0000ff); g.drawString("", 3, 3, 0); g.setColor(0x00ff00); g.drawString("", 0, 0, 0); f.append(Image.createImage(img)); f.append("\n" + L.s[26] + '\n'); break; case HLP_ALGO: f.append(L.s[27]); break; case HLP_ABOUT: try { f.append(Image.createImage("/M.gif")); } catch (IOException e) { try { f.append(Image.createImage("M.gif")); } catch (IOException e2) { } } f.append("Menstral " + getAppProperty("MIDlet-Version") + "\n"); f.append("\u00a9 Mihai Preda\n" + getAppProperty("MIDlet-Info-URL") + "\n" + L.s[28]); f.append(L.s[3]); break; } } display.setCurrent(hlpForms[idx]); } void doHelp(Displayable d) { int pos = (hlpForms != null) ? hlpForms.length - 1 : -1; while (pos >= 0 && d != hlpForms[pos]) { --pos; } showHelp(pos + 1); } static String interval(int v1, int v2) { if (v1 == v2) { return ": " + v1 + ". "; } else { return ": " + v1 + "-" + v2 + ". "; } } void doExplain() { Form explain = new Form(L.s[29]); addCmd(explain, cmdOk); Region r = dateCanvas.getCurrentRegion(); if (r.noInfo) { explain.append(L.s[30]); } else { if (r.exact) { explain.append(L.s[31] + ": " + r.len + ". "); } else { if (r.theSize > 0) { explain.append(L.s[32] + ": " + r.theSize + ", " + L.s[33] + interval(r.min1 + r.unsure, r.max1 - r.unsure)); } explain.append(L.s[34] + ": " + r.len + ". " + L.s[35] + interval(r.min2, r.max2) + L.s[36] + interval(r.min1, r.max1)); } explain.append(L.s[37] + interval(dateCanvas.getOvl(r.min2), dateCanvas.getOvl(r.max2))); //explain.append("Infertile up to " + infert1 + " and from " + infert2 + "."); } display.setCurrent(explain); } void strAddVal(int v) { if (v < 10) { str.append('0'); } str.append(v).append('/'); } static final private String SMS_HEADER = "Menstral backup v.1"; String serializeDates() { str.setLength(0); str.append(SMS_HEADER); int curYear = 0; int curMonth = 0; int year, month, day; for (int i = 1; i < theSize - 1; ++i) { toYMD(dates[i], ymd); year = ymd[0]; month = ymd[1] + 1; day = ymd[2]; if (year != curYear) { str.append('\n').append(year).append(':'); curYear = year; curMonth = 1; } str.append(' ').append(day); if (month != curMonth) { str.append('/').append(month); curMonth = month; } ++curMonth; } return str.toString(); } String smsDestNumber = null; int smsDestPort = 0; void sendSMS(String destNumber, int port) { Alert alert = new Alert(L.s[74], L.s[74] + "...", null, AlertType.INFO); //alert.setIndicator(new Gauge(null, false, Gauge.INDEFINITE, Gauge.CONTINOUS_RUNNING)); display.setCurrent(alert); smsDestNumber = destNumber; smsDestPort = port; doSend = true; new Thread(this).start(); } boolean doSend; //String receivedSms; MessageConnection smsConnection; static final int PORT_NUMBER = 38697; public void run() { if (doSend) { try { String smsDestination = "sms://" + (smsDestPort > 0 ? smsDestNumber + ':' + smsDestPort : smsDestNumber); smsConnection = (MessageConnection) Connector.open(smsDestination); Message msg = smsConnection.newMessage(smsDestPort == 0 ? MessageConnection.TEXT_MESSAGE : MessageConnection.BINARY_MESSAGE); if (smsDestPort == 0) { ((TextMessage) msg).setPayloadText(serializeDates()); } else { ((BinaryMessage) msg).setPayloadData(getRawProfile()); } smsConnection.send(msg); smsConnection.close(); display.setCurrent(new Alert(L.s[75], L.s[75], null, AlertType.CONFIRMATION), dateCanvas); } catch (Exception e) { display.setCurrent(new Alert(L.s[76], ""+e, null, AlertType.ERROR), dateCanvas); //System.out.println("sms " + e); } } else { //receive try { smsConnection = (MessageConnection) Connector.open("sms://:" + PORT_NUMBER); byte raw[] = ((BinaryMessage) smsConnection.receive()).getPayloadData(); if (smsConnection != null) { smsConnection.close(); } setProfile(lastProfile + 1); readProfile(raw); writeProfile(); display.setCurrent(new Alert(L.s[77], L.s[78] + " #" + activeProfile, null, AlertType.CONFIRMATION), dateCanvas); } catch (Exception e) { //receivedSms = null; display.setCurrent(new Alert(L.s[79], ""+e, null, AlertType.ERROR), dateCanvas); } } smsConnection = null; } void doSMSBackup() { doTransferVsBackup = false; Form form = new Form(L.s[70]); form.append(L.s[80]); smsNumberField = new TextField(L.s[81], null, 13, TextField.PHONENUMBER); form.append(smsNumberField); form.addCommand(cmdSmsSend); form.addCommand(cmdCancel); form.setCommandListener(this); display.setCurrent(form); } TextField smsNumberField; boolean doTransferVsBackup; void doSMSTransferSend() { doTransferVsBackup = true; Form form = new Form(L.s[82]); form.append(L.s[84]); smsNumberField = new TextField(L.s[81], null, 13, TextField.PHONENUMBER); form.append(smsNumberField); form.addCommand(cmdSmsSend); form.addCommand(cmdCancel); form.setCommandListener(this); display.setCurrent(form); } Alert alertReceive = null; void doSMSTransferReceive() { if (alertReceive == null) { alertReceive = new Alert(L.s[83], L.s[85], null, AlertType.INFO); //alert.setIndicator(new Gauge(null, false, Gauge.INDEFINITE, Gauge.CONTINOUS_RUNNING)); alertReceive.setTimeout(Alert.FOREVER); alertReceive.addCommand(cmdSmsCancel); alertReceive.setCommandListener(this); } display.setCurrent(alertReceive); doSend = false; new Thread(this).start(); } int ymd[] = new int[3]; Menu periodListMenu() { int year, month, day, len; int size = theSize - 2; Cmd cmds[] = new Cmd[size]; for (int i = 0; i < size; ++i) { toYMD(dates[i+1], ymd); year = ymd[0]; month = ymd[1] + 1; day = ymd[2]; str.setLength(0); strAddVal(day); strAddVal(month); strAddVal(year % 100); str.deleteCharAt(str.length() - 1); len = 200; if (i < theSize -3) { len = dates[i+2] - dates[i+1]; } if (len < 100) { str.append(" " + len); } else { str.append(" n/a"); } cmds[i] = new Cmd(str.toString(), CMD_PERIOD_START+i); } str.setLength(0); return new Menu(_(38), cmds, (size > 0) ? size - 1 : 0); } static void computeAvgMinMax(int size, int vals[], int outAvgMinMax[]) { int min = vals[0], max = vals[0], sum = vals[0]; int v; for (int i = 1; i < size; ++i) { v = vals[i]; sum += v; if (v < min) { min = v; } else if (v > max) { max = v; } } outAvgMinMax[0] = (sum + (size - 1)/2) / size; outAvgMinMax[1] = min; outAvgMinMax[2] = max; } int avgMinMax[] = new int[3]; void doStats() { Form form = new Form(L.s[7]); addCmd(form, cmdOk); int[] info = intervals1; int n = doBefore(info, theSize - 2); if (n >= 1) { computeAvgMinMax(n, info, avgMinMax); str.setLength(0); str.append('\n' + L.s[39] + ' ' + n + ' ' + L.s[40] + ": " + L.s[41] + ' ' + avgMinMax[0] + ", " + L.s[42] + ' ' + avgMinMax[1] + ", " + L.s[43] + ' ' + avgMinMax[2] + ' ' +L.s[44]); form.append(str.toString()); } else { form.append(L.s[45]); } str.setLength(0); display.setCurrent(form); } Menu profileMenu() { int size = lastProfile+1; Cmd cmds[] = new Cmd[size]; for (int i = 1; i <= lastProfile; ++i) { cmds[i-1] = new Cmd(i==activeProfile?"#"+i+" ("+L.s[46]+')':"#"+i, CMD_PROFILE_START+i-1); } cmds[size-1] = new Cmd("#"+(lastProfile+1)+" ("+L.s[47]+')', CMD_PROFILE_START+size-1); return new Menu(_(19), cmds, activeProfile - 1); } public void commandAction(Command c, Displayable d) { int id = ((Cmd)c).id; Log.log("Command " + id); display.setCurrent(dateCanvas); switch (id) { case CMD_MARK: doMark(); break; case CMD_HELP: //fall through case CMD_MORE: doHelp(d); break; case CMD_STATS: doStats(); break; case CMD_EXPLAIN: doExplain(); break; case CMD_EXIT: notifyDestroyed(); break; case CMD_COLOR_LIGHT: case CMD_COLOR_DARK: dateCanvas.setColorScheme(id - CMD_COLOR_LIGHT); writeProfile(); dateCanvas.fullRedraw(); break; case CMD_START_MONDAY: case CMD_START_SUNDAY: dateCanvas.mondayFirst = (id == CMD_START_MONDAY); dateCanvas.computeStartDayOfWeek(); writeHead(); dateCanvas.fullRedraw(); break; case CMD_PERIODLEN_4: case CMD_PERIODLEN_5: case CMD_PERIODLEN_6: dateCanvas.periodLen = (id - CMD_PERIODLEN_4) + 4; writeProfile(); dateCanvas.fullRedraw(); break; case CMD_LUTEAL_13: case CMD_LUTEAL_14: case CMD_LUTEAL_15: dateCanvas.lutealLen = (id - CMD_LUTEAL_13) + 13; writeProfile(); dateCanvas.fullRedraw(); break; case CMD_GOAL_BOTH: case CMD_GOAL_FERTILE: case CMD_GOAL_INFERTILE: dateCanvas.goal = id - CMD_GOAL_BOTH; writeProfile(); dateCanvas.fullRedraw(); break; case CMD_PROFILE: { Menu submenu = profileMenu(); submenu.setParent(this, d); display.setCurrent(submenu.list); break; } case CMD_PERIOD_LIST: { Menu submenu = periodListMenu(); submenu.setParent(this, d); display.setCurrent(submenu.list); break; } case CMD_SMS_SEND: doSMSTransferSend(); break; case CMD_SMS_RECEIVE: doSMSTransferReceive(); break; case CMD_SMS_SEND2: String destNumber = ((TextField) smsNumberField).getString(); sendSMS(destNumber, doTransferVsBackup ? PORT_NUMBER : 0); break; case CMD_BACKUP: doSMSBackup(); break; case CMD_SMS_CANCEL: try { smsConnection.close(); } catch (IOException e) { } } if (id >= CMD_PERIOD_START) { //todo } else if (id >= CMD_PROFILE_START) { setProfile(id - CMD_PROFILE_START + 1); dateCanvas.fullRedraw(); } } public void startApp() { display = Display.getDisplay(this); display.setCurrent(dateCanvas); } protected void pauseApp() { } protected void destroyApp(boolean unconditional) { //notifyDestroyed(); } //-------- STORE --- static final int MAX_SIZE = 255; short dates[] = new short [MAX_SIZE]; int theSize; Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); static final int DAYS = 24 * 3600 * 1000; int out[] = new int[4]; RecordStore rs = null; ByteArrayOutputStream bos = new ByteArrayOutputStream(20); int activeProfile = 0; int passwd = 0; int lastProfile = 0; static final void assrt(boolean b) { if (BuildOptions.LOG_ENABLED) { if (!b) { System.out.println("assertion failed"); throw new IllegalStateException(); } } } void setProfile(int profile) { activeProfile = profile; writeHead(); readProfile(); } void insureRecord(int recNo) throws RecordStoreException { if (rs.getNextRecordID() == recNo) { rs.addRecord(null, 0, 0); } } void readProfile(byte raw[]) { int settings = 0; if (raw == null) { settings = 0; passwd = 0; theSize = 2; dates[0] = 0; dates[1] = Short.MAX_VALUE; } else { DataInputStream in = new DataInputStream(new ByteArrayInputStream(raw)); try { settings = in.readShort(); passwd = in.readShort(); theSize = in.readShort(); //assrt(size <= MAX_SIZE); for (int i = 0; i < theSize; ++i) { dates[i] = in.readShort(); } } catch (IOException e) { } } //} catch (Exception e) { //} //assrt(size >= 2 && dates[0] == 0 && dates[size - 1] == Short.MAX_VALUE); if (activeProfile > lastProfile) { lastProfile = activeProfile; } dateCanvas.setColorScheme(settings & 0x000f); int perLen = (settings & 0x00f0) >> 4; int lutLen = (settings & 0x0f00) >> 8; dateCanvas.goal = (settings & 0x3000) >> 12; dateCanvas.periodLen = (perLen != 0) ? perLen : 5; dateCanvas.lutealLen = (lutLen != 0) ? lutLen + 4 : 14; //dateCanvas.goToday(); } void readProfile() { int recNo = activeProfile + 1; try { insureRecord(recNo); byte raw[] = rs.getRecord(recNo); readProfile(raw); } catch (RecordStoreException e) { } } byte[] getRawProfile() { bos.reset(); int settings = dateCanvas.colorSchemeNb + (dateCanvas.periodLen << 4) + ((dateCanvas.lutealLen - 4) << 8) + (dateCanvas.goal << 12); DataOutputStream out = new DataOutputStream(bos); try { out.writeShort(settings); out.writeShort(passwd); out.writeShort(theSize); for (int i = 0; i < theSize; ++i) { out.writeShort(dates[i]); } } catch (IOException e) { } return bos.toByteArray(); } void writeProfile() { try { byte data[] = getRawProfile(); rs.setRecord(activeProfile + 1, data, 0, data.length); } catch (Exception e) { //System.out.println(e); } } private void readHead() { byte b[] = null; try { insureRecord(1); b = rs.getRecord(1); } catch (RecordStoreException e) { throw new RuntimeException("RecordStoreException"); } if (b == null || b.length == 0) { //firstRun = true; activeProfile = 1; //langId = 0; dateCanvas.mondayFirst = true; } else { //assrt(b.length == 4 && b[0] == 1); activeProfile = b[1] & 0x0f; //langId = (b[2] >> 1); dateCanvas.mondayFirst = ((b[2] & 0x01) == 0); } } private void writeHead() { byte head[] = new byte[] {1, (byte)(activeProfile & 0x0f), (byte)((dateCanvas.mondayFirst ? 0 : 1) /*| (langId << 1)*/), 0}; try { rs.setRecord(1, head, 0, 4); } catch (RecordStoreException e) { } } int findPos(short date) { int pos = 0; while (pos < theSize && dates[pos] <= date) { ++pos; } //assrt(pos < size); --pos; return pos; } short getDate(int year, int month, int day) { cal.set(Calendar.YEAR, year); cal.set(Calendar.MONTH, month); cal.set(Calendar.DATE, day); return (short)(cal.getTime().getTime() / DAYS); } Date tmpdate = new Date(); void toYMD(short date, int ymd[]) { tmpdate.setTime((long)date * DAYS); cal.setTime(tmpdate); ymd[0] = cal.get(Calendar.YEAR); ymd[1] = cal.get(Calendar.MONTH); ymd[2] = cal.get(Calendar.DATE); } void toggle(short date) { int pos = findPos(date); if (dates[pos] == date) { remove(date); } else { add(date); } writeProfile(); } private void add(short date) { if (theSize == MAX_SIZE) { return; //full, can't add } int pos = findPos(date); if (dates[pos] != date) { ++pos; ++theSize; for (int i = theSize; i > pos; --i) { dates[i] = dates[i - 1]; } dates[pos] = date; } } private void remove(short date) { int pos = findPos(date); if (dates[pos] == date) { --theSize; for (int i = pos; i < theSize; ++i) { dates[i] = dates[i + 1]; } } } static final int MIN_INTERVAL = 21; static final int MAX_INTERVAL = 39; boolean isMarked(short d) { return dates[findPos(d)] == d; } static void computeFitInterval(int avg, int date1, int date2, int date, int out[]) { int len = date2 - date1; int fitLen = computeFitLen(avg, len); //assrt(date1 <= date && date <= date2); int dist = date - date1; int i = 1; while((i * fitLen + 49) / 100 <= dist) ++i; --i; int n = len * 100 / fitLen; int p1 = (i * fitLen + 49) / 100; int p2 = ((i + 1) * fitLen + 49) / 100; out[0] = i; out[1] = n; out[2] = date1 + p1; out[3] = p2 - p1; } static int computeFitLen(int avg, int len) { len *= 100; avg *= 100; int n = len / avg; if (n == 0) { return len; } int s1 = len / n; int s2 = len / (n + 1); return (s1 - avg < avg - s2) ? s1 : s2; } public int getDaysInfo(short date, Region outRegs[]) { int nRegs = 0; short endDate = (short)(date + DateCanvas.N_DAYS_1); int firstDay = 0; int ret; Region r; r = outRegs[nRegs++]; ret = doRegion(date, r); firstDay = ret + 1; //assrt(r.avg > 0); date += r.len + ret; while (date < endDate && nRegs < DateCanvas.N_REGS) { r = outRegs[nRegs++]; ret = doRegion(date, r); //assrt(ret == 0 && r.avg > 0); date += r.len; //System.out.println(r); } return firstDay; } int intervals1[] = new int[13], intervals2[] = new int[13]; int doBefore(int intervals[], int pos) { int intSize = 0; int len; for (int i = pos; i > 0 && intSize < 10; --i, ++intSize) { len = dates[i] - dates[i - 1]; if (len < M.MIN_INTERVAL || len > M.MAX_INTERVAL) { break; } intervals[intSize] = len; } return intSize; } int doAfter(int intervals[], int pos) { int intSize = 0; int len; for (int i = pos + 1; i < theSize - 1 && intSize < 10; ++i, ++intSize) { len = dates[i + 1] - dates[i]; if (len < M.MIN_INTERVAL || len > M.MAX_INTERVAL) { break; } intervals[intSize] = len; } return intSize; } int computeExpectedLen(int size, int intervals[]) { if (size == 0) { return 28; } if (size > 7) { size = 7; } computeAvgMinMax(size, intervals, avgMinMax); return avgMinMax[0]; } int doRegion(short date, Region outReg) { int pos = findPos(date); boolean hasBefore = (pos > 0), hasAfter = (pos < theSize -2); //System.out.println("begin " + beginDay + // " pos " + pos + " b " + hasBefore + "; " + hasAfter); if (!hasBefore && !hasAfter) { outReg.setNoInfo(DateCanvas.N_DAYS); return 0; } int size1 = 0, size2 = 0; if (hasBefore) { size1 = doBefore(intervals1, pos); } if (hasAfter) { size2 = doAfter(intervals2, pos); } int beg, distN; int intervals[] = null; int intSize; if (size1 <= size2) { intervals = intervals2; intSize = size2; } else { intervals = intervals1; intSize = size1; } int avg = computeExpectedLen(intSize, intervals); if (hasBefore && hasAfter) { computeFitInterval(avg, dates[pos], dates[pos + 1], date, out); int n1 = out[0], n = out[1], len = out[3]; beg = out[2]; distN = (n1 <= (n - n1 - 1)) ? n1 : (n - n1 -1); if (len < MIN_INTERVAL || len > MAX_INTERVAL) { outReg.setNoInfo(len); } else if (n == 1) { outReg.setExact(len); } else { outReg.set(len, intSize, intervals); } } else { if (hasBefore) { int dist = date - dates[pos]; //distN = dist / avg; beg = date - dist % avg; } else { int dist = dates[pos + 1] - date -1; //distN = dist / avg; beg = date - (avg - dist%avg - 1); } outReg.set(avg, intSize, intervals); } return beg - date; } }