Exemplos de uso


01. Configurando o DataSourceQuery para trazer uma coluna calculada com a média ponderada

02. Desabilitando a validação de permissões no DataSource

03. Desabilitando o filtro por classes filhas nas cláusulas "where" de classe

04. Especializando a classe de importação de filtros e colunas

05. Exibindo os níveis de uma dimensão

06. Exibindo níveis de dimensões do tipo classe

07. Filtrando os valores no DataSourceQuery por classe

08. Obtendo os filtros e colunas do DataSource

09. Substituindo valores nulos nas colunas do DataSource

10. Usando ExtraFilter no DataSource

11. Usando filtros com valor nulo no DataSourceQuery

12. Utilizando o método getIQueryFilters com filtros de tabelas de JOIN's




Configurando o DataSourceQuery para trazer uma coluna calculada com a média ponderada

O DataSourceQuery suporta o cálculo de médias ponderadas das suas colunas. Segue um exemplo.

Suponha que você tenha a seguinte definição de DataSource:

{
  includes: [
    -1897036629 //IQuery.ijs
  ],

  help: "Help do DataSource",

  availableFilters: [
    {
      classKey: -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED",
      options: {}
    }
  ],

  availableColumns: [
    {
      classKey:  -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED"
    }
  ],

  //Chamado após a construção do DataSource.
  onCreate: function (dataSource) {
    var flt = dataSource.filters.filter("PED_EMISSAO_START");
    flt.help = "Data de Emissão Inicial";
    var flt = dataSource.filters.filter("PED_EMISSAO_END");
    flt.help = "Data de Emissão Final";
  },

  onGetQuery: function (dataSource, filters, columns) {
    var flts = filters.getIQueryFilters("PED_*", "and");
    var cols = columns.getIQueryColumns("PED_*");
    var iquery = IQuery.from(-2008879000/* Pedidos ou Provisões */)
    .where(flts)
    .column(cols)
    return iquery.toSql();
  }
}

Na definição do DataSourceQuery, utilize o seguinte código:

{
  dataSourceKey: 40108470 /* /datasource/Exemplo 000.ids */,
  filters: [
    {name: "PED_EMISSAO_START"},
    {name: "PED_EMISSAO_END"}
  ],

  columns: [
    {name: "PED_RECURSO.NOME"},
    {name: "PED_QUANTIDADE", aggregate: "avg", weight: "PED_UNITARIO" }
  ],

  orderby: "PED_RECURSO.NOME"
}

No exemplo acima, será calculada a média ponderada do campo QUANTIDADE usando o campo UNITARIO como peso.


Desabilitando a validação de permissões no DataSource

Em alguns casos é necessário que uma consulta do DataSource não cheque as permissões de usuário.

A nível de iQuery esta validação é feita através dos métodos uncheckPermission e checkPermission. No DataSource esta característica pode ser implementada através de um filtro extra que indica se a validação deve ser feita.

{
  includes: [
    -1897036629 //IQuery.ijs
  ],

  help: "Help do DataSource",

  availableFilters: [
    {
      classKey: -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED"
    },
    {
      name: "PED_XVALIDAPERMISSAO",
      type: DataSourceTypes.BOOLEAN
    }
  ],

  availableColumns: [
    {
      classKey: -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED"
    }
  ],

  //Chamado durante a construção do DataSource.
  onCreate: function (dataSource) {
    flt = dataSource.filters.filter("PED_XVALIDAPERMISSAO");
    flt.defaultValue = true;
  },

  onGetQuery: function (dataSource, filters, columns) {
    var flts = filters.getIQueryFilters("PED_*;-PED_XVALIDAPERMISSAO", "and");
    var cols = columns.getIQueryColumns("PED_*;-PED_XVALIDAPERMISSAO");
    var iquery = new IQuery()
    var flt = filters.filter("PED_XVALIDAPERMISSAO")
    if(!flt.value){
      iquery.uncheckPermission();
    }

    iquery.from(-2008879000 /* Pedidos ou Provisões */ ).where(flts).column(cols);
    return iquery.toSql();
  }
}

