content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to you under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.calcite.materialize;
  
  import org.apache.calcite.util.Util;
  
  import com.google.common.collect.ImmutableList;
  import com.google.common.collect.ImmutableMap;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  import org.pentaho.aggdes.algorithm.Algorithm;
  import org.pentaho.aggdes.algorithm.Progress;
  import org.pentaho.aggdes.algorithm.Result;
  import org.pentaho.aggdes.algorithm.impl.MonteCarloAlgorithm;
  import org.pentaho.aggdes.algorithm.util.ArgumentUtils;
  import org.pentaho.aggdes.model.Aggregate;
  import org.pentaho.aggdes.model.Attribute;
  import org.pentaho.aggdes.model.Dialect;
  import org.pentaho.aggdes.model.Dimension;
  import org.pentaho.aggdes.model.Measure;
  import org.pentaho.aggdes.model.Parameter;
  import org.pentaho.aggdes.model.Schema;
  import org.pentaho.aggdes.model.StatisticsProvider;
  import org.pentaho.aggdes.model.Table;
  
  import java.io.PrintWriter;
  import java.util.List;
  
  /**
   * Algorithm that suggests a set of initial tiles (materialized aggregate views)
   * for a given lattice.
   */
  public class TileSuggester {
    private final Lattice lattice;
  
    public TileSuggester(Lattice lattice) {
      this.lattice = lattice;
    }
  
    public Iterable<? extends Lattice.Tile> tiles() {
      final Algorithm algorithm = new MonteCarloAlgorithm();
      final PrintWriter pw = Util.printWriter(System.out);
      final Progress progress = new ArgumentUtils.TextProgress(pw);
      final StatisticsProvider statisticsProvider =
          new StatisticsProviderImpl(lattice);
      final double f = statisticsProvider.getFactRowCount();
      final ImmutableMap.Builder<Parameter, Object> map = ImmutableMap.builder();
      if (lattice.algorithmMaxMillis >= 0) {
        map.put(Algorithm.ParameterEnum.TIME_LIMIT_SECONDS,
            Math.max(1, (int) (lattice.algorithmMaxMillis / 1000L)));
      }
      map.put(Algorithm.ParameterEnum.AGGREGATE_LIMIT, 3);
      map.put(Algorithm.ParameterEnum.COST_LIMIT, f * 5d);
      final SchemaImpl schema = new SchemaImpl(lattice, statisticsProvider);
      final Result result = algorithm.run(schema, map.build(), progress);
      final ImmutableList.Builder<Lattice.Tile> tiles = ImmutableList.builder();
      for (Aggregate aggregate : result.getAggregates()) {
        tiles.add(toTile(aggregate));
      }
      return tiles.build();
    }
  
    private Lattice.Tile toTile(Aggregate aggregate) {
      final Lattice.TileBuilder tileBuilder = new Lattice.TileBuilder();
      for (Lattice.Measure measure : lattice.defaultMeasures) {
        tileBuilder.addMeasure(measure);
      }
      for (Attribute attribute : aggregate.getAttributes()) {
        tileBuilder.addDimension(((AttributeImpl) attribute).column);
      }
      return tileBuilder.build();
    }
  
    /** Implementation of {@link Schema} based on a {@link Lattice}. */
    private static class SchemaImpl implements Schema {
      private final StatisticsProvider statisticsProvider;
      private final TableImpl table;
      private final ImmutableList<AttributeImpl> attributes;
  
      SchemaImpl(Lattice lattice, StatisticsProvider statisticsProvider) {
        this.statisticsProvider = statisticsProvider;
        this.table = new TableImpl();
        final ImmutableList.Builder<AttributeImpl> attributeBuilder =
            ImmutableList.builder();
        for (Lattice.Column column : lattice.columns) {
          attributeBuilder.add(new AttributeImpl(column, table));
        }
        this.attributes = attributeBuilder.build();
      }
  
      @Override public List<? extends Table> getTables() {
        return ImmutableList.of(table);
      }
  
      @Override public List<Measure> getMeasures() {
        throw new UnsupportedOperationException();
      }
  
      @Override public List<? extends Dimension> getDimensions() {
        throw new UnsupportedOperationException();
      }
  
      @Override public List<? extends Attribute> getAttributes() {
        return attributes;
      }
  
      @Override public StatisticsProvider getStatisticsProvider() {
        return statisticsProvider;
      }
  
      @Override public Dialect getDialect() {
        throw new UnsupportedOperationException();
      }
  
      @Override public String generateAggregateSql(Aggregate aggregate,
          List<String> columnNameList) {
        throw new UnsupportedOperationException();
      }
    }
  
    /** Implementation of {@link Table} based on a {@link Lattice}.
     * There is only one table (in this sense of table) in a lattice.
     * The algorithm does not really care about tables. */
    private static class TableImpl implements Table {
      @Override public String getLabel() {
        return "TABLE";
      }
  
      @Override public @Nullable Table getParent() {
        return null;
      }
    }
  
    /** Implementation of {@link Attribute} based on a {@link Lattice.Column}. */
    private static class AttributeImpl implements Attribute {
      private final Lattice.Column column;
      private final TableImpl table;
  
      private AttributeImpl(Lattice.Column column, TableImpl table) {
        this.column = column;
        this.table = table;
      }
  
      @Override public String toString() {
        return getLabel();
      }
  
      @Override public String getLabel() {
        return column.alias;
      }
  
      @Override public Table getTable() {
        return table;
      }
  
      @Override public double estimateSpace() {
        return 0;
      }
  
      @Override public @Nullable String getCandidateColumnName() {
        return null;
      }
  
      @Override public @Nullable String getDatatype(Dialect dialect) {
        return null;
      }
  
      @Override public List<Attribute> getAncestorAttributes() {
        return ImmutableList.of();
      }
    }
  
    /** Implementation of {@link org.pentaho.aggdes.model.StatisticsProvider}
     * that asks the lattice. */
    private static class StatisticsProviderImpl implements StatisticsProvider {
      private final Lattice lattice;
  
      StatisticsProviderImpl(Lattice lattice) {
        this.lattice = lattice;
      }
  
      @Override public double getFactRowCount() {
        return lattice.getFactRowCount();
      }
  
      @Override public double getRowCount(List<Attribute> attributes) {
        return lattice.getRowCount(
            Util.transform(attributes, input -> ((AttributeImpl) input).column));
      }
  
      @Override public double getSpace(List<Attribute> attributes) {
        return attributes.size();
      }
  
      @Override public double getLoadTime(List<Attribute> attributes) {
        return getSpace(attributes) * getRowCount(attributes);
      }
    }
  }
  