001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2005-2005 Julian Hyde
008// Copyright (C) 2005-2009 Pentaho
009// All Rights Reserved.
010*/
011package mondrian.rolap;
012
013import mondrian.olap.Axis;
014
015/**
016 * Modulos implementations encapsulate algorithms to map between integral
017 * ordinals and position arrays. There are particular implementations for
018 * the most likely cases where the number of axes is 1, 2 and 3
019 * as well as a general implementation.
020 * <p>
021 * Suppose the result is 4 x 3 x 2, then modulo = {1, 4, 12, 24}.
022 *
023 * <p>
024 * Then the ordinal of cell (3, 2, 1)
025 * <p><blockquote><pre>
026 *  = (modulo[0] * 3) + (modulo[1] * 2) + (modulo[2] * 1)
027 *  = (1 * 3) + (4 * 2) + (12 * 1)
028 *  = 23
029 * </pre></blockquote><p>
030 * <p>
031 * Reverse calculation:
032 * <p><blockquote><pre>
033 * p[0] = (23 % modulo[1]) / modulo[0] = (23 % 4) / 1 = 3
034 * p[1] = (23 % modulo[2]) / modulo[1] = (23 % 12) / 4 = 2
035 * p[2] = (23 % modulo[3]) / modulo[2] = (23 % 24) / 12 = 1
036 * </pre></blockquote><p>
037 *
038 * @author jhyde
039 */
040public interface Modulos {
041    public class Generator {
042        public static Modulos create(Axis[] axes) {
043            switch (axes.length) {
044            case 0:
045                return new Modulos.Zero(axes);
046            case 1:
047                return new Modulos.One(axes);
048            case 2:
049                return new Modulos.Two(axes);
050            case 3:
051                return new Modulos.Three(axes);
052            default:
053                return new Modulos.Many(axes);
054            }
055        }
056        // Used for testing only
057        public static Modulos createMany(Axis[] axes) {
058            return new Modulos.Many(axes);
059        }
060        public static Modulos createMany(int[] lengths) {
061            return new Modulos.Many(lengths);
062        }
063    }
064    public abstract class Base implements Modulos {
065        protected final int[] modulos;
066        protected Base(final Axis[] axes) {
067            this.modulos = new int[axes.length + 1];
068            this.modulos[0] = 1;
069        }
070        protected Base(final int[] lengths) {
071            this.modulos = new int[lengths.length + 1];
072            this.modulos[0] = 1;
073        }
074        public abstract int[] getCellPos(int cellOrdinal);
075        public abstract int getCellOrdinal(int[] pos);
076
077        public String toString() {
078            StringBuilder buf = new StringBuilder();
079            buf.append('(');
080            for (int i = 0; i < modulos.length; i++) {
081                if (i > 0) {
082                    buf.append(',');
083                }
084                buf.append(modulos[i]);
085            }
086            buf.append(')');
087            return buf.toString();
088        }
089    }
090    public class Zero extends Base {
091        private static final int[] pos = new int[0];
092        private Zero(final Axis[] axes) {
093            super(axes);
094        }
095        public final int[] getCellPos(final int cellOrdinal) {
096            return pos;
097        }
098        public final int getCellOrdinal(final int[] pos) {
099            return 0;
100        }
101    }
102    public class One extends Base {
103        private One(final Axis[] axes) {
104            super(axes);
105
106            this.modulos[1] = axes[0].getPositions().size();
107        }
108        public final int[] getCellPos(final int cellOrdinal) {
109            return new int[] {
110                (cellOrdinal % this.modulos[1])
111            };
112        }
113        public final int getCellOrdinal(final int[] pos) {
114            return (pos[0] * modulos[0]);
115        }
116    }
117    public class Two extends Base {
118        private Two(final Axis[] axes) {
119            super(axes);
120
121            int modulo = axes[0].getPositions().size();
122            this.modulos[1] = modulo;
123            modulo *= axes[1].getPositions().size();
124            this.modulos[2] = modulo;
125        }
126        public final int[] getCellPos(final int cellOrdinal) {
127            final int[] modulos = this.modulos;
128            return new int[] {
129                (cellOrdinal % modulos[1]),
130                (cellOrdinal % modulos[2]) / modulos[1]
131            };
132        }
133        public final int getCellOrdinal(final int[] pos) {
134            final int[] modulos = this.modulos;
135            return (pos[0] * modulos[0])
136                + (pos[1] * modulos[1]);
137        }
138    }
139    public class Three extends Base {
140        private Three(final Axis[] axes) {
141            super(axes);
142
143            int modulo = axes[0].getPositions().size();
144            this.modulos[1] = modulo;
145            modulo *= axes[1].getPositions().size();
146            this.modulos[2] = modulo;
147            modulo *= axes[2].getPositions().size();
148            this.modulos[3] = modulo;
149        }
150        public final int[] getCellPos(final int cellOrdinal) {
151            final int[] modulos = this.modulos;
152            return new int[] {
153                (cellOrdinal % modulos[1]),
154                (cellOrdinal % modulos[2]) / modulos[1],
155                (cellOrdinal % modulos[3]) / modulos[2]
156            };
157        }
158        public final int getCellOrdinal(final int[] pos) {
159            final int[] modulos = this.modulos;
160            return (pos[0] * modulos[0])
161                + (pos[1] * modulos[1])
162                + (pos[2] * modulos[2]);
163        }
164    }
165    public class Many extends Base {
166        private Many(final Axis[] axes) {
167            super(axes);
168
169            int modulo = 1;
170            for (int i = 0; i < axes.length; i++) {
171                modulo *= axes[i].getPositions().size();
172                this.modulos[i + 1] = modulo;
173            }
174        }
175        private Many(final int[] lengths) {
176            super(lengths);
177
178            int modulo = 1;
179            for (int i = 0; i < lengths.length; i++) {
180                modulo *= lengths[i];
181                this.modulos[i + 1] = modulo;
182            }
183        }
184        public int[] getCellPos(final int cellOrdinal) {
185            final int[] modulos = this.modulos;
186            final int size = modulos.length - 1;
187            final int[] pos = new int[size];
188            for (int i = 0; i < size; i++) {
189                pos[i] = (cellOrdinal % modulos[i + 1]) / modulos[i];
190            }
191            return pos;
192        }
193        public int getCellOrdinal(final int[] pos) {
194            final int[] modulos = this.modulos;
195            final int size = modulos.length - 1;
196            int ordinal = 0;
197            for (int i = 0; i < size; i++) {
198                ordinal += pos[i] * modulos[i];
199            }
200            return ordinal;
201        }
202    }
203
204    /**
205     * Converts a cell ordinal to a set of cell coordinates. Converse of
206     * {@link #getCellOrdinal}. For example, if this result is 10 x 10 x 10,
207     * then cell ordinal 537 has coordinates (5, 3, 7).
208     *
209     * @param cellOrdinal Cell ordinal
210     * @return cell coordinates
211     */
212    int[] getCellPos(int cellOrdinal);
213
214    /**
215     * Converts a set of cell coordinates to a cell ordinal. Converse of
216     * {@link #getCellPos}.
217     *
218     * @param pos Cell coordinates
219     * @return cell ordinal
220     */
221    int getCellOrdinal(int[] pos);
222}
223
224// End Modulos.java