Desabilitando o filtro por classes filhas nas cláusulas "where" de classe

O DataSource faz parte da biblioteca bdo, do Framework. Para utilizá-lo, você deve incluí-lo da seguinte forma:

__includeOnce('ufs:/bdo/datasource/DataSource.js');

A propriedade disableGetChildren permite definir se as chaves das classes filhas da classe informada no filtro do DataSource serão adicionadas à cláusula ‘WHERE’ da consulta SQL gerada. Caso não seja informada, o DataSource considerará seu valor padrão, que é false.

O exemplo abaixo pode ser executado no iDBSql para efeito de testes:

var dts = new DataSource(45116432);
var filters = dts.createFilters([
  {
    name: "PED_CLASSE",
    classKey: -189999895
  }
]);


var columns = dts.createColumns([
  {name: "PED_RECURSO.NOME"},
  {name: "PED_QUANTIDADE" }
]);

dts.getQuery(filters, columns);

Para desabilitar a busca nas classes filhas basta configurar a propriedade para ‘true’:

var dts = new DataSource(45116432);
var filters = dts.createFilters([
  {
    name: "PED_CLASSE",
    classKey: -189999895,
    disableGetChildren: true
  }
]);


var columns = dts.createColumns([
  {name: "PED_RECURSO.NOME"},
  {name: "PED_QUANTIDADE" }
]);

dts.getQuery( filters, columns );

Especializando a classe de importação de filtros e colunas

Para utilizar o módulo DataSource, ele deve ser incluído conforme exemplo abaixo:

var DataSource = require('@nginstack/datasource/lib/DataSource');

Os dados do sistema estão logicamente distribuidos em classes, e cada classe possui uma relação de hierarquia com outras classes. Cada classe possui um conjunto de campos próprios e campos herdados de suas classes “mães”. Esta característica nos levou a implementar o conceito de especialização da classe de importação de filtros e colunas. Vejamos um exemplo prático para melhor compreender:

Dada a seguinte definição de DataSource:

{
  includes: [
    -1897036629 //IQuery.ijs
  ],

  availableFilters: [
    {classKey: -2007890000 /* Pessoas */, prefix: "PES"}
  ],

  availableColumns: [
    {classKey: -2007890000 /* Pessoas */, prefix: "PES"}
  ],

  onGetQuery: function (dataSource, filters, columns) {
    var pessoas = IQuery.from( -2007890000 /* Pessoas */ );
    var filtros = filters.getIQueryFilters("PES_*");
    var colunas = columns.getIQueryColumns("PES_*");
    return pessoas.where(filtros).column(colunas).toSql();
},

  onMountDataSet: function (dataSource, ds) {
    return ds;
  }
}

Chamamos a atenção para as declarações:

availableFilters: [
    {classKey: -2007890000 /* Pessoas */, prefix: "PES"}
],

availableColumns: [
    {classKey: -2007890000 /* Pessoas */, prefix: "PES"}
],

Note que as colunas e filtros do DataSource acima são compostos pelos campos da classe Pessoas. Mas o se o usuário do DataSource tentar filtrar por um filtro cujo campo pertença a uma classe filha de Pessoas? Por exemplo, o campo PROFISSAO, que está definido em Clientes, mas não está definido em Pessoas. Como fazer, neste caso, para filtrar pelo campo PROFISSAO?

A solução é indicar ao DataSource que ele deve especializar a classe de importação de filtro e colunas, que no exemplo acima é Clientes. Isso pode ser feito da seguinte forma:

Usando a API do DataSource diretamente:

var options = {
  specializedFilters: [{classKey: -2007889000 /* Clientes */, prefix: "PES"}],
  specializedColumns: [{classKey: -2007889000 /* Clientes */, prefix: "PES"}]
}

var dts = new DataSource(-1897036071 /* /products/.../pessoa.ids */, options)
var filtros = dts.createFilters([{name: "PES_PROFISSAO", value: -1897054030 /* 0300 */}]);
var colunas = dts.createColumns([{name: "PES_PROFISSAO"}])

dts.getDataSet(filtros, colunas)

Usando a definição do DataSourceQuery:

{
  dataSource: {
    key: 45736580 /* /products/custom/.../Exemplo Pessoa.ids */,
    specializedFilters: [
      {classKey: -2007889000 /* clientes */, prefix: "PES"}
    ],
    specializedColumns: [
      {classKey: -2007889000 /* clientes */, prefix: "PES"}
    ]
  },

  filters: [
    {name: "PES_PROFISSAO", classKey: -1897054119 /* Profissões */}
  ],

  columns: [
    {name: "PES_NOME"}
  ],

  orderBy: "PES_NOME"
}

Note que dentro do objeto literal da propriedade dataSource há mais duas propriedades: specializedFilters e specializedColumns. Elas são responsáveis por realizar a especialização da classe de importação de filtros e colunas.


Exibindo os níveis de uma dimensão

Em modelagem multidimensional de banco de dados, existem os conceitos de dimensão e de níveis da dimensão. No DataSource temos algo semelhante. Ao usar um “.” (ponto final) no nome de uma coluna, você está exibindo um nível de uma dimensão.

Exemplo:

/* /products/.../dataSources/pedido.ids */
var dts = new DataSource(-1894442793);
var filtros = dts.createFilters([
  {name: "PED_EMISSAO_START", value: new Date()},
  {name: "PED_EMISSAO_END", value: new Date()}
]);

var colunas = dts.createColumns([
  {name: "PED_RECURSO.NOME"},
  {name: "PED_TOTAL", aggregate: "sum"}
]);

dts.getDataSet(filtros, colunas)

No exemplo acima NOME é um nível da dimensão RECURSO.

Por padrão os níveis extraídos das dimensões são campos inferidos de um campo lookup. Foi implementado no DataSource a possibilidade de se extrair outros tipos de níveis que não são campos de dimensões. Esses níveis só podem ser inferidos de campos que representam classes.


Exibindo níveis de dimensões do tipo classe

Vamos ao exemplo:

/* /products/.../dataSources/pedido.ids */
var dts = new DataSource(-1894442793);
var filtros = dts.createFilters([
  {name: "PED_EMISSAO_START", value: new Date()},
  {name: "PED_EMISSAO_END", value: new Date()}
]);

var colunas = dts.createColumns([
  {
    //O "!" indica que o nível URL não é um campo
    name: "PED_RECURSO.CLASSE!URL"
  },
  {
    name: "PED_TOTAL", aggregate: "sum"
  }
]);

dts.getDataSet(filtros, colunas)

Note que o uso de sinal de exclamação invés do ponto é para evitar ambiguidades entre níveis inferidos de valores de campos dos outros níveis.

O nível URL das dimensões de classes podem ser configurados por dois atributos das colunas do DataSource, depth e backDepth.

Exemplo do uso do atributo depth:

/* /products/.../dataSources/pedido.ids */
var dts = new DataSource(-1894442793);
var filtros = dts.createFilters([
  {name: "PED_EMISSAO_START", value: new Date()},
  {name: "PED_EMISSAO_END", value: new Date()}
]);

var colunas = dts.createColumns([
    {
        name: "PED_RECURSO.CLASSE!URL",
        depth: 2
    },
    {
        name: "PED_TOTAL", aggregate: "sum"
    }
]);

dts.getDataSet(filtros, colunas);
O resultado será:



Note que o atributo depth limitou o nível de exibição da hierarquia de classe para duas classes.

Exemplo do uso do atributo backDepth:

/* /products/.../dataSources/pedido.ids */
var dts = new DataSource(-1894442793);
var filtros = dts.createFilters([
    {name: "PED_EMISSAO_START", value: new Date()},
    {name: "PED_EMISSAO_END", value: new Date()}
]);

var colunas = dts.createColumns([
  {
    name: "PED_RECURSO.CLASSE!URL",
    backDepth: 2
  },
  {
    name: "PED_TOTAL", aggregate: "sum"
  }
]);

dts.getDataSet(filtros, colunas)

O atributo backDepth limita, de baixo para cima, a exibição da hierarquia de classes para duas classes.


Filtrando os valores no DataSourceQuery por classe

Os filtros do DataSourceQuery possuem a propriedade classKey, cuja finalidade é filtrar os valores informados em um campo classe. Dessa forma, o DataSourceQuery verifica se os valores no campo são a própria classe informada ou suas filhas.

Vejamos os exemplo abaixo:

Vamos usar a seguinte definição para o DataSource:

{
  includes: [
    -1897036629 //IQuery.ijs
  ],

  help: "Help do DataSource",

  availableFilters: [
    {
      classKey: -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED",
      options: {}
    }
  ],

  availableColumns: [
    {
      classKey:  -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED"
    }
  ],

  //Chamado após a construção do DataSource.
  onCreate: function (dataSource) {
    var flt = dataSource.filters.filter("PED_EMISSAO_START");
    flt.help = "Data de Emissão Inicial";
    var flt = dataSource.filters.filter("PED_EMISSAO_END");
    flt.help = "Data de Emissão Final";
  },

  onGetQuery: function (dataSource, filters, columns) {
    var flts = filters.getIQueryFilters("PED_*", "and")
    var cols = columns.getIQueryColumns("PED_*")
    var iquery = IQuery.from(-2008879000 /* Pedidos ou Provisões */)
    .where(flts)
    .column(cols)
    return iquery.toSql();
  }
}

Vamos usar a seguinte definição para o DataSourceQuery:

{
  /* /products/custom/.../datasource/Exemplo 000.ids */
  dataSourceKey: 40108470,
  filters: [
    {name: "PED_CLASSE", classKey: -1899998945 /* C Energia */ }
  ],
  columns: [
    {name: "PED_RECURSO.NOME"},
    {name: "PED_QUANTIDADE"},
    {name: "PED_UNITARIO"}
  ],
  orderBy: "PED_RECURSO.NOME"
}

Caso algum valor informado falhe na validação de classe, será gerado um erro informando qual dos valores fugiu à regra. Como exemplo, se executarmos o DataSourceQuery definido acima, obteremos o seguinte erro:

O valor ’-2008879000 (Pedidos ou Provisões)’ do filtro PED_CLASSE não é uma classe filha de ’-1899998945 (C Energia)’.


Obtendo os filtros e colunas do DataSource

É possível determinar, via código, quais colunas e filtros um DataSource está utilizando. Isso é útil quando a definição do DataSource usada para montar uma instância é selecionada dinamicamente. Também é possível recuperar esses filtros e colunas pelo seu prefixo.

Para isso, utilize os métodos DataSourceFilters.prototype.getIQueryFilters e DataSourceColumns.prototype.getIQueryColumns. Cada método recebe uma string que indica o que deve retornar.

Seguem alguns exemplos de uso desses métodos:

//Traz todas as colunas do IDS
dataSource.columns.getIQueryColumns("*");

//Traz todas a colunas cujo o prefixo é PED_
dataSource.columns.getIQueryColumns("PED_*");

//Traz todas as colunas cujo prefixo é PED_, com excessão das colunas
//PED_RECURSO e PED_EMISSAO
dataSource.columns.getIQueryColumns("PED_*,-PED_RECURSO,-PED_EMISSAO");

Substituindo valores nulos nas colunas do DataSource

Em alguns casos é necessário usar valores padrão em colunas vazias. Esses valores são definidos através da propriedade coalesceValue. Veja como usar essa funcionalidade na API de DataSource.

Na definição do DataSource:

{
  includes: [
    -1897036629 //IQuery.ijs
  ],

  help: "Help do DataSource",

  availableFilters: [
    {
      classKey: -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED",
      options: {}
    }
  ],

  availableColumns: [
    {name: "PED_RECURSO.NOME", alias: "Produto"}, //Campo de agrupamento
    {name: "PED_RECURSO.CODIGO", alias: "Codigo", coalesceValue: "NÃO INFORMADO"},

    //Campo totalizador
    {name: "PED_QUANTIDADE", aggregate: "sum", alias: "Qtde"},

    //Campo totalizador
    {name: "PED_TOTAL", aggregate: "sum", alias: "Vlr"},
    {name: "PED_EMISSAO", pivot: true} //Campo pivot horizontal
  ],

  //Chamado durante a construção do DataSource.
  onCreate: function (dataSource) {
    var flt = dataSource.filters.filter("PED_EMISSAO_START");
    flt.help = "Data de Emissão Inicial";
    var flt = dataSource.filters.filter("PED_EMISSAO_END");
    flt.help = "Data de Emissão Final";
  },

  onGetQuery: function (dataSource, filters, columns) {
    var flts = filters.getIQueryFilters("PED_*", "and");
    var cols = columns.getIQueryColumns("PED_*");
    var iquery = IQuery.from(-2008879000 /* Pedidos ou Provisões */);
    .where(flts)
    .column(cols)
    return iquery.toSql();
  }
}

Usando ExtraFilter no DataSource

ExtraFilter é um recurso presente na API de segurança do Engine que filtra os registros de um DataSet de acordo com os valores especificados no cadastro de permissões e no cadastro de usuários.

Um exemplo do uso do recurso de ExtraFilter é filtrar pedidos pelo estabelecimento. Ou seja, apenas usuários do estabelecimento X poderão ver os pedidos do estabelecimento X. Vejamos como é feita a configuração de um ExtraFilter no sistema para filtrar registros de um DataSet por estabelecimento.

Na tabela iPermission existe um campo chamado ESTABELECI onde são informados os estabelecimentos de um usuário ou grupo de usuários. Após configurar quais serão os usuário que podem visualizar os registros vinculados a um ou mais estabelecimentos, é necessário informar quais serão as classes que sofrerão esse controle de permissão. Por exemplo, a classe “/Dados/Transacionais/Operacoes/Pedidos ou Provisoes” sofre o controle de permissão de ExtraFilter, pois na definição da classe temos a seguinte declaração:

this.permissionExtraFilter.push(["ESTABELECI", "ESTABELECI"]);

Uma vez configurado o Extra Filter, as consultas realizadas através do objeto iQuery serão afetadas por ele.

Seguindo o exemplo da classe “Pedidos e Provisões”, e dada a seguinte definição de DataSource:

{
  includes: [-1897036629], // IQuery.ijs
  help: "Help do DataSource",

  availableFilters: [
    {
      classKey: -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED",
      options: {onlyVisible: true}
    }
  ],

  availableColumns: [
    {
      classKey: -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED",
      options: {onlyVisible: true}
    }
  ],

  onGetQuery: function (dataSource, filters, columns) {
    var flts = filters.getIQueryFilters("PED_*", "and");
    var cols = columns.getIQueryColumns("PED_*");
    var iquery = IQuery.from(-2008879000 /* Pedidos ou Provisões */)
      .where(flts)
      .column(cols);

    // Para usar Extra Filter é necessário retornar uma instância de um
    // iQuery COM COLUNAS, ao invés de retornar um String com SQL.
    return iquery;  
  }
}

E sua invocação:

var dts = new DataSource(45684180) /* /products/.../datasource/Exemplo 002.ids */
var filtros = dts.createFilters(
  [
    {name: "PED_EMISSAO_START", value: new Date(2011,2,3)},
    {name: "PED_EMISSAO_END", value: new Date(2011,2,3)}
  ]
);

var colunas = dts.createColumns(
  [
    {name: "PED_CHAVE"},
    {name: "PED_CHCRIACAO"},
    {name: "PED_ESTABELECI.NOME", alias: "ESTABELECIMENTO"},
    {name: "PED_CLASSE.NOME", alias: "CLASSE"}
  ]
);

dts.getDataSet(filtros, colunas);

O DataSet retornado pela consulta será afetado pelos Extra Filters configurados.


Usando filtros com valor nulo no DataSourceQuery

É possível fazer consultas com DataSourceQuery cujos filtros selecionem apenas resultados com valores nulos. Para isso, configuramos o operador do filtro com o operador de igualdade e ajustamos o valor do filtro para nulo. Segue abaixo um exemplo.

Dada a seguinte definição do DataSource:

{
  includes: [
    -1897036629 //IQuery.ijs
  ],

  help: "Help do DataSource",

  availableFilters: [
    {
      classKey: -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED",
      options: {}
    }
  ],

  availableColumns: [
    {
      classKey:  -2008879000 /* Pedidos ou Provisões */,
      prefix: "PED"
    }
  ],

  //Chamado após a construção do DataSource.
  onCreate: function (dataSource) {
    var flt = dataSource.filters.filter("PED_EMISSAO_START");
    flt.help = "Data de Emissão Inicial";
    var flt = dataSource.filters.filter("PED_EMISSAO_END");
    flt.help = "Data de Emissão Final";
  },

  onGetQuery: function (dataSource, filters, columns) {
    var flts = filters.getIQueryFilters("PED_*", "and");
    var cols = columns.getIQueryColumns("PED_*");
    var iquery = IQuery.from( -2008879000 /* Pedidos ou Provisões */)
    .where(flts)
    .column(cols)
    return iquery.toSql();
  }
}

Na definição do DataSourceQuery, usamos o operador nulo da seguinte forma:

{
    /* /products/custom/.../datasource/Exemplo 000.ids */
    dataSourceKey: 40108470,
    filters: [
      {name: "PED_EMISSAO_START"},
      {name: "PED_EMISSAO_END"},
      {name: "PED_UNITARIO", operator: "=", value: null}
    ],
    columns: [
      {name: "PED_RECURSO.NOME"}
    ],
    orderby: "PED_RECURSO.NOME"
}

Utilizando o método getIQueryFilters com filtros de tabelas de JOIN's

O IQuery possui a funcionalidade de gerar Join de forma implícita através do recurso do dimensionamento. É possível também criar Joins explícitos através dos métodos innerJoin e outterJoin.

O método DataSource.prototype.getIQueryFilters possui a possibilidade de gerar filtros do iQuery sobre as tabelas derivadas de JOINs. Segue, abaixo, um exemplo de uso.

Vamos usar a seguinte definição para o DataSource:

{
  includes: [
    //IQuery.ijs
    -1897036629,
    //FiltroCalculadoDePedido.ijs
    -1894442718
  ],

  availableFilters: [
    {classKey: -2008879000 /* Pedidos ou Provisões */, prefix: "PED"},
    {classKey: -2008889000 /* Títulos*/, prefix: "TIT"}
  ],

  availableColumns: [
    {classKey: -2008879000 /* Pedidos ou Provisões */, prefix: "PED"},
    {classKey: -2008889000 /* Títulos*/, prefix: "TIT" }
  ],

  onGetQuery: function (dataSource, filters, columns) {
    var titulos = IQuery.from( -2008889000 /* Titulos */)

    var iquery = IQuery.from( -2008879000 /* Pedidos ou Provisões */ )
      .leftOuterJoin(titulos).on({
        field: "CHCRIACAO",
        voperator: "=", 
        value: titulos.column("CHCRIACAO")
      })
      .where([
        filters.getIQueryFilters("TIT_*", "and", titulos), 
        filters.getIQueryFilters("PED_*", "and", pedidos)
      ])
      .column(columns.getIQueryColumns("*"));

    return iquery.toSql();
  }
}

Executamos o DataSource da seguinte forma:

/* /products/custom/.../datasource/Teste.ids */
var dts = new DataSource(40108823);
var filters = dts.createFilters([
  {name:"TIT_EMISSAO_START", operator: ">=", value: new Date()},
  {name:"TIT_EMISSAO_END", operator: "<=", value: new Date()}
]);

var columns = dts.createColumns([{name: "PED_CHAVE"}]);

dts.getQuery(filters, columns);

Esta é a consulta SQL gerada pelo exemplo acima:

select PEDIDO2.CHAVE as PED_CHAVE
from PEDIDO PEDIDO2
    left outer join TITULO TITULO1 on (
        PEDIDO2.CHCRIACAO = TITULO1.CHCRIACAO)
where (
    TITULO1.EMISSAO >= '02/02/2010' and 
    TITULO1.EMISSAO <= '02/02/2010')
    and ((
        TITULO1.CLASSE is null or TITULO1.CLASSE in (-2008889000,
        -2008888890,-2008888790,-1899998525,-1899998524,-1899998370,
        -1899998369,-1899998367, -1899933806,51518,51519,35337524, 35337539,35337623,35337638)))
    and (
        PEDIDO2.CLASSE in (-2008879000,-1899999820,-1899999819,-1899999818,
        -1899999817,-1899999814,-1899999812,-1899999754,-1899999752,
        -1899999480,-1899999479,-1899999478,-1899999477,-1899999476,
        -1899999475,-1899999474,-1899999473,-1899999472,-1899999471,
        -1899999470,-1899999469,-1899999468,-1899999467,-1899999466,
        -1899999465,-1899999383,-1899999300,-1899999299,-1899999295,
        -1899999289,-1899999288,-1899999287,-1899999286,-1899999285,
        -1899999278,-1899999274,-1899999273,-1899999272,-1899999271,
        -1899999269,-1899999104,-1899999103,-1899998951,-1899998950,
        -1899998949,-1899998948,-1899998947,-1899998946,-1899998945,
        -1899998944,-1899998943,-1899998942,-1899998941,-1899998940,
        -1899998939,-1899998925,-1899998923,-1899998922,-1899998921,
        -1899998920,-1899998919,-1899998918,-1899998917,-1899998916,
        -1899998915,-1899998914,-1899998913,-1899998912,-1899998901,
        -1899998382,-1898190299,-1898190298,-1898190297,-1898190136,
        -1898187711,-1898187709,-1898187600,-1898187552,-1898187551,
        -1897054192,-1897054190,-1897054189,-1897054188,-1897054187,
        -1897054186,-1897054185,-1897054184,-1897054183,-1897054182,
        -1897054181,-1897054180,-1897054179,-1897054178,-1897053396,
        -1897053239,-1897053225,-1897053120,-1897053119,-1897053118,
        -1897053117,-1897052491,-1897052280,-1897052279,-1897052278,
        -1897052277,-1897052276,-1897052275,-1897052154,-1897050841,
        -1897048773,-1897048772,-1897048771,-1897048743,-1897048742,
        -1897048700,-1897048154,-1897048153,-1897036337,-1896648405,
        -1896648404,-1896648282,-1896648274,-1896648273,-1896648229,
        -1896648217,-1896648208,-1896648207,-1896648206,-1896648197,
        -1896648195,-1896648194,-1896648193,-1896648190,-1896648103,
        -1896647767,-1896646976,-1896646955,-1896646934,-1896646914,
        -1895947335,-1895947334,-1895947333,-1895946241,-1895836214,
        -1894743941,-1894543798,-1894543796,-1894543717,-1894443036,
        -1894442985,1990,12682,34011,34015,34017,34018,34131,34440,38715,
        38754,42351,65941,79382,162703,177781,183608,183609,186447,188473,
        2025108,5320945,6024675,12060574,35337554,35337574,35915925,
        35915943,35915961,35915979,35915997,36762192,36762211,37428549,
        37859429,38292040,38530826,40035178,40035242))